summaryrefslogtreecommitdiff
path: root/ntpd
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2014-12-02 09:01:21 +0000
committer <>2014-12-04 16:11:25 +0000
commitbdab5265fcbf3f472545073a23f8999749a9f2b9 (patch)
treec6018dd03dea906f8f1fb5f105f05b71a7dc250a /ntpd
downloadntp-bdab5265fcbf3f472545073a23f8999749a9f2b9.tar.gz
Imported from /home/lorry/working-area/delta_ntp/ntp-dev-4.2.7p482.tar.gz.ntp-dev-4.2.7p482
Diffstat (limited to 'ntpd')
-rw-r--r--ntpd/Makefile.am438
-rw-r--r--ntpd/Makefile.in1790
-rw-r--r--ntpd/check_y2k.c626
-rw-r--r--ntpd/cmd_args.c202
-rw-r--r--ntpd/complete.conf.in67
-rw-r--r--ntpd/declcond.h21
-rw-r--r--ntpd/invoke-ntp.conf.menu1
-rw-r--r--ntpd/invoke-ntp.conf.texi2634
-rw-r--r--ntpd/invoke-ntp.keys.menu1
-rw-r--r--ntpd/invoke-ntp.keys.texi125
-rw-r--r--ntpd/invoke-ntpd.menu1
-rw-r--r--ntpd/invoke-ntpd.texi702
-rw-r--r--ntpd/jupiter.h255
-rw-r--r--ntpd/keyword-gen-utd1
-rw-r--r--ntpd/keyword-gen.c752
-rw-r--r--ntpd/ntp.conf.5man3004
-rw-r--r--ntpd/ntp.conf.5mdoc2808
-rw-r--r--ntpd/ntp.conf.def2795
-rw-r--r--ntpd/ntp.conf.html2625
-rw-r--r--ntpd/ntp.conf.man.in3004
-rw-r--r--ntpd/ntp.conf.mdoc.in2808
-rw-r--r--ntpd/ntp.conf.texi49
-rw-r--r--ntpd/ntp.keys.5man173
-rw-r--r--ntpd/ntp.keys.5mdoc158
-rw-r--r--ntpd/ntp.keys.def152
-rw-r--r--ntpd/ntp.keys.html200
-rw-r--r--ntpd/ntp.keys.man.in173
-rw-r--r--ntpd/ntp.keys.mdoc.in158
-rw-r--r--ntpd/ntp.keys.texi49
-rw-r--r--ntpd/ntp_config.c4957
-rw-r--r--ntpd/ntp_control.c4942
-rw-r--r--ntpd/ntp_crypto.c3987
-rw-r--r--ntpd/ntp_filegen.c645
-rw-r--r--ntpd/ntp_io.c4688
-rw-r--r--ntpd/ntp_keyword.h1032
-rw-r--r--ntpd/ntp_leapsec.c1009
-rw-r--r--ntpd/ntp_leapsec.h218
-rw-r--r--ntpd/ntp_loopfilter.c1224
-rw-r--r--ntpd/ntp_monitor.c496
-rw-r--r--ntpd/ntp_parser.c3612
-rw-r--r--ntpd/ntp_parser.h459
-rw-r--r--ntpd/ntp_parser.y1638
-rw-r--r--ntpd/ntp_peer.c1038
-rw-r--r--ntpd/ntp_prio_q.c238
-rw-r--r--ntpd/ntp_proto.c4168
-rw-r--r--ntpd/ntp_refclock.c1332
-rw-r--r--ntpd/ntp_request.c2670
-rw-r--r--ntpd/ntp_restrict.c669
-rw-r--r--ntpd/ntp_scanner.c755
-rw-r--r--ntpd/ntp_scanner.h130
-rw-r--r--ntpd/ntp_signd.c239
-rw-r--r--ntpd/ntp_timer.c570
-rw-r--r--ntpd/ntp_util.c1031
-rw-r--r--ntpd/ntpd-opts.c1952
-rw-r--r--ntpd/ntpd-opts.def611
-rw-r--r--ntpd/ntpd-opts.h458
-rw-r--r--ntpd/ntpd.1ntpdman987
-rw-r--r--ntpd/ntpd.1ntpdmdoc891
-rw-r--r--ntpd/ntpd.c1478
-rw-r--r--ntpd/ntpd.html995
-rw-r--r--ntpd/ntpd.man.in987
-rw-r--r--ntpd/ntpd.mdoc.in891
-rw-r--r--ntpd/ntpd.texi113
-rw-r--r--ntpd/ntpdbase-opts.def463
-rw-r--r--ntpd/ntpsim.c657
-rw-r--r--ntpd/ppsapi_timepps.h26
-rw-r--r--ntpd/refclock_acts.c911
-rw-r--r--ntpd/refclock_arbiter.c479
-rw-r--r--ntpd/refclock_arc.c1587
-rw-r--r--ntpd/refclock_as2201.c388
-rw-r--r--ntpd/refclock_atom.c239
-rw-r--r--ntpd/refclock_bancomm.c548
-rw-r--r--ntpd/refclock_chronolog.c343
-rw-r--r--ntpd/refclock_chu.c1683
-rw-r--r--ntpd/refclock_conf.c326
-rw-r--r--ntpd/refclock_datum.c782
-rw-r--r--ntpd/refclock_dumbclock.c383
-rw-r--r--ntpd/refclock_fg.c335
-rw-r--r--ntpd/refclock_gpsdjson.c1341
-rw-r--r--ntpd/refclock_gpsvme.c253
-rw-r--r--ntpd/refclock_heath.c450
-rw-r--r--ntpd/refclock_hopfpci.c258
-rw-r--r--ntpd/refclock_hopfser.c372
-rw-r--r--ntpd/refclock_hpgps.c623
-rw-r--r--ntpd/refclock_irig.c1045
-rw-r--r--ntpd/refclock_jjy.c1743
-rw-r--r--ntpd/refclock_jupiter.c1121
-rw-r--r--ntpd/refclock_leitch.c600
-rw-r--r--ntpd/refclock_local.c230
-rw-r--r--ntpd/refclock_msfees.c1450
-rw-r--r--ntpd/refclock_mx4200.c1631
-rw-r--r--ntpd/refclock_neoclock4x.c1124
-rw-r--r--ntpd/refclock_nmea.c1956
-rw-r--r--ntpd/refclock_oncore.c4083
-rw-r--r--ntpd/refclock_palisade.c1253
-rw-r--r--ntpd/refclock_palisade.h201
-rw-r--r--ntpd/refclock_parse.c6204
-rw-r--r--ntpd/refclock_pcf.c227
-rw-r--r--ntpd/refclock_pst.c318
-rw-r--r--ntpd/refclock_ripencc.c5255
-rw-r--r--ntpd/refclock_shm.c571
-rw-r--r--ntpd/refclock_tpro.c208
-rw-r--r--ntpd/refclock_true.c986
-rw-r--r--ntpd/refclock_tsyncpci.c912
-rw-r--r--ntpd/refclock_tt560.c270
-rw-r--r--ntpd/refclock_ulink.c568
-rw-r--r--ntpd/refclock_wwv.c2709
-rw-r--r--ntpd/refclock_wwvb.c603
-rw-r--r--ntpd/refclock_zyfer.c331
109 files changed, 124928 insertions, 0 deletions
diff --git a/ntpd/Makefile.am b/ntpd/Makefile.am
new file mode 100644
index 0000000..b3af527
--- /dev/null
+++ b/ntpd/Makefile.am
@@ -0,0 +1,438 @@
+NULL=
+
+bin_PROGRAMS= $(NTPD_DB) $(NTPDSIM_DB)
+libexec_PROGRAMS= $(NTPD_DL) $(NTPDSIM_DL)
+sbin_PROGRAMS= $(NTPD_DS) $(NTPDSIM_DS)
+
+noinst_LIBRARIES= libntpd.a
+
+AM_CFLAGS = $(CFLAGS_NTP)
+
+AM_CPPFLAGS = $(NTP_INCS)
+AM_CPPFLAGS += $(LIBOPTS_CFLAGS)
+AM_CPPFLAGS += $(CPPFLAGS_NTP)
+
+AM_LDFLAGS = $(LDFLAGS_NTP)
+
+# LDADD might need RESLIB and ADJLIB.
+LDADD = version.o libntpd.a $(LIBPARSE)
+AM_YFLAGS = -d -t -r all
+
+if SAVECONFIG_ENABLED
+if NTP_CROSSCOMPILE
+CHECK_SAVECONFIG=
+else
+CHECK_SAVECONFIG= check-saveconfig
+endif
+else !SAVECONFIG_ENABLED
+CHECK_SAVECONFIG=
+endif
+
+#
+# VPHACK and VPHACK_AFTER are enabled on non-GNU makes (such as
+# BSD make) to work around issues specific to compiling
+# ntp_parser.y into ntp_parser.h and ntp_parser.c in a VPATH
+# configuration where we would like (for a change) the output
+# files ntp_parser.[ch] to be placed in the source directory,
+# as opposed to the build directory. This allows a single
+# host of a flock configured with Bison to update ntp_parser.[ch]
+# used by the rest.
+#
+
+if VPATH_HACK
+VPHACK= vphack
+VPHACK_AFTER= vphack_after
+else
+VPHACK=
+VPHACK_AFTER=
+endif
+
+vphack:
+ test -e ntp_parser.c || ln -s $(srcdir)/ntp_parser.c .
+ test -e ntp_parser.h || ln -s $(srcdir)/ntp_parser.h .
+
+#
+# ylwrap script which invokes Bison replaces ntp_parser.h
+# symlink with the updated file, when ntp_parser.h changes.
+# vphack_after detects this and copies the updated file to srcdir
+# and re-creates the ntp_parser.h symlink in its place.
+#
+
+vphack_after:
+ test -L ntp_parser.h || ( \
+ mv ntp_parser.h $(srcdir)/ntp_parser.h && \
+ ln -s $(srcdir)/ntp_parser.h . \
+ )
+
+# BUILT_SOURCES which should also be in EXTRA_DIST
+B_S_DIST= \
+ $(srcdir)/ntpd-opts.c \
+ $(srcdir)/ntpd-opts.h \
+ $(NULL)
+
+BUILT_SOURCES= \
+ $(VPHACK) \
+ $(LIBPARSE) \
+ ntp_parser.c \
+ ntp_parser.h \
+ $(VPHACK_AFTER) \
+ $(B_S_DIST) \
+ $(NULL)
+
+man1_MANS=
+man5_MANS= ntp.conf.5 ntp.keys.5
+man8_MANS=
+man_MANS= ntpd.$(NTPD_MS)
+
+# ntpdsim.1 is a remnant along with all the ntpdsim-opts.* files, the
+# simulator currently uses ntpd-opts.[ch]. This also means there is no
+# longer a reason to have ntpdbase-opts.def split off of ntpd-opts.def.
+
+LDADD_NTPD_COMMON = $(LDADD_LIBNTP) $(LIBOPTS_LDADD) $(PTHREAD_LIBS)
+LDADD_NTPD_COMMON += $(LIBM) $(LDADD_NTP) $(LSCF)
+ntpd_LDADD = $(LDADD) ../libntp/libntp.a $(LDADD_LIBNTP) $(LIBM) $(LDADD_NTPD_COMMON)
+ntpdsim_LDADD = $(LDADD) ../libntp/libntpsim.a $(LDADD_NTPD_COMMON)
+ntpdsim_CPPFLAGS = $(AM_CPPFLAGS) -DSIM
+check_y2k_LDADD = $(LDADD) ../libntp/libntp.a $(LDADD_LIBNTP) $(LIBM) $(LDADD_LIBNTP) $(PTHREAD_LIBS)
+## we don't want $(LDADD) in keyword_gen_LDADD
+keyword_gen_LDADD = ../libntp/libntp.a $(LDADD_LIBNTP) $(LIBM) $(PTHREAD_LIBS)
+
+DISTCLEANFILES = \
+ keyword-gen \
+ .version \
+ version.c \
+ config.log \
+ $(man5_MANS) \
+ $(man_MANS) \
+ $(NULL)
+
+CLEANFILES = \
+ check-saveconfig \
+ compsave.conf \
+ k-g-u-submake \
+ $(EXTRA_PROGRAMS) \
+ $(NULL)
+
+EXTRA_DIST = \
+ complete.conf.in \
+ invoke-ntp.conf.menu \
+ invoke-ntp.conf.texi \
+ invoke-ntp.keys.menu \
+ invoke-ntp.keys.texi \
+ invoke-ntpd.menu \
+ invoke-ntpd.texi \
+ keyword-gen-utd \
+ ntp.conf.5man \
+ ntp.conf.5mdoc \
+ ntp.conf.def \
+ ntp.conf.man.in \
+ ntp.conf.mdoc.in \
+ ntp.conf.html \
+ ntp.conf.texi \
+ ntp.keys.5man \
+ ntp.keys.5mdoc \
+ ntp.keys.def \
+ ntp.keys.man.in \
+ ntp.keys.mdoc.in \
+ ntp.keys.html \
+ ntp.keys.texi \
+ ntpd-opts.def \
+ ntpd.1ntpdman \
+ ntpd.1ntpdmdoc \
+ ntpd.man.in \
+ ntpd.mdoc.in \
+ ntpd.html \
+ ntpd.texi \
+ ntpdbase-opts.def \
+ refclock_msfees.c \
+ $(B_S_DIST) \
+ $(NULL)
+
+### Y2Kfixes
+check_PROGRAMS = @MAKE_CHECK_Y2K@
+EXTRA_PROGRAMS = check_y2k keyword-gen ntpd ntpdsim
+
+html_DATA= \
+ $(srcdir)/ntp.conf.html \
+ $(srcdir)/ntp.keys.html \
+ $(srcdir)/ntpd.html \
+ $(NULL)
+
+noinst_DATA = \
+ $(srcdir)/invoke-ntp.conf.menu \
+ $(srcdir)/invoke-ntp.conf.texi \
+ $(srcdir)/invoke-ntp.keys.menu \
+ $(srcdir)/invoke-ntp.keys.texi \
+ $(srcdir)/invoke-ntpd.menu \
+ $(srcdir)/invoke-ntpd.texi \
+ $(srcdir)/ntp.conf.man.in \
+ $(srcdir)/ntp.conf.mdoc.in \
+ $(srcdir)/ntp.keys.man.in \
+ $(srcdir)/ntp.keys.mdoc.in \
+ $(srcdir)/ntpd.man.in \
+ $(srcdir)/ntpd.mdoc.in \
+ $(NULL)
+
+noinst_HEADERS = \
+ declcond.h \
+ ntp_leapsec.h \
+ $(NULL)
+
+install-data-local: install-html
+
+run_ag= cd $(srcdir) && env PATH="$(abs_builddir):$(PATH)" AUTOGEN_DNE_DATE=-D \
+ autogen -L ../sntp/include -L ../sntp/ag-tpl --writable
+std_def_list = \
+ $(top_srcdir)/sntp/include/debug-opt.def \
+ $(top_srcdir)/sntp/include/autogen-version.def \
+ $(top_srcdir)/sntp/include/copyright.def \
+ $(top_srcdir)/sntp/include/homerc.def \
+ $(top_srcdir)/sntp/include/ntp.lic \
+ $(top_srcdir)/sntp/include/version.def \
+ $(NULL)
+
+check-local: $(MAKE_CHECK_Y2K) $(CHECK_SAVECONFIG)
+ test -z "$(MAKE_CHECK_Y2K)" || ./$(MAKE_CHECK_Y2K)
+
+ntpd_SOURCES = \
+ ntp_config.c \
+ ntp_keyword.h \
+ ntp_io.c \
+ ntp_parser.y \
+ ntp_scanner.c \
+ ntp_scanner.h \
+ ntpd.c \
+ ntpd-opts.c \
+ ntpd-opts.h \
+ $(NULL)
+
+ntpdsim_SOURCES = \
+ $(ntpd_SOURCES) \
+ ntp_prio_q.c \
+ ntpsim.c \
+ $(NULL)
+
+# libntpd_a_SOURCES do not use #ifdef SIM
+
+libntpd_a_SOURCES = \
+ cmd_args.c \
+ jupiter.h \
+ ntp_control.c \
+ ntp_crypto.c \
+ ntp_filegen.c \
+ ntp_leapsec.c \
+ ntp_loopfilter.c \
+ ntp_monitor.c \
+ ntp_peer.c \
+ ntp_proto.c \
+ ntp_refclock.c \
+ ntp_request.c \
+ ntp_restrict.c \
+ ntp_signd.c \
+ ntp_timer.c \
+ ntp_util.c \
+ ppsapi_timepps.h \
+ refclock_acts.c \
+ refclock_arbiter.c \
+ refclock_arc.c \
+ refclock_as2201.c \
+ refclock_atom.c \
+ refclock_bancomm.c \
+ refclock_chronolog.c \
+ refclock_chu.c \
+ refclock_conf.c \
+ refclock_datum.c \
+ refclock_dumbclock.c \
+ refclock_fg.c \
+ refclock_gpsdjson.c \
+ refclock_gpsvme.c \
+ refclock_heath.c \
+ refclock_hopfser.c \
+ refclock_hopfpci.c \
+ refclock_hpgps.c \
+ refclock_irig.c \
+ refclock_jjy.c \
+ refclock_jupiter.c \
+ refclock_leitch.c \
+ refclock_local.c \
+ refclock_mx4200.c \
+ refclock_neoclock4x.c \
+ refclock_nmea.c \
+ refclock_oncore.c \
+ refclock_palisade.c \
+ refclock_palisade.h \
+ refclock_parse.c \
+ refclock_pcf.c \
+ refclock_pst.c \
+ refclock_ripencc.c \
+ refclock_shm.c \
+ refclock_tpro.c \
+ refclock_true.c \
+ refclock_tt560.c \
+ refclock_ulink.c \
+ refclock_wwv.c \
+ refclock_wwvb.c \
+ refclock_zyfer.c \
+ refclock_tsyncpci.c \
+ $(NULL)
+
+k-g-u-submake: keyword-gen
+ ./keyword-gen $(srcdir)/ntp_parser.h > k-g.out
+ @grep -v diff_ignore_line < k-g.out > cmp1
+ @grep -v diff_ignore_line < $(srcdir)/ntp_keyword.h > cmp2
+ @cmp cmp1 cmp2 > /dev/null || \
+ { mv -f k-g.out $(srcdir)/ntp_keyword.h && \
+ echo 'Generated changed ntp_keyword.h.' ;}
+ @[ ! -f k-g.out ] || \
+ { rm k-g.out && echo 'ntp_keyword.h is up to date.' ;}
+ @rm cmp1 cmp2
+ @echo 'keyword-gen and ntp_keyword.h are up to date.' > $@
+
+$(srcdir)/keyword-gen-utd: $(srcdir)/keyword-gen.c $(srcdir)/ntp_parser.h
+ $(MAKE) $(AM_MAKEFLAGS) k-g-u-submake # avoid explicit dependency
+ grep diff_ignore_line $(srcdir)/ntp_keyword.h > k-g-u
+ mv -f k-g-u $@
+
+$(srcdir)/ntp_keyword.h: $(srcdir)/keyword-gen-utd
+ @: do-nothing action to avoid default SCCS get
+ @: .h updated if needed by k-g-u-submake rule
+
+$(srcdir)/ntpd-opts.h: $(srcdir)/ntpd-opts.c
+ @: do-nothing action to avoid default SCCS get, .h built with .c
+
+$(srcdir)/ntpd-opts.c: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
+ $(run_ag) ntpd-opts.def
+
+###
+
+$(srcdir)/ntpd.1ntpdman: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
+ $(run_ag) -DMAN_SECTION=1ntpdman -Tagman-cmd.tpl ntpd-opts.def
+
+$(srcdir)/ntpd.man.in: $(srcdir)/ntpd.1ntpdman $(top_srcdir)/sntp/scripts/mansec2subst.sed
+ sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntpd.1ntpdman > $(srcdir)/ntpd.man.in+
+ mv $(srcdir)/ntpd.man.in+ $(srcdir)/ntpd.man.in
+
+###
+
+$(srcdir)/ntpd.1ntpdmdoc: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
+ $(run_ag) -DMAN_SECTION=1ntpdmdoc -Tagmdoc-cmd.tpl ntpd-opts.def
+
+$(srcdir)/ntpd.mdoc.in: $(srcdir)/ntpd.1ntpdmdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed
+ sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntpd.1ntpdmdoc > $(srcdir)/ntpd.mdoc.in+
+ mv $(srcdir)/ntpd.mdoc.in+ $(srcdir)/ntpd.mdoc.in
+
+###
+
+ntpd.$(NTPD_MS): $(srcdir)/ntpd.$(MANTAGFMT).in $(top_builddir)/config.status
+ $(top_builddir)/config.status --file=ntpd.$(NTPD_MS)+:$(srcdir)/ntpd.$(MANTAGFMT).in
+ mv ntpd.$(NTPD_MS)+ ntpd.$(NTPD_MS)
+
+###
+
+$(srcdir)/invoke-ntp.conf.menu: $(srcdir)/invoke-ntp.conf.texi
+ @: do-nothing action to avoid default SCCS get, .menu built with .texi
+
+$(srcdir)/invoke-ntp.conf.texi: $(srcdir)/ntp.conf.def $(std_def_list)
+ $(run_ag) -Tagtexi-file.tpl -DLEVEL=section ntp.conf.def
+
+$(srcdir)/invoke-ntp.keys.menu: $(srcdir)/invoke-ntp.keys.texi
+ @: do-nothing action to avoid default SCCS get, .menu built with .texi
+
+$(srcdir)/invoke-ntp.keys.texi: $(srcdir)/ntp.keys.def $(std_def_list)
+ $(run_ag) -Tagtexi-file.tpl -DLEVEL=section ntp.keys.def
+
+$(srcdir)/ntp.conf.html: $(srcdir)/ntp.conf.texi $(top_srcdir)/sntp/include/version.texi
+ cd $(srcdir) && ( makeinfo --force --html --no-split -o ntp.conf.html ntp.conf.texi || true )
+
+$(srcdir)/ntp.keys.html: $(srcdir)/ntp.keys.texi $(top_srcdir)/sntp/include/version.texi
+ cd $(srcdir) && ( makeinfo --force --html --no-split -o ntp.keys.html ntp.keys.texi || true )
+
+$(srcdir)/ntpd.html: $(srcdir)/ntpd.texi $(top_srcdir)/sntp/include/version.texi
+ cd $(srcdir) && ( makeinfo --force --html --no-split -o ntpd.html ntpd.texi || true )
+
+###
+
+$(srcdir)/ntp.conf.5man: $(srcdir)/ntp.conf.def $(std_def_list)
+ $(run_ag) -DMAN_SECTION=5man -Tagman-cmd.tpl ntp.conf.def
+
+$(srcdir)/ntp.conf.man.in: $(srcdir)/ntp.conf.5man $(top_srcdir)/sntp/scripts/mansec2subst.sed
+ sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp.conf.5man > $(srcdir)/ntp.conf.man.in+
+ mv $(srcdir)/ntp.conf.man.in+ $(srcdir)/ntp.conf.man.in
+
+###
+
+$(srcdir)/ntp.conf.5mdoc: $(srcdir)/ntp.conf.def $(std_def_list)
+ $(run_ag) -DMAN_SECTION=5mdoc -Tagmdoc-cmd.tpl ntp.conf.def
+
+$(srcdir)/ntp.conf.mdoc.in: $(srcdir)/ntp.conf.5mdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed
+ sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp.conf.5mdoc > $(srcdir)/ntp.conf.mdoc.in+
+ mv $(srcdir)/ntp.conf.mdoc.in+ $(srcdir)/ntp.conf.mdoc.in
+
+###
+
+ntp.conf.5: $(srcdir)/ntp.conf.$(MANTAGFMT).in $(top_builddir)/config.status
+ $(top_builddir)/config.status --file=ntp.conf.5+:$(srcdir)/ntp.conf.$(MANTAGFMT).in
+ mv ntp.conf.5+ ntp.conf.5
+
+###
+
+$(srcdir)/ntp.keys.5man: $(srcdir)/ntp.keys.def $(std_def_list)
+ $(run_ag) -DMAN_SECTION=5man -Tagman-file.tpl ntp.keys.def
+
+$(srcdir)/ntp.keys.man.in: $(srcdir)/ntp.keys.5man $(top_srcdir)/sntp/scripts/mansec2subst.sed
+ sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp.keys.5man > $(srcdir)/ntp.keys.man.in+
+ mv $(srcdir)/ntp.keys.man.in+ $(srcdir)/ntp.keys.man.in
+
+###
+
+$(srcdir)/ntp.keys.5mdoc: $(srcdir)/ntp.keys.def $(std_def_list)
+ $(run_ag) -DMAN_SECTION=5mdoc -Tagmdoc-file.tpl ntp.keys.def
+
+$(srcdir)/ntp.keys.mdoc.in: $(srcdir)/ntp.keys.5mdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed
+ sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp.keys.5mdoc > $(srcdir)/ntp.keys.mdoc.in+
+ mv $(srcdir)/ntp.keys.mdoc.in+ $(srcdir)/ntp.keys.mdoc.in
+
+###
+
+ntp.keys.5: $(srcdir)/ntp.keys.$(MANTAGFMT).in $(top_builddir)/config.status
+ $(top_builddir)/config.status --file=ntp.keys.5+:$(srcdir)/ntp.keys.$(MANTAGFMT).in
+ mv ntp.keys.5+ ntp.keys.5
+
+###
+
+$(srcdir)/invoke-ntpd.menu: $(srcdir)/invoke-ntpd.texi
+ @: do-nothing action to avoid default SCCS get, .menu built with .texi
+
+$(srcdir)/invoke-ntpd.texi: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
+ $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntpd-opts.def
+ $(top_srcdir)/scripts/build/check--help $@
+
+$(PROGRAMS): $(LDADD)
+
+compsave.conf: ntpd complete.conf
+ ./ntpd --configfile complete.conf --saveconfigquit $@
+
+check-saveconfig: complete.conf compsave.conf
+ -diff -u complete.conf compsave.conf
+ cmp complete.conf compsave.conf && echo stamp > $@
+
+../libntp/libntpsim.a:
+ cd ../libntp && $(MAKE) $(AM_MAKEFLAGS) libntpsim.a
+
+../libparse/libparse.a:
+ cd ../libparse && $(MAKE) $(AM_MAKEFLAGS) check-libparse
+
+$(top_srcdir)/sntp/scm-rev:
+ cd ../sntp && $(MAKE) $(AM_MAKEFLAGS) check-scm-rev
+
+version.c: $(ntpd_OBJECTS) ../libntp/libntp.a @LIBPARSE@ Makefile $(top_srcdir)/sntp/scm-rev
+ env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/build/mkver ntpd
+
+version.o: version.c
+ env CCACHE_DISABLE=1 $(COMPILE) -c version.c -o version.o
+
+include $(top_srcdir)/bincheck.mf
+include $(top_srcdir)/check-libopts.mf
+include $(top_srcdir)/sntp/check-libntp.mf
+include $(top_srcdir)/depsver.mf
+include $(top_srcdir)/includes.mf
diff --git a/ntpd/Makefile.in b/ntpd/Makefile.in
new file mode 100644
index 0000000..ddb2ad3
--- /dev/null
+++ b/ntpd/Makefile.in
@@ -0,0 +1,1790 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# we traditionally installed software in bindir, while it should have gone
+# in sbindir. Now that we offer a choice, look in the "other" installation
+# subdir to warn folks if there is another version there.
+
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+EXTRA_PROGRAMS = check_y2k$(EXEEXT) keyword-gen$(EXEEXT) ntpd$(EXEEXT) \
+ ntpdsim$(EXEEXT)
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in $(srcdir)/complete.conf.in \
+ $(top_srcdir)/bincheck.mf $(top_srcdir)/check-libopts.mf \
+ $(top_srcdir)/depsver.mf $(top_srcdir)/includes.mf \
+ $(top_srcdir)/sntp/check-libntp.mf ntp_parser.c ntp_parser.h
+subdir = ntpd
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/sntp/libopts/m4/libopts.m4 \
+ $(top_srcdir)/sntp/libopts/m4/stdnoreturn.m4 \
+ $(top_srcdir)/sntp/libevent/m4/openldap-thread-check.m4 \
+ $(top_srcdir)/sntp/libevent/m4/openldap.m4 \
+ $(top_srcdir)/sntp/m4/define_dir.m4 \
+ $(top_srcdir)/sntp/m4/hms_search_lib.m4 \
+ $(top_srcdir)/sntp/m4/libtool.m4 \
+ $(top_srcdir)/sntp/m4/ltoptions.m4 \
+ $(top_srcdir)/sntp/m4/ltsugar.m4 \
+ $(top_srcdir)/sntp/m4/ltversion.m4 \
+ $(top_srcdir)/sntp/m4/lt~obsolete.m4 \
+ $(top_srcdir)/sntp/m4/ntp_cacheversion.m4 \
+ $(top_srcdir)/sntp/m4/ntp_compiler.m4 \
+ $(top_srcdir)/sntp/m4/ntp_crosscompile.m4 \
+ $(top_srcdir)/sntp/m4/ntp_debug.m4 \
+ $(top_srcdir)/sntp/m4/ntp_dir_sep.m4 \
+ $(top_srcdir)/sntp/m4/ntp_facilitynames.m4 \
+ $(top_srcdir)/sntp/m4/ntp_googletest.m4 \
+ $(top_srcdir)/sntp/m4/ntp_ipv6.m4 \
+ $(top_srcdir)/sntp/m4/ntp_lib_m.m4 \
+ $(top_srcdir)/sntp/m4/ntp_libevent.m4 \
+ $(top_srcdir)/sntp/m4/ntp_libntp.m4 \
+ $(top_srcdir)/sntp/m4/ntp_lineeditlibs.m4 \
+ $(top_srcdir)/sntp/m4/ntp_locinfo.m4 \
+ $(top_srcdir)/sntp/m4/ntp_openssl.m4 \
+ $(top_srcdir)/sntp/m4/ntp_pkg_config.m4 \
+ $(top_srcdir)/sntp/m4/ntp_prog_cc.m4 \
+ $(top_srcdir)/sntp/m4/ntp_rlimit.m4 \
+ $(top_srcdir)/sntp/m4/ntp_sntp.m4 \
+ $(top_srcdir)/sntp/m4/ntp_ver_suffix.m4 \
+ $(top_srcdir)/sntp/m4/ntp_vpathhack.m4 \
+ $(top_srcdir)/sntp/m4/os_cflags.m4 \
+ $(top_srcdir)/sntp/m4/snprintf.m4 \
+ $(top_srcdir)/sntp/m4/version.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = complete.conf
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_$(V))
+am__v_AR_ = $(am__v_AR_$(AM_DEFAULT_VERBOSITY))
+am__v_AR_0 = @echo " AR " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+libntpd_a_AR = $(AR) $(ARFLAGS)
+libntpd_a_LIBADD =
+am__objects_1 =
+am_libntpd_a_OBJECTS = cmd_args.$(OBJEXT) ntp_control.$(OBJEXT) \
+ ntp_crypto.$(OBJEXT) ntp_filegen.$(OBJEXT) \
+ ntp_leapsec.$(OBJEXT) ntp_loopfilter.$(OBJEXT) \
+ ntp_monitor.$(OBJEXT) ntp_peer.$(OBJEXT) ntp_proto.$(OBJEXT) \
+ ntp_refclock.$(OBJEXT) ntp_request.$(OBJEXT) \
+ ntp_restrict.$(OBJEXT) ntp_signd.$(OBJEXT) ntp_timer.$(OBJEXT) \
+ ntp_util.$(OBJEXT) refclock_acts.$(OBJEXT) \
+ refclock_arbiter.$(OBJEXT) refclock_arc.$(OBJEXT) \
+ refclock_as2201.$(OBJEXT) refclock_atom.$(OBJEXT) \
+ refclock_bancomm.$(OBJEXT) refclock_chronolog.$(OBJEXT) \
+ refclock_chu.$(OBJEXT) refclock_conf.$(OBJEXT) \
+ refclock_datum.$(OBJEXT) refclock_dumbclock.$(OBJEXT) \
+ refclock_fg.$(OBJEXT) refclock_gpsdjson.$(OBJEXT) \
+ refclock_gpsvme.$(OBJEXT) refclock_heath.$(OBJEXT) \
+ refclock_hopfser.$(OBJEXT) refclock_hopfpci.$(OBJEXT) \
+ refclock_hpgps.$(OBJEXT) refclock_irig.$(OBJEXT) \
+ refclock_jjy.$(OBJEXT) refclock_jupiter.$(OBJEXT) \
+ refclock_leitch.$(OBJEXT) refclock_local.$(OBJEXT) \
+ refclock_mx4200.$(OBJEXT) refclock_neoclock4x.$(OBJEXT) \
+ refclock_nmea.$(OBJEXT) refclock_oncore.$(OBJEXT) \
+ refclock_palisade.$(OBJEXT) refclock_parse.$(OBJEXT) \
+ refclock_pcf.$(OBJEXT) refclock_pst.$(OBJEXT) \
+ refclock_ripencc.$(OBJEXT) refclock_shm.$(OBJEXT) \
+ refclock_tpro.$(OBJEXT) refclock_true.$(OBJEXT) \
+ refclock_tt560.$(OBJEXT) refclock_ulink.$(OBJEXT) \
+ refclock_wwv.$(OBJEXT) refclock_wwvb.$(OBJEXT) \
+ refclock_zyfer.$(OBJEXT) refclock_tsyncpci.$(OBJEXT) \
+ $(am__objects_1)
+libntpd_a_OBJECTS = $(am_libntpd_a_OBJECTS)
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" \
+ "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man1dir)" \
+ "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" \
+ "$(DESTDIR)$(htmldir)"
+PROGRAMS = $(bin_PROGRAMS) $(libexec_PROGRAMS) $(sbin_PROGRAMS)
+check_y2k_SOURCES = check_y2k.c
+check_y2k_OBJECTS = check_y2k.$(OBJEXT)
+am__DEPENDENCIES_1 =
+am__DEPENDENCIES_2 = version.o libntpd.a $(am__DEPENDENCIES_1)
+check_y2k_DEPENDENCIES = $(am__DEPENDENCIES_2) ../libntp/libntp.a \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+keyword_gen_SOURCES = keyword-gen.c
+keyword_gen_OBJECTS = keyword-gen.$(OBJEXT)
+keyword_gen_DEPENDENCIES = ../libntp/libntp.a $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_ntpd_OBJECTS = ntp_config.$(OBJEXT) ntp_io.$(OBJEXT) \
+ ntp_parser.$(OBJEXT) ntp_scanner.$(OBJEXT) ntpd.$(OBJEXT) \
+ ntpd-opts.$(OBJEXT) $(am__objects_1)
+ntpd_OBJECTS = $(am_ntpd_OBJECTS)
+am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+ntpd_DEPENDENCIES = $(am__DEPENDENCIES_2) ../libntp/libntp.a \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_3)
+am__objects_2 = ntpdsim-ntp_config.$(OBJEXT) ntpdsim-ntp_io.$(OBJEXT) \
+ ntpdsim-ntp_parser.$(OBJEXT) ntpdsim-ntp_scanner.$(OBJEXT) \
+ ntpdsim-ntpd.$(OBJEXT) ntpdsim-ntpd-opts.$(OBJEXT) \
+ $(am__objects_1)
+am_ntpdsim_OBJECTS = $(am__objects_2) ntpdsim-ntp_prio_q.$(OBJEXT) \
+ ntpdsim-ntpsim.$(OBJEXT) $(am__objects_1)
+ntpdsim_OBJECTS = $(am_ntpdsim_OBJECTS)
+ntpdsim_DEPENDENCIES = $(am__DEPENDENCIES_2) ../libntp/libntpsim.a \
+ $(am__DEPENDENCIES_3)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/sntp/libevent/build-aux/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS)
+LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(YACC) $(YFLAGS) $(AM_YFLAGS)
+AM_V_YACC = $(am__v_YACC_$(V))
+am__v_YACC_ = $(am__v_YACC_$(AM_DEFAULT_VERBOSITY))
+am__v_YACC_0 = @echo " YACC " $@;
+YLWRAP = $(top_srcdir)/sntp/libevent/build-aux/ylwrap
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(libntpd_a_SOURCES) check_y2k.c keyword-gen.c \
+ $(ntpd_SOURCES) $(ntpdsim_SOURCES)
+DIST_SOURCES = $(libntpd_a_SOURCES) check_y2k.c keyword-gen.c \
+ $(ntpd_SOURCES) $(ntpdsim_SOURCES)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+man1dir = $(mandir)/man1
+man5dir = $(mandir)/man5
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man1_MANS) $(man5_MANS) $(man8_MANS) $(man_MANS)
+DATA = $(html_DATA) $(noinst_DATA)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CALC_TICKADJ_DB = @CALC_TICKADJ_DB@
+CALC_TICKADJ_DL = @CALC_TICKADJ_DL@
+CALC_TICKADJ_DS = @CALC_TICKADJ_DS@
+CALC_TICKADJ_MS = @CALC_TICKADJ_MS@
+CALC_TICKADJ_NI = @CALC_TICKADJ_NI@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CFLAGS_NTP = @CFLAGS_NTP@
+CHUTEST = @CHUTEST@
+CONFIG_SHELL = @CONFIG_SHELL@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_NTP = @CPPFLAGS_NTP@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DCFD = @DCFD@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EDITLINE_LIBS = @EDITLINE_LIBS@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+GTEST_CONFIG = @GTEST_CONFIG@
+GTEST_CPPFLAGS = @GTEST_CPPFLAGS@
+GTEST_CXXFLAGS = @GTEST_CXXFLAGS@
+GTEST_LDFLAGS = @GTEST_LDFLAGS@
+GTEST_LIBS = @GTEST_LIBS@
+HAVE_INLINE = @HAVE_INLINE@
+HAVE_RLIMIT_MEMLOCK = @HAVE_RLIMIT_MEMLOCK@
+HAVE_RLIMIT_STACK = @HAVE_RLIMIT_STACK@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDADD_LIBNTP = @LDADD_LIBNTP@
+LDADD_NLIST = @LDADD_NLIST@
+LDADD_NTP = @LDADD_NTP@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_NTP = @LDFLAGS_NTP@
+LIBISC_PTHREADS_NOTHREADS = @LIBISC_PTHREADS_NOTHREADS@
+LIBM = @LIBM@
+LIBOBJS = @LIBOBJS@
+LIBOPTS_CFLAGS = @LIBOPTS_CFLAGS@
+LIBOPTS_DIR = @LIBOPTS_DIR@
+LIBOPTS_LDADD = @LIBOPTS_LDADD@
+LIBPARSE = @LIBPARSE@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBTOOL_DEPS = @LIBTOOL_DEPS@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LSCF = @LSCF@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MAKE_ADJTIMED = @MAKE_ADJTIMED@
+MAKE_CHECK_LAYOUT = @MAKE_CHECK_LAYOUT@
+MAKE_CHECK_Y2K = @MAKE_CHECK_Y2K@
+MAKE_LIBNTPSIM = @MAKE_LIBNTPSIM@
+MAKE_LIBPARSE = @MAKE_LIBPARSE@
+MAKE_LIBPARSE_KERNEL = @MAKE_LIBPARSE_KERNEL@
+MAKE_NTPDSIM = @MAKE_NTPDSIM@
+MAKE_NTPSNMPD = @MAKE_NTPSNMPD@
+MAKE_NTPTIME = @MAKE_NTPTIME@
+MAKE_PARSEKMODULE = @MAKE_PARSEKMODULE@
+MAKE_TICKADJ = @MAKE_TICKADJ@
+MAKE_TIMETRIM = @MAKE_TIMETRIM@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MANTAGFMT = @MANTAGFMT@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NTPDATE_DB = @NTPDATE_DB@
+NTPDATE_DL = @NTPDATE_DL@
+NTPDATE_DS = @NTPDATE_DS@
+NTPDATE_MS = @NTPDATE_MS@
+NTPDATE_NI = @NTPDATE_NI@
+NTPDC_DB = @NTPDC_DB@
+NTPDC_DL = @NTPDC_DL@
+NTPDC_DS = @NTPDC_DS@
+NTPDC_MS = @NTPDC_MS@
+NTPDC_NI = @NTPDC_NI@
+NTPDSIM_DB = @NTPDSIM_DB@
+NTPDSIM_DL = @NTPDSIM_DL@
+NTPDSIM_DS = @NTPDSIM_DS@
+NTPDSIM_MS = @NTPDSIM_MS@
+NTPDSIM_NI = @NTPDSIM_NI@
+NTPD_DB = @NTPD_DB@
+NTPD_DL = @NTPD_DL@
+NTPD_DS = @NTPD_DS@
+NTPD_MS = @NTPD_MS@
+NTPD_NI = @NTPD_NI@
+NTPQ_DB = @NTPQ_DB@
+NTPQ_DL = @NTPQ_DL@
+NTPQ_DS = @NTPQ_DS@
+NTPQ_MS = @NTPQ_MS@
+NTPQ_NI = @NTPQ_NI@
+NTPSNMPD_DB = @NTPSNMPD_DB@
+NTPSNMPD_DL = @NTPSNMPD_DL@
+NTPSNMPD_DS = @NTPSNMPD_DS@
+NTPSNMPD_MS = @NTPSNMPD_MS@
+NTPSNMPD_NI = @NTPSNMPD_NI@
+NTPSWEEP_DB = @NTPSWEEP_DB@
+NTPSWEEP_DL = @NTPSWEEP_DL@
+NTPSWEEP_DS = @NTPSWEEP_DS@
+NTPSWEEP_MS = @NTPSWEEP_MS@
+NTPSWEEP_NI = @NTPSWEEP_NI@
+NTPTIME_DB = @NTPTIME_DB@
+NTPTIME_DL = @NTPTIME_DL@
+NTPTIME_DS = @NTPTIME_DS@
+NTPTIME_MS = @NTPTIME_MS@
+NTPTIME_NI = @NTPTIME_NI@
+NTPTRACE_DB = @NTPTRACE_DB@
+NTPTRACE_DL = @NTPTRACE_DL@
+NTPTRACE_DS = @NTPTRACE_DS@
+NTPTRACE_MS = @NTPTRACE_MS@
+NTPTRACE_NI = @NTPTRACE_NI@
+NTP_KEYGEN_DB = @NTP_KEYGEN_DB@
+NTP_KEYGEN_DL = @NTP_KEYGEN_DL@
+NTP_KEYGEN_DS = @NTP_KEYGEN_DS@
+NTP_KEYGEN_MS = @NTP_KEYGEN_MS@
+NTP_KEYGEN_NI = @NTP_KEYGEN_NI@
+NTP_KEYSDIR = @NTP_KEYSDIR@
+NTP_WAIT_DB = @NTP_WAIT_DB@
+NTP_WAIT_DL = @NTP_WAIT_DL@
+NTP_WAIT_DS = @NTP_WAIT_DS@
+NTP_WAIT_MS = @NTP_WAIT_MS@
+NTP_WAIT_NI = @NTP_WAIT_NI@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_NET_SNMP_CONFIG = @PATH_NET_SNMP_CONFIG@
+PATH_PERL = @PATH_PERL@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PATH_TEST = @PATH_TEST@
+PERLLIBDIR = @PERLLIBDIR@
+PKG_CONFIG = @PKG_CONFIG@
+POSIX_SHELL = @POSIX_SHELL@
+PROPDELAY = @PROPDELAY@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SNMP_CFLAGS = @SNMP_CFLAGS@
+SNMP_CPPFLAGS = @SNMP_CPPFLAGS@
+SNMP_LIBS = @SNMP_LIBS@
+SNTP = @SNTP@
+SNTP_DB = @SNTP_DB@
+SNTP_DL = @SNTP_DL@
+SNTP_DS = @SNTP_DS@
+SNTP_MS = @SNTP_MS@
+SNTP_NI = @SNTP_NI@
+STDNORETURN_H = @STDNORETURN_H@
+STRIP = @STRIP@
+TESTDCF = @TESTDCF@
+TICKADJ_DB = @TICKADJ_DB@
+TICKADJ_DL = @TICKADJ_DL@
+TICKADJ_DS = @TICKADJ_DS@
+TICKADJ_MS = @TICKADJ_MS@
+TICKADJ_NI = @TICKADJ_NI@
+TIMETRIM_DB = @TIMETRIM_DB@
+TIMETRIM_DL = @TIMETRIM_DL@
+TIMETRIM_DS = @TIMETRIM_DS@
+TIMETRIM_MS = @TIMETRIM_MS@
+TIMETRIM_NI = @TIMETRIM_NI@
+VERSION = @VERSION@
+VER_SUFFIX = @VER_SUFFIX@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+NULL =
+bin_PROGRAMS = $(NTPD_DB) $(NTPDSIM_DB)
+libexec_PROGRAMS = $(NTPD_DL) $(NTPDSIM_DL)
+sbin_PROGRAMS = $(NTPD_DS) $(NTPDSIM_DS)
+noinst_LIBRARIES = libntpd.a
+AM_CFLAGS = $(CFLAGS_NTP)
+AM_CPPFLAGS = $(NTP_INCS) $(LIBOPTS_CFLAGS) $(CPPFLAGS_NTP)
+AM_LDFLAGS = $(LDFLAGS_NTP)
+
+# LDADD might need RESLIB and ADJLIB.
+LDADD = version.o libntpd.a $(LIBPARSE)
+AM_YFLAGS = -d -t -r all
+@NTP_CROSSCOMPILE_FALSE@@SAVECONFIG_ENABLED_TRUE@CHECK_SAVECONFIG = check-saveconfig
+@NTP_CROSSCOMPILE_TRUE@@SAVECONFIG_ENABLED_TRUE@CHECK_SAVECONFIG =
+@SAVECONFIG_ENABLED_FALSE@CHECK_SAVECONFIG =
+@VPATH_HACK_FALSE@VPHACK =
+
+#
+# VPHACK and VPHACK_AFTER are enabled on non-GNU makes (such as
+# BSD make) to work around issues specific to compiling
+# ntp_parser.y into ntp_parser.h and ntp_parser.c in a VPATH
+# configuration where we would like (for a change) the output
+# files ntp_parser.[ch] to be placed in the source directory,
+# as opposed to the build directory. This allows a single
+# host of a flock configured with Bison to update ntp_parser.[ch]
+# used by the rest.
+#
+@VPATH_HACK_TRUE@VPHACK = vphack
+@VPATH_HACK_FALSE@VPHACK_AFTER =
+@VPATH_HACK_TRUE@VPHACK_AFTER = vphack_after
+
+# BUILT_SOURCES which should also be in EXTRA_DIST
+B_S_DIST = \
+ $(srcdir)/ntpd-opts.c \
+ $(srcdir)/ntpd-opts.h \
+ $(NULL)
+
+BUILT_SOURCES = $(VPHACK) $(LIBPARSE) ntp_parser.c ntp_parser.h \
+ $(VPHACK_AFTER) $(B_S_DIST) $(NULL) check-libopts check-libntp \
+ .deps-ver
+man1_MANS =
+man5_MANS = ntp.conf.5 ntp.keys.5
+man8_MANS =
+man_MANS = ntpd.$(NTPD_MS)
+
+# ntpdsim.1 is a remnant along with all the ntpdsim-opts.* files, the
+# simulator currently uses ntpd-opts.[ch]. This also means there is no
+# longer a reason to have ntpdbase-opts.def split off of ntpd-opts.def.
+LDADD_NTPD_COMMON = $(LDADD_LIBNTP) $(LIBOPTS_LDADD) $(PTHREAD_LIBS) \
+ $(LIBM) $(LDADD_NTP) $(LSCF)
+ntpd_LDADD = $(LDADD) ../libntp/libntp.a $(LDADD_LIBNTP) $(LIBM) $(LDADD_NTPD_COMMON)
+ntpdsim_LDADD = $(LDADD) ../libntp/libntpsim.a $(LDADD_NTPD_COMMON)
+ntpdsim_CPPFLAGS = $(AM_CPPFLAGS) -DSIM
+check_y2k_LDADD = $(LDADD) ../libntp/libntp.a $(LDADD_LIBNTP) $(LIBM) $(LDADD_LIBNTP) $(PTHREAD_LIBS)
+keyword_gen_LDADD = ../libntp/libntp.a $(LDADD_LIBNTP) $(LIBM) $(PTHREAD_LIBS)
+DISTCLEANFILES = \
+ keyword-gen \
+ .version \
+ version.c \
+ config.log \
+ $(man5_MANS) \
+ $(man_MANS) \
+ $(NULL)
+
+CLEANFILES = check-saveconfig compsave.conf k-g-u-submake \
+ $(EXTRA_PROGRAMS) $(NULL) check-libopts check-libntp .deps-ver
+EXTRA_DIST = \
+ complete.conf.in \
+ invoke-ntp.conf.menu \
+ invoke-ntp.conf.texi \
+ invoke-ntp.keys.menu \
+ invoke-ntp.keys.texi \
+ invoke-ntpd.menu \
+ invoke-ntpd.texi \
+ keyword-gen-utd \
+ ntp.conf.5man \
+ ntp.conf.5mdoc \
+ ntp.conf.def \
+ ntp.conf.man.in \
+ ntp.conf.mdoc.in \
+ ntp.conf.html \
+ ntp.conf.texi \
+ ntp.keys.5man \
+ ntp.keys.5mdoc \
+ ntp.keys.def \
+ ntp.keys.man.in \
+ ntp.keys.mdoc.in \
+ ntp.keys.html \
+ ntp.keys.texi \
+ ntpd-opts.def \
+ ntpd.1ntpdman \
+ ntpd.1ntpdmdoc \
+ ntpd.man.in \
+ ntpd.mdoc.in \
+ ntpd.html \
+ ntpd.texi \
+ ntpdbase-opts.def \
+ refclock_msfees.c \
+ $(B_S_DIST) \
+ $(NULL)
+
+
+### Y2Kfixes
+check_PROGRAMS = @MAKE_CHECK_Y2K@
+html_DATA = \
+ $(srcdir)/ntp.conf.html \
+ $(srcdir)/ntp.keys.html \
+ $(srcdir)/ntpd.html \
+ $(NULL)
+
+noinst_DATA = \
+ $(srcdir)/invoke-ntp.conf.menu \
+ $(srcdir)/invoke-ntp.conf.texi \
+ $(srcdir)/invoke-ntp.keys.menu \
+ $(srcdir)/invoke-ntp.keys.texi \
+ $(srcdir)/invoke-ntpd.menu \
+ $(srcdir)/invoke-ntpd.texi \
+ $(srcdir)/ntp.conf.man.in \
+ $(srcdir)/ntp.conf.mdoc.in \
+ $(srcdir)/ntp.keys.man.in \
+ $(srcdir)/ntp.keys.mdoc.in \
+ $(srcdir)/ntpd.man.in \
+ $(srcdir)/ntpd.mdoc.in \
+ $(NULL)
+
+noinst_HEADERS = \
+ declcond.h \
+ ntp_leapsec.h \
+ $(NULL)
+
+run_ag = cd $(srcdir) && env PATH="$(abs_builddir):$(PATH)" AUTOGEN_DNE_DATE=-D \
+ autogen -L ../sntp/include -L ../sntp/ag-tpl --writable
+
+std_def_list = \
+ $(top_srcdir)/sntp/include/debug-opt.def \
+ $(top_srcdir)/sntp/include/autogen-version.def \
+ $(top_srcdir)/sntp/include/copyright.def \
+ $(top_srcdir)/sntp/include/homerc.def \
+ $(top_srcdir)/sntp/include/ntp.lic \
+ $(top_srcdir)/sntp/include/version.def \
+ $(NULL)
+
+ntpd_SOURCES = \
+ ntp_config.c \
+ ntp_keyword.h \
+ ntp_io.c \
+ ntp_parser.y \
+ ntp_scanner.c \
+ ntp_scanner.h \
+ ntpd.c \
+ ntpd-opts.c \
+ ntpd-opts.h \
+ $(NULL)
+
+ntpdsim_SOURCES = \
+ $(ntpd_SOURCES) \
+ ntp_prio_q.c \
+ ntpsim.c \
+ $(NULL)
+
+
+# libntpd_a_SOURCES do not use #ifdef SIM
+libntpd_a_SOURCES = \
+ cmd_args.c \
+ jupiter.h \
+ ntp_control.c \
+ ntp_crypto.c \
+ ntp_filegen.c \
+ ntp_leapsec.c \
+ ntp_loopfilter.c \
+ ntp_monitor.c \
+ ntp_peer.c \
+ ntp_proto.c \
+ ntp_refclock.c \
+ ntp_request.c \
+ ntp_restrict.c \
+ ntp_signd.c \
+ ntp_timer.c \
+ ntp_util.c \
+ ppsapi_timepps.h \
+ refclock_acts.c \
+ refclock_arbiter.c \
+ refclock_arc.c \
+ refclock_as2201.c \
+ refclock_atom.c \
+ refclock_bancomm.c \
+ refclock_chronolog.c \
+ refclock_chu.c \
+ refclock_conf.c \
+ refclock_datum.c \
+ refclock_dumbclock.c \
+ refclock_fg.c \
+ refclock_gpsdjson.c \
+ refclock_gpsvme.c \
+ refclock_heath.c \
+ refclock_hopfser.c \
+ refclock_hopfpci.c \
+ refclock_hpgps.c \
+ refclock_irig.c \
+ refclock_jjy.c \
+ refclock_jupiter.c \
+ refclock_leitch.c \
+ refclock_local.c \
+ refclock_mx4200.c \
+ refclock_neoclock4x.c \
+ refclock_nmea.c \
+ refclock_oncore.c \
+ refclock_palisade.c \
+ refclock_palisade.h \
+ refclock_parse.c \
+ refclock_pcf.c \
+ refclock_pst.c \
+ refclock_ripencc.c \
+ refclock_shm.c \
+ refclock_tpro.c \
+ refclock_true.c \
+ refclock_tt560.c \
+ refclock_ulink.c \
+ refclock_wwv.c \
+ refclock_wwvb.c \
+ refclock_zyfer.c \
+ refclock_tsyncpci.c \
+ $(NULL)
+
+NTP_INCS = -I$(top_srcdir)/include -I$(top_srcdir)/lib/isc/include \
+ -I$(top_srcdir)/lib/isc/$(LIBISC_PTHREADS_NOTHREADS)/include \
+ -I$(top_srcdir)/lib/isc/unix/include
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj .y
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/bincheck.mf $(top_srcdir)/check-libopts.mf $(top_srcdir)/sntp/check-libntp.mf $(top_srcdir)/depsver.mf $(top_srcdir)/includes.mf $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign ntpd/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign ntpd/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+complete.conf: $(top_builddir)/config.status $(srcdir)/complete.conf.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libntpd.a: $(libntpd_a_OBJECTS) $(libntpd_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libntpd.a
+ $(AM_V_AR)$(libntpd_a_AR) libntpd.a $(libntpd_a_OBJECTS) $(libntpd_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libntpd.a
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+clean-checkPROGRAMS:
+ @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(libexecdir)" || $(MKDIR_P) "$(DESTDIR)$(libexecdir)"
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+check_y2k$(EXEEXT): $(check_y2k_OBJECTS) $(check_y2k_DEPENDENCIES)
+ @rm -f check_y2k$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(check_y2k_OBJECTS) $(check_y2k_LDADD) $(LIBS)
+keyword-gen$(EXEEXT): $(keyword_gen_OBJECTS) $(keyword_gen_DEPENDENCIES)
+ @rm -f keyword-gen$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(keyword_gen_OBJECTS) $(keyword_gen_LDADD) $(LIBS)
+ntp_parser.h: ntp_parser.c
+ @if test ! -f $@; then \
+ rm -f ntp_parser.c; \
+ $(MAKE) $(AM_MAKEFLAGS) ntp_parser.c; \
+ else :; fi
+ntpd$(EXEEXT): $(ntpd_OBJECTS) $(ntpd_DEPENDENCIES)
+ @rm -f ntpd$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(ntpd_OBJECTS) $(ntpd_LDADD) $(LIBS)
+ntpdsim$(EXEEXT): $(ntpdsim_OBJECTS) $(ntpdsim_DEPENDENCIES)
+ @rm -f ntpdsim$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(ntpdsim_OBJECTS) $(ntpdsim_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_y2k.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd_args.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keyword-gen.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_config.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_control.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_crypto.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_filegen.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_io.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_leapsec.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_loopfilter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_monitor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_parser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_peer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_proto.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_refclock.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_request.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_restrict.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_scanner.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_signd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_timer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpd-opts.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntp_config.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntp_io.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntp_parser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntp_prio_q.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntp_scanner.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntpd-opts.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntpd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntpsim.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_acts.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_arbiter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_arc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_as2201.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_atom.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_bancomm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_chronolog.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_chu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_conf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_datum.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_dumbclock.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_fg.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_gpsdjson.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_gpsvme.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_heath.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_hopfpci.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_hopfser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_hpgps.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_irig.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_jjy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_jupiter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_leitch.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_local.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_mx4200.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_neoclock4x.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_nmea.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_oncore.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_palisade.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_parse.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_pcf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_pst.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_ripencc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_shm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_tpro.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_true.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_tsyncpci.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_tt560.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_ulink.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_wwv.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_wwvb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_zyfer.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+ntpdsim-ntp_config.o: ntp_config.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_config.o -MD -MP -MF $(DEPDIR)/ntpdsim-ntp_config.Tpo -c -o ntpdsim-ntp_config.o `test -f 'ntp_config.c' || echo '$(srcdir)/'`ntp_config.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntp_config.Tpo $(DEPDIR)/ntpdsim-ntp_config.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_config.c' object='ntpdsim-ntp_config.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_config.o `test -f 'ntp_config.c' || echo '$(srcdir)/'`ntp_config.c
+
+ntpdsim-ntp_config.obj: ntp_config.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_config.obj -MD -MP -MF $(DEPDIR)/ntpdsim-ntp_config.Tpo -c -o ntpdsim-ntp_config.obj `if test -f 'ntp_config.c'; then $(CYGPATH_W) 'ntp_config.c'; else $(CYGPATH_W) '$(srcdir)/ntp_config.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntp_config.Tpo $(DEPDIR)/ntpdsim-ntp_config.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_config.c' object='ntpdsim-ntp_config.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_config.obj `if test -f 'ntp_config.c'; then $(CYGPATH_W) 'ntp_config.c'; else $(CYGPATH_W) '$(srcdir)/ntp_config.c'; fi`
+
+ntpdsim-ntp_io.o: ntp_io.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_io.o -MD -MP -MF $(DEPDIR)/ntpdsim-ntp_io.Tpo -c -o ntpdsim-ntp_io.o `test -f 'ntp_io.c' || echo '$(srcdir)/'`ntp_io.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntp_io.Tpo $(DEPDIR)/ntpdsim-ntp_io.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_io.c' object='ntpdsim-ntp_io.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_io.o `test -f 'ntp_io.c' || echo '$(srcdir)/'`ntp_io.c
+
+ntpdsim-ntp_io.obj: ntp_io.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_io.obj -MD -MP -MF $(DEPDIR)/ntpdsim-ntp_io.Tpo -c -o ntpdsim-ntp_io.obj `if test -f 'ntp_io.c'; then $(CYGPATH_W) 'ntp_io.c'; else $(CYGPATH_W) '$(srcdir)/ntp_io.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntp_io.Tpo $(DEPDIR)/ntpdsim-ntp_io.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_io.c' object='ntpdsim-ntp_io.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_io.obj `if test -f 'ntp_io.c'; then $(CYGPATH_W) 'ntp_io.c'; else $(CYGPATH_W) '$(srcdir)/ntp_io.c'; fi`
+
+ntpdsim-ntp_parser.o: ntp_parser.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_parser.o -MD -MP -MF $(DEPDIR)/ntpdsim-ntp_parser.Tpo -c -o ntpdsim-ntp_parser.o `test -f 'ntp_parser.c' || echo '$(srcdir)/'`ntp_parser.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntp_parser.Tpo $(DEPDIR)/ntpdsim-ntp_parser.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_parser.c' object='ntpdsim-ntp_parser.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_parser.o `test -f 'ntp_parser.c' || echo '$(srcdir)/'`ntp_parser.c
+
+ntpdsim-ntp_parser.obj: ntp_parser.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_parser.obj -MD -MP -MF $(DEPDIR)/ntpdsim-ntp_parser.Tpo -c -o ntpdsim-ntp_parser.obj `if test -f 'ntp_parser.c'; then $(CYGPATH_W) 'ntp_parser.c'; else $(CYGPATH_W) '$(srcdir)/ntp_parser.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntp_parser.Tpo $(DEPDIR)/ntpdsim-ntp_parser.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_parser.c' object='ntpdsim-ntp_parser.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_parser.obj `if test -f 'ntp_parser.c'; then $(CYGPATH_W) 'ntp_parser.c'; else $(CYGPATH_W) '$(srcdir)/ntp_parser.c'; fi`
+
+ntpdsim-ntp_scanner.o: ntp_scanner.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_scanner.o -MD -MP -MF $(DEPDIR)/ntpdsim-ntp_scanner.Tpo -c -o ntpdsim-ntp_scanner.o `test -f 'ntp_scanner.c' || echo '$(srcdir)/'`ntp_scanner.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntp_scanner.Tpo $(DEPDIR)/ntpdsim-ntp_scanner.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_scanner.c' object='ntpdsim-ntp_scanner.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_scanner.o `test -f 'ntp_scanner.c' || echo '$(srcdir)/'`ntp_scanner.c
+
+ntpdsim-ntp_scanner.obj: ntp_scanner.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_scanner.obj -MD -MP -MF $(DEPDIR)/ntpdsim-ntp_scanner.Tpo -c -o ntpdsim-ntp_scanner.obj `if test -f 'ntp_scanner.c'; then $(CYGPATH_W) 'ntp_scanner.c'; else $(CYGPATH_W) '$(srcdir)/ntp_scanner.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntp_scanner.Tpo $(DEPDIR)/ntpdsim-ntp_scanner.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_scanner.c' object='ntpdsim-ntp_scanner.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_scanner.obj `if test -f 'ntp_scanner.c'; then $(CYGPATH_W) 'ntp_scanner.c'; else $(CYGPATH_W) '$(srcdir)/ntp_scanner.c'; fi`
+
+ntpdsim-ntpd.o: ntpd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpd.o -MD -MP -MF $(DEPDIR)/ntpdsim-ntpd.Tpo -c -o ntpdsim-ntpd.o `test -f 'ntpd.c' || echo '$(srcdir)/'`ntpd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntpd.Tpo $(DEPDIR)/ntpdsim-ntpd.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpd.c' object='ntpdsim-ntpd.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpd.o `test -f 'ntpd.c' || echo '$(srcdir)/'`ntpd.c
+
+ntpdsim-ntpd.obj: ntpd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpd.obj -MD -MP -MF $(DEPDIR)/ntpdsim-ntpd.Tpo -c -o ntpdsim-ntpd.obj `if test -f 'ntpd.c'; then $(CYGPATH_W) 'ntpd.c'; else $(CYGPATH_W) '$(srcdir)/ntpd.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntpd.Tpo $(DEPDIR)/ntpdsim-ntpd.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpd.c' object='ntpdsim-ntpd.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpd.obj `if test -f 'ntpd.c'; then $(CYGPATH_W) 'ntpd.c'; else $(CYGPATH_W) '$(srcdir)/ntpd.c'; fi`
+
+ntpdsim-ntpd-opts.o: ntpd-opts.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpd-opts.o -MD -MP -MF $(DEPDIR)/ntpdsim-ntpd-opts.Tpo -c -o ntpdsim-ntpd-opts.o `test -f 'ntpd-opts.c' || echo '$(srcdir)/'`ntpd-opts.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntpd-opts.Tpo $(DEPDIR)/ntpdsim-ntpd-opts.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpd-opts.c' object='ntpdsim-ntpd-opts.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpd-opts.o `test -f 'ntpd-opts.c' || echo '$(srcdir)/'`ntpd-opts.c
+
+ntpdsim-ntpd-opts.obj: ntpd-opts.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpd-opts.obj -MD -MP -MF $(DEPDIR)/ntpdsim-ntpd-opts.Tpo -c -o ntpdsim-ntpd-opts.obj `if test -f 'ntpd-opts.c'; then $(CYGPATH_W) 'ntpd-opts.c'; else $(CYGPATH_W) '$(srcdir)/ntpd-opts.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntpd-opts.Tpo $(DEPDIR)/ntpdsim-ntpd-opts.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpd-opts.c' object='ntpdsim-ntpd-opts.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpd-opts.obj `if test -f 'ntpd-opts.c'; then $(CYGPATH_W) 'ntpd-opts.c'; else $(CYGPATH_W) '$(srcdir)/ntpd-opts.c'; fi`
+
+ntpdsim-ntp_prio_q.o: ntp_prio_q.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_prio_q.o -MD -MP -MF $(DEPDIR)/ntpdsim-ntp_prio_q.Tpo -c -o ntpdsim-ntp_prio_q.o `test -f 'ntp_prio_q.c' || echo '$(srcdir)/'`ntp_prio_q.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntp_prio_q.Tpo $(DEPDIR)/ntpdsim-ntp_prio_q.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_prio_q.c' object='ntpdsim-ntp_prio_q.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_prio_q.o `test -f 'ntp_prio_q.c' || echo '$(srcdir)/'`ntp_prio_q.c
+
+ntpdsim-ntp_prio_q.obj: ntp_prio_q.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_prio_q.obj -MD -MP -MF $(DEPDIR)/ntpdsim-ntp_prio_q.Tpo -c -o ntpdsim-ntp_prio_q.obj `if test -f 'ntp_prio_q.c'; then $(CYGPATH_W) 'ntp_prio_q.c'; else $(CYGPATH_W) '$(srcdir)/ntp_prio_q.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntp_prio_q.Tpo $(DEPDIR)/ntpdsim-ntp_prio_q.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_prio_q.c' object='ntpdsim-ntp_prio_q.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_prio_q.obj `if test -f 'ntp_prio_q.c'; then $(CYGPATH_W) 'ntp_prio_q.c'; else $(CYGPATH_W) '$(srcdir)/ntp_prio_q.c'; fi`
+
+ntpdsim-ntpsim.o: ntpsim.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpsim.o -MD -MP -MF $(DEPDIR)/ntpdsim-ntpsim.Tpo -c -o ntpdsim-ntpsim.o `test -f 'ntpsim.c' || echo '$(srcdir)/'`ntpsim.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntpsim.Tpo $(DEPDIR)/ntpdsim-ntpsim.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpsim.c' object='ntpdsim-ntpsim.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpsim.o `test -f 'ntpsim.c' || echo '$(srcdir)/'`ntpsim.c
+
+ntpdsim-ntpsim.obj: ntpsim.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpsim.obj -MD -MP -MF $(DEPDIR)/ntpdsim-ntpsim.Tpo -c -o ntpdsim-ntpsim.obj `if test -f 'ntpsim.c'; then $(CYGPATH_W) 'ntpsim.c'; else $(CYGPATH_W) '$(srcdir)/ntpsim.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ntpdsim-ntpsim.Tpo $(DEPDIR)/ntpdsim-ntpsim.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpsim.c' object='ntpdsim-ntpsim.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ntpdsim_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpsim.obj `if test -f 'ntpsim.c'; then $(CYGPATH_W) 'ntpsim.c'; else $(CYGPATH_W) '$(srcdir)/ntpsim.c'; fi`
+
+.y.c:
+ $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h $*.h y.output $*.output -- $(YACCCOMPILE)
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man1: $(man1_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man1dir)" || $(MKDIR_P) "$(DESTDIR)$(man1dir)"
+ @list='$(man1_MANS)'; test -n "$(man1dir)" || exit 0; \
+ { for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.1[a-z]*$$/p'; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man1:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man1_MANS)'; test -n "$(man1dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.1[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ test -z "$$files" || { \
+ echo " ( cd '$(DESTDIR)$(man1dir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(man1dir)" && rm -f $$files; }
+install-man5: $(man5_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man5dir)" || $(MKDIR_P) "$(DESTDIR)$(man5dir)"
+ @list='$(man5_MANS)'; test -n "$(man5dir)" || exit 0; \
+ { for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.5[a-z]*$$/p'; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man5:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man5_MANS)'; test -n "$(man5dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.5[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ test -z "$$files" || { \
+ echo " ( cd '$(DESTDIR)$(man5dir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(man5dir)" && rm -f $$files; }
+install-man8: $(man8_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ { for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.8[a-z]*$$/p'; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.8[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ test -z "$$files" || { \
+ echo " ( cd '$(DESTDIR)$(man8dir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(man8dir)" && rm -f $$files; }
+install-htmlDATA: $(html_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(htmldir)" || $(MKDIR_P) "$(DESTDIR)$(htmldir)"
+ @list='$(html_DATA)'; test -n "$(htmldir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(htmldir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(htmldir)" || exit $$?; \
+ done
+
+uninstall-htmlDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(html_DATA)'; test -n "$(htmldir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(htmldir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(htmldir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @list='$(MANS)'; if test -n "$$list"; then \
+ list=`for p in $$list; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
+ if test -n "$$list" && \
+ grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
+ echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
+ grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \
+ echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \
+ echo " typically \`make maintainer-clean' will remove them" >&2; \
+ exit 1; \
+ else :; fi; \
+ else :; fi
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+ $(MAKE) $(AM_MAKEFLAGS) check-local
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(MANS) $(DATA) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(htmldir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -rm -f ntp_parser.c
+ -rm -f ntp_parser.h
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
+ clean-libexecPROGRAMS clean-libtool clean-noinstLIBRARIES \
+ clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local install-htmlDATA install-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS install-libexecPROGRAMS \
+ install-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man1 install-man5 install-man8
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-htmlDATA \
+ uninstall-libexecPROGRAMS uninstall-man uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man1 uninstall-man5 uninstall-man8
+
+.MAKE: all check check-am install install-am install-exec-am \
+ install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am check-local clean \
+ clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
+ clean-libexecPROGRAMS clean-libtool clean-noinstLIBRARIES \
+ clean-sbinPROGRAMS ctags distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-binPROGRAMS install-data install-data-am \
+ install-data-local install-dvi install-dvi-am install-exec \
+ install-exec-am install-exec-hook install-html install-html-am \
+ install-htmlDATA install-info install-info-am \
+ install-libexecPROGRAMS install-man install-man1 install-man5 \
+ install-man8 install-pdf install-pdf-am install-ps \
+ install-ps-am install-sbinPROGRAMS install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-binPROGRAMS \
+ uninstall-htmlDATA uninstall-libexecPROGRAMS uninstall-man \
+ uninstall-man1 uninstall-man5 uninstall-man8 \
+ uninstall-sbinPROGRAMS
+
+
+vphack:
+ test -e ntp_parser.c || ln -s $(srcdir)/ntp_parser.c .
+ test -e ntp_parser.h || ln -s $(srcdir)/ntp_parser.h .
+
+#
+# ylwrap script which invokes Bison replaces ntp_parser.h
+# symlink with the updated file, when ntp_parser.h changes.
+# vphack_after detects this and copies the updated file to srcdir
+# and re-creates the ntp_parser.h symlink in its place.
+#
+
+vphack_after:
+ test -L ntp_parser.h || ( \
+ mv ntp_parser.h $(srcdir)/ntp_parser.h && \
+ ln -s $(srcdir)/ntp_parser.h . \
+ )
+
+install-data-local: install-html
+
+check-local: $(MAKE_CHECK_Y2K) $(CHECK_SAVECONFIG)
+ test -z "$(MAKE_CHECK_Y2K)" || ./$(MAKE_CHECK_Y2K)
+
+k-g-u-submake: keyword-gen
+ ./keyword-gen $(srcdir)/ntp_parser.h > k-g.out
+ @grep -v diff_ignore_line < k-g.out > cmp1
+ @grep -v diff_ignore_line < $(srcdir)/ntp_keyword.h > cmp2
+ @cmp cmp1 cmp2 > /dev/null || \
+ { mv -f k-g.out $(srcdir)/ntp_keyword.h && \
+ echo 'Generated changed ntp_keyword.h.' ;}
+ @[ ! -f k-g.out ] || \
+ { rm k-g.out && echo 'ntp_keyword.h is up to date.' ;}
+ @rm cmp1 cmp2
+ @echo 'keyword-gen and ntp_keyword.h are up to date.' > $@
+
+$(srcdir)/keyword-gen-utd: $(srcdir)/keyword-gen.c $(srcdir)/ntp_parser.h
+ $(MAKE) $(AM_MAKEFLAGS) k-g-u-submake # avoid explicit dependency
+ grep diff_ignore_line $(srcdir)/ntp_keyword.h > k-g-u
+ mv -f k-g-u $@
+
+$(srcdir)/ntp_keyword.h: $(srcdir)/keyword-gen-utd
+ @: do-nothing action to avoid default SCCS get
+ @: .h updated if needed by k-g-u-submake rule
+
+$(srcdir)/ntpd-opts.h: $(srcdir)/ntpd-opts.c
+ @: do-nothing action to avoid default SCCS get, .h built with .c
+
+$(srcdir)/ntpd-opts.c: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
+ $(run_ag) ntpd-opts.def
+
+###
+
+$(srcdir)/ntpd.1ntpdman: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
+ $(run_ag) -DMAN_SECTION=1ntpdman -Tagman-cmd.tpl ntpd-opts.def
+
+$(srcdir)/ntpd.man.in: $(srcdir)/ntpd.1ntpdman $(top_srcdir)/sntp/scripts/mansec2subst.sed
+ sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntpd.1ntpdman > $(srcdir)/ntpd.man.in+
+ mv $(srcdir)/ntpd.man.in+ $(srcdir)/ntpd.man.in
+
+###
+
+$(srcdir)/ntpd.1ntpdmdoc: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
+ $(run_ag) -DMAN_SECTION=1ntpdmdoc -Tagmdoc-cmd.tpl ntpd-opts.def
+
+$(srcdir)/ntpd.mdoc.in: $(srcdir)/ntpd.1ntpdmdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed
+ sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntpd.1ntpdmdoc > $(srcdir)/ntpd.mdoc.in+
+ mv $(srcdir)/ntpd.mdoc.in+ $(srcdir)/ntpd.mdoc.in
+
+###
+
+ntpd.$(NTPD_MS): $(srcdir)/ntpd.$(MANTAGFMT).in $(top_builddir)/config.status
+ $(top_builddir)/config.status --file=ntpd.$(NTPD_MS)+:$(srcdir)/ntpd.$(MANTAGFMT).in
+ mv ntpd.$(NTPD_MS)+ ntpd.$(NTPD_MS)
+
+###
+
+$(srcdir)/invoke-ntp.conf.menu: $(srcdir)/invoke-ntp.conf.texi
+ @: do-nothing action to avoid default SCCS get, .menu built with .texi
+
+$(srcdir)/invoke-ntp.conf.texi: $(srcdir)/ntp.conf.def $(std_def_list)
+ $(run_ag) -Tagtexi-file.tpl -DLEVEL=section ntp.conf.def
+
+$(srcdir)/invoke-ntp.keys.menu: $(srcdir)/invoke-ntp.keys.texi
+ @: do-nothing action to avoid default SCCS get, .menu built with .texi
+
+$(srcdir)/invoke-ntp.keys.texi: $(srcdir)/ntp.keys.def $(std_def_list)
+ $(run_ag) -Tagtexi-file.tpl -DLEVEL=section ntp.keys.def
+
+$(srcdir)/ntp.conf.html: $(srcdir)/ntp.conf.texi $(top_srcdir)/sntp/include/version.texi
+ cd $(srcdir) && ( makeinfo --force --html --no-split -o ntp.conf.html ntp.conf.texi || true )
+
+$(srcdir)/ntp.keys.html: $(srcdir)/ntp.keys.texi $(top_srcdir)/sntp/include/version.texi
+ cd $(srcdir) && ( makeinfo --force --html --no-split -o ntp.keys.html ntp.keys.texi || true )
+
+$(srcdir)/ntpd.html: $(srcdir)/ntpd.texi $(top_srcdir)/sntp/include/version.texi
+ cd $(srcdir) && ( makeinfo --force --html --no-split -o ntpd.html ntpd.texi || true )
+
+###
+
+$(srcdir)/ntp.conf.5man: $(srcdir)/ntp.conf.def $(std_def_list)
+ $(run_ag) -DMAN_SECTION=5man -Tagman-cmd.tpl ntp.conf.def
+
+$(srcdir)/ntp.conf.man.in: $(srcdir)/ntp.conf.5man $(top_srcdir)/sntp/scripts/mansec2subst.sed
+ sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp.conf.5man > $(srcdir)/ntp.conf.man.in+
+ mv $(srcdir)/ntp.conf.man.in+ $(srcdir)/ntp.conf.man.in
+
+###
+
+$(srcdir)/ntp.conf.5mdoc: $(srcdir)/ntp.conf.def $(std_def_list)
+ $(run_ag) -DMAN_SECTION=5mdoc -Tagmdoc-cmd.tpl ntp.conf.def
+
+$(srcdir)/ntp.conf.mdoc.in: $(srcdir)/ntp.conf.5mdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed
+ sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp.conf.5mdoc > $(srcdir)/ntp.conf.mdoc.in+
+ mv $(srcdir)/ntp.conf.mdoc.in+ $(srcdir)/ntp.conf.mdoc.in
+
+###
+
+ntp.conf.5: $(srcdir)/ntp.conf.$(MANTAGFMT).in $(top_builddir)/config.status
+ $(top_builddir)/config.status --file=ntp.conf.5+:$(srcdir)/ntp.conf.$(MANTAGFMT).in
+ mv ntp.conf.5+ ntp.conf.5
+
+###
+
+$(srcdir)/ntp.keys.5man: $(srcdir)/ntp.keys.def $(std_def_list)
+ $(run_ag) -DMAN_SECTION=5man -Tagman-file.tpl ntp.keys.def
+
+$(srcdir)/ntp.keys.man.in: $(srcdir)/ntp.keys.5man $(top_srcdir)/sntp/scripts/mansec2subst.sed
+ sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp.keys.5man > $(srcdir)/ntp.keys.man.in+
+ mv $(srcdir)/ntp.keys.man.in+ $(srcdir)/ntp.keys.man.in
+
+###
+
+$(srcdir)/ntp.keys.5mdoc: $(srcdir)/ntp.keys.def $(std_def_list)
+ $(run_ag) -DMAN_SECTION=5mdoc -Tagmdoc-file.tpl ntp.keys.def
+
+$(srcdir)/ntp.keys.mdoc.in: $(srcdir)/ntp.keys.5mdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed
+ sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp.keys.5mdoc > $(srcdir)/ntp.keys.mdoc.in+
+ mv $(srcdir)/ntp.keys.mdoc.in+ $(srcdir)/ntp.keys.mdoc.in
+
+###
+
+ntp.keys.5: $(srcdir)/ntp.keys.$(MANTAGFMT).in $(top_builddir)/config.status
+ $(top_builddir)/config.status --file=ntp.keys.5+:$(srcdir)/ntp.keys.$(MANTAGFMT).in
+ mv ntp.keys.5+ ntp.keys.5
+
+###
+
+$(srcdir)/invoke-ntpd.menu: $(srcdir)/invoke-ntpd.texi
+ @: do-nothing action to avoid default SCCS get, .menu built with .texi
+
+$(srcdir)/invoke-ntpd.texi: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
+ $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntpd-opts.def
+ $(top_srcdir)/scripts/build/check--help $@
+
+$(PROGRAMS): $(LDADD)
+
+compsave.conf: ntpd complete.conf
+ ./ntpd --configfile complete.conf --saveconfigquit $@
+
+check-saveconfig: complete.conf compsave.conf
+ -diff -u complete.conf compsave.conf
+ cmp complete.conf compsave.conf && echo stamp > $@
+
+../libntp/libntpsim.a:
+ cd ../libntp && $(MAKE) $(AM_MAKEFLAGS) libntpsim.a
+
+../libparse/libparse.a:
+ cd ../libparse && $(MAKE) $(AM_MAKEFLAGS) check-libparse
+
+$(top_srcdir)/sntp/scm-rev:
+ cd ../sntp && $(MAKE) $(AM_MAKEFLAGS) check-scm-rev
+
+version.c: $(ntpd_OBJECTS) ../libntp/libntp.a @LIBPARSE@ Makefile $(top_srcdir)/sntp/scm-rev
+ env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/build/mkver ntpd
+
+version.o: version.c
+ env CCACHE_DISABLE=1 $(COMPILE) -c version.c -o version.o
+
+install-exec-hook:
+ @test -z "${bin_PROGRAMS}${bin_SCRIPTS}" \
+ || for i in ${bin_PROGRAMS} ${bin_SCRIPTS} " "; do \
+ test ! -f ${sbindir}/$$i \
+ || echo "*** $$i is also in ${sbindir}!"; \
+ done
+ @test -z "${sbin_PROGRAMS}${asbin_SCRIPTS}" \
+ || for i in ${sbin_PROGRAMS} ${sbin_SCRIPTS} " "; do \
+ test ! -f ${bindir}/$$i \
+ || echo "*** $$i is also in ${bindir}!"; \
+ done
+
+#
+
+check-libopts: ../sntp/libopts/libopts.la
+ @echo stamp > $@
+
+../sntp/libopts/libopts.la:
+ -cd ../sntp/libopts && $(MAKE) $(AM_MAKEFLAGS) libopts.la
+
+check-libntp: ../libntp/libntp.a
+ @echo stamp > $@
+
+../libntp/libntp.a:
+ cd ../libntp && $(MAKE) $(AM_MAKEFLAGS) libntp.a
+$(DEPDIR)/deps-ver: $(top_srcdir)/deps-ver
+ @[ -f $@ ] || \
+ cp $(top_srcdir)/deps-ver $@
+ @[ -w $@ ] || \
+ chmod ug+w $@
+ @cmp $(top_srcdir)/deps-ver $@ > /dev/null || ( \
+ $(MAKE) $(AM_MAKEFLAGS) clean && \
+ echo -n "Prior $(subdir)/$(DEPDIR) version " && \
+ cat $@ && \
+ rm -rf $(DEPDIR) && \
+ mkdir $(DEPDIR) && \
+ case "$(top_builddir)" in \
+ .) \
+ ./config.status Makefile depfiles \
+ ;; \
+ *) \
+ cd "$(top_builddir)" && \
+ ./config.status $(subdir)/Makefile depfiles && \
+ cd $(subdir) \
+ ;; \
+ esac && \
+ echo -n "Cleaned $(subdir)/$(DEPDIR) version " && \
+ cat $(top_srcdir)/deps-ver \
+ )
+ cp $(top_srcdir)/deps-ver $@
+
+.deps-ver: $(top_srcdir)/deps-ver
+ @[ ! -d $(DEPDIR) ] || $(MAKE) $(AM_MAKEFLAGS) $(DEPDIR)/deps-ver
+ @touch $@
+
+#
+# depsver.mf included in Makefile.am for directories with .deps
+#
+# When building in the same directory with sources that change over
+# time, such as when tracking using bk, the .deps files can become
+# stale with respect to moved, deleted, or superceded headers. Most
+# commonly, this would exhibit as make reporting a failure to make a
+# header file which is no longer in the location given. To address
+# this issue, we use a deps-ver file which is updated with each change
+# that breaks old .deps files. A copy of deps-ver is made into
+# $(DEPDIR) if not already present. If $(DEPDIR)/deps-ver is present
+# with different contents than deps-ver, we make clean to ensure all
+# .o files built before the incompatible change are rebuilt along with
+# their updated .deps files, then remove $(DEPDIR) and recreate it as
+# empty stubs.
+#
+# It is normal when configured with --disable-dependency-tracking for
+# the DEPDIR to not have been created. For this reason, we use the
+# intermediate target .deps-ver, which invokes make recursively if
+# DEPDIR exists.
+#
+# If you modify depsver.mf, please make the changes to the master
+# copy, the one in sntp is copied by the bootstrap script from it.
+#
+# This comment block follows rather than leads the related code so that
+# it stays with it in the generated Makefile.in and Makefile.
+#
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/ntpd/check_y2k.c b/ntpd/check_y2k.c
new file mode 100644
index 0000000..12d1a59
--- /dev/null
+++ b/ntpd/check_y2k.c
@@ -0,0 +1,626 @@
+/* check_y2k.c -- test ntp code constructs for Y2K correctness Y2KFixes [*/
+
+ /*
+ Code invoked by `make check`. Not part of ntpd and not to be
+ installed.
+
+ On any code I even wonder about, I've cut and pasted the code
+ here and ran it as a test case just to be sure.
+
+ For code not in "ntpd" proper, we have tried to call most
+ repaired functions from herein to properly test them
+ (something never done before!). This has found several bugs,
+ not normal Y2K bugs, that will strike in Y2K so repair them
+ we did.
+
+ Program exits with 0 on success, 1 on Y2K failure (stdout messages).
+ Exit of 2 indicates internal logic bug detected OR failure of
+ what should be our correct formulas.
+
+ While "make check" should only check logic for source within that
+ specific directory, this check goes outside the scope of the local
+ directory. It's not a perfect world (besides, there is a lot of
+ interdependence here, and it really needs to be tested in
+ a controled order).
+ */
+
+/* { definitions lifted from ntpd.c to allow us to complie with
+ "#include ntp.h". I have not taken the time to reduce the clutter. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <stdio.h>
+#include <errno.h>
+#ifndef SYS_WINNT
+# if !defined(VMS) /*wjm*/
+# include <sys/param.h>
+# endif /* VMS */
+# if HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+# endif /* HAVE_SYS_SIGNAL_H */
+# include <sys/signal.h>
+# ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+# endif /* HAVE_SYS_IOCTL_H */
+# if !defined(VMS) /*wjm*/
+# include <sys/resource.h>
+# endif /* VMS */
+#else
+# include <signal.h>
+# include <process.h>
+# include <io.h>
+# include "../libntp/log.h"
+#endif /* SYS_WINNT */
+#if defined(HAVE_RTPRIO)
+# ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+# endif
+# ifdef HAVE_SYS_LOCK_H
+# include <sys/lock.h>
+# endif
+# include <sys/rtprio.h>
+#else
+# ifdef HAVE_PLOCK
+# ifdef HAVE_SYS_LOCK_H
+# include <sys/lock.h>
+# endif
+# endif
+#endif
+#if defined(HAVE_SCHED_SETSCHEDULER)
+# ifdef HAVE_SCHED_H
+# include <sched.h>
+# else
+# ifdef HAVE_SYS_SCHED_H
+# include <sys/sched.h>
+# endif
+# endif
+#endif
+#if defined(HAVE_SYS_MMAN_H)
+# include <sys/mman.h>
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#endif
+
+#ifdef SYS_DOMAINOS
+# include <apollo/base.h>
+#endif /* SYS_DOMAINOS */
+
+/* } end definitions lifted from ntpd.c */
+
+#include "ntp_calendar.h"
+#include "parse.h"
+
+#define GoodLeap(Year) (((Year)%4 || (!((Year)%100) && (Year)%400)) ? 0 : 13 )
+
+char const *progname = "check_y2k";
+
+long
+Days ( int Year ) /* return number of days since year "0" */
+{
+ long Return;
+ /* this is a known to be good algorithm */
+ Return = Year * 365; /* first aproximation to the value */
+ if ( Year >= 1 )
+ { /* see notes in libparse/parse.c if you want a PROPER
+ * **generic algorithm. */
+ Return += (Year+3) / 4; /* add in (too many) leap days */
+ Return -= (Year-1) / 100; /* reduce by (too many) centurys */
+ Return += (Year-1) / 400; /* get final answer */
+ }
+
+ return Return;
+}
+
+static int year0 = 1900; /* sarting year for NTP time */
+static int yearend; /* ending year we test for NTP time.
+ * 32-bit systems: through 2036, the
+ **year in which NTP time overflows.
+ * 64-bit systems: a reasonable upper
+ **limit (well, maybe somewhat beyond
+ **reasonable, but well before the
+ **max time, by which time the earth
+ **will be dead.) */
+static time_t Time;
+static struct tm LocalTime;
+
+#define Error(year) if ( (year)>=2036 && LocalTime.tm_year < 110 ) \
+ Warnings++; else Fatals++
+
+int
+main( void )
+{
+ int Fatals;
+ int Warnings;
+ int year;
+
+ Time = time( (time_t *)NULL )
+#ifdef TESTTIMEOFFSET
+ + test_time_offset
+#endif
+ ;
+ LocalTime = *localtime( &Time );
+
+ year = ( sizeof( u_long ) > 4 ) /* save max span using year as temp */
+ ? ( 400 * 3 ) /* three greater gregorian cycles */
+ : ((int)(0x7FFFFFFF / 365.242 / 24/60/60)* 2 ); /*32-bit limit*/
+ /* NOTE: will automacially expand test years on
+ * 64 bit machines.... this may cause some of the
+ * existing ntp logic to fail for years beyond
+ * 2036 (the current 32-bit limit). If all checks
+ * fail ONLY beyond year 2036 you may ignore such
+ * errors, at least for a decade or so. */
+ yearend = year0 + year;
+
+ puts( " internal self check" );
+ { /* verify our own logic used to verify repairs */
+ unsigned long days;
+
+ if ( year0 >= yearend )
+ {
+ fprintf( stdout, "year0=%d NOT LESS THAN yearend=%d (span=%d)\n",
+ (int)year0, (int)yearend, (int)year );
+ exit(2);
+ }
+
+ {
+ int save_year;
+
+ save_year = LocalTime.tm_year; /* save current year */
+
+ year = 1980;
+ LocalTime.tm_year = year - 1900;
+ Fatals = Warnings = 0;
+ Error(year); /* should increment Fatals */
+ if ( Fatals == 0 )
+ {
+ fprintf( stdout,
+ "%4d: %s(%d): FATAL DID NOT INCREMENT (Fatals=%d Warnings=%d)\n",
+ (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings );
+ exit(2);
+ }
+
+ year = 2100; /* test year > limit but CURRENT year < limit */
+ Fatals = Warnings = 0;
+ Error(year); /* should increment Fatals */
+ if ( Warnings == 0 )
+ {
+ fprintf( stdout,
+ "%4d: %s(%d): WARNING DID NOT INCREMENT (Fatals=%d Warnings=%d)\n",
+ (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings );
+ exit(2);
+ }
+ Fatals = Warnings = 0;
+ LocalTime.tm_year = year - 1900; /* everything > limit */
+ Error(1980); /* should increment Fatals */
+ if ( Fatals == 0 )
+ {
+ fprintf( stdout,
+ "%4d: %s(%d): FATALS DID NOT INCREMENT (Fatals=%d Warnings=%d)\n",
+ (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings );
+ exit(2);
+ }
+
+ LocalTime.tm_year = save_year;
+ }
+
+ days = 365+1; /* days in year 0 + 1 more day */
+ for ( year = 1; year <= 2500; year++ )
+ {
+ long Test;
+ Test = Days( year );
+ if ( days != Test )
+ {
+ fprintf( stdout, "%04d: Days() DAY COUNT ERROR: s/b=%ld was=%ld\n",
+ year, (long)days, (long)Test );
+ exit(2); /* would throw off many other tests */
+ }
+
+ Test = julian0(year); /* compare with julian0() macro */
+ if ( days != Test )
+ {
+ fprintf( stdout, "%04d: julian0() DAY COUNT ERROR: s/b=%ld was=%ld\n",
+ year, (long)days, (long)Test );
+ exit(2); /* would throw off many other tests */
+ }
+
+ days += 365;
+ if ( isleap_4(year) ) days++;
+ }
+
+ if ( isleap_4(1999) )
+ {
+ fprintf( stdout, "isleap_4(1999) REPORTED TRUE\n" );
+ exit(2);
+ }
+ if ( !isleap_4(2000) )
+ {
+ fprintf( stdout, "isleap_4(2000) REPORTED FALSE\n" );
+ exit(2);
+ }
+ if ( isleap_4(2001) )
+ {
+ fprintf( stdout, "isleap_4(1999) REPORTED TRUE\n" );
+ exit(2);
+ }
+
+ if ( !isleap_tm(2000-1900) )
+ {
+ fprintf( stdout, "isleap_tm(100) REPORTED FALSE\n" );
+ exit(2);
+ }
+ }
+
+ Fatals = Warnings = 0;
+
+ puts( " include/ntp.h" );
+ { /* test our new isleap_*() #define "functions" */
+
+ for ( year = 1400; year <= 2200; year++ )
+ {
+ int LeapSw;
+ int IsLeapSw;
+
+ LeapSw = GoodLeap(year);
+ IsLeapSw = isleap_4(year);
+
+ if ( !!LeapSw != !!IsLeapSw )
+ {
+ Error(year);
+ fprintf( stdout,
+ " %4d %2d %3d *** ERROR\n", year, LeapSw, IsLeapSw );
+ break;
+ }
+
+ IsLeapSw = isleap_tm(year-1900);
+
+ if ( !!LeapSw != !!IsLeapSw )
+ {
+ Error(year);
+ fprintf( stdout,
+ " %4d %2d %3d *** ERROR\n", year, LeapSw, IsLeapSw );
+ break;
+ }
+ }
+ }
+
+ puts( " include/ntp_calendar.h" );
+ { /* I belive this is good, but just to be sure... */
+
+ /* we are testing this #define */
+#define is_leapyear(y) (y%4 == 0 && !(y%100 == 0 && !(y%400 == 0)))
+
+ for ( year = 1400; year <= 2200; year++ )
+ {
+ int LeapSw;
+
+ LeapSw = GoodLeap(year);
+
+ if ( !(!LeapSw) != !(!is_leapyear(year)) )
+ {
+ Error(year);
+ fprintf( stdout,
+ " %4d %2d *** ERROR\n", year, LeapSw );
+ break;
+ }
+ }
+ }
+
+
+ puts( " libparse/parse.c" );
+ {
+ long Days1970; /* days from 1900 to 1970 */
+
+ struct ParseTime /* womp up a test structure to all cut/paste code */
+ {
+ int year;
+ } Clock_Time, *clock_time;
+
+ clock_time = &Clock_Time;
+
+ /* first test this #define */
+#define days_per_year(x) ((x) % 4 ? 365 : ((x % 400) ? ((x % 100) ? 366 : 365) : 366))
+
+ for ( year = 1400; year <= 2200; year++ )
+ {
+ int LeapSw;
+ int DayCnt;
+
+ LeapSw = GoodLeap(year);
+ DayCnt = (int)days_per_year(year);
+
+ if ( ( LeapSw ? 366 : 365 ) != DayCnt )
+ {
+ Error(year);
+ fprintf( stdout,
+ " days_per_year() %4d %2d %3d *** ERROR\n",
+ year, LeapSw, DayCnt );
+ break;
+ }
+ }
+
+ /* test (what is now julian0) calculations */
+
+ Days1970 = Days( 1970 ); /* get days since 1970 using a known good */
+
+ for ( year = 1970; year < yearend; year++ )
+ {
+ unsigned long t;
+ long DaysYear ;
+
+ clock_time->year = year;
+
+ /* here is the code we are testing, cut and pasted out of the source */
+#if 0 /* old BUGGY code that has Y2K (and many other) failures */
+ /* ghealton: this logic FAILED with great frequency when run
+ * over a period of time, including for year 2000. True, it
+ * had more successes than failures, but that's not really good
+ * enough for critical time distribution software.
+ * It is so awful I wonder if it has had a history of failure
+ * and fixes? */
+ t = (clock_time->year - 1970) * 365;
+ t += (clock_time->year >> 2) - (1970 >> 2);
+ t -= clock_time->year / 100 - 1970 / 100;
+ t += clock_time->year / 400 - 1970 / 400;
+
+ /* (immediate feare of rounding errors on integer
+ * **divisions proved well founded) */
+
+#else
+ /* my replacement, based on Days() above */
+ t = julian0(year) - julian0(1970);
+#endif
+
+ /* compare result in t against trusted calculations */
+ DaysYear = Days( year ); /* get days to this year */
+ if ( t != DaysYear - Days1970 )
+ {
+ Error(year);
+ fprintf( stdout,
+ " %4d 1970=%-8ld %4d=%-8ld %-3ld t=%-8ld *** ERROR ***\n",
+ year, (long)Days1970,
+ year,
+ (long)DaysYear,
+ (long)(DaysYear - Days1970),
+ (long)t );
+ }
+ }
+
+#if 1 /* { */
+ {
+ debug = 1; /* enable debugging */
+ for ( year = 1970; year < yearend; year++ )
+ { /* (limited by theory unix 2038 related bug lives by, but
+ * ends in yearend) */
+ clocktime_t ct;
+ time_t Observed;
+ time_t Expected;
+ u_long Flag;
+ unsigned long t;
+
+ ct.day = 1;
+ ct.month = 1;
+ ct.year = year;
+ ct.hour = ct.minute = ct.second = ct.usecond = 0;
+ ct.utcoffset = 0;
+ ct.utctime = 0;
+ ct.flags = 0;
+
+ Flag = 0;
+ Observed = parse_to_unixtime( &ct, &Flag );
+ if ( ct.year != year )
+ {
+ fprintf( stdout,
+ "%04d: parse_to_unixtime(,%d) CORRUPTED ct.year: was %d\n",
+ (int)year, (int)Flag, (int)ct.year );
+ Error(year);
+ break;
+ }
+ t = julian0(year) - julian0(1970); /* Julian day from 1970 */
+ Expected = t * 24 * 60 * 60;
+ if ( Observed != Expected || Flag )
+ { /* time difference */
+ fprintf( stdout,
+ "%04d: parse_to_unixtime(,%d) FAILURE: was=%lu s/b=%lu (%ld)\n",
+ year, (int)Flag,
+ (unsigned long)Observed, (unsigned long)Expected,
+ ((long)Observed - (long)Expected) );
+ Error(year);
+ break;
+ }
+
+ if ( year >= YEAR_PIVOT+1900 )
+ {
+ /* check year % 100 code we put into parse_to_unixtime() */
+ ct.utctime = 0;
+ ct.year = year % 100;
+ Flag = 0;
+
+ Observed = parse_to_unixtime( &ct, &Flag );
+
+ if ( Observed != Expected || Flag )
+ { /* time difference */
+ fprintf( stdout,
+"%04d: parse_to_unixtime(%d,%d) FAILURE: was=%lu s/b=%lu (%ld)\n",
+ year, (int)ct.year, (int)Flag,
+ (unsigned long)Observed, (unsigned long)Expected,
+ ((long)Observed - (long)Expected) );
+ Error(year);
+ break;
+ }
+
+ /* check year - 1900 code we put into parse_to_unixtime() */
+ ct.utctime = 0;
+ ct.year = year - 1900;
+ Flag = 0;
+
+ Observed = parse_to_unixtime( &ct, &Flag );
+
+ if ( Observed != Expected || Flag )
+ { /* time difference */
+ fprintf( stdout,
+"%04d: parse_to_unixtime(%d,%d) FAILURE: was=%lu s/b=%lu (%ld)\n",
+ year, (int)ct.year, (int)Flag,
+ (unsigned long)Observed, (unsigned long)Expected,
+ ((long)Observed - (long)Expected) );
+ Error(year);
+ break;
+ }
+
+
+ }
+ }
+#endif /* } */
+ }
+ }
+
+ puts( " libntp/caljulian.c" );
+ { /* test caljulian() */
+ struct calendar ot;
+ u_long ntp_time; /* NTP time */
+
+ year = year0; /* calculate the basic year */
+ printf( " starting year %04d\n", (int)year0 );
+ printf( " ending year %04d\n", (int)yearend );
+
+
+ ntp_time = julian0( year0 ); /* NTP starts in 1900-01-01 */
+#if DAY_NTP_STARTS == 693596
+ ntp_time -= 365; /* BIAS required for successful test */
+#endif
+ if ( DAY_NTP_STARTS != ntp_time )
+ {
+ Error(year);
+ fprintf( stdout,
+ "%04d: DAY_NTP_STARTS (%ld) NOT TRUE VALUE OF %ld (%ld)\n",
+ (int)year0,
+ (long)DAY_NTP_STARTS, (long)ntp_time,
+ (long)DAY_NTP_STARTS - (long)ntp_time );
+ }
+
+ for ( ; year < yearend; year++ )
+ {
+
+ /* 01-01 for the current year */
+ ntp_time = Days( year ) - Days( year0 ); /* days into NTP time */
+ ntp_time *= 24 * 60 * 60; /* convert into seconds */
+ caljulian( ntp_time, &ot ); /* convert January 1 */
+ if ( ot.year != year
+ || ot.month != 1
+ || ot.monthday != 1 )
+ {
+ Error(year);
+ fprintf( stdout, "%lu: EXPECTED %04d-01-01: FOUND %04d-%02d-%02d\n",
+ (unsigned long)ntp_time,
+ year,
+ (int)ot.year, (int)ot.month, (int)ot.monthday );
+ break;
+ }
+
+ ntp_time += (31 + 28-1) * ( 24 * 60 * 60 ); /* advance to 02-28 */
+ caljulian( ntp_time, &ot ); /* convert Feb 28 */
+ if ( ot.year != year
+ || ot.month != 2
+ || ot.monthday != 28 )
+ {
+ Error(year);
+ fprintf( stdout, "%lu: EXPECTED %04d-02-28: FOUND %04d-%02d-%02d\n",
+ (unsigned long)ntp_time,
+ year,
+ (int)ot.year, (int)ot.month, (int)ot.monthday );
+ break;
+ }
+
+ {
+ int m; /* expected month */
+ int d; /* expected day */
+
+ m = isleap_4(year) ? 2 : 3;
+ d = isleap_4(year) ? 29 : 1;
+
+ ntp_time += ( 24 * 60 * 60 ); /* advance to the next day */
+ caljulian( ntp_time, &ot ); /* convert this day */
+ if ( ot.year != year
+ || ot.month != m
+ || ot.monthday != d )
+ {
+ Error(year);
+ fprintf( stdout, "%lu: EXPECTED %04d-%02d-%02d: FOUND %04d-%02d-%02d\n",
+ (unsigned long)ntp_time,
+ year, m, d,
+ (int)ot.year, (int)ot.month, (int)ot.monthday );
+ break;
+ }
+
+ }
+ }
+ }
+
+ puts( " libntp/caltontp.c" );
+ { /* test caltontp() */
+ struct calendar ot;
+ u_long ntp_time; /* NTP time */
+
+ year = year0; /* calculate the basic year */
+ printf( " starting year %04d\n", (int)year0 );
+ printf( " ending year %04d\n", (int)yearend );
+
+
+ for ( ; year < yearend; year++ )
+ {
+ u_long ObservedNtp;
+
+ /* 01-01 for the current year */
+ ot.year = year;
+ ot.month = ot.monthday = 1; /* unused, but set anyway JIC */
+ ot.yearday = 1; /* this is the magic value used by caltontp() */
+ ot.hour = ot.minute = ot.second = 0;
+
+ ntp_time = Days( year ) - Days( year0 ); /* days into NTP time */
+ ntp_time *= 24 * 60 * 60; /* convert into seconds */
+ ObservedNtp = caltontp( &ot );
+ if ( ntp_time != ObservedNtp )
+ {
+ Error(year);
+ fprintf( stdout, "%d: EXPECTED %lu: FOUND %lu (%ld)\n",
+ (int)year,
+ (unsigned long)ntp_time, (unsigned long)ObservedNtp ,
+ (long)ntp_time - (long)ObservedNtp );
+
+ break;
+ }
+
+ /* now call caljulian as a type of failsafe supercheck */
+ caljulian( ObservedNtp, &ot ); /* convert January 1 */
+ if ( ot.year != year
+ || ot.month != 1
+ || ot.monthday != 1 )
+ {
+ Error(year);
+ fprintf( stdout, "%lu: caljulian FAILSAFE EXPECTED %04d-01-01: FOUND %04d-%02d-%02d\n",
+ (unsigned long)ObservedNtp,
+ year,
+ (int)ot.year, (int)ot.month, (int)ot.monthday );
+ break;
+ }
+ }
+ }
+
+ if ( Warnings > 0 )
+ fprintf( stdout, "%d WARNINGS\n", Warnings );
+ if ( Fatals > 0 )
+ fprintf( stdout, "%d FATAL ERRORS\n", Fatals );
+ return Fatals ? 1 : 0;
+}
+ /* Y2KFixes ] */
diff --git a/ntpd/cmd_args.c b/ntpd/cmd_args.c
new file mode 100644
index 0000000..045c0a9
--- /dev/null
+++ b/ntpd/cmd_args.c
@@ -0,0 +1,202 @@
+/*
+ * cmd_args.c = command-line argument processing
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#include "ntp_config.h"
+#include "ntp_cmdargs.h"
+
+#include "ntpd-opts.h"
+
+/*
+ * Definitions of things either imported from or exported to outside
+ */
+extern char const *progname;
+
+#ifdef HAVE_NETINFO
+extern int check_netinfo;
+#endif
+
+
+/*
+ * getCmdOpts - apply most command line options
+ *
+ * A few options are examined earlier in ntpd.c ntpdmain() and
+ * ports/winnt/ntpd/ntservice.c main().
+ */
+void
+getCmdOpts(
+ int argc,
+ char ** argv
+ )
+{
+ extern const char *config_file;
+ int errflg;
+
+ /*
+ * Initialize, initialize
+ */
+ errflg = 0;
+
+ if (ipv4_works && ipv6_works) {
+ if (HAVE_OPT( IPV4 ))
+ ipv6_works = 0;
+ else if (HAVE_OPT( IPV6 ))
+ ipv4_works = 0;
+ } else if (!ipv4_works && !ipv6_works) {
+ msyslog(LOG_ERR, "Neither IPv4 nor IPv6 networking detected, fatal.");
+ exit(1);
+ } else if (HAVE_OPT( IPV4 ) && !ipv4_works)
+ msyslog(LOG_WARNING, "-4/--ipv4 ignored, IPv4 networking not found.");
+ else if (HAVE_OPT( IPV6 ) && !ipv6_works)
+ msyslog(LOG_WARNING, "-6/--ipv6 ignored, IPv6 networking not found.");
+
+ if (HAVE_OPT( AUTHREQ ))
+ proto_config(PROTO_AUTHENTICATE, 1, 0., NULL);
+ else if (HAVE_OPT( AUTHNOREQ ))
+ proto_config(PROTO_AUTHENTICATE, 0, 0., NULL);
+
+ if (HAVE_OPT( BCASTSYNC ))
+ proto_config(PROTO_BROADCLIENT, 1, 0., NULL);
+
+ if (HAVE_OPT( CONFIGFILE )) {
+ config_file = OPT_ARG( CONFIGFILE );
+#ifdef HAVE_NETINFO
+ check_netinfo = 0;
+#endif
+ }
+
+ if (HAVE_OPT( DRIFTFILE ))
+ stats_config(STATS_FREQ_FILE, OPT_ARG( DRIFTFILE ));
+
+ if (HAVE_OPT( PANICGATE ))
+ allow_panic = TRUE;
+
+#ifdef HAVE_DROPROOT
+ if (HAVE_OPT( JAILDIR )) {
+ droproot = 1;
+ chrootdir = OPT_ARG( JAILDIR );
+ }
+#endif
+
+ if (HAVE_OPT( KEYFILE ))
+ getauthkeys(OPT_ARG( KEYFILE ));
+
+ if (HAVE_OPT( PIDFILE ))
+ stats_config(STATS_PID_FILE, OPT_ARG( PIDFILE ));
+
+ if (HAVE_OPT( QUIT ))
+ mode_ntpdate = TRUE;
+
+ if (HAVE_OPT( PROPAGATIONDELAY ))
+ do {
+ double tmp;
+ const char *my_ntp_optarg = OPT_ARG( PROPAGATIONDELAY );
+
+ if (sscanf(my_ntp_optarg, "%lf", &tmp) != 1) {
+ msyslog(LOG_ERR,
+ "command line broadcast delay value %s undecodable",
+ my_ntp_optarg);
+ } else {
+ proto_config(PROTO_BROADDELAY, 0, tmp, NULL);
+ }
+ } while (0);
+
+ if (HAVE_OPT( STATSDIR ))
+ stats_config(STATS_STATSDIR, OPT_ARG( STATSDIR ));
+
+ if (HAVE_OPT( TRUSTEDKEY )) {
+ int ct = STACKCT_OPT( TRUSTEDKEY );
+ const char** pp = STACKLST_OPT( TRUSTEDKEY );
+
+ do {
+ u_long tkey;
+ const char* p = *pp++;
+
+ tkey = (int)atol(p);
+ if (tkey == 0 || tkey > NTP_MAXKEY) {
+ msyslog(LOG_ERR,
+ "command line trusted key %s is invalid",
+ p);
+ } else {
+ authtrust(tkey, 1);
+ }
+ } while (--ct > 0);
+ }
+
+#ifdef HAVE_DROPROOT
+ if (HAVE_OPT( USER )) {
+ droproot = 1;
+ user = estrdup(OPT_ARG( USER ));
+ group = strrchr(user, ':');
+ if (group != NULL) {
+ size_t len;
+
+ *group++ = '\0'; /* get rid of the ':' */
+ len = group - user;
+ group = estrdup(group);
+ user = erealloc(user, len);
+ }
+ }
+#endif
+
+ if (HAVE_OPT( VAR )) {
+ int ct;
+ const char ** pp;
+ const char * v_assign;
+
+ ct = STACKCT_OPT( VAR );
+ pp = STACKLST_OPT( VAR );
+
+ do {
+ v_assign = *pp++;
+ set_sys_var(v_assign, strlen(v_assign) + 1, RW);
+ } while (--ct > 0);
+ }
+
+ if (HAVE_OPT( DVAR )) {
+ int ct = STACKCT_OPT( DVAR );
+ const char** pp = STACKLST_OPT( DVAR );
+
+ do {
+ const char* my_ntp_optarg = *pp++;
+
+ set_sys_var(my_ntp_optarg, strlen(my_ntp_optarg)+1,
+ (u_short) (RW | DEF));
+ } while (--ct > 0);
+ }
+
+ if (HAVE_OPT( SLEW ))
+ loop_config(LOOP_MAX, 600);
+
+ if (HAVE_OPT( UPDATEINTERVAL )) {
+ long val = OPT_VALUE_UPDATEINTERVAL;
+
+ if (val >= 0)
+ interface_interval = val;
+ else {
+ fprintf(stderr,
+ "command line interface update interval %ld must not be negative\n",
+ val);
+ msyslog(LOG_ERR,
+ "command line interface update interval %ld must not be negative",
+ val);
+ errflg++;
+ }
+ }
+
+
+ /* save list of servers from cmd line for config_peers() use */
+ if (argc > 0) {
+ cmdline_server_count = argc;
+ cmdline_servers = argv;
+ }
+
+ /* display usage & exit with any option processing errors */
+ if (errflg)
+ optionUsage(&ntpdOptions, 2); /* does not return */
+}
diff --git a/ntpd/complete.conf.in b/ntpd/complete.conf.in
new file mode 100644
index 0000000..a820094
--- /dev/null
+++ b/ntpd/complete.conf.in
@@ -0,0 +1,67 @@
+saveconfigdir "/etc/ntp/conf"
+driftfile "/etc/ntp.drift" 1e-7
+logfile "/var/log/ntp.log"
+leapfile "/etc/ntp.leapseconds"
+nonvolatile 1e-7
+ident "udent"
+logconfig =allall -allinfo -allevents -allstatistics -allstatus +allall -clockinfo -clockevents -clockstatistics -clockstatus +clockall -syncinfo -syncevents -syncstatistics -syncstatus +syncall -sysinfo -sysevents -sysstatistics -sysstatus +sysall
+statsdir "/etc/ntp/stats"
+statistics clockstats cryptostats loopstats peerstats protostats rawstats sysstats timingstats
+filegen clockstats file clockstats type none enable
+filegen cryptostats file cryptostats type pid link disable
+filegen loopstats file loopstats type day nolink enable
+filegen peerstats file peerstats type week enable
+filegen protostats file stats type month enable
+filegen rawstats file rawstats type year nolink enable
+filegen sysstats file sysstats type age enable
+filegen timingstats file timingstats type none disable
+crypto digest md5 host myhostname ident wedent pw cryptopass randfile /.rnd
+revoke 10
+keysdir "/etc/ntp/keys"
+keys "/etc/ntp.keys"
+trustedkey 1 2 3 4 5 6 7 8 9 10 11 12 (14 ... 16) 18 (32768 ... 65534)
+controlkey 12
+requestkey 12
+enable auth ntp monitor stats
+disable bclient calibrate kernel mode7
+tos beacon 3600 ceiling 16 cohort 0 floor 1 maxclock 10 maxdist 1.5 minclock 3 mindist 0.001 minsane 1 orphan 16 orphanwait 300
+rlimit@HAVE_RLIMIT_MEMLOCK@@HAVE_RLIMIT_STACK@
+tinker allan 1500 dispersion 15 freq 0 huffpuff 7200 panic 1000 step 0.128 stepout 900 tick 0.01
+broadcastclient
+server 127.127.1.0 mode 4294967295 prefer true
+fudge 127.127.1.0 time1 0 time2 1.1 stratum 7 refid Abcd
+pool 0.north-america.pool.ntp.org. iburst preempt
+server 1.north-america.pool.ntp.org. iburst
+server -4 2.north-america.pool.ntp.org. minpoll 6 maxpoll 10 iburst
+server -6 ntp.davehart.net. minpoll 6 maxpoll 10 version 5 burst iburst
+peer -6 davehart.broker.freenet6.net. ident "autokey-group" xleave autokey
+peer -4 192.168.192.168 key 1 noselect
+server [fe80::123%1]
+broadcast 192.168.192.255
+manycastclient 224.0.1.1
+manycastclient ff05::101
+manycastserver 224.0.1.1 ff05::101
+multicastclient 224.0.1.1 ff05::101
+mru maxage 64 mindepth 600 initalloc 600 initmem 16 incalloc 99 incmem 4 maxdepth 1024 maxmem 4096
+discard minimum 1 average 3 monitor 3000
+restrict default
+restrict default nomodify limited kod noserve nomrulist
+restrict source
+restrict source nomodify limited kod
+restrict trusted.host.name.example.com. nomodify
+restrict [fe80::1] mask [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]
+restrict 127.0.0.1 mask 255.255.255.255
+restrict ::1
+interface drop ipv6
+interface ignore ipv4
+interface drop wildcard
+interface listen eth0
+interface listen ipv6
+interface listen 192.168.192.0/24
+interface listen 192.168.193.1
+phone "ATDT13034944774" "ATDT12027621594"
+setvar varnondef = "this variable does not have default after the value"
+setvar vanity = "name plate" default
+trap 127.0.0.1 interface 127.0.0.1 port 1234
+trap 127.0.0.2
+reset allpeers auth ctl io mem sys timer
diff --git a/ntpd/declcond.h b/ntpd/declcond.h
new file mode 100644
index 0000000..870e5a7
--- /dev/null
+++ b/ntpd/declcond.h
@@ -0,0 +1,21 @@
+/*
+ * declcond.h - declarations conditionalized for ntpd
+ *
+ * The NTP reference implementation distribution includes two distinct
+ * declcond.h files, one in ntpd/ used only by ntpd, and another in
+ * include/ used by libntp and utilities. This relies on the source
+ * file's directory being ahead of include/ in the include search.
+ *
+ * The ntpd variant of declcond.h declares "debug" only #ifdef DEBUG,
+ * as the --disable-debugging version of ntpd should not reference
+ * "debug". The libntp and utilities variant always declares debug,
+ * as it is used in those codebases even without DEBUG defined.
+ */
+#ifndef DECLCOND_H
+#define DECLCOND_H
+
+#ifdef DEBUG /* uncommented in ntpd/declcond.h */
+extern int debug;
+#endif /* uncommented in ntpd/declcond.h */
+
+#endif /* DECLCOND_H */
diff --git a/ntpd/invoke-ntp.conf.menu b/ntpd/invoke-ntp.conf.menu
new file mode 100644
index 0000000..e5c6ef3
--- /dev/null
+++ b/ntpd/invoke-ntp.conf.menu
@@ -0,0 +1 @@
+* ntp.conf Notes:: Notes about ntp.conf
diff --git a/ntpd/invoke-ntp.conf.texi b/ntpd/invoke-ntp.conf.texi
new file mode 100644
index 0000000..78d3baf
--- /dev/null
+++ b/ntpd/invoke-ntp.conf.texi
@@ -0,0 +1,2634 @@
+@node ntp.conf Notes
+@section Notes about ntp.conf
+@pindex ntp.conf
+@cindex Network Time Protocol (NTP) daemon configuration file format
+@ignore
+#
+# EDIT THIS FILE WITH CAUTION (invoke-ntp.conf.texi)
+#
+# It has been AutoGen-ed December 2, 2014 at 08:56:50 AM by AutoGen 5.18.5pre4
+# From the definitions ntp.conf.def
+# and the template file agtexi-file.tpl
+@end ignore
+
+
+
+The
+@code{ntp.conf}
+configuration file is read at initial startup by the
+@code{ntpd(1ntpdmdoc)}
+daemon in order to specify the synchronization sources,
+modes and other related information.
+Usually, it is installed in the
+@file{/etc}
+directory,
+but could be installed elsewhere
+(see the daemon's
+@code{-c}
+command line option).
+
+The file format is similar to other
+@sc{unix}
+configuration files.
+Comments begin with a
+@quoteleft{}#@quoteright{}
+character and extend to the end of the line;
+blank lines are ignored.
+Configuration commands consist of an initial keyword
+followed by a list of arguments,
+some of which may be optional, separated by whitespace.
+Commands may not be continued over multiple lines.
+Arguments may be host names,
+host addresses written in numeric, dotted-quad form,
+integers, floating point numbers (when specifying times in seconds)
+and text strings.
+
+The rest of this page describes the configuration and control options.
+The
+"Notes on Configuring NTP and Setting up an NTP Subnet"
+page
+(available as part of the HTML documentation
+provided in
+@file{/usr/share/doc/ntp})
+contains an extended discussion of these options.
+In addition to the discussion of general
+@ref{Configuration Options},
+there are sections describing the following supported functionality
+and the options used to control it:
+@itemize @bullet
+@item
+@ref{Authentication Support}
+@item
+@ref{Monitoring Support}
+@item
+@ref{Access Control Support}
+@item
+@ref{Automatic NTP Configuration Options}
+@item
+@ref{Reference Clock Support}
+@item
+@ref{Miscellaneous Options}
+@end itemize
+
+Following these is a section describing
+@ref{Miscellaneous Options}.
+While there is a rich set of options available,
+the only required option is one or more
+@code{pool},
+@code{server},
+@code{peer},
+@code{broadcast}
+or
+@code{manycastclient}
+commands.
+@node Configuration Support
+@subsection Configuration Support
+Following is a description of the configuration commands in
+NTPv4.
+These commands have the same basic functions as in NTPv3 and
+in some cases new functions and new arguments.
+There are two
+classes of commands, configuration commands that configure a
+persistent association with a remote server or peer or reference
+clock, and auxiliary commands that specify environmental variables
+that control various related operations.
+@subsubsection Configuration Commands
+The various modes are determined by the command keyword and the
+type of the required IP address.
+Addresses are classed by type as
+(s) a remote server or peer (IPv4 class A, B and C), (b) the
+broadcast address of a local interface, (m) a multicast address (IPv4
+class D), or (r) a reference clock address (127.127.x.x).
+Note that
+only those options applicable to each command are listed below.
+Use
+of options not listed may not be caught as an error, but may result
+in some weird and even destructive behavior.
+
+If the Basic Socket Interface Extensions for IPv6 (RFC-2553)
+is detected, support for the IPv6 address family is generated
+in addition to the default support of the IPv4 address family.
+In a few cases, including the reslist billboard generated
+by ntpdc, IPv6 addresses are automatically generated.
+IPv6 addresses can be identified by the presence of colons
+@quotedblleft{}:@quotedblright{}
+in the address field.
+IPv6 addresses can be used almost everywhere where
+IPv4 addresses can be used,
+with the exception of reference clock addresses,
+which are always IPv4.
+
+Note that in contexts where a host name is expected, a
+@code{-4}
+qualifier preceding
+the host name forces DNS resolution to the IPv4 namespace,
+while a
+@code{-6}
+qualifier forces DNS resolution to the IPv6 namespace.
+See IPv6 references for the
+equivalent classes for that address family.
+@table @asis
+@item @code{pool} @kbd{address} @code{[@code{burst}]} @code{[@code{iburst}]} @code{[@code{version} @kbd{version}]} @code{[@code{prefer}]} @code{[@code{minpoll} @kbd{minpoll}]} @code{[@code{maxpoll} @kbd{maxpoll}]}
+@item @code{server} @kbd{address} @code{[@code{key} @kbd{key} @kbd{|} @code{autokey}]} @code{[@code{burst}]} @code{[@code{iburst}]} @code{[@code{version} @kbd{version}]} @code{[@code{prefer}]} @code{[@code{minpoll} @kbd{minpoll}]} @code{[@code{maxpoll} @kbd{maxpoll}]}
+@item @code{peer} @kbd{address} @code{[@code{key} @kbd{key} @kbd{|} @code{autokey}]} @code{[@code{version} @kbd{version}]} @code{[@code{prefer}]} @code{[@code{minpoll} @kbd{minpoll}]} @code{[@code{maxpoll} @kbd{maxpoll}]}
+@item @code{broadcast} @kbd{address} @code{[@code{key} @kbd{key} @kbd{|} @code{autokey}]} @code{[@code{version} @kbd{version}]} @code{[@code{prefer}]} @code{[@code{minpoll} @kbd{minpoll}]} @code{[@code{ttl} @kbd{ttl}]}
+@item @code{manycastclient} @kbd{address} @code{[@code{key} @kbd{key} @kbd{|} @code{autokey}]} @code{[@code{version} @kbd{version}]} @code{[@code{prefer}]} @code{[@code{minpoll} @kbd{minpoll}]} @code{[@code{maxpoll} @kbd{maxpoll}]} @code{[@code{ttl} @kbd{ttl}]}
+@end table
+
+These five commands specify the time server name or address to
+be used and the mode in which to operate.
+The
+@kbd{address}
+can be
+either a DNS name or an IP address in dotted-quad notation.
+Additional information on association behavior can be found in the
+"Association Management"
+page
+(available as part of the HTML documentation
+provided in
+@file{/usr/share/doc/ntp}).
+@table @asis
+@item @code{pool}
+For type s addresses, this command mobilizes a persistent
+client mode association with a number of remote servers.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+@item @code{server}
+For type s and r addresses, this command mobilizes a persistent
+client mode association with the specified remote server or local
+radio clock.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+This command should
+@emph{not}
+be used for type
+b or m addresses.
+@item @code{peer}
+For type s addresses (only), this command mobilizes a
+persistent symmetric-active mode association with the specified
+remote peer.
+In this mode the local clock can be synchronized to
+the remote peer or the remote peer can be synchronized to the local
+clock.
+This is useful in a network of servers where, depending on
+various failure scenarios, either the local or remote peer may be
+the better source of time.
+This command should NOT be used for type
+b, m or r addresses.
+@item @code{broadcast}
+For type b and m addresses (only), this
+command mobilizes a persistent broadcast mode association.
+Multiple
+commands can be used to specify multiple local broadcast interfaces
+(subnets) and/or multiple multicast groups.
+Note that local
+broadcast messages go only to the interface associated with the
+subnet specified, but multicast messages go to all interfaces.
+In broadcast mode the local server sends periodic broadcast
+messages to a client population at the
+@kbd{address}
+specified, which is usually the broadcast address on (one of) the
+local network(s) or a multicast address assigned to NTP.
+The IANA
+has assigned the multicast group address IPv4 224.0.1.1 and
+IPv6 ff05::101 (site local) exclusively to
+NTP, but other nonconflicting addresses can be used to contain the
+messages within administrative boundaries.
+Ordinarily, this
+specification applies only to the local server operating as a
+sender; for operation as a broadcast client, see the
+@code{broadcastclient}
+or
+@code{multicastclient}
+commands
+below.
+@item @code{manycastclient}
+For type m addresses (only), this command mobilizes a
+manycast client mode association for the multicast address
+specified.
+In this case a specific address must be supplied which
+matches the address used on the
+@code{manycastserver}
+command for
+the designated manycast servers.
+The NTP multicast address
+224.0.1.1 assigned by the IANA should NOT be used, unless specific
+means are taken to avoid spraying large areas of the Internet with
+these messages and causing a possibly massive implosion of replies
+at the sender.
+The
+@code{manycastserver}
+command specifies that the local server
+is to operate in client mode with the remote servers that are
+discovered as the result of broadcast/multicast messages.
+The
+client broadcasts a request message to the group address associated
+with the specified
+@kbd{address}
+and specifically enabled
+servers respond to these messages.
+The client selects the servers
+providing the best time and continues as with the
+@code{server}
+command.
+The remaining servers are discarded as if never
+heard.
+@end table
+
+Options:
+@table @asis
+@item @code{autokey}
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the autokey scheme
+described in
+@ref{Authentication Options}.
+@item @code{burst}
+when the server is reachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first and second packets
+can be changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to improve timekeeping quality
+with the
+@code{server}
+command and s addresses.
+@item @code{iburst}
+When the server is unreachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first two packets can be
+changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to speed the initial synchronization
+acquisition with the
+@code{server}
+command and s addresses and when
+@code{ntpd(1ntpdmdoc)}
+is started with the
+@code{-q}
+option.
+@item @code{key} @kbd{key}
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the specified
+@kbd{key}
+identifier with values from 1 to 65534, inclusive.
+The
+default is to include no encryption field.
+@item @code{minpoll} @kbd{minpoll}
+@item @code{maxpoll} @kbd{maxpoll}
+These options specify the minimum and maximum poll intervals
+for NTP messages, as a power of 2 in seconds
+The maximum poll
+interval defaults to 10 (1,024 s), but can be increased by the
+@code{maxpoll}
+option to an upper limit of 17 (36.4 h).
+The
+minimum poll interval defaults to 6 (64 s), but can be decreased by
+the
+@code{minpoll}
+option to a lower limit of 4 (16 s).
+@item @code{noselect}
+Marks the server as unused, except for display purposes.
+The server is discarded by the selection algroithm.
+@item @code{prefer}
+Marks the server as preferred.
+All other things being equal,
+this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+"Mitigation Rules and the prefer Keyword"
+page
+(available as part of the HTML documentation
+provided in
+@file{/usr/share/doc/ntp})
+for further information.
+@item @code{ttl} @kbd{ttl}
+This option is used only with broadcast server and manycast
+client modes.
+It specifies the time-to-live
+@kbd{ttl}
+to
+use on broadcast server and multicast server and the maximum
+@kbd{ttl}
+for the expanding ring search with manycast
+client packets.
+Selection of the proper value, which defaults to
+127, is something of a black art and should be coordinated with the
+network administrator.
+@item @code{version} @kbd{version}
+Specifies the version number to be used for outgoing NTP
+packets.
+Versions 1-4 are the choices, with version 4 the
+default.
+@end table
+@subsubsection Auxiliary Commands
+@table @asis
+@item @code{broadcastclient}
+This command enables reception of broadcast server messages to
+any local interface (type b) address.
+Upon receiving a message for
+the first time, the broadcast client measures the nominal server
+propagation delay using a brief client/server exchange with the
+server, then enters the broadcast client mode, in which it
+synchronizes to succeeding broadcast messages.
+Note that, in order
+to avoid accidental or malicious disruption in this mode, both the
+server and client should operate using symmetric-key or public-key
+authentication as described in
+@ref{Authentication Options}.
+@item @code{manycastserver} @kbd{address} @kbd{...}
+This command enables reception of manycast client messages to
+the multicast group address(es) (type m) specified.
+At least one
+address is required, but the NTP multicast address 224.0.1.1
+assigned by the IANA should NOT be used, unless specific means are
+taken to limit the span of the reply and avoid a possibly massive
+implosion at the original sender.
+Note that, in order to avoid
+accidental or malicious disruption in this mode, both the server
+and client should operate using symmetric-key or public-key
+authentication as described in
+@ref{Authentication Options}.
+@item @code{multicastclient} @kbd{address} @kbd{...}
+This command enables reception of multicast server messages to
+the multicast group address(es) (type m) specified.
+Upon receiving
+a message for the first time, the multicast client measures the
+nominal server propagation delay using a brief client/server
+exchange with the server, then enters the broadcast client mode, in
+which it synchronizes to succeeding multicast messages.
+Note that,
+in order to avoid accidental or malicious disruption in this mode,
+both the server and client should operate using symmetric-key or
+public-key authentication as described in
+@ref{Authentication Options}.
+@end table
+@node Authentication Support
+@subsection Authentication Support
+Authentication support allows the NTP client to verify that the
+server is in fact known and trusted and not an intruder intending
+accidentally or on purpose to masquerade as that server.
+The NTPv3
+specification RFC-1305 defines a scheme which provides
+cryptographic authentication of received NTP packets.
+Originally,
+this was done using the Data Encryption Standard (DES) algorithm
+operating in Cipher Block Chaining (CBC) mode, commonly called
+DES-CBC.
+Subsequently, this was replaced by the RSA Message Digest
+5 (MD5) algorithm using a private key, commonly called keyed-MD5.
+Either algorithm computes a message digest, or one-way hash, which
+can be used to verify the server has the correct private key and
+key identifier.
+
+NTPv4 retains the NTPv3 scheme, properly described as symmetric key
+cryptography and, in addition, provides a new Autokey scheme
+based on public key cryptography.
+Public key cryptography is generally considered more secure
+than symmetric key cryptography, since the security is based
+on a private value which is generated by each server and
+never revealed.
+With Autokey all key distribution and
+management functions involve only public values, which
+considerably simplifies key distribution and storage.
+Public key management is based on X.509 certificates,
+which can be provided by commercial services or
+produced by utility programs in the OpenSSL software library
+or the NTPv4 distribution.
+
+While the algorithms for symmetric key cryptography are
+included in the NTPv4 distribution, public key cryptography
+requires the OpenSSL software library to be installed
+before building the NTP distribution.
+Directions for doing that
+are on the Building and Installing the Distribution page.
+
+Authentication is configured separately for each association
+using the
+@code{key}
+or
+@code{autokey}
+subcommand on the
+@code{peer},
+@code{server},
+@code{broadcast}
+and
+@code{manycastclient}
+configuration commands as described in
+@ref{Configuration Options}
+page.
+The authentication
+options described below specify the locations of the key files,
+if other than default, which symmetric keys are trusted
+and the interval between various operations, if other than default.
+
+Authentication is always enabled,
+although ineffective if not configured as
+described below.
+If a NTP packet arrives
+including a message authentication
+code (MAC), it is accepted only if it
+passes all cryptographic checks.
+The
+checks require correct key ID, key value
+and message digest.
+If the packet has
+been modified in any way or replayed
+by an intruder, it will fail one or more
+of these checks and be discarded.
+Furthermore, the Autokey scheme requires a
+preliminary protocol exchange to obtain
+the server certificate, verify its
+credentials and initialize the protocol
+
+The
+@code{auth}
+flag controls whether new associations or
+remote configuration commands require cryptographic authentication.
+This flag can be set or reset by the
+@code{enable}
+and
+@code{disable}
+commands and also by remote
+configuration commands sent by a
+@code{ntpdc(1ntpdcmdoc)}
+program running in
+another machine.
+If this flag is enabled, which is the default
+case, new broadcast client and symmetric passive associations and
+remote configuration commands must be cryptographically
+authenticated using either symmetric key or public key cryptography.
+If this
+flag is disabled, these operations are effective
+even if not cryptographic
+authenticated.
+It should be understood
+that operating with the
+@code{auth}
+flag disabled invites a significant vulnerability
+where a rogue hacker can
+masquerade as a falseticker and seriously
+disrupt system timekeeping.
+It is
+important to note that this flag has no purpose
+other than to allow or disallow
+a new association in response to new broadcast
+and symmetric active messages
+and remote configuration commands and, in particular,
+the flag has no effect on
+the authentication process itself.
+
+An attractive alternative where multicast support is available
+is manycast mode, in which clients periodically troll
+for servers as described in the
+@ref{Automatic NTP Configuration Options}
+page.
+Either symmetric key or public key
+cryptographic authentication can be used in this mode.
+The principle advantage
+of manycast mode is that potential servers need not be
+configured in advance,
+since the client finds them during regular operation,
+and the configuration
+files for all clients can be identical.
+
+The security model and protocol schemes for
+both symmetric key and public key
+cryptography are summarized below;
+further details are in the briefings, papers
+and reports at the NTP project page linked from
+@code{http://www.ntp.org/}.
+@subsubsection Symmetric-Key Cryptography
+The original RFC-1305 specification allows any one of possibly
+65,534 keys, each distinguished by a 32-bit key identifier, to
+authenticate an association.
+The servers and clients involved must
+agree on the key and key identifier to
+authenticate NTP packets.
+Keys and
+related information are specified in a key
+file, usually called
+@file{ntp.keys},
+which must be distributed and stored using
+secure means beyond the scope of the NTP protocol itself.
+Besides the keys used
+for ordinary NTP associations,
+additional keys can be used as passwords for the
+@code{ntpq(1ntpqmdoc)}
+and
+@code{ntpdc(1ntpdcmdoc)}
+utility programs.
+
+When
+@code{ntpd(1ntpdmdoc)}
+is first started, it reads the key file specified in the
+@code{keys}
+configuration command and installs the keys
+in the key cache.
+However,
+individual keys must be activated with the
+@code{trusted}
+command before use.
+This
+allows, for instance, the installation of possibly
+several batches of keys and
+then activating or deactivating each batch
+remotely using
+@code{ntpdc(1ntpdcmdoc)}.
+This also provides a revocation capability that can be used
+if a key becomes compromised.
+The
+@code{requestkey}
+command selects the key used as the password for the
+@code{ntpdc(1ntpdcmdoc)}
+utility, while the
+@code{controlkey}
+command selects the key used as the password for the
+@code{ntpq(1ntpqmdoc)}
+utility.
+@subsubsection Public Key Cryptography
+NTPv4 supports the original NTPv3 symmetric key scheme
+described in RFC-1305 and in addition the Autokey protocol,
+which is based on public key cryptography.
+The Autokey Version 2 protocol described on the Autokey Protocol
+page verifies packet integrity using MD5 message digests
+and verifies the source with digital signatures and any of several
+digest/signature schemes.
+Optional identity schemes described on the Identity Schemes
+page and based on cryptographic challenge/response algorithms
+are also available.
+Using all of these schemes provides strong security against
+replay with or without modification, spoofing, masquerade
+and most forms of clogging attacks.
+
+The Autokey protocol has several modes of operation
+corresponding to the various NTP modes supported.
+Most modes use a special cookie which can be
+computed independently by the client and server,
+but encrypted in transmission.
+All modes use in addition a variant of the S-KEY scheme,
+in which a pseudo-random key list is generated and used
+in reverse order.
+These schemes are described along with an executive summary,
+current status, briefing slides and reading list on the
+@ref{Autonomous Authentication}
+page.
+
+The specific cryptographic environment used by Autokey servers
+and clients is determined by a set of files
+and soft links generated by the
+@code{ntp-keygen(1ntpkeygenmdoc)}
+program.
+This includes a required host key file,
+required certificate file and optional sign key file,
+leapsecond file and identity scheme files.
+The
+digest/signature scheme is specified in the X.509 certificate
+along with the matching sign key.
+There are several schemes
+available in the OpenSSL software library, each identified
+by a specific string such as
+@code{md5WithRSAEncryption},
+which stands for the MD5 message digest with RSA
+encryption scheme.
+The current NTP distribution supports
+all the schemes in the OpenSSL library, including
+those based on RSA and DSA digital signatures.
+
+NTP secure groups can be used to define cryptographic compartments
+and security hierarchies.
+It is important that every host
+in the group be able to construct a certificate trail to one
+or more trusted hosts in the same group.
+Each group
+host runs the Autokey protocol to obtain the certificates
+for all hosts along the trail to one or more trusted hosts.
+This requires the configuration file in all hosts to be
+engineered so that, even under anticipated failure conditions,
+the NTP subnet will form such that every group host can find
+a trail to at least one trusted host.
+@subsubsection Naming and Addressing
+It is important to note that Autokey does not use DNS to
+resolve addresses, since DNS can't be completely trusted
+until the name servers have synchronized clocks.
+The cryptographic name used by Autokey to bind the host identity
+credentials and cryptographic values must be independent
+of interface, network and any other naming convention.
+The name appears in the host certificate in either or both
+the subject and issuer fields, so protection against
+DNS compromise is essential.
+
+By convention, the name of an Autokey host is the name returned
+by the Unix
+@code{gethostname(2)}
+system call or equivalent in other systems.
+By the system design
+model, there are no provisions to allow alternate names or aliases.
+However, this is not to say that DNS aliases, different names
+for each interface, etc., are constrained in any way.
+
+It is also important to note that Autokey verifies authenticity
+using the host name, network address and public keys,
+all of which are bound together by the protocol specifically
+to deflect masquerade attacks.
+For this reason Autokey
+includes the source and destinatino IP addresses in message digest
+computations and so the same addresses must be available
+at both the server and client.
+For this reason operation
+with network address translation schemes is not possible.
+This reflects the intended robust security model where government
+and corporate NTP servers are operated outside firewall perimeters.
+@subsubsection Operation
+A specific combination of authentication scheme (none,
+symmetric key, public key) and identity scheme is called
+a cryptotype, although not all combinations are compatible.
+There may be management configurations where the clients,
+servers and peers may not all support the same cryptotypes.
+A secure NTPv4 subnet can be configured in many ways while
+keeping in mind the principles explained above and
+in this section.
+Note however that some cryptotype
+combinations may successfully interoperate with each other,
+but may not represent good security practice.
+
+The cryptotype of an association is determined at the time
+of mobilization, either at configuration time or some time
+later when a message of appropriate cryptotype arrives.
+When mobilized by a
+@code{server}
+or
+@code{peer}
+configuration command and no
+@code{key}
+or
+@code{autokey}
+subcommands are present, the association is not
+authenticated; if the
+@code{key}
+subcommand is present, the association is authenticated
+using the symmetric key ID specified; if the
+@code{autokey}
+subcommand is present, the association is authenticated
+using Autokey.
+
+When multiple identity schemes are supported in the Autokey
+protocol, the first message exchange determines which one is used.
+The client request message contains bits corresponding
+to which schemes it has available.
+The server response message
+contains bits corresponding to which schemes it has available.
+Both server and client match the received bits with their own
+and select a common scheme.
+
+Following the principle that time is a public value,
+a server responds to any client packet that matches
+its cryptotype capabilities.
+Thus, a server receiving
+an unauthenticated packet will respond with an unauthenticated
+packet, while the same server receiving a packet of a cryptotype
+it supports will respond with packets of that cryptotype.
+However, unconfigured broadcast or manycast client
+associations or symmetric passive associations will not be
+mobilized unless the server supports a cryptotype compatible
+with the first packet received.
+By default, unauthenticated associations will not be mobilized
+unless overridden in a decidedly dangerous way.
+
+Some examples may help to reduce confusion.
+Client Alice has no specific cryptotype selected.
+Server Bob has both a symmetric key file and minimal Autokey files.
+Alice's unauthenticated messages arrive at Bob, who replies with
+unauthenticated messages.
+Cathy has a copy of Bob's symmetric
+key file and has selected key ID 4 in messages to Bob.
+Bob verifies the message with his key ID 4.
+If it's the
+same key and the message is verified, Bob sends Cathy a reply
+authenticated with that key.
+If verification fails,
+Bob sends Cathy a thing called a crypto-NAK, which tells her
+something broke.
+She can see the evidence using the
+@code{ntpq(1ntpqmdoc)}
+program.
+
+Denise has rolled her own host key and certificate.
+She also uses one of the identity schemes as Bob.
+She sends the first Autokey message to Bob and they
+both dance the protocol authentication and identity steps.
+If all comes out okay, Denise and Bob continue as described above.
+
+It should be clear from the above that Bob can support
+all the girls at the same time, as long as he has compatible
+authentication and identity credentials.
+Now, Bob can act just like the girls in his own choice of servers;
+he can run multiple configured associations with multiple different
+servers (or the same server, although that might not be useful).
+But, wise security policy might preclude some cryptotype
+combinations; for instance, running an identity scheme
+with one server and no authentication with another might not be wise.
+@subsubsection Key Management
+The cryptographic values used by the Autokey protocol are
+incorporated as a set of files generated by the
+@code{ntp-keygen(1ntpkeygenmdoc)}
+utility program, including symmetric key, host key and
+public certificate files, as well as sign key, identity parameters
+and leapseconds files.
+Alternatively, host and sign keys and
+certificate files can be generated by the OpenSSL utilities
+and certificates can be imported from public certificate
+authorities.
+Note that symmetric keys are necessary for the
+@code{ntpq(1ntpqmdoc)}
+and
+@code{ntpdc(1ntpdcmdoc)}
+utility programs.
+The remaining files are necessary only for the
+Autokey protocol.
+
+Certificates imported from OpenSSL or public certificate
+authorities have certian limitations.
+The certificate should be in ASN.1 syntax, X.509 Version 3
+format and encoded in PEM, which is the same format
+used by OpenSSL.
+The overall length of the certificate encoded
+in ASN.1 must not exceed 1024 bytes.
+The subject distinguished
+name field (CN) is the fully qualified name of the host
+on which it is used; the remaining subject fields are ignored.
+The certificate extension fields must not contain either
+a subject key identifier or a issuer key identifier field;
+however, an extended key usage field for a trusted host must
+contain the value
+@code{trustRoot};.
+Other extension fields are ignored.
+@subsubsection Authentication Commands
+@table @asis
+@item @code{autokey} @code{[@kbd{logsec}]}
+Specifies the interval between regenerations of the session key
+list used with the Autokey protocol.
+Note that the size of the key
+list for each association depends on this interval and the current
+poll interval.
+The default value is 12 (4096 s or about 1.1 hours).
+For poll intervals above the specified interval, a session key list
+with a single entry will be regenerated for every message
+sent.
+@item @code{controlkey} @kbd{key}
+Specifies the key identifier to use with the
+@code{ntpq(1ntpqmdoc)}
+utility, which uses the standard
+protocol defined in RFC-1305.
+The
+@kbd{key}
+argument is
+the key identifier for a trusted key, where the value can be in the
+range 1 to 65,534, inclusive.
+@item @code{crypto} @code{[@code{cert} @kbd{file}]} @code{[@code{leap} @kbd{file}]} @code{[@code{randfile} @kbd{file}]} @code{[@code{host} @kbd{file}]} @code{[@code{sign} @kbd{file}]} @code{[@code{gq} @kbd{file}]} @code{[@code{gqpar} @kbd{file}]} @code{[@code{iffpar} @kbd{file}]} @code{[@code{mvpar} @kbd{file}]} @code{[@code{pw} @kbd{password}]}
+This command requires the OpenSSL library.
+It activates public key
+cryptography, selects the message digest and signature
+encryption scheme and loads the required private and public
+values described above.
+If one or more files are left unspecified,
+the default names are used as described above.
+Unless the complete path and name of the file are specified, the
+location of a file is relative to the keys directory specified
+in the
+@code{keysdir}
+command or default
+@file{/usr/local/etc}.
+Following are the subcommands:
+@table @asis
+@item @code{cert} @kbd{file}
+Specifies the location of the required host public certificate file.
+This overrides the link
+@file{ntpkey_cert_}@kbd{hostname}
+in the keys directory.
+@item @code{gqpar} @kbd{file}
+Specifies the location of the optional GQ parameters file.
+This
+overrides the link
+@file{ntpkey_gq_}@kbd{hostname}
+in the keys directory.
+@item @code{host} @kbd{file}
+Specifies the location of the required host key file.
+This overrides
+the link
+@file{ntpkey_key_}@kbd{hostname}
+in the keys directory.
+@item @code{iffpar} @kbd{file}
+Specifies the location of the optional IFF parameters file.This
+overrides the link
+@file{ntpkey_iff_}@kbd{hostname}
+in the keys directory.
+@item @code{leap} @kbd{file}
+Specifies the location of the optional leapsecond file.
+This overrides the link
+@file{ntpkey_leap}
+in the keys directory.
+@item @code{mvpar} @kbd{file}
+Specifies the location of the optional MV parameters file.
+This
+overrides the link
+@file{ntpkey_mv_}@kbd{hostname}
+in the keys directory.
+@item @code{pw} @kbd{password}
+Specifies the password to decrypt files containing private keys and
+identity parameters.
+This is required only if these files have been
+encrypted.
+@item @code{randfile} @kbd{file}
+Specifies the location of the random seed file used by the OpenSSL
+library.
+The defaults are described in the main text above.
+@item @code{sign} @kbd{file}
+Specifies the location of the optional sign key file.
+This overrides
+the link
+@file{ntpkey_sign_}@kbd{hostname}
+in the keys directory.
+If this file is
+not found, the host key is also the sign key.
+@end table
+@item @code{keys} @kbd{keyfile}
+Specifies the complete path and location of the MD5 key file
+containing the keys and key identifiers used by
+@code{ntpd(1ntpdmdoc)},
+@code{ntpq(1ntpqmdoc)}
+and
+@code{ntpdc(1ntpdcmdoc)}
+when operating with symmetric key cryptography.
+This is the same operation as the
+@code{-k}
+command line option.
+@item @code{keysdir} @kbd{path}
+This command specifies the default directory path for
+cryptographic keys, parameters and certificates.
+The default is
+@file{/usr/local/etc/}.
+@item @code{requestkey} @kbd{key}
+Specifies the key identifier to use with the
+@code{ntpdc(1ntpdcmdoc)}
+utility program, which uses a
+proprietary protocol specific to this implementation of
+@code{ntpd(1ntpdmdoc)}.
+The
+@kbd{key}
+argument is a key identifier
+for the trusted key, where the value can be in the range 1 to
+65,534, inclusive.
+@item @code{revoke} @kbd{logsec}
+Specifies the interval between re-randomization of certain
+cryptographic values used by the Autokey scheme, as a power of 2 in
+seconds.
+These values need to be updated frequently in order to
+deflect brute-force attacks on the algorithms of the scheme;
+however, updating some values is a relatively expensive operation.
+The default interval is 16 (65,536 s or about 18 hours).
+For poll
+intervals above the specified interval, the values will be updated
+for every message sent.
+@item @code{trustedkey} @kbd{key} @kbd{...}
+Specifies the key identifiers which are trusted for the
+purposes of authenticating peers with symmetric key cryptography,
+as well as keys used by the
+@code{ntpq(1ntpqmdoc)}
+and
+@code{ntpdc(1ntpdcmdoc)}
+programs.
+The authentication procedures require that both the local
+and remote servers share the same key and key identifier for this
+purpose, although different keys can be used with different
+servers.
+The
+@kbd{key}
+arguments are 32-bit unsigned
+integers with values from 1 to 65,534.
+@end table
+@subsubsection Error Codes
+The following error codes are reported via the NTP control
+and monitoring protocol trap mechanism.
+@table @asis
+@item 101
+(bad field format or length)
+The packet has invalid version, length or format.
+@item 102
+(bad timestamp)
+The packet timestamp is the same or older than the most recent received.
+This could be due to a replay or a server clock time step.
+@item 103
+(bad filestamp)
+The packet filestamp is the same or older than the most recent received.
+This could be due to a replay or a key file generation error.
+@item 104
+(bad or missing public key)
+The public key is missing, has incorrect format or is an unsupported type.
+@item 105
+(unsupported digest type)
+The server requires an unsupported digest/signature scheme.
+@item 106
+(mismatched digest types)
+Not used.
+@item 107
+(bad signature length)
+The signature length does not match the current public key.
+@item 108
+(signature not verified)
+The message fails the signature check.
+It could be bogus or signed by a
+different private key.
+@item 109
+(certificate not verified)
+The certificate is invalid or signed with the wrong key.
+@item 110
+(certificate not verified)
+The certificate is not yet valid or has expired or the signature could not
+be verified.
+@item 111
+(bad or missing cookie)
+The cookie is missing, corrupted or bogus.
+@item 112
+(bad or missing leapseconds table)
+The leapseconds table is missing, corrupted or bogus.
+@item 113
+(bad or missing certificate)
+The certificate is missing, corrupted or bogus.
+@item 114
+(bad or missing identity)
+The identity key is missing, corrupt or bogus.
+@end table
+@node Monitoring Support
+@subsection Monitoring Support
+@code{ntpd(1ntpdmdoc)}
+includes a comprehensive monitoring facility suitable
+for continuous, long term recording of server and client
+timekeeping performance.
+See the
+@code{statistics}
+command below
+for a listing and example of each type of statistics currently
+supported.
+Statistic files are managed using file generation sets
+and scripts in the
+@file{./scripts}
+directory of this distribution.
+Using
+these facilities and
+@sc{unix}
+@code{cron(8)}
+jobs, the data can be
+automatically summarized and archived for retrospective analysis.
+@subsubsection Monitoring Commands
+@table @asis
+@item @code{statistics} @kbd{name} @kbd{...}
+Enables writing of statistics records.
+Currently, eight kinds of
+@kbd{name}
+statistics are supported.
+@table @asis
+@item @code{clockstats}
+Enables recording of clock driver statistics information.
+Each update
+received from a clock driver appends a line of the following form to
+the file generation set named
+@code{clockstats}:
+@verbatim
+49213 525.624 127.127.4.1 93 226 00:08:29.606 D
+@end verbatim
+
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the
+clock address in dotted-quad notation.
+The final field shows the last
+timecode received from the clock in decoded ASCII format, where
+meaningful.
+In some clock drivers a good deal of additional information
+can be gathered and displayed as well.
+See information specific to each
+clock for further details.
+@item @code{cryptostats}
+This option requires the OpenSSL cryptographic software library.
+It
+enables recording of cryptographic public key protocol information.
+Each message received by the protocol module appends a line of the
+following form to the file generation set named
+@code{cryptostats}:
+@verbatim
+49213 525.624 127.127.4.1 message
+@end verbatim
+
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the peer
+address in dotted-quad notation, The final message field includes the
+message type and certain ancillary information.
+See the
+@ref{Authentication Options}
+section for further information.
+@item @code{loopstats}
+Enables recording of loop filter statistics information.
+Each
+update of the local clock outputs a line of the following form to
+the file generation set named
+@code{loopstats}:
+@verbatim
+50935 75440.031 0.000006019 13.778190 0.000351733 0.0133806
+@end verbatim
+
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next five fields
+show time offset (seconds), frequency offset (parts per million -
+PPM), RMS jitter (seconds), Allan deviation (PPM) and clock
+discipline time constant.
+@item @code{peerstats}
+Enables recording of peer statistics information.
+This includes
+statistics records of all peers of a NTP server and of special
+signals, where present and configured.
+Each valid update appends a
+line of the following form to the current element of a file
+generation set named
+@code{peerstats}:
+@verbatim
+48773 10847.650 127.127.4.1 9714 -0.001605376 0.000000000 0.001424877 0.000958674
+@end verbatim
+
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the peer address in dotted-quad notation and status,
+respectively.
+The status field is encoded in hex in the format
+described in Appendix A of the NTP specification RFC 1305.
+The final four fields show the offset,
+delay, dispersion and RMS jitter, all in seconds.
+@item @code{rawstats}
+Enables recording of raw-timestamp statistics information.
+This
+includes statistics records of all peers of a NTP server and of
+special signals, where present and configured.
+Each NTP message
+received from a peer or clock driver appends a line of the
+following form to the file generation set named
+@code{rawstats}:
+@verbatim
+50928 2132.543 128.4.1.1 128.4.1.20 3102453281.584327000 3102453281.58622800031 02453332.540806000 3102453332.541458000
+@end verbatim
+
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the remote peer or clock address followed by the local address
+in dotted-quad notation.
+The final four fields show the originate,
+receive, transmit and final NTP timestamps in order.
+The timestamp
+values are as received and before processing by the various data
+smoothing and mitigation algorithms.
+@item @code{sysstats}
+Enables recording of ntpd statistics counters on a periodic basis.
+Each
+hour a line of the following form is appended to the file generation
+set named
+@code{sysstats}:
+@verbatim
+50928 2132.543 36000 81965 0 9546 56 71793 512 540 10 147
+@end verbatim
+
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The remaining ten fields show
+the statistics counter values accumulated since the last generated
+line.
+@table @asis
+@item Time since restart @code{36000}
+Time in hours since the system was last rebooted.
+@item Packets received @code{81965}
+Total number of packets received.
+@item Packets processed @code{0}
+Number of packets received in response to previous packets sent
+@item Current version @code{9546}
+Number of packets matching the current NTP version.
+@item Previous version @code{56}
+Number of packets matching the previous NTP version.
+@item Bad version @code{71793}
+Number of packets matching neither NTP version.
+@item Access denied @code{512}
+Number of packets denied access for any reason.
+@item Bad length or format @code{540}
+Number of packets with invalid length, format or port number.
+@item Bad authentication @code{10}
+Number of packets not verified as authentic.
+@item Rate exceeded @code{147}
+Number of packets discarded due to rate limitation.
+@end table
+@item @code{statsdir} @kbd{directory_path}
+Indicates the full path of a directory where statistics files
+should be created (see below).
+This keyword allows
+the (otherwise constant)
+@code{filegen}
+filename prefix to be modified for file generation sets, which
+is useful for handling statistics logs.
+@item @code{filegen} @kbd{name} @code{[@code{file} @kbd{filename}]} @code{[@code{type} @kbd{typename}]} @code{[@code{link} | @code{nolink}]} @code{[@code{enable} | @code{disable}]}
+Configures setting of generation file set name.
+Generation
+file sets provide a means for handling files that are
+continuously growing during the lifetime of a server.
+Server statistics are a typical example for such files.
+Generation file sets provide access to a set of files used
+to store the actual data.
+At any time at most one element
+of the set is being written to.
+The type given specifies
+when and how data will be directed to a new element of the set.
+This way, information stored in elements of a file set
+that are currently unused are available for administrational
+operations without the risk of disturbing the operation of ntpd.
+(Most important: they can be removed to free space for new data
+produced.)
+
+Note that this command can be sent from the
+@code{ntpdc(1ntpdcmdoc)}
+program running at a remote location.
+@table @asis
+@item @code{name}
+This is the type of the statistics records, as shown in the
+@code{statistics}
+command.
+@item @code{file} @kbd{filename}
+This is the file name for the statistics records.
+Filenames of set
+members are built from three concatenated elements
+@code{prefix},
+@code{filename}
+and
+@code{suffix}:
+@table @asis
+@item @code{prefix}
+This is a constant filename path.
+It is not subject to
+modifications via the
+@kbd{filegen}
+option.
+It is defined by the
+server, usually specified as a compile-time constant.
+It may,
+however, be configurable for individual file generation sets
+via other commands.
+For example, the prefix used with
+@kbd{loopstats}
+and
+@kbd{peerstats}
+generation can be configured using the
+@kbd{statsdir}
+option explained above.
+@item @code{filename}
+This string is directly concatenated to the prefix mentioned
+above (no intervening
+@quoteleft{}/@quoteright{}).
+This can be modified using
+the file argument to the
+@kbd{filegen}
+statement.
+No
+@file{..}
+elements are
+allowed in this component to prevent filenames referring to
+parts outside the filesystem hierarchy denoted by
+@kbd{prefix}.
+@item @code{suffix}
+This part is reflects individual elements of a file set.
+It is
+generated according to the type of a file set.
+@end table
+@item @code{type} @kbd{typename}
+A file generation set is characterized by its type.
+The following
+types are supported:
+@table @asis
+@item @code{none}
+The file set is actually a single plain file.
+@item @code{pid}
+One element of file set is used per incarnation of a ntpd
+server.
+This type does not perform any changes to file set
+members during runtime, however it provides an easy way of
+separating files belonging to different
+@code{ntpd(1ntpdmdoc)}
+server incarnations.
+The set member filename is built by appending a
+@quoteleft{}.@quoteright{}
+to concatenated
+@kbd{prefix}
+and
+@kbd{filename}
+strings, and
+appending the decimal representation of the process ID of the
+@code{ntpd(1ntpdmdoc)}
+server process.
+@item @code{day}
+One file generation set element is created per day.
+A day is
+defined as the period between 00:00 and 24:00 UTC.
+The file set
+member suffix consists of a
+@quoteleft{}.@quoteright{}
+and a day specification in
+the form
+@code{YYYYMMdd}.
+@code{YYYY}
+is a 4-digit year number (e.g., 1992).
+@code{MM}
+is a two digit month number.
+@code{dd}
+is a two digit day number.
+Thus, all information written at 10 December 1992 would end up
+in a file named
+@kbd{prefix}
+@kbd{filename}.19921210.
+@item @code{week}
+Any file set member contains data related to a certain week of
+a year.
+The term week is defined by computing day-of-year
+modulo 7.
+Elements of such a file generation set are
+distinguished by appending the following suffix to the file set
+filename base: A dot, a 4-digit year number, the letter
+@code{W},
+and a 2-digit week number.
+For example, information from January,
+10th 1992 would end up in a file with suffix
+.No . Ns Ar 1992W1 .
+@item @code{month}
+One generation file set element is generated per month.
+The
+file name suffix consists of a dot, a 4-digit year number, and
+a 2-digit month.
+@item @code{year}
+One generation file element is generated per year.
+The filename
+suffix consists of a dot and a 4 digit year number.
+@item @code{age}
+This type of file generation sets changes to a new element of
+the file set every 24 hours of server operation.
+The filename
+suffix consists of a dot, the letter
+@code{a},
+and an 8-digit number.
+This number is taken to be the number of seconds the server is
+running at the start of the corresponding 24-hour period.
+Information is only written to a file generation by specifying
+@code{enable};
+output is prevented by specifying
+@code{disable}.
+@end table
+@item @code{link} | @code{nolink}
+It is convenient to be able to access the current element of a file
+generation set by a fixed name.
+This feature is enabled by
+specifying
+@code{link}
+and disabled using
+@code{nolink}.
+If link is specified, a
+hard link from the current file set element to a file without
+suffix is created.
+When there is already a file with this name and
+the number of links of this file is one, it is renamed appending a
+dot, the letter
+@code{C},
+and the pid of the ntpd server process.
+When the
+number of links is greater than one, the file is unlinked.
+This
+allows the current file to be accessed by a constant name.
+@item @code{enable} @code{|} @code{disable}
+Enables or disables the recording function.
+@end table
+@end table
+@end table
+@node Access Control Support
+@subsection Access Control Support
+The
+@code{ntpd(1ntpdmdoc)}
+daemon implements a general purpose address/mask based restriction
+list.
+The list contains address/match entries sorted first
+by increasing address values and and then by increasing mask values.
+A match occurs when the bitwise AND of the mask and the packet
+source address is equal to the bitwise AND of the mask and
+address in the list.
+The list is searched in order with the
+last match found defining the restriction flags associated
+with the entry.
+Additional information and examples can be found in the
+"Notes on Configuring NTP and Setting up a NTP Subnet"
+page
+(available as part of the HTML documentation
+provided in
+@file{/usr/share/doc/ntp}).
+
+The restriction facility was implemented in conformance
+with the access policies for the original NSFnet backbone
+time servers.
+Later the facility was expanded to deflect
+cryptographic and clogging attacks.
+While this facility may
+be useful for keeping unwanted or broken or malicious clients
+from congesting innocent servers, it should not be considered
+an alternative to the NTP authentication facilities.
+Source address based restrictions are easily circumvented
+by a determined cracker.
+
+Clients can be denied service because they are explicitly
+included in the restrict list created by the restrict command
+or implicitly as the result of cryptographic or rate limit
+violations.
+Cryptographic violations include certificate
+or identity verification failure; rate limit violations generally
+result from defective NTP implementations that send packets
+at abusive rates.
+Some violations cause denied service
+only for the offending packet, others cause denied service
+for a timed period and others cause the denied service for
+an indefinate period.
+When a client or network is denied access
+for an indefinate period, the only way at present to remove
+the restrictions is by restarting the server.
+@subsubsection The Kiss-of-Death Packet
+Ordinarily, packets denied service are simply dropped with no
+further action except incrementing statistics counters.
+Sometimes a
+more proactive response is needed, such as a server message that
+explicitly requests the client to stop sending and leave a message
+for the system operator.
+A special packet format has been created
+for this purpose called the "kiss-of-death" (KoD) packet.
+KoD packets have the leap bits set unsynchronized and stratum set
+to zero and the reference identifier field set to a four-byte
+ASCII code.
+If the
+@code{noserve}
+or
+@code{notrust}
+flag of the matching restrict list entry is set,
+the code is "DENY"; if the
+@code{limited}
+flag is set and the rate limit
+is exceeded, the code is "RATE".
+Finally, if a cryptographic violation occurs, the code is "CRYP".
+
+A client receiving a KoD performs a set of sanity checks to
+minimize security exposure, then updates the stratum and
+reference identifier peer variables, sets the access
+denied (TEST4) bit in the peer flash variable and sends
+a message to the log.
+As long as the TEST4 bit is set,
+the client will send no further packets to the server.
+The only way at present to recover from this condition is
+to restart the protocol at both the client and server.
+This
+happens automatically at the client when the association times out.
+It will happen at the server only if the server operator cooperates.
+@subsubsection Access Control Commands
+@table @asis
+@item @code{discard} @code{[@code{average} @kbd{avg}]} @code{[@code{minimum} @kbd{min}]} @code{[@code{monitor} @kbd{prob}]}
+Set the parameters of the
+@code{limited}
+facility which protects the server from
+client abuse.
+The
+@code{average}
+subcommand specifies the minimum average packet
+spacing, while the
+@code{minimum}
+subcommand specifies the minimum packet spacing.
+Packets that violate these minima are discarded
+and a kiss-o'-death packet returned if enabled.
+The default
+minimum average and minimum are 5 and 2, respectively.
+The monitor subcommand specifies the probability of discard
+for packets that overflow the rate-control window.
+@item @code{restrict} @code{address} @code{[@code{mask} @kbd{mask}]} @code{[@kbd{flag} @kbd{...}]}
+The
+@kbd{address}
+argument expressed in
+dotted-quad form is the address of a host or network.
+Alternatively, the
+@kbd{address}
+argument can be a valid host DNS name.
+The
+@kbd{mask}
+argument expressed in dotted-quad form defaults to
+@code{255.255.255.255},
+meaning that the
+@kbd{address}
+is treated as the address of an individual host.
+A default entry (address
+@code{0.0.0.0},
+mask
+@code{0.0.0.0})
+is always included and is always the first entry in the list.
+Note that text string
+@code{default},
+with no mask option, may
+be used to indicate the default entry.
+In the current implementation,
+@code{flag}
+always
+restricts access, i.e., an entry with no flags indicates that free
+access to the server is to be given.
+The flags are not orthogonal,
+in that more restrictive flags will often make less restrictive
+ones redundant.
+The flags can generally be classed into two
+categories, those which restrict time service and those which
+restrict informational queries and attempts to do run-time
+reconfiguration of the server.
+One or more of the following flags
+may be specified:
+@table @asis
+@item @code{ignore}
+Deny packets of all kinds, including
+@code{ntpq(1ntpqmdoc)}
+and
+@code{ntpdc(1ntpdcmdoc)}
+queries.
+@item @code{kod}
+If this flag is set when an access violation occurs, a kiss-o'-death
+(KoD) packet is sent.
+KoD packets are rate limited to no more than one
+per second.
+If another KoD packet occurs within one second after the
+last one, the packet is dropped.
+@item @code{limited}
+Deny service if the packet spacing violates the lower limits specified
+in the discard command.
+A history of clients is kept using the
+monitoring capability of
+@code{ntpd(1ntpdmdoc)}.
+Thus, monitoring is always active as
+long as there is a restriction entry with the
+@code{limited}
+flag.
+@item @code{lowpriotrap}
+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.
+@item @code{nomodify}
+Deny
+@code{ntpq(1ntpqmdoc)}
+and
+@code{ntpdc(1ntpdcmdoc)}
+queries which attempt to modify the state of the
+server (i.e., run time reconfiguration).
+Queries which return
+information are permitted.
+@item @code{noquery}
+Deny
+@code{ntpq(1ntpqmdoc)}
+and
+@code{ntpdc(1ntpdcmdoc)}
+queries.
+Time service is not affected.
+@item @code{nopeer}
+Deny packets which would result in mobilizing a new association.
+This
+includes broadcast and symmetric active packets when a configured
+association does not exist.
+It also includes
+@code{pool}
+associations, so if you want to use servers from a
+@code{pool}
+directive and also want to use
+@code{nopeer}
+by default, you'll want a
+@code{restrict source ...} @code{line} @code{as} @code{well} @code{that} @code{does}
+@item not
+include the
+@code{nopeer}
+directive.
+@item @code{noserve}
+Deny all packets except
+@code{ntpq(1ntpqmdoc)}
+and
+@code{ntpdc(1ntpdcmdoc)}
+queries.
+@item @code{notrap}
+Decline to provide mode 6 control message trap service to matching
+hosts.
+The trap service is a subsystem of the ntpdq control message
+protocol which is intended for use by remote event logging programs.
+@item @code{notrust}
+Deny service unless the packet is cryptographically authenticated.
+@item @code{ntpport}
+This is actually a match algorithm modifier, rather than a
+restriction flag.
+Its presence causes the restriction entry to be
+matched only if the source port in the packet is the standard NTP
+UDP port (123).
+Both
+@code{ntpport}
+and
+@code{non-ntpport}
+may
+be specified.
+The
+@code{ntpport}
+is considered more specific and
+is sorted later in the list.
+@item @code{version}
+Deny packets that do not match the current NTP version.
+@end table
+
+Default restriction list entries with the flags ignore, interface,
+ntpport, for each of the local host's interface addresses are
+inserted into the table at startup to prevent the server
+from attempting to synchronize to its own time.
+A default entry is also always present, though if it is
+otherwise unconfigured; no flags are associated
+with the default entry (i.e., everything besides your own
+NTP server is unrestricted).
+@end table
+@node Automatic NTP Configuration Options
+@subsection Automatic NTP Configuration Options
+@subsubsection Manycasting
+Manycasting is a automatic discovery and configuration paradigm
+new to NTPv4.
+It is intended as a means for a multicast client
+to troll the nearby network neighborhood to find cooperating
+manycast servers, validate them using cryptographic means
+and evaluate their time values with respect to other servers
+that might be lurking in the vicinity.
+The intended result is that each manycast client mobilizes
+client associations with some number of the "best"
+of the nearby manycast servers, yet automatically reconfigures
+to sustain this number of servers should one or another fail.
+
+Note that the manycasting paradigm does not coincide
+with the anycast paradigm described in RFC-1546,
+which is designed to find a single server from a clique
+of servers providing the same service.
+The manycast paradigm is designed to find a plurality
+of redundant servers satisfying defined optimality criteria.
+
+Manycasting can be used with either symmetric key
+or public key cryptography.
+The public key infrastructure (PKI)
+offers the best protection against compromised keys
+and is generally considered stronger, at least with relatively
+large key sizes.
+It is implemented using the Autokey protocol and
+the OpenSSL cryptographic library available from
+@code{http://www.openssl.org/}.
+The library can also be used with other NTPv4 modes
+as well and is highly recommended, especially for broadcast modes.
+
+A persistent manycast client association is configured
+using the manycastclient command, which is similar to the
+server command but with a multicast (IPv4 class
+@code{D}
+or IPv6 prefix
+@code{FF})
+group address.
+The IANA has designated IPv4 address 224.1.1.1
+and IPv6 address FF05::101 (site local) for NTP.
+When more servers are needed, it broadcasts manycast
+client messages to this address at the minimum feasible rate
+and minimum feasible time-to-live (TTL) hops, depending
+on how many servers have already been found.
+There can be as many manycast client associations
+as different group address, each one serving as a template
+for a future ephemeral unicast client/server association.
+
+Manycast servers configured with the
+@code{manycastserver}
+command listen on the specified group address for manycast
+client messages.
+Note the distinction between manycast client,
+which actively broadcasts messages, and manycast server,
+which passively responds to them.
+If a manycast server is
+in scope of the current TTL and is itself synchronized
+to a valid source and operating at a stratum level equal
+to or lower than the manycast client, it replies to the
+manycast client message with an ordinary unicast server message.
+
+The manycast client receiving this message mobilizes
+an ephemeral client/server association according to the
+matching manycast client template, but only if cryptographically
+authenticated and the server stratum is less than or equal
+to the client stratum.
+Authentication is explicitly required
+and either symmetric key or public key (Autokey) can be used.
+Then, the client polls the server at its unicast address
+in burst mode in order to reliably set the host clock
+and validate the source.
+This normally results
+in a volley of eight client/server at 2-s intervals
+during which both the synchronization and cryptographic
+protocols run concurrently.
+Following the volley,
+the client runs the NTP intersection and clustering
+algorithms, which act to discard all but the "best"
+associations according to stratum and synchronization
+distance.
+The surviving associations then continue
+in ordinary client/server mode.
+
+The manycast client polling strategy is designed to reduce
+as much as possible the volume of manycast client messages
+and the effects of implosion due to near-simultaneous
+arrival of manycast server messages.
+The strategy is determined by the
+@code{manycastclient},
+@code{tos}
+and
+@code{ttl}
+configuration commands.
+The manycast poll interval is
+normally eight times the system poll interval,
+which starts out at the
+@code{minpoll}
+value specified in the
+@code{manycastclient},
+command and, under normal circumstances, increments to the
+@code{maxpolll}
+value specified in this command.
+Initially, the TTL is
+set at the minimum hops specified by the ttl command.
+At each retransmission the TTL is increased until reaching
+the maximum hops specified by this command or a sufficient
+number client associations have been found.
+Further retransmissions use the same TTL.
+
+The quality and reliability of the suite of associations
+discovered by the manycast client is determined by the NTP
+mitigation algorithms and the
+@code{minclock}
+and
+@code{minsane}
+values specified in the
+@code{tos}
+configuration command.
+At least
+@code{minsane}
+candidate servers must be available and the mitigation
+algorithms produce at least
+@code{minclock}
+survivors in order to synchronize the clock.
+Byzantine agreement principles require at least four
+candidates in order to correctly discard a single falseticker.
+For legacy purposes,
+@code{minsane}
+defaults to 1 and
+@code{minclock}
+defaults to 3.
+For manycast service
+@code{minsane}
+should be explicitly set to 4, assuming at least that
+number of servers are available.
+
+If at least
+@code{minclock}
+servers are found, the manycast poll interval is immediately
+set to eight times
+@code{maxpoll}.
+If less than
+@code{minclock}
+servers are found when the TTL has reached the maximum hops,
+the manycast poll interval is doubled.
+For each transmission
+after that, the poll interval is doubled again until
+reaching the maximum of eight times
+@code{maxpoll}.
+Further transmissions use the same poll interval and
+TTL values.
+Note that while all this is going on,
+each client/server association found is operating normally
+it the system poll interval.
+
+Administratively scoped multicast boundaries are normally
+specified by the network router configuration and,
+in the case of IPv6, the link/site scope prefix.
+By default, the increment for TTL hops is 32 starting
+from 31; however, the
+@code{ttl}
+configuration command can be
+used to modify the values to match the scope rules.
+
+It is often useful to narrow the range of acceptable
+servers which can be found by manycast client associations.
+Because manycast servers respond only when the client
+stratum is equal to or greater than the server stratum,
+primary (stratum 1) servers fill find only primary servers
+in TTL range, which is probably the most common objective.
+However, unless configured otherwise, all manycast clients
+in TTL range will eventually find all primary servers
+in TTL range, which is probably not the most common
+objective in large networks.
+The
+@code{tos}
+command can be used to modify this behavior.
+Servers with stratum below
+@code{floor}
+or above
+@code{ceiling}
+specified in the
+@code{tos}
+command are strongly discouraged during the selection
+process; however, these servers may be temporally
+accepted if the number of servers within TTL range is
+less than
+@code{minclock}.
+
+The above actions occur for each manycast client message,
+which repeats at the designated poll interval.
+However, once the ephemeral client association is mobilized,
+subsequent manycast server replies are discarded,
+since that would result in a duplicate association.
+If during a poll interval the number of client associations
+falls below
+@code{minclock},
+all manycast client prototype associations are reset
+to the initial poll interval and TTL hops and operation
+resumes from the beginning.
+It is important to avoid
+frequent manycast client messages, since each one requires
+all manycast servers in TTL range to respond.
+The result could well be an implosion, either minor or major,
+depending on the number of servers in range.
+The recommended value for
+@code{maxpoll}
+is 12 (4,096 s).
+
+It is possible and frequently useful to configure a host
+as both manycast client and manycast server.
+A number of hosts configured this way and sharing a common
+group address will automatically organize themselves
+in an optimum configuration based on stratum and
+synchronization distance.
+For example, consider an NTP
+subnet of two primary servers and a hundred or more
+dependent clients.
+With two exceptions, all servers
+and clients have identical configuration files including both
+@code{multicastclient}
+and
+@code{multicastserver}
+commands using, for instance, multicast group address
+239.1.1.1.
+The only exception is that each primary server
+configuration file must include commands for the primary
+reference source such as a GPS receiver.
+
+The remaining configuration files for all secondary
+servers and clients have the same contents, except for the
+@code{tos}
+command, which is specific for each stratum level.
+For stratum 1 and stratum 2 servers, that command is
+not necessary.
+For stratum 3 and above servers the
+@code{floor}
+value is set to the intended stratum number.
+Thus, all stratum 3 configuration files are identical,
+all stratum 4 files are identical and so forth.
+
+Once operations have stabilized in this scenario,
+the primary servers will find the primary reference source
+and each other, since they both operate at the same
+stratum (1), but not with any secondary server or client,
+since these operate at a higher stratum.
+The secondary
+servers will find the servers at the same stratum level.
+If one of the primary servers loses its GPS receiver,
+it will continue to operate as a client and other clients
+will time out the corresponding association and
+re-associate accordingly.
+
+Some administrators prefer to avoid running
+@code{ntpd(1ntpdmdoc)}
+continuously and run either
+@code{ntpdate(8)}
+or
+@code{ntpd(1ntpdmdoc)}
+@code{-q}
+as a cron job.
+In either case the servers must be
+configured in advance and the program fails if none are
+available when the cron job runs.
+A really slick
+application of manycast is with
+@code{ntpd(1ntpdmdoc)}
+@code{-q}.
+The program wakes up, scans the local landscape looking
+for the usual suspects, selects the best from among
+the rascals, sets the clock and then departs.
+Servers do not have to be configured in advance and
+all clients throughout the network can have the same
+configuration file.
+@subsubsection Manycast Interactions with Autokey
+Each time a manycast client sends a client mode packet
+to a multicast group address, all manycast servers
+in scope generate a reply including the host name
+and status word.
+The manycast clients then run
+the Autokey protocol, which collects and verifies
+all certificates involved.
+Following the burst interval
+all but three survivors are cast off,
+but the certificates remain in the local cache.
+It often happens that several complete signing trails
+from the client to the primary servers are collected in this way.
+
+About once an hour or less often if the poll interval
+exceeds this, the client regenerates the Autokey key list.
+This is in general transparent in client/server mode.
+However, about once per day the server private value
+used to generate cookies is refreshed along with all
+manycast client associations.
+In this case all
+cryptographic values including certificates is refreshed.
+If a new certificate has been generated since
+the last refresh epoch, it will automatically revoke
+all prior certificates that happen to be in the
+certificate cache.
+At the same time, the manycast
+scheme starts all over from the beginning and
+the expanding ring shrinks to the minimum and increments
+from there while collecting all servers in scope.
+@subsubsection Manycast Options
+@table @asis
+@item @code{tos} @code{[@code{ceiling} @kbd{ceiling} | @code{cohort} @code{@{} @code{0} | @code{1} @code{@}} | @code{floor} @kbd{floor} | @code{minclock} @kbd{minclock} | @code{minsane} @kbd{minsane}]}
+This command affects the clock selection and clustering
+algorithms.
+It can be used to select the quality and
+quantity of peers used to synchronize the system clock
+and is most useful in manycast mode.
+The variables operate
+as follows:
+@table @asis
+@item @code{ceiling} @kbd{ceiling}
+Peers with strata above
+@code{ceiling}
+will be discarded if there are at least
+@code{minclock}
+peers remaining.
+This value defaults to 15, but can be changed
+to any number from 1 to 15.
+@item @code{cohort} @code{@{0 | 1@}}
+This is a binary flag which enables (0) or disables (1)
+manycast server replies to manycast clients with the same
+stratum level.
+This is useful to reduce implosions where
+large numbers of clients with the same stratum level
+are present.
+The default is to enable these replies.
+@item @code{floor} @kbd{floor}
+Peers with strata below
+@code{floor}
+will be discarded if there are at least
+@code{minclock}
+peers remaining.
+This value defaults to 1, but can be changed
+to any number from 1 to 15.
+@item @code{minclock} @kbd{minclock}
+The clustering algorithm repeatedly casts out outlyer
+associations until no more than
+@code{minclock}
+associations remain.
+This value defaults to 3,
+but can be changed to any number from 1 to the number of
+configured sources.
+@item @code{minsane} @kbd{minsane}
+This is the minimum number of candidates available
+to the clock selection algorithm in order to produce
+one or more truechimers for the clustering algorithm.
+If fewer than this number are available, the clock is
+undisciplined and allowed to run free.
+The default is 1
+for legacy purposes.
+However, according to principles of
+Byzantine agreement,
+@code{minsane}
+should be at least 4 in order to detect and discard
+a single falseticker.
+@end table
+@item @code{ttl} @kbd{hop} @kbd{...}
+This command specifies a list of TTL values in increasing
+order, up to 8 values can be specified.
+In manycast mode these values are used in turn
+in an expanding-ring search.
+The default is eight
+multiples of 32 starting at 31.
+@end table
+@node Reference Clock Support
+@subsection Reference Clock Support
+The NTP Version 4 daemon supports some three dozen different radio,
+satellite and modem reference clocks plus a special pseudo-clock
+used for backup or when no other clock source is available.
+Detailed descriptions of individual device drivers and options can
+be found in the
+"Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+@file{/usr/share/doc/ntp}).
+Additional information can be found in the pages linked
+there, including the
+"Debugging Hints for Reference Clock Drivers"
+and
+"How To Write a Reference Clock Driver"
+pages
+(available as part of the HTML documentation
+provided in
+@file{/usr/share/doc/ntp}).
+In addition, support for a PPS
+signal is available as described in the
+"Pulse-per-second (PPS) Signal Interfacing"
+page
+(available as part of the HTML documentation
+provided in
+@file{/usr/share/doc/ntp}).
+Many
+drivers support special line discipline/streams modules which can
+significantly improve the accuracy using the driver.
+These are
+described in the
+"Line Disciplines and Streams Drivers"
+page
+(available as part of the HTML documentation
+provided in
+@file{/usr/share/doc/ntp}).
+
+A reference clock will generally (though not always) be a radio
+timecode receiver which is synchronized to a source of standard
+time such as the services offered by the NRC in Canada and NIST and
+USNO in the US.
+The interface between the computer and the timecode
+receiver is device dependent, but is usually a serial port.
+A
+device driver specific to each reference clock must be selected and
+compiled in the distribution; however, most common radio, satellite
+and modem clocks are included by default.
+Note that an attempt to
+configure a reference clock when the driver has not been compiled
+or the hardware port has not been appropriately configured results
+in a scalding remark to the system log file, but is otherwise non
+hazardous.
+
+For the purposes of configuration,
+@code{ntpd(1ntpdmdoc)}
+treats
+reference clocks in a manner analogous to normal NTP peers as much
+as possible.
+Reference clocks are identified by a syntactically
+correct but invalid IP address, in order to distinguish them from
+normal NTP peers.
+Reference clock addresses are of the form
+@code{127.127.}@kbd{t}.@kbd{u},
+where
+@kbd{t}
+is an integer
+denoting the clock type and
+@kbd{u}
+indicates the unit
+number in the range 0-3.
+While it may seem overkill, it is in fact
+sometimes useful to configure multiple reference clocks of the same
+type, in which case the unit numbers must be unique.
+
+The
+@code{server}
+command is used to configure a reference
+clock, where the
+@kbd{address}
+argument in that command
+is the clock address.
+The
+@code{key},
+@code{version}
+and
+@code{ttl}
+options are not used for reference clock support.
+The
+@code{mode}
+option is added for reference clock support, as
+described below.
+The
+@code{prefer}
+option can be useful to
+persuade the server to cherish a reference clock with somewhat more
+enthusiasm than other reference clocks or peers.
+Further
+information on this option can be found in the
+"Mitigation Rules and the prefer Keyword"
+(available as part of the HTML documentation
+provided in
+@file{/usr/share/doc/ntp})
+page.
+The
+@code{minpoll}
+and
+@code{maxpoll}
+options have
+meaning only for selected clock drivers.
+See the individual clock
+driver document pages for additional information.
+
+The
+@code{fudge}
+command is used to provide additional
+information for individual clock drivers and normally follows
+immediately after the
+@code{server}
+command.
+The
+@kbd{address}
+argument specifies the clock address.
+The
+@code{refid}
+and
+@code{stratum}
+options can be used to
+override the defaults for the device.
+There are two optional
+device-dependent time offsets and four flags that can be included
+in the
+@code{fudge}
+command as well.
+
+The stratum number of a reference clock is by default zero.
+Since the
+@code{ntpd(1ntpdmdoc)}
+daemon adds one to the stratum of each
+peer, a primary server ordinarily displays an external stratum of
+one.
+In order to provide engineered backups, it is often useful to
+specify the reference clock stratum as greater than zero.
+The
+@code{stratum}
+option is used for this purpose.
+Also, in cases
+involving both a reference clock and a pulse-per-second (PPS)
+discipline signal, it is useful to specify the reference clock
+identifier as other than the default, depending on the driver.
+The
+@code{refid}
+option is used for this purpose.
+Except where noted,
+these options apply to all clock drivers.
+@subsubsection Reference Clock Commands
+@table @asis
+@item @code{server} @code{127.127.}@kbd{t}.@kbd{u} @code{[@code{prefer}]} @code{[@code{mode} @kbd{int}]} @code{[@code{minpoll} @kbd{int}]} @code{[@code{maxpoll} @kbd{int}]}
+This command can be used to configure reference clocks in
+special ways.
+The options are interpreted as follows:
+@table @asis
+@item @code{prefer}
+Marks the reference clock as preferred.
+All other things being
+equal, this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+"Mitigation Rules and the prefer Keyword"
+page
+(available as part of the HTML documentation
+provided in
+@file{/usr/share/doc/ntp})
+for further information.
+@item @code{mode} @kbd{int}
+Specifies a mode number which is interpreted in a
+device-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+@item @code{minpoll} @kbd{int}
+@item @code{maxpoll} @kbd{int}
+These options specify the minimum and maximum polling interval
+for reference clock messages, as a power of 2 in seconds
+For
+most directly connected reference clocks, both
+@code{minpoll}
+and
+@code{maxpoll}
+default to 6 (64 s).
+For modem reference clocks,
+@code{minpoll}
+defaults to 10 (17.1 m) and
+@code{maxpoll}
+defaults to 14 (4.5 h).
+The allowable range is 4 (16 s) to 17 (36.4 h) inclusive.
+@end table
+@item @code{fudge} @code{127.127.}@kbd{t}.@kbd{u} @code{[@code{time1} @kbd{sec}]} @code{[@code{time2} @kbd{sec}]} @code{[@code{stratum} @kbd{int}]} @code{[@code{refid} @kbd{string}]} @code{[@code{mode} @kbd{int}]} @code{[@code{flag1} @code{0} @code{|} @code{1}]} @code{[@code{flag2} @code{0} @code{|} @code{1}]} @code{[@code{flag3} @code{0} @code{|} @code{1}]} @code{[@code{flag4} @code{0} @code{|} @code{1}]}
+This command can be used to configure reference clocks in
+special ways.
+It must immediately follow the
+@code{server}
+command which configures the driver.
+Note that the same capability
+is possible at run time using the
+@code{ntpdc(1ntpdcmdoc)}
+program.
+The options are interpreted as
+follows:
+@table @asis
+@item @code{time1} @kbd{sec}
+Specifies a constant to be added to the time offset produced by
+the driver, a fixed-point decimal number in seconds.
+This is used
+as a calibration constant to adjust the nominal time offset of a
+particular clock to agree with an external standard, such as a
+precision PPS signal.
+It also provides a way to correct a
+systematic error or bias due to serial port or operating system
+latencies, different cable lengths or receiver internal delay.
+The
+specified offset is in addition to the propagation delay provided
+by other means, such as internal DIPswitches.
+Where a calibration
+for an individual system and driver is available, an approximate
+correction is noted in the driver documentation pages.
+Note: in order to facilitate calibration when more than one
+radio clock or PPS signal is supported, a special calibration
+feature is available.
+It takes the form of an argument to the
+@code{enable}
+command described in
+@ref{Miscellaneous Options}
+page and operates as described in the
+"Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+@file{/usr/share/doc/ntp}).
+@item @code{time2} @kbd{secs}
+Specifies a fixed-point decimal number in seconds, which is
+interpreted in a driver-dependent way.
+See the descriptions of
+specific drivers in the
+"Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+@file{/usr/share/doc/ntp}).
+@item @code{stratum} @kbd{int}
+Specifies the stratum number assigned to the driver, an integer
+between 0 and 15.
+This number overrides the default stratum number
+ordinarily assigned by the driver itself, usually zero.
+@item @code{refid} @kbd{string}
+Specifies an ASCII string of from one to four characters which
+defines the reference identifier used by the driver.
+This string
+overrides the default identifier ordinarily assigned by the driver
+itself.
+@item @code{mode} @kbd{int}
+Specifies a mode number which is interpreted in a
+device-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+@item @code{flag1} @code{0} @code{|} @code{1}
+@item @code{flag2} @code{0} @code{|} @code{1}
+@item @code{flag3} @code{0} @code{|} @code{1}
+@item @code{flag4} @code{0} @code{|} @code{1}
+These four flags are used for customizing the clock driver.
+The
+interpretation of these values, and whether they are used at all,
+is a function of the particular clock driver.
+However, by
+convention
+@code{flag4}
+is used to enable recording monitoring
+data to the
+@code{clockstats}
+file configured with the
+@code{filegen}
+command.
+Further information on the
+@code{filegen}
+command can be found in
+@ref{Monitoring Options}.
+@end table
+@end table
+@node Miscellaneous Options
+@subsection Miscellaneous Options
+@table @asis
+@item @code{broadcastdelay} @kbd{seconds}
+The broadcast and multicast modes require a special calibration
+to determine the network delay between the local and remote
+servers.
+Ordinarily, this is done automatically by the initial
+protocol exchanges between the client and server.
+In some cases,
+the calibration procedure may fail due to network or server access
+controls, for example.
+This command specifies the default delay to
+be used under these circumstances.
+Typically (for Ethernet), a
+number between 0.003 and 0.007 seconds is appropriate.
+The default
+when this command is not used is 0.004 seconds.
+@item @code{calldelay} @kbd{delay}
+This option controls the delay in seconds between the first and second
+packets sent in burst or iburst mode to allow additional time for a modem
+or ISDN call to complete.
+@item @code{driftfile} @kbd{driftfile}
+This command specifies the complete path and name of the file used to
+record the frequency of the local clock oscillator.
+This is the same
+operation as the
+@code{-f}
+command line option.
+If the file exists, it is read at
+startup in order to set the initial frequency and then updated once per
+hour with the current frequency computed by the daemon.
+If the file name is
+specified, but the file itself does not exist, the starts with an initial
+frequency of zero and creates the file when writing it for the first time.
+If this command is not given, the daemon will always start with an initial
+frequency of zero.
+
+The file format consists of a single line containing a single
+floating point number, which records the frequency offset measured
+in parts-per-million (PPM).
+The file is updated by first writing
+the current drift value into a temporary file and then renaming
+this file to replace the old version.
+This implies that
+@code{ntpd(1ntpdmdoc)}
+must have write permission for the directory the
+drift file is located in, and that file system links, symbolic or
+otherwise, should be avoided.
+@item @code{enable} @code{[@code{auth} | @code{bclient} | @code{calibrate} | @code{kernel} | @code{mode7} | @code{monitor} | @code{ntp} | @code{stats}]}
+@item @code{disable} @code{[@code{auth} | @code{bclient} | @code{calibrate} | @code{kernel} | @code{mode7} | @code{monitor} | @code{ntp} | @code{stats}]}
+Provides a way to enable or disable various server options.
+Flags not mentioned are unaffected.
+Note that all of these flags
+can be controlled remotely using the
+@code{ntpdc(1ntpdcmdoc)}
+utility program.
+@table @asis
+@item @code{auth}
+Enables the server to synchronize with unconfigured peers only if the
+peer has been correctly authenticated using either public key or
+private key cryptography.
+The default for this flag is
+@code{enable}.
+@item @code{bclient}
+Enables the server to listen for a message from a broadcast or
+multicast server, as in the
+@code{multicastclient}
+command with default
+address.
+The default for this flag is
+@code{disable}.
+@item @code{calibrate}
+Enables the calibrate feature for reference clocks.
+The default for
+this flag is
+@code{disable}.
+@item @code{kernel}
+Enables the kernel time discipline, if available.
+The default for this
+flag is
+@code{enable}
+if support is available, otherwise
+@code{disable}.
+@item @code{mode7}
+Enables processing of NTP mode 7 implementation-specific requests
+which are used by the deprecated
+@code{ntpdc(1ntpdcmdoc)}
+program.
+The default for this flag is disable.
+This flag is excluded from runtime configuration using
+@code{ntpq(1ntpqmdoc)}.
+The
+@code{ntpq(1ntpqmdoc)}
+program provides the same capabilities as
+@code{ntpdc(1ntpdcmdoc)}
+using standard mode 6 requests.
+@item @code{monitor}
+Enables the monitoring facility.
+See the
+@code{ntpdc(1ntpdcmdoc)}
+program
+and the
+@code{monlist}
+command or further information.
+The
+default for this flag is
+@code{enable}.
+@item @code{ntp}
+Enables time and frequency discipline.
+In effect, this switch opens and
+closes the feedback loop, which is useful for testing.
+The default for
+this flag is
+@code{enable}.
+@item @code{stats}
+Enables the statistics facility.
+See the
+@ref{Monitoring Options}
+section for further information.
+The default for this flag is
+@code{disable}.
+@end table
+@item @code{includefile} @kbd{includefile}
+This command allows additional configuration commands
+to be included from a separate file.
+Include files may
+be nested to a depth of five; upon reaching the end of any
+include file, command processing resumes in the previous
+configuration file.
+This option is useful for sites that run
+@code{ntpd(1ntpdmdoc)}
+on multiple hosts, with (mostly) common options (e.g., a
+restriction list).
+@item @code{logconfig} @kbd{configkeyword}
+This command controls the amount and type of output written to
+the system
+@code{syslog(3)}
+facility or the alternate
+@code{logfile}
+log file.
+By default, all output is turned on.
+All
+@kbd{configkeyword}
+keywords can be prefixed with
+@quoteleft{}=@quoteright{},
+@quoteleft{}+@quoteright{}
+and
+@quoteleft{}-@quoteright{},
+where
+@quoteleft{}=@quoteright{}
+sets the
+@code{syslog(3)}
+priority mask,
+@quoteleft{}+@quoteright{}
+adds and
+@quoteleft{}-@quoteright{}
+removes
+messages.
+@code{syslog(3)}
+messages can be controlled in four
+classes
+(@code{clock}, @code{peer}, @code{sys} and @code{sync}).
+Within these classes four types of messages can be
+controlled: informational messages
+(@code{info}),
+event messages
+(@code{events}),
+statistics messages
+(@code{statistics})
+and
+status messages
+(@code{status}).
+
+Configuration keywords are formed by concatenating the message class with
+the event class.
+The
+@code{all}
+prefix can be used instead of a message class.
+A
+message class may also be followed by the
+@code{all}
+keyword to enable/disable all
+messages of the respective message class.Thus, a minimal log configuration
+could look like this:
+@verbatim
+logconfig =syncstatus +sysevents
+@end verbatim
+
+This would just list the synchronizations state of
+@code{ntpd(1ntpdmdoc)}
+and the major system events.
+For a simple reference server, the
+following minimum message configuration could be useful:
+@verbatim
+logconfig =syncall +clockall
+@end verbatim
+
+This configuration will list all clock information and
+synchronization information.
+All other events and messages about
+peers, system events and so on is suppressed.
+@item @code{logfile} @kbd{logfile}
+This command specifies the location of an alternate log file to
+be used instead of the default system
+@code{syslog(3)}
+facility.
+This is the same operation as the -l command line option.
+@item @code{setvar} @kbd{variable} @code{[@code{default}]}
+This command adds an additional system variable.
+These
+variables can be used to distribute additional information such as
+the access policy.
+If the variable of the form
+@code{name}@code{=}@kbd{value}
+is followed by the
+@code{default}
+keyword, the
+variable will be listed as part of the default system variables
+(@code{rv} command)).
+These additional variables serve
+informational purposes only.
+They are not related to the protocol
+other that they can be listed.
+The known protocol variables will
+always override any variables defined via the
+@code{setvar}
+mechanism.
+There are three special variables that contain the names
+of all variable of the same group.
+The
+@code{sys_var_list}
+holds
+the names of all system variables.
+The
+@code{peer_var_list}
+holds
+the names of all peer variables and the
+@code{clock_var_list}
+holds the names of the reference clock variables.
+@item @code{tinker} @code{[@code{allan} @kbd{allan} | @code{dispersion} @kbd{dispersion} | @code{freq} @kbd{freq} | @code{huffpuff} @kbd{huffpuff} | @code{panic} @kbd{panic} | @code{step} @kbd{srep} | @code{stepout} @kbd{stepout}]}
+This command can be used to alter several system variables in
+very exceptional circumstances.
+It should occur in the
+configuration file before any other configuration options.
+The
+default values of these variables have been carefully optimized for
+a wide range of network speeds and reliability expectations.
+In
+general, they interact in intricate ways that are hard to predict
+and some combinations can result in some very nasty behavior.
+Very
+rarely is it necessary to change the default values; but, some
+folks cannot resist twisting the knobs anyway and this command is
+for them.
+Emphasis added: twisters are on their own and can expect
+no help from the support group.
+
+The variables operate as follows:
+@table @asis
+@item @code{allan} @kbd{allan}
+The argument becomes the new value for the minimum Allan
+intercept, which is a parameter of the PLL/FLL clock discipline
+algorithm.
+The value in log2 seconds defaults to 7 (1024 s), which is also the lower
+limit.
+@item @code{dispersion} @kbd{dispersion}
+The argument becomes the new value for the dispersion increase rate,
+normally .000015 s/s.
+@item @code{freq} @kbd{freq}
+The argument becomes the initial value of the frequency offset in
+parts-per-million.
+This overrides the value in the frequency file, if
+present, and avoids the initial training state if it is not.
+@item @code{huffpuff} @kbd{huffpuff}
+The argument becomes the new value for the experimental
+huff-n'-puff filter span, which determines the most recent interval
+the algorithm will search for a minimum delay.
+The lower limit is
+900 s (15 m), but a more reasonable value is 7200 (2 hours).
+There
+is no default, since the filter is not enabled unless this command
+is given.
+@item @code{panic} @kbd{panic}
+The argument is the panic threshold, normally 1000 s.
+If set to zero,
+the panic sanity check is disabled and a clock offset of any value will
+be accepted.
+@item @code{step} @kbd{step}
+The argument is the step threshold, which by default is 0.128 s.
+It can
+be set to any positive number in seconds.
+If set to zero, step
+adjustments will never occur.
+Note: The kernel time discipline is
+disabled if the step threshold is set to zero or greater than the
+default.
+@item @code{stepout} @kbd{stepout}
+The argument is the stepout timeout, which by default is 900 s.
+It can
+be set to any positive number in seconds.
+If set to zero, the stepout
+pulses will not be suppressed.
+@end table
+@item @code{rlimit} @code{[@code{memlock} @kbd{Nmegabytes} | @code{stacksize} @kbd{N4kPages} @code{filenum} @kbd{Nfiledescriptors}]}
+@table @asis
+@item @code{memlock} @kbd{Nmegabytes}
+Specify the number of megabytes of memory that can be allocated.
+Probably only available under Linux, this option is useful
+when dropping root (the
+@code{-i}
+option).
+The default is 32 megabytes. Setting this to zero will prevent any attemp to lock memory.
+@item @code{stacksize} @kbd{N4kPages}
+Specifies the maximum size of the process stack on systems with the
+@item @code{filenum} @kbd{Nfiledescriptors}
+Specifies the maximum number of file descriptors ntpd may have open at once. Defaults to the system default.
+@code{mlockall()}
+function.
+Defaults to 50 4k pages (200 4k pages in OpenBSD).
+@end table
+@item @code{trap} @kbd{host_address} @code{[@code{port} @kbd{port_number}]} @code{[@code{interface} @kbd{interface_address}]}
+This command configures a trap receiver at the given host
+address and port number for sending messages with the specified
+local interface address.
+If the port number is unspecified, a value
+of 18447 is used.
+If the interface address is not specified, the
+message is sent with a source address of the local interface the
+message is sent through.
+Note that on a multihomed host the
+interface used may vary from time to time with routing changes.
+
+The trap receiver will generally log event messages and other
+information from the server in a log file.
+While such monitor
+programs may also request their own trap dynamically, configuring a
+trap receiver will ensure that no messages are lost when the server
+is started.
+@item @code{hop} @kbd{...}
+This command specifies a list of TTL values in increasing order, up to 8
+values can be specified.
+In manycast mode these values are used in turn in
+an expanding-ring search.
+The default is eight multiples of 32 starting at
+31.
+@end table
+
+This section was generated by @strong{AutoGen},
+using the @code{agtexi-cmd} template and the option descriptions for the @code{ntp.conf} program.
+This software is released under the NTP license, <http://ntp.org/license>.
+
+@menu
+* ntp.conf Files:: Files
+* ntp.conf See Also:: See Also
+* ntp.conf Bugs:: Bugs
+* ntp.conf Notes:: Notes
+@end menu
+
+@node ntp.conf Files
+@subsection ntp.conf Files
+@table @asis
+@item @file{/etc/ntp.conf}
+the default name of the configuration file
+@item @file{ntp.keys}
+private MD5 keys
+@item @file{ntpkey}
+RSA private key
+@item @file{ntpkey_}@kbd{host}
+RSA public key
+@item @file{ntp_dh}
+Diffie-Hellman agreement parameters
+@end table
+@node ntp.conf See Also
+@subsection ntp.conf See Also
+@code{ntpd(1ntpdmdoc)},
+@code{ntpdc(1ntpdcmdoc)},
+@code{ntpq(1ntpqmdoc)}
+
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+@code{http://www.ntp.org/}.
+A snapshot of this documentation is available in HTML format in
+@file{/usr/share/doc/ntp}.
+@*
+
+@*
+David L. Mills, @emph{Network Time Protocol (Version 4)}, RFC5905
+@node ntp.conf Bugs
+@subsection ntp.conf Bugs
+The syntax checking is not picky; some combinations of
+ridiculous and even hilarious options and modes may not be
+detected.
+
+The
+@file{ntpkey_}@kbd{host}
+files are really digital
+certificates.
+These should be obtained via secure directory
+services when they become universally available.
+@node ntp.conf Notes
+@subsection ntp.conf Notes
+This document was derived from FreeBSD.
diff --git a/ntpd/invoke-ntp.keys.menu b/ntpd/invoke-ntp.keys.menu
new file mode 100644
index 0000000..2185e4b
--- /dev/null
+++ b/ntpd/invoke-ntp.keys.menu
@@ -0,0 +1 @@
+* ntp.keys Notes:: Notes about ntp.keys
diff --git a/ntpd/invoke-ntp.keys.texi b/ntpd/invoke-ntp.keys.texi
new file mode 100644
index 0000000..b79bba8
--- /dev/null
+++ b/ntpd/invoke-ntp.keys.texi
@@ -0,0 +1,125 @@
+@node ntp.keys Notes
+@section Notes about ntp.keys
+@pindex ntp.keys
+@cindex NTP symmetric key file format
+@ignore
+#
+# EDIT THIS FILE WITH CAUTION (invoke-ntp.keys.texi)
+#
+# It has been AutoGen-ed December 2, 2014 at 08:56:52 AM by AutoGen 5.18.5pre4
+# From the definitions ntp.keys.def
+# and the template file agtexi-file.tpl
+@end ignore
+
+
+
+This document describes the format of an NTP symmetric key file.
+For a description of the use of this type of file, see the
+"Authentication Support"
+section of the
+@code{ntp.conf(5)}
+page.
+
+@code{ntpd(8)}
+reads its keys from a file specified using the
+@code{-k}
+command line option or the
+@code{keys}
+statement in the configuration file.
+While key number 0 is fixed by the NTP standard
+(as 56 zero bits)
+and may not be changed,
+one or more keys numbered between 1 and 65534
+may be arbitrarily set in the keys file.
+
+The key file uses the same comment conventions
+as the configuration file.
+Key entries use a fixed format of the form
+
+@example
+@kbd{keyno} @kbd{type} @kbd{key}
+@end example
+
+where
+@kbd{keyno}
+is a positive integer (between 1 and 65534),
+@kbd{type}
+is the message digest algorithm,
+and
+@kbd{key}
+is the key itself.
+
+The
+@kbd{key}
+may be given in a format
+controlled by the
+@kbd{type}
+field.
+The
+@kbd{type}
+@code{MD5}
+is always supported.
+If
+@code{ntpd}
+was built with the OpenSSL library
+then any digest library supported by that library may be specified.
+However, if compliance with FIPS 140-2 is required the
+@kbd{type}
+must be either
+@code{SHA}
+or
+@code{SHA1}.
+
+What follows are some key types, and corresponding formats:
+
+@table @asis
+@item @code{MD5}
+The key is 1 to 16 printable characters terminated by
+an EOL,
+whitespace,
+or
+a
+@code{#}
+(which is the "start of comment" character).
+
+@item @code{SHA}
+@item @code{SHA1}
+@item @code{RMD160}
+The key is a hex-encoded ASCII string of 40 characters,
+which is truncated as necessary.
+@end table
+
+Note that the keys used by the
+@code{ntpq(8)}
+and
+@code{ntpdc(8)}
+programs are checked against passwords
+requested by the programs and entered by hand,
+so it is generally appropriate to specify these keys in ASCII format.
+
+This section was generated by @strong{AutoGen},
+using the @code{agtexi-cmd} template and the option descriptions for the @code{ntp.keys} program.
+This software is released under the NTP license, <http://ntp.org/license>.
+
+@menu
+* ntp.keys Files:: Files
+* ntp.keys See Also:: See Also
+* ntp.keys Notes:: Notes
+@end menu
+
+@node ntp.keys Files
+@subsection ntp.keys Files
+@table @asis
+@item @file{/etc/ntp.keys}
+the default name of the configuration file
+@end table
+@node ntp.keys See Also
+@subsection ntp.keys See Also
+@code{ntp.conf(5)},
+@code{ntpd(1ntpdmdoc)},
+@code{ntpdate(1ntpdatemdoc)},
+@code{ntpdc(1ntpdcmdoc)},
+@code{sntp(1sntpmdoc)}
+@node ntp.keys Notes
+@subsection ntp.keys Notes
+This document was derived from FreeBSD.
diff --git a/ntpd/invoke-ntpd.menu b/ntpd/invoke-ntpd.menu
new file mode 100644
index 0000000..3425d82
--- /dev/null
+++ b/ntpd/invoke-ntpd.menu
@@ -0,0 +1 @@
+* ntpd Invocation:: Invoking ntpd
diff --git a/ntpd/invoke-ntpd.texi b/ntpd/invoke-ntpd.texi
new file mode 100644
index 0000000..11ccce4
--- /dev/null
+++ b/ntpd/invoke-ntpd.texi
@@ -0,0 +1,702 @@
+@node ntpd Invocation
+@section Invoking ntpd
+@pindex ntpd
+@cindex NTP daemon program
+@ignore
+#
+# EDIT THIS FILE WITH CAUTION (invoke-ntpd.texi)
+#
+# It has been AutoGen-ed December 2, 2014 at 08:56:55 AM by AutoGen 5.18.5pre4
+# From the definitions ntpd-opts.def
+# and the template file agtexi-cmd.tpl
+@end ignore
+
+
+
+The
+@code{ntpd}
+utility is an operating system daemon which sets
+and maintains the system time of day in synchronism with Internet
+standard time servers.
+It is a complete implementation of the
+Network Time Protocol (NTP) version 4, as defined by RFC-5905,
+but also retains compatibility with
+version 3, as defined by RFC-1305, and versions 1
+and 2, as defined by RFC-1059 and RFC-1119, respectively.
+
+The
+@code{ntpd}
+utility does most computations in 64-bit floating point
+arithmetic and does relatively clumsy 64-bit fixed point operations
+only when necessary to preserve the ultimate precision, about 232
+picoseconds.
+While the ultimate precision is not achievable with
+ordinary workstations and networks of today, it may be required
+with future gigahertz CPU clocks and gigabit LANs.
+
+Ordinarily,
+@code{ntpd}
+reads the
+@code{ntp.conf(5)}
+configuration file at startup time in order to determine the
+synchronization sources and operating modes.
+It is also possible to
+specify a working, although limited, configuration entirely on the
+command line, obviating the need for a configuration file.
+This may
+be particularly useful when the local host is to be configured as a
+broadcast/multicast client, with all peers being determined by
+listening to broadcasts at run time.
+
+If NetInfo support is built into
+@code{ntpd}
+then
+@code{ntpd}
+will attempt to read its configuration from the
+NetInfo if the default
+@code{ntp.conf(5)}
+file cannot be read and no file is
+specified by the
+@code{-c}
+option.
+
+Various internal
+@code{ntpd}
+variables can be displayed and
+configuration options altered while the
+@code{ntpd}
+is running
+using the
+@code{ntpq(1ntpqmdoc)}
+and
+@code{ntpdc(1ntpdcmdoc)}
+utility programs.
+
+When
+@code{ntpd}
+starts it looks at the value of
+@code{umask(2)},
+and if zero
+@code{ntpd}
+will set the
+@code{umask(2)}
+to 022.
+
+This section was generated by @strong{AutoGen},
+using the @code{agtexi-cmd} template and the option descriptions for the @code{ntpd} program.
+This software is released under the NTP license, <http://ntp.org/license>.
+
+@menu
+* ntpd usage:: ntpd help/usage (@option{--help})
+* ntpd ipv4:: ipv4 option (-4)
+* ntpd ipv6:: ipv6 option (-6)
+* ntpd authreq:: authreq option (-a)
+* ntpd authnoreq:: authnoreq option (-A)
+* ntpd configfile:: configfile option (-c)
+* ntpd driftfile:: driftfile option (-f)
+* ntpd panicgate:: panicgate option (-g)
+* ntpd jaildir:: jaildir option (-i)
+* ntpd interface:: interface option (-I)
+* ntpd keyfile:: keyfile option (-k)
+* ntpd logfile:: logfile option (-l)
+* ntpd novirtualips:: novirtualips option (-L)
+* ntpd modifymmtimer:: modifymmtimer option (-M)
+* ntpd nice:: nice option (-N)
+* ntpd pidfile:: pidfile option (-p)
+* ntpd priority:: priority option (-P)
+* ntpd quit:: quit option (-q)
+* ntpd propagationdelay:: propagationdelay option (-r)
+* ntpd saveconfigquit:: saveconfigquit option
+* ntpd statsdir:: statsdir option (-s)
+* ntpd trustedkey:: trustedkey option (-t)
+* ntpd user:: user option (-u)
+* ntpd updateinterval:: updateinterval option (-U)
+* ntpd wait-sync:: wait-sync option (-w)
+* ntpd slew:: slew option (-x)
+* ntpd usepcc:: usepcc option
+* ntpd pccfreq:: pccfreq option
+* ntpd mdns:: mdns option (-m)
+* ntpd config:: presetting/configuring ntpd
+* ntpd exit status:: exit status
+* ntpd Usage:: Usage
+* ntpd Files:: Files
+* ntpd See Also:: See Also
+* ntpd Bugs:: Bugs
+* ntpd Notes:: Notes
+@end menu
+
+@node ntpd usage
+@subsection ntpd help/usage (@option{--help})
+@cindex ntpd help
+
+This is the automatically generated usage text for ntpd.
+
+The text printed is the same whether selected with the @code{help} option
+(@option{--help}) or the @code{more-help} option (@option{--more-help}). @code{more-help} will print
+the usage text by passing it through a pager program.
+@code{more-help} is disabled on platforms without a working
+@code{fork(2)} function. The @code{PAGER} environment variable is
+used to select the program, defaulting to @file{more}. Both will exit
+with a status code of 0.
+
+@exampleindent 0
+@example
+ntpd - NTP daemon program - Ver. 4.2.7p482
+Usage: ntpd [ -<flag> [<val>] | --<name>[@{=| @}<val>] ]... \
+ [ <server1> ... <serverN> ]
+ Flg Arg Option-Name Description
+ -4 no ipv4 Force IPv4 DNS name resolution
+ - prohibits the option 'ipv6'
+ -6 no ipv6 Force IPv6 DNS name resolution
+ - prohibits the option 'ipv4'
+ -a no authreq Require crypto authentication
+ - prohibits the option 'authnoreq'
+ -A no authnoreq Do not require crypto authentication
+ - prohibits the option 'authreq'
+ -b no bcastsync Allow us to sync to broadcast servers
+ -c Str configfile configuration file name
+ -d no debug-level Increase debug verbosity level
+ - may appear multiple times
+ -D Num set-debug-level Set the debug verbosity level
+ - may appear multiple times
+ -f Str driftfile frequency drift file name
+ -g no panicgate Allow the first adjustment to be Big
+ - may appear multiple times
+ -i Str jaildir Jail directory
+ -I Str interface Listen on an interface name or address
+ - may appear multiple times
+ -k Str keyfile path to symmetric keys
+ -l Str logfile path to the log file
+ -L no novirtualips Do not listen to virtual interfaces
+ -n no nofork Do not fork
+ - prohibits the option 'wait-sync'
+ -N no nice Run at high priority
+ -p Str pidfile path to the PID file
+ -P Num priority Process priority
+ -q no quit Set the time and quit
+ - prohibits these options:
+ saveconfigquit
+ wait-sync
+ -r Str propagationdelay Broadcast/propagation delay
+ Str saveconfigquit Save parsed configuration and quit
+ - prohibits these options:
+ quit
+ wait-sync
+ -s Str statsdir Statistics file location
+ -t Str trustedkey Trusted key number
+ - may appear multiple times
+ -u Str user Run as userid (or userid:groupid)
+ -U Num updateinterval interval in seconds between scans for new or dropped interfaces
+ Str var make ARG an ntp variable (RW)
+ - may appear multiple times
+ Str dvar make ARG an ntp variable (RW|DEF)
+ - may appear multiple times
+ -w Num wait-sync Seconds to wait for first clock sync
+ - prohibits these options:
+ nofork
+ quit
+ saveconfigquit
+ -x no slew Slew up to 600 seconds
+ opt version output version information and exit
+ -? no help display extended usage information and exit
+ -! no more-help extended usage information passed thru pager
+
+Options are specified by doubled hyphens and their name or by a single
+hyphen and the flag character.
+
+
+The following option preset mechanisms are supported:
+ - examining environment variables named NTPD_*
+
+Please send bug reports to: <http://bugs.ntp.org, bugs@@ntp.org>
+@end example
+@exampleindent 4
+
+@node ntpd ipv4
+@subsection ipv4 option (-4)
+@cindex ntpd-ipv4
+
+This is the ``force ipv4 dns name resolution'' option.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must not appear in combination with any of the following options:
+ipv6.
+@end itemize
+
+Force DNS resolution of following host names on the command line
+to the IPv4 namespace.
+@node ntpd ipv6
+@subsection ipv6 option (-6)
+@cindex ntpd-ipv6
+
+This is the ``force ipv6 dns name resolution'' option.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must not appear in combination with any of the following options:
+ipv4.
+@end itemize
+
+Force DNS resolution of following host names on the command line
+to the IPv6 namespace.
+@node ntpd authreq
+@subsection authreq option (-a)
+@cindex ntpd-authreq
+
+This is the ``require crypto authentication'' option.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must not appear in combination with any of the following options:
+authnoreq.
+@end itemize
+
+Require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is the default.
+@node ntpd authnoreq
+@subsection authnoreq option (-A)
+@cindex ntpd-authnoreq
+
+This is the ``do not require crypto authentication'' option.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must not appear in combination with any of the following options:
+authreq.
+@end itemize
+
+Do not require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is almost never a good idea.
+@node ntpd configfile
+@subsection configfile option (-c)
+@cindex ntpd-configfile
+
+This is the ``configuration file name'' option.
+This option takes a string argument.
+The name and path of the configuration file,
+@file{/etc/ntp.conf}
+by default.
+@node ntpd driftfile
+@subsection driftfile option (-f)
+@cindex ntpd-driftfile
+
+This is the ``frequency drift file name'' option.
+This option takes a string argument.
+The name and path of the frequency file,
+@file{/etc/ntp.drift}
+by default.
+This is the same operation as the
+@code{driftfile} @kbd{driftfile}
+configuration specification in the
+@file{/etc/ntp.conf}
+file.
+@node ntpd panicgate
+@subsection panicgate option (-g)
+@cindex ntpd-panicgate
+
+This is the ``allow the first adjustment to be big'' option.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@end itemize
+
+Normally,
+@code{ntpd}
+exits with a message to the system log if the offset exceeds the panic threshold, which is 1000 s by default. This option allows the time to be set to any value without restriction; however, this can happen only once. If the threshold is exceeded after that,
+@code{ntpd}
+will exit with a message to the system log. This option can be used with the
+@code{-q}
+and
+@code{-x}
+options.
+See the
+@code{tinker}
+configuration file directive for other options.
+@node ntpd jaildir
+@subsection jaildir option (-i)
+@cindex ntpd-jaildir
+
+This is the ``jail directory'' option.
+This option takes a string argument.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{HAVE_DROPROOT} during the compilation.
+@end itemize
+
+Chroot the server to the directory
+@kbd{jaildir}
+.
+This option also implies that the server attempts to drop root privileges at startup.
+You may need to also specify a
+@code{-u}
+option.
+This option is only available if the OS supports adjusting the clock
+without full root privileges.
+This option is supported under NetBSD (configure with
+@code{--enable-clockctl}) or Linux (configure with
+@code{--enable-linuxcaps}) or Solaris (configure with @code{--enable-solarisprivs}).
+@node ntpd interface
+@subsection interface option (-I)
+@cindex ntpd-interface
+
+This is the ``listen on an interface name or address'' option.
+This option takes a string argument @file{iface}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@end itemize
+
+Open the network address given, or all the addresses associated with the
+given interface name. This option may appear multiple times. This option
+also implies not opening other addresses, except wildcard and localhost.
+This option is deprecated. Please consider using the configuration file
+@code{interface} command, which is more versatile.
+@node ntpd keyfile
+@subsection keyfile option (-k)
+@cindex ntpd-keyfile
+
+This is the ``path to symmetric keys'' option.
+This option takes a string argument.
+Specify the name and path of the symmetric key file.
+@file{/etc/ntp.keys}
+is the default.
+This is the same operation as the
+@code{keys} @kbd{keyfile}
+configuration file directive.
+@node ntpd logfile
+@subsection logfile option (-l)
+@cindex ntpd-logfile
+
+This is the ``path to the log file'' option.
+This option takes a string argument.
+Specify the name and path of the log file.
+The default is the system log file.
+This is the same operation as the
+@code{logfile} @kbd{logfile}
+configuration file directive.
+@node ntpd novirtualips
+@subsection novirtualips option (-L)
+@cindex ntpd-novirtualips
+
+This is the ``do not listen to virtual interfaces'' option.
+Do not listen to virtual interfaces, defined as those with
+names containing a colon. This option is deprecated. Please
+consider using the configuration file @code{interface} command, which
+is more versatile.
+@node ntpd modifymmtimer
+@subsection modifymmtimer option (-M)
+@cindex ntpd-modifymmtimer
+
+This is the ``modify multimedia timer (windows only)'' option.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{SYS_WINNT} during the compilation.
+@end itemize
+
+Set the Windows Multimedia Timer to highest resolution. This
+ensures the resolution does not change while ntpd is running,
+avoiding timekeeping glitches associated with changes.
+@node ntpd nice
+@subsection nice option (-N)
+@cindex ntpd-nice
+
+This is the ``run at high priority'' option.
+To the extent permitted by the operating system, run
+@code{ntpd}
+at the highest priority.
+@node ntpd pidfile
+@subsection pidfile option (-p)
+@cindex ntpd-pidfile
+
+This is the ``path to the pid file'' option.
+This option takes a string argument.
+Specify the name and path of the file used to record
+@code{ntpd}'s
+process ID.
+This is the same operation as the
+@code{pidfile} @kbd{pidfile}
+configuration file directive.
+@node ntpd priority
+@subsection priority option (-P)
+@cindex ntpd-priority
+
+This is the ``process priority'' option.
+This option takes a number argument.
+To the extent permitted by the operating system, run
+@code{ntpd}
+at the specified
+@code{sched_setscheduler(SCHED_FIFO)}
+priority.
+@node ntpd quit
+@subsection quit option (-q)
+@cindex ntpd-quit
+
+This is the ``set the time and quit'' option.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must not appear in combination with any of the following options:
+saveconfigquit, wait-sync.
+@end itemize
+
+@code{ntpd}
+will not daemonize and will exit after the clock is first
+synchronized. This behavior mimics that of the
+@code{ntpdate}
+program, which will soon be replaced with a shell script.
+The
+@code{-g}
+and
+@code{-x}
+options can be used with this option.
+Note: The kernel time discipline is disabled with this option.
+@node ntpd propagationdelay
+@subsection propagationdelay option (-r)
+@cindex ntpd-propagationdelay
+
+This is the ``broadcast/propagation delay'' option.
+This option takes a string argument.
+Specify the default propagation delay from the broadcast/multicast server to this client. This is necessary only if the delay cannot be computed automatically by the protocol.
+@node ntpd saveconfigquit
+@subsection saveconfigquit option
+@cindex ntpd-saveconfigquit
+
+This is the ``save parsed configuration and quit'' option.
+This option takes a string argument.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{SAVECONFIG} during the compilation.
+@item
+must not appear in combination with any of the following options:
+quit, wait-sync.
+@end itemize
+
+Cause @code{ntpd} to parse its startup configuration file and save an
+equivalent to the given filename and exit. This option was
+designed for automated testing.
+@node ntpd statsdir
+@subsection statsdir option (-s)
+@cindex ntpd-statsdir
+
+This is the ``statistics file location'' option.
+This option takes a string argument.
+Specify the directory path for files created by the statistics facility.
+This is the same operation as the
+@code{statsdir} @kbd{statsdir}
+configuration file directive.
+@node ntpd trustedkey
+@subsection trustedkey option (-t)
+@cindex ntpd-trustedkey
+
+This is the ``trusted key number'' option.
+This option takes a string argument @file{tkey}.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@end itemize
+
+Add the specified key number to the trusted key list.
+@node ntpd user
+@subsection user option (-u)
+@cindex ntpd-user
+
+This is the ``run as userid (or userid:groupid)'' option.
+This option takes a string argument.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{HAVE_DROPROOT} during the compilation.
+@end itemize
+
+Specify a user, and optionally a group, to switch to.
+This option is only available if the OS supports adjusting the clock
+without full root privileges.
+This option is supported under NetBSD (configure with
+@code{--enable-clockctl}) or Linux (configure with
+@code{--enable-linuxcaps}) or Solaris (configure with @code{--enable-solarisprivs}).
+@node ntpd updateinterval
+@subsection updateinterval option (-U)
+@cindex ntpd-updateinterval
+
+This is the ``interval in seconds between scans for new or dropped interfaces'' option.
+This option takes a number argument.
+Give the time in seconds between two scans for new or dropped interfaces.
+For systems with routing socket support the scans will be performed shortly after the interface change
+has been detected by the system.
+Use 0 to disable scanning. 60 seconds is the minimum time between scans.
+@node ntpd wait-sync
+@subsection wait-sync option (-w)
+@cindex ntpd-wait-sync
+
+This is the ``seconds to wait for first clock sync'' option.
+This option takes a number argument.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{HAVE_WORKING_FORK} during the compilation.
+@item
+must not appear in combination with any of the following options:
+nofork, quit, saveconfigquit.
+@end itemize
+
+If greater than zero, alters @code{ntpd}'s behavior when forking to
+daemonize. Instead of exiting with status 0 immediately after
+the fork, the parent waits up to the specified number of
+seconds for the child to first synchronize the clock. The exit
+status is zero (success) if the clock was synchronized,
+otherwise it is @code{ETIMEDOUT}.
+This provides the option for a script starting @code{ntpd} to easily
+wait for the first set of the clock before proceeding.
+@node ntpd slew
+@subsection slew option (-x)
+@cindex ntpd-slew
+
+This is the ``slew up to 600 seconds'' option.
+Normally, the time is slewed if the offset is less than the step threshold, which is 128 ms by default, and stepped if above the threshold.
+This option sets the threshold to 600 s, which is well within the accuracy window to set the clock manually.
+Note: Since the slew rate of typical Unix kernels is limited to 0.5 ms/s, each second of adjustment requires an amortization interval of 2000 s.
+Thus, an adjustment as much as 600 s will take almost 14 days to complete.
+This option can be used with the
+@code{-g}
+and
+@code{-q}
+options.
+See the
+@code{tinker}
+configuration file directive for other options.
+Note: The kernel time discipline is disabled with this option.
+@node ntpd usepcc
+@subsection usepcc option
+@cindex ntpd-usepcc
+
+This is the ``use cpu cycle counter (windows only)'' option.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{SYS_WINNT} during the compilation.
+@end itemize
+
+Attempt to substitute the CPU counter for @code{QueryPerformanceCounter}.
+The CPU counter and @code{QueryPerformanceCounter} are compared, and if
+they have the same frequency, the CPU counter (RDTSC on x86) is
+used directly, saving the overhead of a system call.
+@node ntpd pccfreq
+@subsection pccfreq option
+@cindex ntpd-pccfreq
+
+This is the ``force cpu cycle counter use (windows only)'' option.
+This option takes a string argument.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{SYS_WINNT} during the compilation.
+@end itemize
+
+Force substitution the CPU counter for @code{QueryPerformanceCounter}.
+The CPU counter (RDTSC on x86) is used unconditionally with the
+given frequency (in Hz).
+@node ntpd mdns
+@subsection mdns option (-m)
+@cindex ntpd-mdns
+
+This is the ``register with mdns as a ntp server'' option.
+
+@noindent
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{HAVE_DNSREGISTRATION} during the compilation.
+@end itemize
+
+Registers as an NTP server with the local mDNS server which allows
+the server to be discovered via mDNS client lookup.
+
+
+@node ntpd config
+@subsection presetting/configuring ntpd
+
+Any option that is not marked as @i{not presettable} may be preset by
+loading values from environment variables named @code{NTPD} and @code{NTPD_<OPTION_NAME>}. @code{<OPTION_NAME>} must be one of
+the options listed above in upper case and segmented with underscores.
+The @code{NTPD} variable will be tokenized and parsed like
+the command line. The remaining variables are tested for existence and their
+values are treated like option arguments.
+
+
+The command line options relating to configuration and/or usage help are:
+
+@subsubheading version (-)
+
+Print the program version to standard out, optionally with licensing
+information, then exit 0. The optional argument specifies how much licensing
+detail to provide. The default is to print just the version. The licensing infomation may be selected with an option argument.
+Only the first letter of the argument is examined:
+
+@table @samp
+@item version
+Only print the version. This is the default.
+@item copyright
+Name the copyright usage licensing terms.
+@item verbose
+Print the full copyright usage licensing terms.
+@end table
+
+@node ntpd exit status
+@subsection ntpd exit status
+
+One of the following exit values will be returned:
+@table @samp
+@item 0 (EXIT_SUCCESS)
+Successful program execution.
+@item 1 (EXIT_FAILURE)
+The operation failed or the command syntax was not valid.
+@end table
+@node ntpd Usage
+@subsection ntpd Usage
+@node ntpd Files
+@subsection ntpd Files
+@node ntpd See Also
+@subsection ntpd See Also
+@node ntpd Bugs
+@subsection ntpd Bugs
+@node ntpd Notes
+@subsection ntpd Notes
diff --git a/ntpd/jupiter.h b/ntpd/jupiter.h
new file mode 100644
index 0000000..ed80b0c
--- /dev/null
+++ b/ntpd/jupiter.h
@@ -0,0 +1,255 @@
+/* @(#) $Header$ (LBL) */
+
+/*
+ * Rockwell Jupiter GPS receiver definitions
+ *
+ * This is all based on the "Zodiac GPS Receiver Family Designer's
+ * Guide" (dated 12/96)
+ */
+
+#define JUPITER_SYNC 0x81ff /* sync word (book says 0xff81 !?!?) */
+#define JUPITER_ALL 0xffff /* disable all output messages */
+
+/* Output messages (sent by the Jupiter board) */
+#define JUPITER_O_GPOS 1000 /* geodetic position status */
+#define JUPITER_O_EPOS 1001 /* ECEF position status */
+#define JUPITER_O_CHAN 1002 /* channel summary */
+#define JUPITER_O_VIS 1003 /* visible satellites */
+#define JUPITER_O_DGPS 1005 /* differential GPS status */
+#define JUPITER_O_MEAS 1007 /* channel measurement */
+#define JUPITER_O_ID 1011 /* receiver id */
+#define JUPITER_O_USER 1012 /* user-settings output */
+#define JUPITER_O_TEST 1100 /* built-in test results */
+#define JUPITER_O_MARK 1102 /* measurement time mark */
+#define JUPITER_O_PULSE 1108 /* UTC time mark pulse output */
+#define JUPITER_O_PORT 1130 /* serial port com parameters in use */
+#define JUPITER_O_EUP 1135 /* EEPROM update */
+#define JUPITER_O_ESTAT 1136 /* EEPROM status */
+
+/* Input messages (sent to the Jupiter board) */
+#define JUPITER_I_PVTINIT 1200 /* geodetic position and velocity */
+#define JUPITER_I_USER 1210 /* user-defined datum */
+#define JUPITER_I_MAPSEL 1211 /* map datum select */
+#define JUPITER_I_ELEV 1212 /* satellite elevation mask control */
+#define JUPITER_I_CAND 1213 /* satellite candidate select */
+#define JUPITER_I_DGPS 1214 /* differential GPS control */
+#define JUPITER_I_COLD 1216 /* cold start control */
+#define JUPITER_I_VALID 1217 /* solution validity criteria */
+#define JUPITER_I_ALT 1219 /* user-entered altitude input */
+#define JUPITER_I_PLAT 1220 /* application platform control */
+#define JUPITER_I_NAV 1221 /* nav configuration */
+#define JUPITER_I_TEST 1300 /* preform built-in test command */
+#define JUPITER_I_RESTART 1303 /* restart command */
+#define JUPITER_I_PORT 1330 /* serial port com parameters */
+#define JUPITER_I_PROTO 1331 /* message protocol control */
+#define JUPITER_I_RDGPS 1351 /* raw DGPS RTCM SC-104 data */
+
+struct jheader {
+ u_short sync; /* (JUPITER_SYNC) */
+ u_short id; /* message id */
+ u_short len; /* number of data short wordss (w/o cksum) */
+ u_char reqid; /* JUPITER_REQID_MASK bits available as id */
+ u_char flags; /* flags */
+ u_short hsum; /* header cksum */
+};
+
+#define JUPITER_REQID_MASK 0x3f /* bits available as id */
+#define JUPITER_FLAG_NAK 0x01 /* negative acknowledgement */
+#define JUPITER_FLAG_ACK 0x02 /* acknowledgement */
+#define JUPITER_FLAG_REQUEST 0x04 /* request ACK or NAK */
+#define JUPITER_FLAG_QUERY 0x08 /* request one shot output message */
+#define JUPITER_FLAG_LOG 0x20 /* request periodic output message */
+#define JUPITER_FLAG_CONN 0x40 /* enable periodic message */
+#define JUPITER_FLAG_DISC 0x80 /* disable periodic message */
+
+#define JUPITER_H_FLAG_BITS \
+ "\020\1NAK\2ACK\3REQUEST\4QUERY\5MBZ\6LOG\7CONN\10DISC"
+
+/* Log request messages (data payload when using JUPITER_FLAG_LOG) */
+struct jrequest {
+ u_short trigger; /* if 0, trigger on time trigger on
+ update (e.g. new almanac) */
+ u_short interval; /* frequency in seconds */
+ u_short offset; /* offset into minute */
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_O_GPOS (1000) */
+struct jgpos {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short sseq; /* sat measurement sequence number */
+ u_short navval; /* navigation soltuion validity */
+ u_short navtype; /* navigation solution type */
+ u_short nmeas; /* # of measurements used in solution */
+ u_short polar; /* if 1 then polar navigation */
+ u_short gweek; /* GPS week number */
+ u_short sweek[2]; /* GPS seconds into week */
+ u_short nsweek[2]; /* GPS nanoseconds into second */
+ u_short utcday; /* 1 to 31 */
+ u_short utcmon; /* 1 to 12 */
+ u_short utcyear; /* 1980 to 2079 */
+ u_short utchour; /* 0 to 23 */
+ u_short utcmin; /* 0 to 59 */
+ u_short utcsec; /* 0 to 59 */
+ u_short utcnsec[2]; /* 0 to 999999999 */
+ u_short lat[2]; /* latitude (radians) */
+ u_short lon[2]; /* longitude (radians) */
+ u_short height[2]; /* height (meters) */
+ u_short gsep; /* geoidal separation */
+ u_short speed[2]; /* ground speed (meters/sec) */
+ u_short course; /* true course (radians) */
+ u_short mvar;
+ u_short climb;
+ u_short mapd;
+ u_short herr[2];
+ u_short verr[2];
+ u_short terr[2];
+ u_short hverr;
+ u_short bias[2];
+ u_short biassd[2];
+ u_short drift[2];
+ u_short driftsd[2];
+ u_short dsum; /* checksum */
+};
+#define JUPITER_O_GPOS_NAV_NOALT 0x01 /* altitude used */
+#define JUPITER_O_GPOS_NAV_NODGPS 0x02 /* no differential GPS */
+#define JUPITER_O_GPOS_NAV_NOSAT 0x04 /* not enough satellites */
+#define JUPITER_O_GPOS_NAV_MAXH 0x08 /* exceeded max EHPE */
+#define JUPITER_O_GPOS_NAV_MAXV 0x10 /* exceeded max EVPE */
+
+/* JUPITER_O_CHAN (1002) */
+struct jchan {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short sseq; /* sat measurement sequence number */
+ u_short gweek; /* GPS week number */
+ u_short sweek[2]; /* GPS seconds into week */
+ u_short gpsns[2]; /* GPS nanoseconds from epoch */
+ struct jchan2 {
+ u_short flags; /* flags */
+ u_short prn; /* satellite PRN */
+ u_short chan; /* channel number */
+ } sat[12];
+ u_short dsum;
+};
+
+/* JUPITER_O_VIS (1003) */
+struct jvis {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short gdop; /* best possible GDOP */
+ u_short pdop; /* best possible PDOP */
+ u_short hdop; /* best possible HDOP */
+ u_short vdop; /* best possible VDOP */
+ u_short tdop; /* best possible TDOP */
+ u_short nvis; /* number of visible satellites */
+ struct jvis2 {
+ u_short prn; /* satellite PRN */
+ u_short azi; /* satellite azimuth (radians) */
+ u_short elev; /* satellite elevation (radians) */
+ } sat[12];
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_O_ID (1011) */
+struct jid {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ char chans[20]; /* number of channels (ascii) */
+ char vers[20]; /* software version (ascii) */
+ char date[20]; /* software date (ascii) */
+ char opts[20]; /* software options (ascii) */
+ char reserved[20];
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_O_USER (1012) */
+struct juser {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short status; /* operatinoal status */
+ u_short coldtmo; /* cold start time-out */
+ u_short dgpstmo; /* DGPS correction time-out*/
+ u_short emask; /* elevation mask */
+ u_short selcand[2]; /* selected candidate */
+ u_short solflags; /* solution validity criteria */
+ u_short nsat; /* number of satellites in track */
+ u_short herr[2]; /* minimum expected horizontal error */
+ u_short verr[2]; /* minimum expected vertical error */
+ u_short platform; /* application platform */
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_O_PULSE (1108) */
+struct jpulse {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short reserved[5];
+ u_short sweek[2]; /* GPS seconds into week */
+ short offs; /* GPS to UTC time offset (seconds) */
+ u_short offns[2]; /* GPS to UTC offset (nanoseconds) */
+ u_short flags; /* flags */
+ u_short dsum; /* checksum */
+};
+#define JUPITER_O_PULSE_VALID 0x1 /* time mark validity */
+#define JUPITER_O_PULSE_UTC 0x2 /* GPS/UTC sync */
+
+/* JUPITER_O_EUP (1135) */
+struct jeup {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_char dataid; /* data id */
+ u_char prn; /* satellite PRN */
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_I_RESTART (1303) */
+struct jrestart {
+ u_short seq; /* sequence number */
+ u_short flags;
+ u_short dsum; /* checksum */
+};
+#define JUPITER_I_RESTART_INVRAM 0x01
+#define JUPITER_I_RESTART_INVEEPROM 0x02
+#define JUPITER_I_RESTART_INVRTC 0x04
+#define JUPITER_I_RESTART_COLD 0x80
+
+/* JUPITER_I_PVTINIT (1200) */
+struct jpvtinit {
+ u_short flags;
+ u_short gweek; /* GPS week number */
+ u_short sweek[2]; /* GPS seconds into week */
+ u_short utcday; /* 1 to 31 */
+ u_short utcmon; /* 1 to 12 */
+ u_short utcyear; /* 1980 to 2079 */
+ u_short utchour; /* 0 to 23 */
+ u_short utcmin; /* 0 to 59 */
+ u_short utcsec; /* 0 to 59 */
+ u_short lat[2]; /* latitude (radians) */
+ u_short lon[2]; /* longitude (radians) */
+ u_short height[2]; /* height (meters) */
+ u_short speed[2]; /* ground speed (meters/sec) */
+ u_short course; /* true course (radians) */
+ u_short climb;
+ u_short dsum;
+};
+#define JUPITER_I_PVTINIT_FORCE 0x01
+#define JUPITER_I_PVTINIT_GPSVAL 0x02
+#define JUPITER_I_PVTINIT_UTCVAL 0x04
+#define JUPITER_I_PVTINIT_POSVAL 0x08
+#define JUPITER_I_PVTINIT_ALTVAL 0x10
+#define JUPITER_I_PVTINIT_SPDVAL 0x12
+#define JUPITER_I_PVTINIT_MAGVAL 0x14
+#define JUPITER_I_PVTINIT_CLIMBVAL 0x18
+
+/* JUPITER_I_PLAT (1220) */
+struct jplat {
+ u_short seq; /* sequence number */
+ u_short platform; /* application platform */
+ u_short dsum;
+};
+#define JUPITER_I_PLAT_DEFAULT 0 /* default dynamics */
+#define JUPITER_I_PLAT_LOW 2 /* pedestrian */
+#define JUPITER_I_PLAT_MED 5 /* land (e.g. automobile) */
+#define JUPITER_I_PLAT_HIGH 6 /* air */
diff --git a/ntpd/keyword-gen-utd b/ntpd/keyword-gen-utd
new file mode 100644
index 0000000..7474a56
--- /dev/null
+++ b/ntpd/keyword-gen-utd
@@ -0,0 +1 @@
+ * Generated 2013-08-04 04:56:14 UTC diff_ignore_line
diff --git a/ntpd/keyword-gen.c b/ntpd/keyword-gen.c
new file mode 100644
index 0000000..d4f7758
--- /dev/null
+++ b/ntpd/keyword-gen.c
@@ -0,0 +1,752 @@
+/*
+ * keyword-gen.c -- generate keyword scanner finite state machine and
+ * keyword_text array.
+ *
+ * This program is run to generate ntp_keyword.h
+ * After making a change here, two output files should be committed at
+ * the same time as keyword-gen.c:
+ * ntp_keyword.h
+ * keyword-gen-utd
+ *
+ * keyword-gen-utd is a sentinel used by Makefile.am to avoid compiling
+ * keyword_gen.c and generating ntp_keyword.h if the input keyword-gen.c
+ * has not changed. This is not solely an optimization, it also breaks
+ * a dependency chain that otherwise would cause programs to be compiled
+ * when running "make dist" or "make distdir". We want these to package
+ * the existing source without building anything but a tarball. See
+ * [Bug 1470].
+ */
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <ntp_stdlib.h>
+#include <ntp_config.h>
+#include <lib_strbuf.h>
+#include "ntp_scanner.h"
+#include "ntp_parser.h"
+
+
+/* Define a structure to hold a (keyword, token) pair */
+struct key_tok {
+ char * key; /* Keyword */
+ u_short token; /* Associated Token */
+ follby followedby; /* nonzero indicates the next token(s)
+ forced to be string(s) */
+};
+
+struct key_tok ntp_keywords[] = {
+{ "...", T_Ellipsis, FOLLBY_TOKEN },
+{ "allpeers", T_Allpeers, FOLLBY_TOKEN },
+{ "automax", T_Automax, FOLLBY_TOKEN },
+{ "broadcast", T_Broadcast, FOLLBY_STRING },
+{ "broadcastclient", T_Broadcastclient, FOLLBY_TOKEN },
+{ "broadcastdelay", T_Broadcastdelay, FOLLBY_TOKEN },
+{ "ctl", T_Ctl, FOLLBY_TOKEN },
+{ "disable", T_Disable, FOLLBY_TOKEN },
+{ "driftfile", T_Driftfile, FOLLBY_STRING },
+{ "enable", T_Enable, FOLLBY_TOKEN },
+{ "end", T_End, FOLLBY_TOKEN },
+{ "filegen", T_Filegen, FOLLBY_TOKEN },
+{ "fudge", T_Fudge, FOLLBY_STRING },
+{ "io", T_Io, FOLLBY_TOKEN },
+{ "includefile", T_Includefile, FOLLBY_STRING },
+{ "leapfile", T_Leapfile, FOLLBY_STRING },
+{ "logconfig", T_Logconfig, FOLLBY_STRINGS_TO_EOC },
+{ "logfile", T_Logfile, FOLLBY_STRING },
+{ "manycastclient", T_Manycastclient, FOLLBY_STRING },
+{ "manycastserver", T_Manycastserver, FOLLBY_STRINGS_TO_EOC },
+{ "mem", T_Mem, FOLLBY_TOKEN },
+{ "multicastclient", T_Multicastclient, FOLLBY_STRINGS_TO_EOC },
+{ "peer", T_Peer, FOLLBY_STRING },
+{ "phone", T_Phone, FOLLBY_STRINGS_TO_EOC },
+{ "pidfile", T_Pidfile, FOLLBY_STRING },
+{ "pool", T_Pool, FOLLBY_STRING },
+{ "discard", T_Discard, FOLLBY_TOKEN },
+{ "reset", T_Reset, FOLLBY_TOKEN },
+{ "restrict", T_Restrict, FOLLBY_TOKEN },
+{ "rlimit", T_Rlimit, FOLLBY_TOKEN },
+{ "server", T_Server, FOLLBY_STRING },
+{ "setvar", T_Setvar, FOLLBY_STRING },
+{ "statistics", T_Statistics, FOLLBY_TOKEN },
+{ "statsdir", T_Statsdir, FOLLBY_STRING },
+{ "sys", T_Sys, FOLLBY_TOKEN },
+{ "tick", T_Tick, FOLLBY_TOKEN },
+{ "timer", T_Timer, FOLLBY_TOKEN },
+{ "tinker", T_Tinker, FOLLBY_TOKEN },
+{ "tos", T_Tos, FOLLBY_TOKEN },
+{ "trap", T_Trap, FOLLBY_STRING },
+{ "unconfig", T_Unconfig, FOLLBY_STRING },
+{ "unpeer", T_Unpeer, FOLLBY_STRING },
+/* authentication_command */
+{ "controlkey", T_ControlKey, FOLLBY_TOKEN },
+{ "crypto", T_Crypto, FOLLBY_TOKEN },
+{ "keys", T_Keys, FOLLBY_STRING },
+{ "keysdir", T_Keysdir, FOLLBY_STRING },
+{ "ntpsigndsocket", T_NtpSignDsocket, FOLLBY_STRING },
+{ "requestkey", T_Requestkey, FOLLBY_TOKEN },
+{ "revoke", T_Revoke, FOLLBY_TOKEN },
+{ "trustedkey", T_Trustedkey, FOLLBY_TOKEN },
+/* IPv4/IPv6 protocol override flag */
+{ "-4", T_Ipv4_flag, FOLLBY_TOKEN },
+{ "-6", T_Ipv6_flag, FOLLBY_TOKEN },
+/* option */
+{ "autokey", T_Autokey, FOLLBY_TOKEN },
+{ "burst", T_Burst, FOLLBY_TOKEN },
+{ "iburst", T_Iburst, FOLLBY_TOKEN },
+{ "key", T_Key, FOLLBY_TOKEN },
+{ "maxpoll", T_Maxpoll, FOLLBY_TOKEN },
+{ "minpoll", T_Minpoll, FOLLBY_TOKEN },
+{ "mode", T_Mode, FOLLBY_TOKEN },
+{ "noselect", T_Noselect, FOLLBY_TOKEN },
+{ "preempt", T_Preempt, FOLLBY_TOKEN },
+{ "true", T_True, FOLLBY_TOKEN },
+{ "prefer", T_Prefer, FOLLBY_TOKEN },
+{ "ttl", T_Ttl, FOLLBY_TOKEN },
+{ "version", T_Version, FOLLBY_TOKEN },
+{ "xleave", T_Xleave, FOLLBY_TOKEN },
+/* crypto_command */
+{ "host", T_Host, FOLLBY_STRING },
+{ "ident", T_Ident, FOLLBY_STRING },
+{ "pw", T_Pw, FOLLBY_STRING },
+{ "randfile", T_Randfile, FOLLBY_STRING },
+{ "digest", T_Digest, FOLLBY_STRING },
+/*** MONITORING COMMANDS ***/
+/* stat */
+{ "clockstats", T_Clockstats, FOLLBY_TOKEN },
+{ "cryptostats", T_Cryptostats, FOLLBY_TOKEN },
+{ "loopstats", T_Loopstats, FOLLBY_TOKEN },
+{ "peerstats", T_Peerstats, FOLLBY_TOKEN },
+{ "rawstats", T_Rawstats, FOLLBY_TOKEN },
+{ "sysstats", T_Sysstats, FOLLBY_TOKEN },
+{ "protostats", T_Protostats, FOLLBY_TOKEN },
+{ "timingstats", T_Timingstats, FOLLBY_TOKEN },
+/* filegen_option */
+{ "file", T_File, FOLLBY_STRING },
+{ "link", T_Link, FOLLBY_TOKEN },
+{ "nolink", T_Nolink, FOLLBY_TOKEN },
+{ "type", T_Type, FOLLBY_TOKEN },
+/* filegen_type */
+{ "age", T_Age, FOLLBY_TOKEN },
+{ "day", T_Day, FOLLBY_TOKEN },
+{ "month", T_Month, FOLLBY_TOKEN },
+{ "none", T_None, FOLLBY_TOKEN },
+{ "pid", T_Pid, FOLLBY_TOKEN },
+{ "week", T_Week, FOLLBY_TOKEN },
+{ "year", T_Year, FOLLBY_TOKEN },
+/*** ORPHAN MODE COMMANDS ***/
+/* tos_option */
+{ "minclock", T_Minclock, FOLLBY_TOKEN },
+{ "maxclock", T_Maxclock, FOLLBY_TOKEN },
+{ "minsane", T_Minsane, FOLLBY_TOKEN },
+{ "floor", T_Floor, FOLLBY_TOKEN },
+{ "ceiling", T_Ceiling, FOLLBY_TOKEN },
+{ "cohort", T_Cohort, FOLLBY_TOKEN },
+{ "mindist", T_Mindist, FOLLBY_TOKEN },
+{ "maxdist", T_Maxdist, FOLLBY_TOKEN },
+{ "beacon", T_Beacon, FOLLBY_TOKEN },
+{ "orphan", T_Orphan, FOLLBY_TOKEN },
+{ "orphanwait", T_Orphanwait, FOLLBY_TOKEN },
+{ "nonvolatile", T_Nonvolatile, FOLLBY_TOKEN },
+/* access_control_flag */
+{ "default", T_Default, FOLLBY_TOKEN },
+{ "source", T_Source, FOLLBY_TOKEN },
+{ "flake", T_Flake, FOLLBY_TOKEN },
+{ "ignore", T_Ignore, FOLLBY_TOKEN },
+{ "limited", T_Limited, FOLLBY_TOKEN },
+{ "mssntp", T_Mssntp, FOLLBY_TOKEN },
+{ "kod", T_Kod, FOLLBY_TOKEN },
+{ "lowpriotrap", T_Lowpriotrap, FOLLBY_TOKEN },
+{ "mask", T_Mask, FOLLBY_TOKEN },
+{ "nomodify", T_Nomodify, FOLLBY_TOKEN },
+{ "nomrulist", T_Nomrulist, FOLLBY_TOKEN },
+{ "nopeer", T_Nopeer, FOLLBY_TOKEN },
+{ "noquery", T_Noquery, FOLLBY_TOKEN },
+{ "noserve", T_Noserve, FOLLBY_TOKEN },
+{ "notrap", T_Notrap, FOLLBY_TOKEN },
+{ "notrust", T_Notrust, FOLLBY_TOKEN },
+{ "ntpport", T_Ntpport, FOLLBY_TOKEN },
+/* discard_option */
+{ "average", T_Average, FOLLBY_TOKEN },
+{ "minimum", T_Minimum, FOLLBY_TOKEN },
+{ "monitor", T_Monitor, FOLLBY_TOKEN },
+/* mru_option */
+{ "incalloc", T_Incalloc, FOLLBY_TOKEN },
+{ "incmem", T_Incmem, FOLLBY_TOKEN },
+{ "initalloc", T_Initalloc, FOLLBY_TOKEN },
+{ "initmem", T_Initmem, FOLLBY_TOKEN },
+{ "mindepth", T_Mindepth, FOLLBY_TOKEN },
+{ "maxage", T_Maxage, FOLLBY_TOKEN },
+{ "maxdepth", T_Maxdepth, FOLLBY_TOKEN },
+{ "maxmem", T_Maxmem, FOLLBY_TOKEN },
+{ "mru", T_Mru, FOLLBY_TOKEN },
+/* fudge_factor */
+{ "abbrev", T_Abbrev, FOLLBY_STRING },
+{ "flag1", T_Flag1, FOLLBY_TOKEN },
+{ "flag2", T_Flag2, FOLLBY_TOKEN },
+{ "flag3", T_Flag3, FOLLBY_TOKEN },
+{ "flag4", T_Flag4, FOLLBY_TOKEN },
+{ "refid", T_Refid, FOLLBY_STRING },
+{ "stratum", T_Stratum, FOLLBY_TOKEN },
+{ "time1", T_Time1, FOLLBY_TOKEN },
+{ "time2", T_Time2, FOLLBY_TOKEN },
+/* system_option */
+{ "auth", T_Auth, FOLLBY_TOKEN },
+{ "bclient", T_Bclient, FOLLBY_TOKEN },
+{ "calibrate", T_Calibrate, FOLLBY_TOKEN },
+{ "kernel", T_Kernel, FOLLBY_TOKEN },
+{ "ntp", T_Ntp, FOLLBY_TOKEN },
+{ "mode7", T_Mode7, FOLLBY_TOKEN },
+{ "stats", T_Stats, FOLLBY_TOKEN },
+/* rlimit_option */
+{ "memlock", T_Memlock, FOLLBY_TOKEN },
+{ "stacksize", T_Stacksize, FOLLBY_TOKEN },
+{ "filenum", T_Filenum, FOLLBY_TOKEN },
+/* tinker_option */
+{ "step", T_Step, FOLLBY_TOKEN },
+{ "panic", T_Panic, FOLLBY_TOKEN },
+{ "dispersion", T_Dispersion, FOLLBY_TOKEN },
+{ "stepout", T_Stepout, FOLLBY_TOKEN },
+{ "allan", T_Allan, FOLLBY_TOKEN },
+{ "huffpuff", T_Huffpuff, FOLLBY_TOKEN },
+{ "freq", T_Freq, FOLLBY_TOKEN },
+/* miscellaneous_command */
+{ "port", T_Port, FOLLBY_TOKEN },
+{ "interface", T_Interface, FOLLBY_TOKEN },
+{ "saveconfigdir", T_Saveconfigdir, FOLLBY_STRING },
+/* interface_command (ignore and interface already defined) */
+{ "nic", T_Nic, FOLLBY_TOKEN },
+{ "all", T_All, FOLLBY_TOKEN },
+{ "ipv4", T_Ipv4, FOLLBY_TOKEN },
+{ "ipv6", T_Ipv6, FOLLBY_TOKEN },
+{ "wildcard", T_Wildcard, FOLLBY_TOKEN },
+{ "listen", T_Listen, FOLLBY_TOKEN },
+{ "drop", T_Drop, FOLLBY_TOKEN },
+/* simulator commands */
+{ "simulate", T_Simulate, FOLLBY_TOKEN },
+{ "simulation_duration",T_Sim_Duration, FOLLBY_TOKEN },
+{ "beep_delay", T_Beep_Delay, FOLLBY_TOKEN },
+{ "duration", T_Duration, FOLLBY_TOKEN },
+{ "server_offset", T_Server_Offset, FOLLBY_TOKEN },
+{ "freq_offset", T_Freq_Offset, FOLLBY_TOKEN },
+{ "wander", T_Wander, FOLLBY_TOKEN },
+{ "jitter", T_Jitter, FOLLBY_TOKEN },
+{ "prop_delay", T_Prop_Delay, FOLLBY_TOKEN },
+{ "proc_delay", T_Proc_Delay, FOLLBY_TOKEN },
+};
+
+typedef struct big_scan_state_tag {
+ char ch; /* Character this state matches on */
+ char followedby; /* Forces next token(s) to T_String */
+ u_short finishes_token; /* nonzero ID if last keyword char */
+ u_short match_next_s; /* next state to check matching ch */
+ u_short other_next_s; /* next state to check if not ch */
+} big_scan_state;
+
+/*
+ * Note: to increase MAXSTATES beyond 2048, be aware it is currently
+ * crammed into 11 bits in scan_state form. Raising to 4096 would be
+ * relatively easy by storing the followedby value in a separate
+ * array with one entry per token, and shrinking the char value to
+ * 7 bits to free a bit for accepting/non-accepting. More than 4096
+ * states will require expanding scan_state beyond 32 bits each.
+ */
+#define MAXSTATES 2048
+#define MAX_TOK_LEN 63
+
+const char * current_keyword;/* for error reporting */
+big_scan_state sst[MAXSTATES]; /* scanner FSM state entries */
+u_short sst_highwater; /* next entry index to consider */
+char * symb[1024]; /* map token ID to symbolic name */
+
+/* for libntp */
+const char * progname = "keyword-gen";
+
+int main (int, char **);
+static void generate_preamble (void);
+static void generate_fsm (void);
+static void generate_token_text (void);
+static u_short create_keyword_scanner (void);
+static u_short create_scan_states (char *, u_short, follby, u_short);
+int compare_key_tok_id (const void *, const void *);
+int compare_key_tok_text (const void *, const void *);
+void populate_symb (char *);
+const char * symbname (u_short);
+
+
+int main(int argc, char **argv)
+{
+ if (argc < 2) {
+ fprintf(stderr, "Usage:\n%s t_header.h\n", argv[0]);
+ exit(1);
+ }
+ debug = 1;
+
+ populate_symb(argv[1]);
+
+ generate_preamble();
+ generate_token_text();
+ generate_fsm();
+
+ return 0;
+}
+
+
+static void
+generate_preamble(void)
+{
+ time_t now;
+ char timestamp[128];
+ char preamble[] =
+"/*\n"
+" * ntp_keyword.h\n"
+" * \n"
+" * NOTE: edit this file with caution, it is generated by keyword-gen.c\n"
+" *\t Generated %s UTC diff_ignore_line\n"
+" *\n"
+" */\n"
+"#include \"ntp_scanner.h\"\n"
+"#include \"ntp_parser.h\"\n"
+"\n";
+
+ time(&now);
+ if (!strftime(timestamp, sizeof(timestamp),
+ "%Y-%m-%d %H:%M:%S", gmtime(&now)))
+ timestamp[0] = '\0';
+
+ printf(preamble, timestamp);
+}
+
+
+static void
+generate_fsm(void)
+{
+ char rprefix[MAX_TOK_LEN + 1];
+ char prefix[MAX_TOK_LEN + 1];
+ char token_id_comment[16 + MAX_TOK_LEN + 1];
+ size_t prefix_len;
+ char *p;
+ char *r;
+ u_short initial_state;
+ u_short this_state;
+ u_short prev_state;
+ u_short state;
+ u_short i;
+ u_short token;
+
+ /*
+ * Sort ntp_keywords in alphabetical keyword order. This is
+ * not necessary, but minimizes nonfunctional changes in the
+ * generated finite state machine when keywords are modified.
+ */
+ qsort(ntp_keywords, COUNTOF(ntp_keywords),
+ sizeof(ntp_keywords[0]), compare_key_tok_text);
+
+ /*
+ * To save space, reserve the state array entry matching each
+ * token number for its terminal state, so the token identifier
+ * does not need to be stored in each state, but can be
+ * recovered trivially. To mark the entry reserved,
+ * finishes_token is nonzero.
+ */
+
+ for (i = 0; i < COUNTOF(ntp_keywords); i++) {
+ token = ntp_keywords[i].token;
+ if (1 > token || token >= COUNTOF(sst)) {
+ fprintf(stderr,
+ "keyword-gen sst[%u] too small "
+ "for keyword '%s' id %d\n",
+ COUNTOF(sst),
+ ntp_keywords[i].key,
+ token);
+ exit(4);
+ }
+ sst[token].finishes_token = token;
+ }
+
+ initial_state = create_keyword_scanner();
+
+ fprintf(stderr,
+ "%d keywords consumed %d states of %d max.\n",
+ (int)COUNTOF(ntp_keywords),
+ sst_highwater - 1,
+ (int)COUNTOF(sst) - 1);
+
+ printf("#define SCANNER_INIT_S %d\n\n", initial_state);
+
+ printf("const scan_state sst[%d] = {\n"
+ "/*SS_T( ch,\tf-by, match, other ),\t\t\t\t */\n"
+ " 0,\t\t\t\t /* %5d %-17s */\n",
+ sst_highwater,
+ 0, "");
+
+ for (i = 1; i < sst_highwater; i++) {
+
+ /* verify fields will fit */
+ if (sst[i].followedby & ~0x3) {
+ fprintf(stderr,
+ "keyword-gen internal error "
+ "sst[%d].followedby %d too big\n",
+ i, sst[i].followedby);
+ exit(7);
+ }
+
+ if (sst_highwater <= sst[i].match_next_s
+ || sst[i].match_next_s & ~0x7ff) {
+ fprintf(stderr,
+ "keyword-gen internal error "
+ "sst[%d].match_next_s %d too big\n",
+ i, sst[i].match_next_s);
+ exit(8);
+ }
+
+ if (sst_highwater <= sst[i].other_next_s
+ || sst[i].other_next_s & ~0x7ff) {
+ fprintf(stderr,
+ "keyword-gen internal error "
+ "sst[%d].other_next_s %d too big\n",
+ i, sst[i].other_next_s);
+ exit(9);
+ }
+
+ if (sst[i].finishes_token) {
+ snprintf(token_id_comment,
+ sizeof(token_id_comment), "%5d %-17s",
+ i, symbname(sst[i].finishes_token));
+ if (i != sst[i].finishes_token) {
+ fprintf(stderr,
+ "keyword-gen internal error "
+ "entry %d finishes token %d\n",
+ i, sst[i].finishes_token);
+ exit(5);
+ }
+ } else {
+ /*
+ * Determine the keyword prefix that leads to this
+ * state. This is expensive but keyword-gen is run
+ * only when it changes. Distributing keyword-gen-utd
+ * achieves that, which is why it must be committed
+ * at the same time as keyword-gen.c and ntp_keyword.h.
+ *
+ * Scan the state array iteratively looking for a state
+ * which leads to the current one, collecting matching
+ * characters along the way. There is only one such
+ * path back to the starting state given the way our
+ * scanner state machine is built and the practice of
+ * using the spelling of the keyword as its T_* token
+ * identifier, which results in never having two
+ * spellings result in the same T_* value.
+ */
+ prefix_len = 0;
+ prev_state = 0;
+ this_state = i;
+ do {
+ for (state = 1; state < sst_highwater; state++)
+ if (sst[state].other_next_s == this_state) {
+ this_state = state;
+ break;
+ } else if (sst[state].match_next_s == this_state) {
+ this_state = state;
+ rprefix[prefix_len] = sst[state].ch;
+ prefix_len++;
+ break;
+ }
+ } while (this_state != initial_state);
+
+ if (prefix_len) {
+ /* reverse rprefix into prefix */
+ p = prefix + prefix_len;
+ r = rprefix;
+ while (r < rprefix + prefix_len)
+ *--p = *r++;
+ }
+ prefix[prefix_len] = '\0';
+
+ snprintf(token_id_comment,
+ sizeof(token_id_comment), "%5d %-17s",
+ i, (initial_state == i)
+ ? "[initial state]"
+ : prefix);
+ }
+
+ printf(" S_ST( '%c',\t%d, %5u, %5u )%s /* %s */\n",
+ sst[i].ch,
+ sst[i].followedby,
+ sst[i].match_next_s,
+ sst[i].other_next_s,
+ (i + 1 < sst_highwater)
+ ? ","
+ : " ",
+ token_id_comment);
+ }
+
+ printf("};\n\n");
+}
+
+
+/* Define a function to create the states of the scanner. This function
+ * is used by the create_keyword_scanner function below.
+ *
+ * This function takes a suffix of a keyword, the token to be returned on
+ * recognizing the complete keyword, and any pre-existing state that exists
+ * for some other keyword that has the same prefix as the current one.
+ */
+static u_short
+create_scan_states(
+ char * text,
+ u_short token,
+ follby followedby,
+ u_short prev_state
+ )
+{
+ u_short my_state;
+ u_short return_state;
+ u_short prev_char_s;
+ u_short curr_char_s;
+
+ return_state = prev_state;
+ curr_char_s = prev_state;
+ prev_char_s = 0;
+
+ /* Find the correct position to insert the state.
+ * All states should be in alphabetical order
+ */
+ while (curr_char_s && (text[0] < sst[curr_char_s].ch)) {
+ prev_char_s = curr_char_s;
+ curr_char_s = sst[curr_char_s].other_next_s;
+ }
+
+ /*
+ * Check if a previously seen keyword has the same prefix as
+ * the current keyword. If so, simply use the state for that
+ * keyword as my_state, otherwise, allocate a new state.
+ */
+ if (curr_char_s && (text[0] == sst[curr_char_s].ch)) {
+ my_state = curr_char_s;
+ if ('\0' == text[1]) {
+ fprintf(stderr,
+ "Duplicate entries for keyword '%s' in"
+ " keyword_gen.c ntp_keywords[].\n",
+ current_keyword);
+ exit(2);
+ }
+ } else {
+ do
+ my_state = sst_highwater++;
+ while (my_state < COUNTOF(sst)
+ && sst[my_state].finishes_token);
+ if (my_state >= COUNTOF(sst)) {
+ fprintf(stderr,
+ "fatal, keyword scanner state array "
+ "sst[%d] is too small, modify\n"
+ "keyword-gen.c to increase.\n",
+ (int)COUNTOF(sst));
+ exit(3);
+ }
+ /* Store the next character of the keyword */
+ sst[my_state].ch = text[0];
+ sst[my_state].other_next_s = curr_char_s;
+ sst[my_state].followedby = FOLLBY_NON_ACCEPTING;
+
+ if (prev_char_s)
+ sst[prev_char_s].other_next_s = my_state;
+ else
+ return_state = my_state;
+ }
+
+ /* Check if the next character is '\0'.
+ * If yes, we are done with the recognition and this is an accepting
+ * state.
+ * If not, we need to continue scanning
+ */
+ if ('\0' == text[1]) {
+ sst[my_state].finishes_token = (u_short)token;
+ sst[my_state].followedby = (char)followedby;
+
+ if (sst[token].finishes_token != (u_short)token) {
+ fprintf(stderr,
+ "fatal, sst[%d] not reserved for %s.\n",
+ token, symbname(token));
+ exit(6);
+ }
+ /* relocate so token id is sst[] index */
+ if (my_state != token) {
+ sst[token] = sst[my_state];
+ ZERO(sst[my_state]);
+ do
+ sst_highwater--;
+ while (sst[sst_highwater].finishes_token);
+ my_state = token;
+ if (prev_char_s)
+ sst[prev_char_s].other_next_s = my_state;
+ else
+ return_state = my_state;
+ }
+ } else
+ sst[my_state].match_next_s =
+ create_scan_states(
+ &text[1],
+ token,
+ followedby,
+ sst[my_state].match_next_s);
+
+ return return_state;
+}
+
+
+/* Define a function that takes a list of (keyword, token) values and
+ * creates a keywords scanner out of it.
+ */
+
+static u_short
+create_keyword_scanner(void)
+{
+ u_short scanner;
+ u_short i;
+
+ sst_highwater = 1; /* index 0 invalid, unused */
+ scanner = 0;
+
+ for (i = 0; i < COUNTOF(ntp_keywords); i++) {
+ current_keyword = ntp_keywords[i].key;
+ scanner =
+ create_scan_states(
+ ntp_keywords[i].key,
+ ntp_keywords[i].token,
+ ntp_keywords[i].followedby,
+ scanner);
+ }
+
+ return scanner;
+}
+
+
+static void
+generate_token_text(void)
+{
+ u_short lowest_id;
+ u_short highest_id;
+ u_short id_count;
+ u_short id;
+ u_short i;
+
+ /* sort ntp_keywords in token ID order */
+ qsort(ntp_keywords, COUNTOF(ntp_keywords),
+ sizeof(ntp_keywords[0]), compare_key_tok_id);
+
+ lowest_id = ntp_keywords[0].token;
+ highest_id = ntp_keywords[COUNTOF(ntp_keywords) - 1].token;
+ id_count = highest_id - lowest_id + 1;
+
+ printf("#define LOWEST_KEYWORD_ID %d\n\n", lowest_id);
+
+ printf("const char * const keyword_text[%d] = {", id_count);
+
+ id = lowest_id;
+ i = 0;
+ while (i < COUNTOF(ntp_keywords)) {
+ while (id < ntp_keywords[i].token) {
+ printf(",\n\t/* %-5d %5d %20s */\tNULL",
+ id - lowest_id, id, symbname(id));
+ id++;
+ }
+ if (i > 0)
+ printf(",");
+ printf("\n\t/* %-5d %5d %20s */\t\"%s\"",
+ id - lowest_id, id, symbname(id),
+ ntp_keywords[i].key);
+ i++;
+ id++;
+ }
+
+ printf("\n};\n\n");
+}
+
+
+int
+compare_key_tok_id(
+ const void *a1,
+ const void *a2
+ )
+{
+ const struct key_tok *p1 = a1;
+ const struct key_tok *p2 = a2;
+
+ if (p1->token == p2->token)
+ return 0;
+
+ if (p1->token < p2->token)
+ return -1;
+ else
+ return 1;
+}
+
+
+int
+compare_key_tok_text(
+ const void *a1,
+ const void *a2
+ )
+{
+ const struct key_tok *p1 = a1;
+ const struct key_tok *p2 = a2;
+
+ return strcmp(p1->key, p2->key);
+}
+
+
+/*
+ * populate_symb() - populate symb[] lookup array with symbolic token
+ * names such that symb[T_Age] == "T_Age", etc.
+ */
+void
+populate_symb(
+ char *header_file
+ )
+{
+ FILE * yh;
+ char line[2 * MAX_TOK_LEN];
+ char name[2 * MAX_TOK_LEN];
+ int token;
+
+ yh = fopen(header_file, "r");
+ if (NULL == yh) {
+ perror("unable to open yacc/bison header file");
+ exit(4);
+ }
+
+ while (NULL != fgets(line, sizeof(line), yh))
+ if (2 == sscanf(line, "#define %s %d", name, &token)
+ && 'T' == name[0] && '_' == name[1] && token >= 0
+ && token < COUNTOF(symb)) {
+
+ symb[token] = estrdup(name);
+ if (strlen(name) > MAX_TOK_LEN) {
+ fprintf(stderr,
+ "MAX_TOK_LEN %d too small for '%s'\n"
+ "Edit keyword-gen.c to raise.\n",
+ MAX_TOK_LEN, name);
+ exit(10);
+ }
+ }
+ fclose(yh);
+}
+
+
+const char *
+symbname(
+ u_short token
+ )
+{
+ char *name;
+
+ if (token < COUNTOF(symb) && symb[token] != NULL) {
+ name = symb[token];
+ } else {
+ LIB_GETBUF(name);
+ snprintf(name, LIB_BUFLENGTH, "%d", token);
+ }
+
+ return name;
+}
diff --git a/ntpd/ntp.conf.5man b/ntpd/ntp.conf.5man
new file mode 100644
index 0000000..389f23e
--- /dev/null
+++ b/ntpd/ntp.conf.5man
@@ -0,0 +1,3004 @@
+.de1 NOP
+. it 1 an-trap
+. if \\n[.$] \,\\$*\/
+..
+.ie t \
+.ds B-Font [CB]
+.ds I-Font [CI]
+.ds R-Font [CR]
+.el \
+.ds B-Font B
+.ds I-Font I
+.ds R-Font R
+.TH ntp.conf 5man "02 Dec 2014" "4.2.7p482" "File Formats"
+.\"
+.\" EDIT THIS FILE WITH CAUTION (/tmp/.ag-7eaizK/ag-wfaayK)
+.\"
+.\" It has been AutoGen-ed December 2, 2014 at 08:56:37 AM by AutoGen 5.18.5pre4
+.\" From the definitions ntp.conf.def
+.\" and the template file agman-cmd.tpl
+.SH NAME
+\f\*[B-Font]ntp.conf\fP
+\- Network Time Protocol (NTP) daemon configuration file format
+.SH SYNOPSIS
+\f\*[B-Font]ntp.conf\fP
+[\f\*[B-Font]\-\-option-name\f[]]
+[\f\*[B-Font]\-\-option-name\f[] \f\*[I-Font]value\f[]]
+.sp \n(Ppu
+.ne 2
+
+All arguments must be options.
+.sp \n(Ppu
+.ne 2
+
+.SH DESCRIPTION
+The
+\f\*[B-Font]ntp.conf\fP
+configuration file is read at initial startup by the
+\fCntpd\fR(1ntpdmdoc)\f[]
+daemon in order to specify the synchronization sources,
+modes and other related information.
+Usually, it is installed in the
+\fI/etc\f[]
+directory,
+but could be installed elsewhere
+(see the daemon's
+\f\*[B-Font]\-c\f[]
+command line option).
+.sp \n(Ppu
+.ne 2
+
+The file format is similar to other
+UNIX
+configuration files.
+Comments begin with a
+\[oq]#\[cq]
+character and extend to the end of the line;
+blank lines are ignored.
+Configuration commands consist of an initial keyword
+followed by a list of arguments,
+some of which may be optional, separated by whitespace.
+Commands may not be continued over multiple lines.
+Arguments may be host names,
+host addresses written in numeric, dotted-quad form,
+integers, floating point numbers (when specifying times in seconds)
+and text strings.
+.sp \n(Ppu
+.ne 2
+
+The rest of this page describes the configuration and control options.
+The
+"Notes on Configuring NTP and Setting up an NTP Subnet"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[])
+contains an extended discussion of these options.
+In addition to the discussion of general
+\fIConfiguration\f[] \fIOptions\f[],
+there are sections describing the following supported functionality
+and the options used to control it:
+.IP \fB\(bu\fP 2
+\fIAuthentication\f[] \fISupport\f[]
+.IP \fB\(bu\fP 2
+\fIMonitoring\f[] \fISupport\f[]
+.IP \fB\(bu\fP 2
+\fIAccess\f[] \fIControl\f[] \fISupport\f[]
+.IP \fB\(bu\fP 2
+\fIAutomatic\f[] \fINTP\f[] \fIConfiguration\f[] \fIOptions\f[]
+.IP \fB\(bu\fP 2
+\fIReference\f[] \fIClock\f[] \fISupport\f[]
+.IP \fB\(bu\fP 2
+\fIMiscellaneous\f[] \fIOptions\f[]
+.PP
+.sp \n(Ppu
+.ne 2
+
+Following these is a section describing
+\fIMiscellaneous\f[] \fIOptions\f[].
+While there is a rich set of options available,
+the only required option is one or more
+\f\*[B-Font]pool\f[],
+\f\*[B-Font]server\f[],
+\f\*[B-Font]peer\f[],
+\f\*[B-Font]broadcast\f[]
+or
+\f\*[B-Font]manycastclient\f[]
+commands.
+.SH Configuration Support
+Following is a description of the configuration commands in
+NTPv4.
+These commands have the same basic functions as in NTPv3 and
+in some cases new functions and new arguments.
+There are two
+classes of commands, configuration commands that configure a
+persistent association with a remote server or peer or reference
+clock, and auxiliary commands that specify environmental variables
+that control various related operations.
+.SS Configuration Commands
+The various modes are determined by the command keyword and the
+type of the required IP address.
+Addresses are classed by type as
+(s) a remote server or peer (IPv4 class A, B and C), (b) the
+broadcast address of a local interface, (m) a multicast address (IPv4
+class D), or (r) a reference clock address (127.127.x.x).
+Note that
+only those options applicable to each command are listed below.
+Use
+of options not listed may not be caught as an error, but may result
+in some weird and even destructive behavior.
+.sp \n(Ppu
+.ne 2
+
+If the Basic Socket Interface Extensions for IPv6 (RFC-2553)
+is detected, support for the IPv6 address family is generated
+in addition to the default support of the IPv4 address family.
+In a few cases, including the reslist billboard generated
+by ntpdc, IPv6 addresses are automatically generated.
+IPv6 addresses can be identified by the presence of colons
+\*[Lq]\&:\*[Rq]
+in the address field.
+IPv6 addresses can be used almost everywhere where
+IPv4 addresses can be used,
+with the exception of reference clock addresses,
+which are always IPv4.
+.sp \n(Ppu
+.ne 2
+
+Note that in contexts where a host name is expected, a
+\f\*[B-Font]\-4\f[]
+qualifier preceding
+the host name forces DNS resolution to the IPv4 namespace,
+while a
+\f\*[B-Font]\-6\f[]
+qualifier forces DNS resolution to the IPv6 namespace.
+See IPv6 references for the
+equivalent classes for that address family.
+.TP 7
+.NOP \f\*[B-Font]pool\f[] \f\*[I-Font]address\f[] [\f\*[B-Font]burst\f[]] [\f\*[B-Font]iburst\f[]] [\f\*[B-Font]version\f[] \f\*[I-Font]version\f[]] [\f\*[B-Font]prefer\f[]] [\f\*[B-Font]minpoll\f[] \f\*[I-Font]minpoll\f[]] [\f\*[B-Font]maxpoll\f[] \f\*[I-Font]maxpoll\f[]]
+.TP 7
+.NOP \f\*[B-Font]server\f[] \f\*[I-Font]address\f[] [\f\*[B-Font]key\f[] \f\*[I-Font]key\f[] \f\*[I-Font]\&|\f[] \f\*[B-Font]autokey\f[]] [\f\*[B-Font]burst\f[]] [\f\*[B-Font]iburst\f[]] [\f\*[B-Font]version\f[] \f\*[I-Font]version\f[]] [\f\*[B-Font]prefer\f[]] [\f\*[B-Font]minpoll\f[] \f\*[I-Font]minpoll\f[]] [\f\*[B-Font]maxpoll\f[] \f\*[I-Font]maxpoll\f[]]
+.TP 7
+.NOP \f\*[B-Font]peer\f[] \f\*[I-Font]address\f[] [\f\*[B-Font]key\f[] \f\*[I-Font]key\f[] \f\*[I-Font]\&|\f[] \f\*[B-Font]autokey\f[]] [\f\*[B-Font]version\f[] \f\*[I-Font]version\f[]] [\f\*[B-Font]prefer\f[]] [\f\*[B-Font]minpoll\f[] \f\*[I-Font]minpoll\f[]] [\f\*[B-Font]maxpoll\f[] \f\*[I-Font]maxpoll\f[]]
+.TP 7
+.NOP \f\*[B-Font]broadcast\f[] \f\*[I-Font]address\f[] [\f\*[B-Font]key\f[] \f\*[I-Font]key\f[] \f\*[I-Font]\&|\f[] \f\*[B-Font]autokey\f[]] [\f\*[B-Font]version\f[] \f\*[I-Font]version\f[]] [\f\*[B-Font]prefer\f[]] [\f\*[B-Font]minpoll\f[] \f\*[I-Font]minpoll\f[]] [\f\*[B-Font]ttl\f[] \f\*[I-Font]ttl\f[]]
+.TP 7
+.NOP \f\*[B-Font]manycastclient\f[] \f\*[I-Font]address\f[] [\f\*[B-Font]key\f[] \f\*[I-Font]key\f[] \f\*[I-Font]\&|\f[] \f\*[B-Font]autokey\f[]] [\f\*[B-Font]version\f[] \f\*[I-Font]version\f[]] [\f\*[B-Font]prefer\f[]] [\f\*[B-Font]minpoll\f[] \f\*[I-Font]minpoll\f[]] [\f\*[B-Font]maxpoll\f[] \f\*[I-Font]maxpoll\f[]] [\f\*[B-Font]ttl\f[] \f\*[I-Font]ttl\f[]]
+.PP
+.sp \n(Ppu
+.ne 2
+
+These five commands specify the time server name or address to
+be used and the mode in which to operate.
+The
+\f\*[I-Font]address\f[]
+can be
+either a DNS name or an IP address in dotted-quad notation.
+Additional information on association behavior can be found in the
+"Association Management"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+.TP 7
+.NOP \f\*[B-Font]pool\f[]
+For type s addresses, this command mobilizes a persistent
+client mode association with a number of remote servers.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+.TP 7
+.NOP \f\*[B-Font]server\f[]
+For type s and r addresses, this command mobilizes a persistent
+client mode association with the specified remote server or local
+radio clock.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+This command should
+\fInot\f[]
+be used for type
+b or m addresses.
+.TP 7
+.NOP \f\*[B-Font]peer\f[]
+For type s addresses (only), this command mobilizes a
+persistent symmetric-active mode association with the specified
+remote peer.
+In this mode the local clock can be synchronized to
+the remote peer or the remote peer can be synchronized to the local
+clock.
+This is useful in a network of servers where, depending on
+various failure scenarios, either the local or remote peer may be
+the better source of time.
+This command should NOT be used for type
+b, m or r addresses.
+.TP 7
+.NOP \f\*[B-Font]broadcast\f[]
+For type b and m addresses (only), this
+command mobilizes a persistent broadcast mode association.
+Multiple
+commands can be used to specify multiple local broadcast interfaces
+(subnets) and/or multiple multicast groups.
+Note that local
+broadcast messages go only to the interface associated with the
+subnet specified, but multicast messages go to all interfaces.
+In broadcast mode the local server sends periodic broadcast
+messages to a client population at the
+\f\*[I-Font]address\f[]
+specified, which is usually the broadcast address on (one of) the
+local network(s) or a multicast address assigned to NTP.
+The IANA
+has assigned the multicast group address IPv4 224.0.1.1 and
+IPv6 ff05::101 (site local) exclusively to
+NTP, but other nonconflicting addresses can be used to contain the
+messages within administrative boundaries.
+Ordinarily, this
+specification applies only to the local server operating as a
+sender; for operation as a broadcast client, see the
+\f\*[B-Font]broadcastclient\f[]
+or
+\f\*[B-Font]multicastclient\f[]
+commands
+below.
+.TP 7
+.NOP \f\*[B-Font]manycastclient\f[]
+For type m addresses (only), this command mobilizes a
+manycast client mode association for the multicast address
+specified.
+In this case a specific address must be supplied which
+matches the address used on the
+\f\*[B-Font]manycastserver\f[]
+command for
+the designated manycast servers.
+The NTP multicast address
+224.0.1.1 assigned by the IANA should NOT be used, unless specific
+means are taken to avoid spraying large areas of the Internet with
+these messages and causing a possibly massive implosion of replies
+at the sender.
+The
+\f\*[B-Font]manycastserver\f[]
+command specifies that the local server
+is to operate in client mode with the remote servers that are
+discovered as the result of broadcast/multicast messages.
+The
+client broadcasts a request message to the group address associated
+with the specified
+\f\*[I-Font]address\f[]
+and specifically enabled
+servers respond to these messages.
+The client selects the servers
+providing the best time and continues as with the
+\f\*[B-Font]server\f[]
+command.
+The remaining servers are discarded as if never
+heard.
+.PP
+.sp \n(Ppu
+.ne 2
+
+Options:
+.TP 7
+.NOP \f\*[B-Font]autokey\f[]
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the autokey scheme
+described in
+\fIAuthentication\f[] \fIOptions\f[].
+.TP 7
+.NOP \f\*[B-Font]burst\f[]
+when the server is reachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first and second packets
+can be changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to improve timekeeping quality
+with the
+\f\*[B-Font]server\f[]
+command and s addresses.
+.TP 7
+.NOP \f\*[B-Font]iburst\f[]
+When the server is unreachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first two packets can be
+changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to speed the initial synchronization
+acquisition with the
+\f\*[B-Font]server\f[]
+command and s addresses and when
+\fCntpd\fR(1ntpdmdoc)\f[]
+is started with the
+\f\*[B-Font]\-q\f[]
+option.
+.TP 7
+.NOP \f\*[B-Font]key\f[] \f\*[I-Font]key\f[]
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the specified
+\f\*[I-Font]key\f[]
+identifier with values from 1 to 65534, inclusive.
+The
+default is to include no encryption field.
+.TP 7
+.NOP \f\*[B-Font]minpoll\f[] \f\*[I-Font]minpoll\f[]
+.TP 7
+.NOP \f\*[B-Font]maxpoll\f[] \f\*[I-Font]maxpoll\f[]
+These options specify the minimum and maximum poll intervals
+for NTP messages, as a power of 2 in seconds
+The maximum poll
+interval defaults to 10 (1,024 s), but can be increased by the
+\f\*[B-Font]maxpoll\f[]
+option to an upper limit of 17 (36.4 h).
+The
+minimum poll interval defaults to 6 (64 s), but can be decreased by
+the
+\f\*[B-Font]minpoll\f[]
+option to a lower limit of 4 (16 s).
+.TP 7
+.NOP \f\*[B-Font]noselect\f[]
+Marks the server as unused, except for display purposes.
+The server is discarded by the selection algroithm.
+.TP 7
+.NOP \f\*[B-Font]prefer\f[]
+Marks the server as preferred.
+All other things being equal,
+this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+"Mitigation Rules and the prefer Keyword"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[])
+for further information.
+.TP 7
+.NOP \f\*[B-Font]ttl\f[] \f\*[I-Font]ttl\f[]
+This option is used only with broadcast server and manycast
+client modes.
+It specifies the time-to-live
+\f\*[I-Font]ttl\f[]
+to
+use on broadcast server and multicast server and the maximum
+\f\*[I-Font]ttl\f[]
+for the expanding ring search with manycast
+client packets.
+Selection of the proper value, which defaults to
+127, is something of a black art and should be coordinated with the
+network administrator.
+.TP 7
+.NOP \f\*[B-Font]version\f[] \f\*[I-Font]version\f[]
+Specifies the version number to be used for outgoing NTP
+packets.
+Versions 1-4 are the choices, with version 4 the
+default.
+.PP
+.SS Auxiliary Commands
+.TP 7
+.NOP \f\*[B-Font]broadcastclient\f[]
+This command enables reception of broadcast server messages to
+any local interface (type b) address.
+Upon receiving a message for
+the first time, the broadcast client measures the nominal server
+propagation delay using a brief client/server exchange with the
+server, then enters the broadcast client mode, in which it
+synchronizes to succeeding broadcast messages.
+Note that, in order
+to avoid accidental or malicious disruption in this mode, both the
+server and client should operate using symmetric-key or public-key
+authentication as described in
+\fIAuthentication\f[] \fIOptions\f[].
+.TP 7
+.NOP \f\*[B-Font]manycastserver\f[] \f\*[I-Font]address\f[] \f\*[I-Font]...\f[]
+This command enables reception of manycast client messages to
+the multicast group address(es) (type m) specified.
+At least one
+address is required, but the NTP multicast address 224.0.1.1
+assigned by the IANA should NOT be used, unless specific means are
+taken to limit the span of the reply and avoid a possibly massive
+implosion at the original sender.
+Note that, in order to avoid
+accidental or malicious disruption in this mode, both the server
+and client should operate using symmetric-key or public-key
+authentication as described in
+\fIAuthentication\f[] \fIOptions\f[].
+.TP 7
+.NOP \f\*[B-Font]multicastclient\f[] \f\*[I-Font]address\f[] \f\*[I-Font]...\f[]
+This command enables reception of multicast server messages to
+the multicast group address(es) (type m) specified.
+Upon receiving
+a message for the first time, the multicast client measures the
+nominal server propagation delay using a brief client/server
+exchange with the server, then enters the broadcast client mode, in
+which it synchronizes to succeeding multicast messages.
+Note that,
+in order to avoid accidental or malicious disruption in this mode,
+both the server and client should operate using symmetric-key or
+public-key authentication as described in
+\fIAuthentication\f[] \fIOptions\f[].
+.PP
+.SH Authentication Support
+Authentication support allows the NTP client to verify that the
+server is in fact known and trusted and not an intruder intending
+accidentally or on purpose to masquerade as that server.
+The NTPv3
+specification RFC-1305 defines a scheme which provides
+cryptographic authentication of received NTP packets.
+Originally,
+this was done using the Data Encryption Standard (DES) algorithm
+operating in Cipher Block Chaining (CBC) mode, commonly called
+DES-CBC.
+Subsequently, this was replaced by the RSA Message Digest
+5 (MD5) algorithm using a private key, commonly called keyed-MD5.
+Either algorithm computes a message digest, or one-way hash, which
+can be used to verify the server has the correct private key and
+key identifier.
+.sp \n(Ppu
+.ne 2
+
+NTPv4 retains the NTPv3 scheme, properly described as symmetric key
+cryptography and, in addition, provides a new Autokey scheme
+based on public key cryptography.
+Public key cryptography is generally considered more secure
+than symmetric key cryptography, since the security is based
+on a private value which is generated by each server and
+never revealed.
+With Autokey all key distribution and
+management functions involve only public values, which
+considerably simplifies key distribution and storage.
+Public key management is based on X.509 certificates,
+which can be provided by commercial services or
+produced by utility programs in the OpenSSL software library
+or the NTPv4 distribution.
+.sp \n(Ppu
+.ne 2
+
+While the algorithms for symmetric key cryptography are
+included in the NTPv4 distribution, public key cryptography
+requires the OpenSSL software library to be installed
+before building the NTP distribution.
+Directions for doing that
+are on the Building and Installing the Distribution page.
+.sp \n(Ppu
+.ne 2
+
+Authentication is configured separately for each association
+using the
+\f\*[B-Font]key\f[]
+or
+\f\*[B-Font]autokey\f[]
+subcommand on the
+\f\*[B-Font]peer\f[],
+\f\*[B-Font]server\f[],
+\f\*[B-Font]broadcast\f[]
+and
+\f\*[B-Font]manycastclient\f[]
+configuration commands as described in
+\fIConfiguration\f[] \fIOptions\f[]
+page.
+The authentication
+options described below specify the locations of the key files,
+if other than default, which symmetric keys are trusted
+and the interval between various operations, if other than default.
+.sp \n(Ppu
+.ne 2
+
+Authentication is always enabled,
+although ineffective if not configured as
+described below.
+If a NTP packet arrives
+including a message authentication
+code (MAC), it is accepted only if it
+passes all cryptographic checks.
+The
+checks require correct key ID, key value
+and message digest.
+If the packet has
+been modified in any way or replayed
+by an intruder, it will fail one or more
+of these checks and be discarded.
+Furthermore, the Autokey scheme requires a
+preliminary protocol exchange to obtain
+the server certificate, verify its
+credentials and initialize the protocol
+.sp \n(Ppu
+.ne 2
+
+The
+\f\*[B-Font]auth\f[]
+flag controls whether new associations or
+remote configuration commands require cryptographic authentication.
+This flag can be set or reset by the
+\f\*[B-Font]enable\f[]
+and
+\f\*[B-Font]disable\f[]
+commands and also by remote
+configuration commands sent by a
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+program running in
+another machine.
+If this flag is enabled, which is the default
+case, new broadcast client and symmetric passive associations and
+remote configuration commands must be cryptographically
+authenticated using either symmetric key or public key cryptography.
+If this
+flag is disabled, these operations are effective
+even if not cryptographic
+authenticated.
+It should be understood
+that operating with the
+\f\*[B-Font]auth\f[]
+flag disabled invites a significant vulnerability
+where a rogue hacker can
+masquerade as a falseticker and seriously
+disrupt system timekeeping.
+It is
+important to note that this flag has no purpose
+other than to allow or disallow
+a new association in response to new broadcast
+and symmetric active messages
+and remote configuration commands and, in particular,
+the flag has no effect on
+the authentication process itself.
+.sp \n(Ppu
+.ne 2
+
+An attractive alternative where multicast support is available
+is manycast mode, in which clients periodically troll
+for servers as described in the
+\fIAutomatic\f[] \fINTP\f[] \fIConfiguration\f[] \fIOptions\f[]
+page.
+Either symmetric key or public key
+cryptographic authentication can be used in this mode.
+The principle advantage
+of manycast mode is that potential servers need not be
+configured in advance,
+since the client finds them during regular operation,
+and the configuration
+files for all clients can be identical.
+.sp \n(Ppu
+.ne 2
+
+The security model and protocol schemes for
+both symmetric key and public key
+cryptography are summarized below;
+further details are in the briefings, papers
+and reports at the NTP project page linked from
+\f[C]http://www.ntp.org/\f[].
+.SS Symmetric-Key Cryptography
+The original RFC-1305 specification allows any one of possibly
+65,534 keys, each distinguished by a 32-bit key identifier, to
+authenticate an association.
+The servers and clients involved must
+agree on the key and key identifier to
+authenticate NTP packets.
+Keys and
+related information are specified in a key
+file, usually called
+\fIntp.keys\f[],
+which must be distributed and stored using
+secure means beyond the scope of the NTP protocol itself.
+Besides the keys used
+for ordinary NTP associations,
+additional keys can be used as passwords for the
+\fCntpq\fR(1ntpqmdoc)\f[]
+and
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+utility programs.
+.sp \n(Ppu
+.ne 2
+
+When
+\fCntpd\fR(1ntpdmdoc)\f[]
+is first started, it reads the key file specified in the
+\f\*[B-Font]keys\f[]
+configuration command and installs the keys
+in the key cache.
+However,
+individual keys must be activated with the
+\f\*[B-Font]trusted\f[]
+command before use.
+This
+allows, for instance, the installation of possibly
+several batches of keys and
+then activating or deactivating each batch
+remotely using
+\fCntpdc\fR(1ntpdcmdoc)\f[].
+This also provides a revocation capability that can be used
+if a key becomes compromised.
+The
+\f\*[B-Font]requestkey\f[]
+command selects the key used as the password for the
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+utility, while the
+\f\*[B-Font]controlkey\f[]
+command selects the key used as the password for the
+\fCntpq\fR(1ntpqmdoc)\f[]
+utility.
+.SS Public Key Cryptography
+NTPv4 supports the original NTPv3 symmetric key scheme
+described in RFC-1305 and in addition the Autokey protocol,
+which is based on public key cryptography.
+The Autokey Version 2 protocol described on the Autokey Protocol
+page verifies packet integrity using MD5 message digests
+and verifies the source with digital signatures and any of several
+digest/signature schemes.
+Optional identity schemes described on the Identity Schemes
+page and based on cryptographic challenge/response algorithms
+are also available.
+Using all of these schemes provides strong security against
+replay with or without modification, spoofing, masquerade
+and most forms of clogging attacks.
+.\" .Pp
+.\" The cryptographic means necessary for all Autokey operations
+.\" is provided by the OpenSSL software library.
+.\" This library is available from http://www.openssl.org/
+.\" and can be installed using the procedures outlined
+.\" in the Building and Installing the Distribution page.
+.\" Once installed,
+.\" the configure and build
+.\" process automatically detects the library and links
+.\" the library routines required.
+.sp \n(Ppu
+.ne 2
+
+The Autokey protocol has several modes of operation
+corresponding to the various NTP modes supported.
+Most modes use a special cookie which can be
+computed independently by the client and server,
+but encrypted in transmission.
+All modes use in addition a variant of the S-KEY scheme,
+in which a pseudo-random key list is generated and used
+in reverse order.
+These schemes are described along with an executive summary,
+current status, briefing slides and reading list on the
+\fIAutonomous\f[] \fIAuthentication\f[]
+page.
+.sp \n(Ppu
+.ne 2
+
+The specific cryptographic environment used by Autokey servers
+and clients is determined by a set of files
+and soft links generated by the
+\fCntp-keygen\fR(1ntpkeygenmdoc)\f[]
+program.
+This includes a required host key file,
+required certificate file and optional sign key file,
+leapsecond file and identity scheme files.
+The
+digest/signature scheme is specified in the X.509 certificate
+along with the matching sign key.
+There are several schemes
+available in the OpenSSL software library, each identified
+by a specific string such as
+\f\*[B-Font]md5WithRSAEncryption\f[],
+which stands for the MD5 message digest with RSA
+encryption scheme.
+The current NTP distribution supports
+all the schemes in the OpenSSL library, including
+those based on RSA and DSA digital signatures.
+.sp \n(Ppu
+.ne 2
+
+NTP secure groups can be used to define cryptographic compartments
+and security hierarchies.
+It is important that every host
+in the group be able to construct a certificate trail to one
+or more trusted hosts in the same group.
+Each group
+host runs the Autokey protocol to obtain the certificates
+for all hosts along the trail to one or more trusted hosts.
+This requires the configuration file in all hosts to be
+engineered so that, even under anticipated failure conditions,
+the NTP subnet will form such that every group host can find
+a trail to at least one trusted host.
+.SS Naming and Addressing
+It is important to note that Autokey does not use DNS to
+resolve addresses, since DNS can't be completely trusted
+until the name servers have synchronized clocks.
+The cryptographic name used by Autokey to bind the host identity
+credentials and cryptographic values must be independent
+of interface, network and any other naming convention.
+The name appears in the host certificate in either or both
+the subject and issuer fields, so protection against
+DNS compromise is essential.
+.sp \n(Ppu
+.ne 2
+
+By convention, the name of an Autokey host is the name returned
+by the Unix
+\fCgethostname\fR(2)\f[]
+system call or equivalent in other systems.
+By the system design
+model, there are no provisions to allow alternate names or aliases.
+However, this is not to say that DNS aliases, different names
+for each interface, etc., are constrained in any way.
+.sp \n(Ppu
+.ne 2
+
+It is also important to note that Autokey verifies authenticity
+using the host name, network address and public keys,
+all of which are bound together by the protocol specifically
+to deflect masquerade attacks.
+For this reason Autokey
+includes the source and destinatino IP addresses in message digest
+computations and so the same addresses must be available
+at both the server and client.
+For this reason operation
+with network address translation schemes is not possible.
+This reflects the intended robust security model where government
+and corporate NTP servers are operated outside firewall perimeters.
+.SS Operation
+A specific combination of authentication scheme (none,
+symmetric key, public key) and identity scheme is called
+a cryptotype, although not all combinations are compatible.
+There may be management configurations where the clients,
+servers and peers may not all support the same cryptotypes.
+A secure NTPv4 subnet can be configured in many ways while
+keeping in mind the principles explained above and
+in this section.
+Note however that some cryptotype
+combinations may successfully interoperate with each other,
+but may not represent good security practice.
+.sp \n(Ppu
+.ne 2
+
+The cryptotype of an association is determined at the time
+of mobilization, either at configuration time or some time
+later when a message of appropriate cryptotype arrives.
+When mobilized by a
+\f\*[B-Font]server\f[]
+or
+\f\*[B-Font]peer\f[]
+configuration command and no
+\f\*[B-Font]key\f[]
+or
+\f\*[B-Font]autokey\f[]
+subcommands are present, the association is not
+authenticated; if the
+\f\*[B-Font]key\f[]
+subcommand is present, the association is authenticated
+using the symmetric key ID specified; if the
+\f\*[B-Font]autokey\f[]
+subcommand is present, the association is authenticated
+using Autokey.
+.sp \n(Ppu
+.ne 2
+
+When multiple identity schemes are supported in the Autokey
+protocol, the first message exchange determines which one is used.
+The client request message contains bits corresponding
+to which schemes it has available.
+The server response message
+contains bits corresponding to which schemes it has available.
+Both server and client match the received bits with their own
+and select a common scheme.
+.sp \n(Ppu
+.ne 2
+
+Following the principle that time is a public value,
+a server responds to any client packet that matches
+its cryptotype capabilities.
+Thus, a server receiving
+an unauthenticated packet will respond with an unauthenticated
+packet, while the same server receiving a packet of a cryptotype
+it supports will respond with packets of that cryptotype.
+However, unconfigured broadcast or manycast client
+associations or symmetric passive associations will not be
+mobilized unless the server supports a cryptotype compatible
+with the first packet received.
+By default, unauthenticated associations will not be mobilized
+unless overridden in a decidedly dangerous way.
+.sp \n(Ppu
+.ne 2
+
+Some examples may help to reduce confusion.
+Client Alice has no specific cryptotype selected.
+Server Bob has both a symmetric key file and minimal Autokey files.
+Alice's unauthenticated messages arrive at Bob, who replies with
+unauthenticated messages.
+Cathy has a copy of Bob's symmetric
+key file and has selected key ID 4 in messages to Bob.
+Bob verifies the message with his key ID 4.
+If it's the
+same key and the message is verified, Bob sends Cathy a reply
+authenticated with that key.
+If verification fails,
+Bob sends Cathy a thing called a crypto-NAK, which tells her
+something broke.
+She can see the evidence using the
+\fCntpq\fR(1ntpqmdoc)\f[]
+program.
+.sp \n(Ppu
+.ne 2
+
+Denise has rolled her own host key and certificate.
+She also uses one of the identity schemes as Bob.
+She sends the first Autokey message to Bob and they
+both dance the protocol authentication and identity steps.
+If all comes out okay, Denise and Bob continue as described above.
+.sp \n(Ppu
+.ne 2
+
+It should be clear from the above that Bob can support
+all the girls at the same time, as long as he has compatible
+authentication and identity credentials.
+Now, Bob can act just like the girls in his own choice of servers;
+he can run multiple configured associations with multiple different
+servers (or the same server, although that might not be useful).
+But, wise security policy might preclude some cryptotype
+combinations; for instance, running an identity scheme
+with one server and no authentication with another might not be wise.
+.SS Key Management
+The cryptographic values used by the Autokey protocol are
+incorporated as a set of files generated by the
+\fCntp-keygen\fR(1ntpkeygenmdoc)\f[]
+utility program, including symmetric key, host key and
+public certificate files, as well as sign key, identity parameters
+and leapseconds files.
+Alternatively, host and sign keys and
+certificate files can be generated by the OpenSSL utilities
+and certificates can be imported from public certificate
+authorities.
+Note that symmetric keys are necessary for the
+\fCntpq\fR(1ntpqmdoc)\f[]
+and
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+utility programs.
+The remaining files are necessary only for the
+Autokey protocol.
+.sp \n(Ppu
+.ne 2
+
+Certificates imported from OpenSSL or public certificate
+authorities have certian limitations.
+The certificate should be in ASN.1 syntax, X.509 Version 3
+format and encoded in PEM, which is the same format
+used by OpenSSL.
+The overall length of the certificate encoded
+in ASN.1 must not exceed 1024 bytes.
+The subject distinguished
+name field (CN) is the fully qualified name of the host
+on which it is used; the remaining subject fields are ignored.
+The certificate extension fields must not contain either
+a subject key identifier or a issuer key identifier field;
+however, an extended key usage field for a trusted host must
+contain the value
+\f\*[B-Font]trustRoot\f[];.
+Other extension fields are ignored.
+.SS Authentication Commands
+.TP 7
+.NOP \f\*[B-Font]autokey\f[] [\f\*[I-Font]logsec\f[]]
+Specifies the interval between regenerations of the session key
+list used with the Autokey protocol.
+Note that the size of the key
+list for each association depends on this interval and the current
+poll interval.
+The default value is 12 (4096 s or about 1.1 hours).
+For poll intervals above the specified interval, a session key list
+with a single entry will be regenerated for every message
+sent.
+.TP 7
+.NOP \f\*[B-Font]controlkey\f[] \f\*[I-Font]key\f[]
+Specifies the key identifier to use with the
+\fCntpq\fR(1ntpqmdoc)\f[]
+utility, which uses the standard
+protocol defined in RFC-1305.
+The
+\f\*[I-Font]key\f[]
+argument is
+the key identifier for a trusted key, where the value can be in the
+range 1 to 65,534, inclusive.
+.TP 7
+.NOP \f\*[B-Font]crypto\f[] [\f\*[B-Font]cert\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]leap\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]randfile\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]host\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]sign\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]gq\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]gqpar\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]iffpar\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]mvpar\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]pw\f[] \f\*[I-Font]password\f[]]
+This command requires the OpenSSL library.
+It activates public key
+cryptography, selects the message digest and signature
+encryption scheme and loads the required private and public
+values described above.
+If one or more files are left unspecified,
+the default names are used as described above.
+Unless the complete path and name of the file are specified, the
+location of a file is relative to the keys directory specified
+in the
+\f\*[B-Font]keysdir\f[]
+command or default
+\fI/usr/local/etc\f[].
+Following are the subcommands:
+.RS
+.TP 7
+.NOP \f\*[B-Font]cert\f[] \f\*[I-Font]file\f[]
+Specifies the location of the required host public certificate file.
+This overrides the link
+\fIntpkey_cert_\f[]\f\*[I-Font]hostname\f[]
+in the keys directory.
+.TP 7
+.NOP \f\*[B-Font]gqpar\f[] \f\*[I-Font]file\f[]
+Specifies the location of the optional GQ parameters file.
+This
+overrides the link
+\fIntpkey_gq_\f[]\f\*[I-Font]hostname\f[]
+in the keys directory.
+.TP 7
+.NOP \f\*[B-Font]host\f[] \f\*[I-Font]file\f[]
+Specifies the location of the required host key file.
+This overrides
+the link
+\fIntpkey_key_\f[]\f\*[I-Font]hostname\f[]
+in the keys directory.
+.TP 7
+.NOP \f\*[B-Font]iffpar\f[] \f\*[I-Font]file\f[]
+Specifies the location of the optional IFF parameters file.This
+overrides the link
+\fIntpkey_iff_\f[]\f\*[I-Font]hostname\f[]
+in the keys directory.
+.TP 7
+.NOP \f\*[B-Font]leap\f[] \f\*[I-Font]file\f[]
+Specifies the location of the optional leapsecond file.
+This overrides the link
+\fIntpkey_leap\f[]
+in the keys directory.
+.TP 7
+.NOP \f\*[B-Font]mvpar\f[] \f\*[I-Font]file\f[]
+Specifies the location of the optional MV parameters file.
+This
+overrides the link
+\fIntpkey_mv_\f[]\f\*[I-Font]hostname\f[]
+in the keys directory.
+.TP 7
+.NOP \f\*[B-Font]pw\f[] \f\*[I-Font]password\f[]
+Specifies the password to decrypt files containing private keys and
+identity parameters.
+This is required only if these files have been
+encrypted.
+.TP 7
+.NOP \f\*[B-Font]randfile\f[] \f\*[I-Font]file\f[]
+Specifies the location of the random seed file used by the OpenSSL
+library.
+The defaults are described in the main text above.
+.TP 7
+.NOP \f\*[B-Font]sign\f[] \f\*[I-Font]file\f[]
+Specifies the location of the optional sign key file.
+This overrides
+the link
+\fIntpkey_sign_\f[]\f\*[I-Font]hostname\f[]
+in the keys directory.
+If this file is
+not found, the host key is also the sign key.
+.RE
+.TP 7
+.NOP \f\*[B-Font]keys\f[] \f\*[I-Font]keyfile\f[]
+Specifies the complete path and location of the MD5 key file
+containing the keys and key identifiers used by
+\fCntpd\fR(1ntpdmdoc)\f[],
+\fCntpq\fR(1ntpqmdoc)\f[]
+and
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+when operating with symmetric key cryptography.
+This is the same operation as the
+\f\*[B-Font]\-k\f[]
+command line option.
+.TP 7
+.NOP \f\*[B-Font]keysdir\f[] \f\*[I-Font]path\f[]
+This command specifies the default directory path for
+cryptographic keys, parameters and certificates.
+The default is
+\fI/usr/local/etc/\f[].
+.TP 7
+.NOP \f\*[B-Font]requestkey\f[] \f\*[I-Font]key\f[]
+Specifies the key identifier to use with the
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+utility program, which uses a
+proprietary protocol specific to this implementation of
+\fCntpd\fR(1ntpdmdoc)\f[].
+The
+\f\*[I-Font]key\f[]
+argument is a key identifier
+for the trusted key, where the value can be in the range 1 to
+65,534, inclusive.
+.TP 7
+.NOP \f\*[B-Font]revoke\f[] \f\*[I-Font]logsec\f[]
+Specifies the interval between re-randomization of certain
+cryptographic values used by the Autokey scheme, as a power of 2 in
+seconds.
+These values need to be updated frequently in order to
+deflect brute-force attacks on the algorithms of the scheme;
+however, updating some values is a relatively expensive operation.
+The default interval is 16 (65,536 s or about 18 hours).
+For poll
+intervals above the specified interval, the values will be updated
+for every message sent.
+.TP 7
+.NOP \f\*[B-Font]trustedkey\f[] \f\*[I-Font]key\f[] \f\*[I-Font]...\f[]
+Specifies the key identifiers which are trusted for the
+purposes of authenticating peers with symmetric key cryptography,
+as well as keys used by the
+\fCntpq\fR(1ntpqmdoc)\f[]
+and
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+programs.
+The authentication procedures require that both the local
+and remote servers share the same key and key identifier for this
+purpose, although different keys can be used with different
+servers.
+The
+\f\*[I-Font]key\f[]
+arguments are 32-bit unsigned
+integers with values from 1 to 65,534.
+.PP
+.SS Error Codes
+The following error codes are reported via the NTP control
+and monitoring protocol trap mechanism.
+.TP 7
+.NOP 101
+(bad field format or length)
+The packet has invalid version, length or format.
+.TP 7
+.NOP 102
+(bad timestamp)
+The packet timestamp is the same or older than the most recent received.
+This could be due to a replay or a server clock time step.
+.TP 7
+.NOP 103
+(bad filestamp)
+The packet filestamp is the same or older than the most recent received.
+This could be due to a replay or a key file generation error.
+.TP 7
+.NOP 104
+(bad or missing public key)
+The public key is missing, has incorrect format or is an unsupported type.
+.TP 7
+.NOP 105
+(unsupported digest type)
+The server requires an unsupported digest/signature scheme.
+.TP 7
+.NOP 106
+(mismatched digest types)
+Not used.
+.TP 7
+.NOP 107
+(bad signature length)
+The signature length does not match the current public key.
+.TP 7
+.NOP 108
+(signature not verified)
+The message fails the signature check.
+It could be bogus or signed by a
+different private key.
+.TP 7
+.NOP 109
+(certificate not verified)
+The certificate is invalid or signed with the wrong key.
+.TP 7
+.NOP 110
+(certificate not verified)
+The certificate is not yet valid or has expired or the signature could not
+be verified.
+.TP 7
+.NOP 111
+(bad or missing cookie)
+The cookie is missing, corrupted or bogus.
+.TP 7
+.NOP 112
+(bad or missing leapseconds table)
+The leapseconds table is missing, corrupted or bogus.
+.TP 7
+.NOP 113
+(bad or missing certificate)
+The certificate is missing, corrupted or bogus.
+.TP 7
+.NOP 114
+(bad or missing identity)
+The identity key is missing, corrupt or bogus.
+.PP
+.SH Monitoring Support
+\fCntpd\fR(1ntpdmdoc)\f[]
+includes a comprehensive monitoring facility suitable
+for continuous, long term recording of server and client
+timekeeping performance.
+See the
+\f\*[B-Font]statistics\f[]
+command below
+for a listing and example of each type of statistics currently
+supported.
+Statistic files are managed using file generation sets
+and scripts in the
+\fI./scripts\f[]
+directory of this distribution.
+Using
+these facilities and
+UNIX
+\fCcron\fR(8)\f[]
+jobs, the data can be
+automatically summarized and archived for retrospective analysis.
+.SS Monitoring Commands
+.TP 7
+.NOP \f\*[B-Font]statistics\f[] \f\*[I-Font]name\f[] \f\*[I-Font]...\f[]
+Enables writing of statistics records.
+Currently, eight kinds of
+\f\*[I-Font]name\f[]
+statistics are supported.
+.RS
+.TP 7
+.NOP \f\*[B-Font]clockstats\f[]
+Enables recording of clock driver statistics information.
+Each update
+received from a clock driver appends a line of the following form to
+the file generation set named
+\f\*[B-Font]clockstats\f[]:
+.br
+.in +4
+.nf
+49213 525.624 127.127.4.1 93 226 00:08:29.606 D
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the
+clock address in dotted-quad notation.
+The final field shows the last
+timecode received from the clock in decoded ASCII format, where
+meaningful.
+In some clock drivers a good deal of additional information
+can be gathered and displayed as well.
+See information specific to each
+clock for further details.
+.TP 7
+.NOP \f\*[B-Font]cryptostats\f[]
+This option requires the OpenSSL cryptographic software library.
+It
+enables recording of cryptographic public key protocol information.
+Each message received by the protocol module appends a line of the
+following form to the file generation set named
+\f\*[B-Font]cryptostats\f[]:
+.br
+.in +4
+.nf
+49213 525.624 127.127.4.1 message
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the peer
+address in dotted-quad notation, The final message field includes the
+message type and certain ancillary information.
+See the
+\fIAuthentication\f[] \fIOptions\f[]
+section for further information.
+.TP 7
+.NOP \f\*[B-Font]loopstats\f[]
+Enables recording of loop filter statistics information.
+Each
+update of the local clock outputs a line of the following form to
+the file generation set named
+\f\*[B-Font]loopstats\f[]:
+.br
+.in +4
+.nf
+50935 75440.031 0.000006019 13.778190 0.000351733 0.0133806
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next five fields
+show time offset (seconds), frequency offset (parts per million \-
+PPM), RMS jitter (seconds), Allan deviation (PPM) and clock
+discipline time constant.
+.TP 7
+.NOP \f\*[B-Font]peerstats\f[]
+Enables recording of peer statistics information.
+This includes
+statistics records of all peers of a NTP server and of special
+signals, where present and configured.
+Each valid update appends a
+line of the following form to the current element of a file
+generation set named
+\f\*[B-Font]peerstats\f[]:
+.br
+.in +4
+.nf
+48773 10847.650 127.127.4.1 9714 \-0.001605376 0.000000000 0.001424877 0.000958674
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the peer address in dotted-quad notation and status,
+respectively.
+The status field is encoded in hex in the format
+described in Appendix A of the NTP specification RFC 1305.
+The final four fields show the offset,
+delay, dispersion and RMS jitter, all in seconds.
+.TP 7
+.NOP \f\*[B-Font]rawstats\f[]
+Enables recording of raw-timestamp statistics information.
+This
+includes statistics records of all peers of a NTP server and of
+special signals, where present and configured.
+Each NTP message
+received from a peer or clock driver appends a line of the
+following form to the file generation set named
+\f\*[B-Font]rawstats\f[]:
+.br
+.in +4
+.nf
+50928 2132.543 128.4.1.1 128.4.1.20 3102453281.584327000 3102453281.58622800031 02453332.540806000 3102453332.541458000
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the remote peer or clock address followed by the local address
+in dotted-quad notation.
+The final four fields show the originate,
+receive, transmit and final NTP timestamps in order.
+The timestamp
+values are as received and before processing by the various data
+smoothing and mitigation algorithms.
+.TP 7
+.NOP \f\*[B-Font]sysstats\f[]
+Enables recording of ntpd statistics counters on a periodic basis.
+Each
+hour a line of the following form is appended to the file generation
+set named
+\f\*[B-Font]sysstats\f[]:
+.br
+.in +4
+.nf
+50928 2132.543 36000 81965 0 9546 56 71793 512 540 10 147
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The remaining ten fields show
+the statistics counter values accumulated since the last generated
+line.
+.RS
+.TP 7
+.NOP Time since restart \f\*[B-Font]36000\f[]
+Time in hours since the system was last rebooted.
+.TP 7
+.NOP Packets received \f\*[B-Font]81965\f[]
+Total number of packets received.
+.TP 7
+.NOP Packets processed \f\*[B-Font]0\f[]
+Number of packets received in response to previous packets sent
+.TP 7
+.NOP Current version \f\*[B-Font]9546\f[]
+Number of packets matching the current NTP version.
+.TP 7
+.NOP Previous version \f\*[B-Font]56\f[]
+Number of packets matching the previous NTP version.
+.TP 7
+.NOP Bad version \f\*[B-Font]71793\f[]
+Number of packets matching neither NTP version.
+.TP 7
+.NOP Access denied \f\*[B-Font]512\f[]
+Number of packets denied access for any reason.
+.TP 7
+.NOP Bad length or format \f\*[B-Font]540\f[]
+Number of packets with invalid length, format or port number.
+.TP 7
+.NOP Bad authentication \f\*[B-Font]10\f[]
+Number of packets not verified as authentic.
+.TP 7
+.NOP Rate exceeded \f\*[B-Font]147\f[]
+Number of packets discarded due to rate limitation.
+.RE
+.TP 7
+.NOP \f\*[B-Font]statsdir\f[] \f\*[I-Font]directory_path\f[]
+Indicates the full path of a directory where statistics files
+should be created (see below).
+This keyword allows
+the (otherwise constant)
+\f\*[B-Font]filegen\f[]
+filename prefix to be modified for file generation sets, which
+is useful for handling statistics logs.
+.TP 7
+.NOP \f\*[B-Font]filegen\f[] \f\*[I-Font]name\f[] [\f\*[B-Font]file\f[] \f\*[I-Font]filename\f[]] [\f\*[B-Font]type\f[] \f\*[I-Font]typename\f[]] [\f\*[B-Font]link\f[] | \f\*[B-Font]nolink\f[]] [\f\*[B-Font]enable\f[] | \f\*[B-Font]disable\f[]]
+Configures setting of generation file set name.
+Generation
+file sets provide a means for handling files that are
+continuously growing during the lifetime of a server.
+Server statistics are a typical example for such files.
+Generation file sets provide access to a set of files used
+to store the actual data.
+At any time at most one element
+of the set is being written to.
+The type given specifies
+when and how data will be directed to a new element of the set.
+This way, information stored in elements of a file set
+that are currently unused are available for administrational
+operations without the risk of disturbing the operation of ntpd.
+(Most important: they can be removed to free space for new data
+produced.)
+.sp \n(Ppu
+.ne 2
+
+Note that this command can be sent from the
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+program running at a remote location.
+.RS
+.TP 7
+.NOP \f\*[B-Font]name\f[]
+This is the type of the statistics records, as shown in the
+\f\*[B-Font]statistics\f[]
+command.
+.TP 7
+.NOP \f\*[B-Font]file\f[] \f\*[I-Font]filename\f[]
+This is the file name for the statistics records.
+Filenames of set
+members are built from three concatenated elements
+\f\*[B-Font]prefix\f[],
+\f\*[B-Font]filename\f[]
+and
+\f\*[B-Font]suffix\f[]:
+.RS
+.TP 7
+.NOP \f\*[B-Font]prefix\f[]
+This is a constant filename path.
+It is not subject to
+modifications via the
+\f\*[I-Font]filegen\f[]
+option.
+It is defined by the
+server, usually specified as a compile-time constant.
+It may,
+however, be configurable for individual file generation sets
+via other commands.
+For example, the prefix used with
+\f\*[I-Font]loopstats\f[]
+and
+\f\*[I-Font]peerstats\f[]
+generation can be configured using the
+\f\*[I-Font]statsdir\f[]
+option explained above.
+.TP 7
+.NOP \f\*[B-Font]filename\f[]
+This string is directly concatenated to the prefix mentioned
+above (no intervening
+\[oq]/\[cq]).
+This can be modified using
+the file argument to the
+\f\*[I-Font]filegen\f[]
+statement.
+No
+\fI..\f[]
+elements are
+allowed in this component to prevent filenames referring to
+parts outside the filesystem hierarchy denoted by
+\f\*[I-Font]prefix\f[].
+.TP 7
+.NOP \f\*[B-Font]suffix\f[]
+This part is reflects individual elements of a file set.
+It is
+generated according to the type of a file set.
+.RE
+.TP 7
+.NOP \f\*[B-Font]type\f[] \f\*[I-Font]typename\f[]
+A file generation set is characterized by its type.
+The following
+types are supported:
+.RS
+.TP 7
+.NOP \f\*[B-Font]none\f[]
+The file set is actually a single plain file.
+.TP 7
+.NOP \f\*[B-Font]pid\f[]
+One element of file set is used per incarnation of a ntpd
+server.
+This type does not perform any changes to file set
+members during runtime, however it provides an easy way of
+separating files belonging to different
+\fCntpd\fR(1ntpdmdoc)\f[]
+server incarnations.
+The set member filename is built by appending a
+\[oq]\&.\[cq]
+to concatenated
+\f\*[I-Font]prefix\f[]
+and
+\f\*[I-Font]filename\f[]
+strings, and
+appending the decimal representation of the process ID of the
+\fCntpd\fR(1ntpdmdoc)\f[]
+server process.
+.TP 7
+.NOP \f\*[B-Font]day\f[]
+One file generation set element is created per day.
+A day is
+defined as the period between 00:00 and 24:00 UTC.
+The file set
+member suffix consists of a
+\[oq]\&.\[cq]
+and a day specification in
+the form
+\f\*[B-Font]YYYYMMdd\f[].
+\f\*[B-Font]YYYY\f[]
+is a 4-digit year number (e.g., 1992).
+\f\*[B-Font]MM\f[]
+is a two digit month number.
+\f\*[B-Font]dd\f[]
+is a two digit day number.
+Thus, all information written at 10 December 1992 would end up
+in a file named
+\f\*[I-Font]prefix\f[]
+\f\*[I-Font]filename\f[].19921210.
+.TP 7
+.NOP \f\*[B-Font]week\f[]
+Any file set member contains data related to a certain week of
+a year.
+The term week is defined by computing day-of-year
+modulo 7.
+Elements of such a file generation set are
+distinguished by appending the following suffix to the file set
+filename base: A dot, a 4-digit year number, the letter
+\f\*[B-Font]W\f[],
+and a 2-digit week number.
+For example, information from January,
+10th 1992 would end up in a file with suffix
+.NOP. \f\*[I-Font]1992W1\f[].
+.TP 7
+.NOP \f\*[B-Font]month\f[]
+One generation file set element is generated per month.
+The
+file name suffix consists of a dot, a 4-digit year number, and
+a 2-digit month.
+.TP 7
+.NOP \f\*[B-Font]year\f[]
+One generation file element is generated per year.
+The filename
+suffix consists of a dot and a 4 digit year number.
+.TP 7
+.NOP \f\*[B-Font]age\f[]
+This type of file generation sets changes to a new element of
+the file set every 24 hours of server operation.
+The filename
+suffix consists of a dot, the letter
+\f\*[B-Font]a\f[],
+and an 8-digit number.
+This number is taken to be the number of seconds the server is
+running at the start of the corresponding 24-hour period.
+Information is only written to a file generation by specifying
+\f\*[B-Font]enable\f[];
+output is prevented by specifying
+\f\*[B-Font]disable\f[].
+.RE
+.TP 7
+.NOP \f\*[B-Font]link\f[] | \f\*[B-Font]nolink\f[]
+It is convenient to be able to access the current element of a file
+generation set by a fixed name.
+This feature is enabled by
+specifying
+\f\*[B-Font]link\f[]
+and disabled using
+\f\*[B-Font]nolink\f[].
+If link is specified, a
+hard link from the current file set element to a file without
+suffix is created.
+When there is already a file with this name and
+the number of links of this file is one, it is renamed appending a
+dot, the letter
+\f\*[B-Font]C\f[],
+and the pid of the ntpd server process.
+When the
+number of links is greater than one, the file is unlinked.
+This
+allows the current file to be accessed by a constant name.
+.TP 7
+.NOP \f\*[B-Font]enable\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]disable\f[]
+Enables or disables the recording function.
+.RE
+.RE
+.PP
+.SH Access Control Support
+The
+\fCntpd\fR(1ntpdmdoc)\f[]
+daemon implements a general purpose address/mask based restriction
+list.
+The list contains address/match entries sorted first
+by increasing address values and and then by increasing mask values.
+A match occurs when the bitwise AND of the mask and the packet
+source address is equal to the bitwise AND of the mask and
+address in the list.
+The list is searched in order with the
+last match found defining the restriction flags associated
+with the entry.
+Additional information and examples can be found in the
+"Notes on Configuring NTP and Setting up a NTP Subnet"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+.sp \n(Ppu
+.ne 2
+
+The restriction facility was implemented in conformance
+with the access policies for the original NSFnet backbone
+time servers.
+Later the facility was expanded to deflect
+cryptographic and clogging attacks.
+While this facility may
+be useful for keeping unwanted or broken or malicious clients
+from congesting innocent servers, it should not be considered
+an alternative to the NTP authentication facilities.
+Source address based restrictions are easily circumvented
+by a determined cracker.
+.sp \n(Ppu
+.ne 2
+
+Clients can be denied service because they are explicitly
+included in the restrict list created by the restrict command
+or implicitly as the result of cryptographic or rate limit
+violations.
+Cryptographic violations include certificate
+or identity verification failure; rate limit violations generally
+result from defective NTP implementations that send packets
+at abusive rates.
+Some violations cause denied service
+only for the offending packet, others cause denied service
+for a timed period and others cause the denied service for
+an indefinate period.
+When a client or network is denied access
+for an indefinate period, the only way at present to remove
+the restrictions is by restarting the server.
+.SS The Kiss-of-Death Packet
+Ordinarily, packets denied service are simply dropped with no
+further action except incrementing statistics counters.
+Sometimes a
+more proactive response is needed, such as a server message that
+explicitly requests the client to stop sending and leave a message
+for the system operator.
+A special packet format has been created
+for this purpose called the "kiss-of-death" (KoD) packet.
+KoD packets have the leap bits set unsynchronized and stratum set
+to zero and the reference identifier field set to a four-byte
+ASCII code.
+If the
+\f\*[B-Font]noserve\f[]
+or
+\f\*[B-Font]notrust\f[]
+flag of the matching restrict list entry is set,
+the code is "DENY"; if the
+\f\*[B-Font]limited\f[]
+flag is set and the rate limit
+is exceeded, the code is "RATE".
+Finally, if a cryptographic violation occurs, the code is "CRYP".
+.sp \n(Ppu
+.ne 2
+
+A client receiving a KoD performs a set of sanity checks to
+minimize security exposure, then updates the stratum and
+reference identifier peer variables, sets the access
+denied (TEST4) bit in the peer flash variable and sends
+a message to the log.
+As long as the TEST4 bit is set,
+the client will send no further packets to the server.
+The only way at present to recover from this condition is
+to restart the protocol at both the client and server.
+This
+happens automatically at the client when the association times out.
+It will happen at the server only if the server operator cooperates.
+.SS Access Control Commands
+.TP 7
+.NOP \f\*[B-Font]discard\f[] [\f\*[B-Font]average\f[] \f\*[I-Font]avg\f[]] [\f\*[B-Font]minimum\f[] \f\*[I-Font]min\f[]] [\f\*[B-Font]monitor\f[] \f\*[I-Font]prob\f[]]
+Set the parameters of the
+\f\*[B-Font]limited\f[]
+facility which protects the server from
+client abuse.
+The
+\f\*[B-Font]average\f[]
+subcommand specifies the minimum average packet
+spacing, while the
+\f\*[B-Font]minimum\f[]
+subcommand specifies the minimum packet spacing.
+Packets that violate these minima are discarded
+and a kiss-o'-death packet returned if enabled.
+The default
+minimum average and minimum are 5 and 2, respectively.
+The monitor subcommand specifies the probability of discard
+for packets that overflow the rate-control window.
+.TP 7
+.NOP \f\*[B-Font]restrict\f[] \f\*[B-Font]address\f[] [\f\*[B-Font]mask\f[] \f\*[I-Font]mask\f[]] [\f\*[I-Font]flag\f[] \f\*[I-Font]...\f[]]
+The
+\f\*[I-Font]address\f[]
+argument expressed in
+dotted-quad form is the address of a host or network.
+Alternatively, the
+\f\*[I-Font]address\f[]
+argument can be a valid host DNS name.
+The
+\f\*[I-Font]mask\f[]
+argument expressed in dotted-quad form defaults to
+\f\*[B-Font]255.255.255.255\f[],
+meaning that the
+\f\*[I-Font]address\f[]
+is treated as the address of an individual host.
+A default entry (address
+\f\*[B-Font]0.0.0.0\f[],
+mask
+\f\*[B-Font]0.0.0.0\f[])
+is always included and is always the first entry in the list.
+Note that text string
+\f\*[B-Font]default\f[],
+with no mask option, may
+be used to indicate the default entry.
+In the current implementation,
+\f\*[B-Font]flag\f[]
+always
+restricts access, i.e., an entry with no flags indicates that free
+access to the server is to be given.
+The flags are not orthogonal,
+in that more restrictive flags will often make less restrictive
+ones redundant.
+The flags can generally be classed into two
+categories, those which restrict time service and those which
+restrict informational queries and attempts to do run-time
+reconfiguration of the server.
+One or more of the following flags
+may be specified:
+.RS
+.TP 7
+.NOP \f\*[B-Font]ignore\f[]
+Deny packets of all kinds, including
+\fCntpq\fR(1ntpqmdoc)\f[]
+and
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+queries.
+.TP 7
+.NOP \f\*[B-Font]kod\f[]
+If this flag is set when an access violation occurs, a kiss-o'-death
+(KoD) packet is sent.
+KoD packets are rate limited to no more than one
+per second.
+If another KoD packet occurs within one second after the
+last one, the packet is dropped.
+.TP 7
+.NOP \f\*[B-Font]limited\f[]
+Deny service if the packet spacing violates the lower limits specified
+in the discard command.
+A history of clients is kept using the
+monitoring capability of
+\fCntpd\fR(1ntpdmdoc)\f[].
+Thus, monitoring is always active as
+long as there is a restriction entry with the
+\f\*[B-Font]limited\f[]
+flag.
+.TP 7
+.NOP \f\*[B-Font]lowpriotrap\f[]
+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.
+.TP 7
+.NOP \f\*[B-Font]nomodify\f[]
+Deny
+\fCntpq\fR(1ntpqmdoc)\f[]
+and
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+queries which attempt to modify the state of the
+server (i.e., run time reconfiguration).
+Queries which return
+information are permitted.
+.TP 7
+.NOP \f\*[B-Font]noquery\f[]
+Deny
+\fCntpq\fR(1ntpqmdoc)\f[]
+and
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+queries.
+Time service is not affected.
+.TP 7
+.NOP \f\*[B-Font]nopeer\f[]
+Deny packets which would result in mobilizing a new association.
+This
+includes broadcast and symmetric active packets when a configured
+association does not exist.
+It also includes
+\f\*[B-Font]pool\f[]
+associations, so if you want to use servers from a
+\f\*[B-Font]pool\f[]
+directive and also want to use
+\f\*[B-Font]nopeer\f[]
+by default, you'll want a
+\f\*[B-Font]restrict source ...\f[] \f\*[B-Font]line\f[] \f\*[B-Font]as\f[] \f\*[B-Font]well\f[] \f\*[B-Font]that\f[] \f\*[B-Font]does\f[]
+.TP 7
+.NOP not
+include the
+\f\*[B-Font]nopeer\f[]
+directive.
+.TP 7
+.NOP \f\*[B-Font]noserve\f[]
+Deny all packets except
+\fCntpq\fR(1ntpqmdoc)\f[]
+and
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+queries.
+.TP 7
+.NOP \f\*[B-Font]notrap\f[]
+Decline to provide mode 6 control message trap service to matching
+hosts.
+The trap service is a subsystem of the ntpdq control message
+protocol which is intended for use by remote event logging programs.
+.TP 7
+.NOP \f\*[B-Font]notrust\f[]
+Deny service unless the packet is cryptographically authenticated.
+.TP 7
+.NOP \f\*[B-Font]ntpport\f[]
+This is actually a match algorithm modifier, rather than a
+restriction flag.
+Its presence causes the restriction entry to be
+matched only if the source port in the packet is the standard NTP
+UDP port (123).
+Both
+\f\*[B-Font]ntpport\f[]
+and
+\f\*[B-Font]non-ntpport\f[]
+may
+be specified.
+The
+\f\*[B-Font]ntpport\f[]
+is considered more specific and
+is sorted later in the list.
+.TP 7
+.NOP \f\*[B-Font]version\f[]
+Deny packets that do not match the current NTP version.
+.RE
+.sp \n(Ppu
+.ne 2
+
+Default restriction list entries with the flags ignore, interface,
+ntpport, for each of the local host's interface addresses are
+inserted into the table at startup to prevent the server
+from attempting to synchronize to its own time.
+A default entry is also always present, though if it is
+otherwise unconfigured; no flags are associated
+with the default entry (i.e., everything besides your own
+NTP server is unrestricted).
+.PP
+.SH Automatic NTP Configuration Options
+.SS Manycasting
+Manycasting is a automatic discovery and configuration paradigm
+new to NTPv4.
+It is intended as a means for a multicast client
+to troll the nearby network neighborhood to find cooperating
+manycast servers, validate them using cryptographic means
+and evaluate their time values with respect to other servers
+that might be lurking in the vicinity.
+The intended result is that each manycast client mobilizes
+client associations with some number of the "best"
+of the nearby manycast servers, yet automatically reconfigures
+to sustain this number of servers should one or another fail.
+.sp \n(Ppu
+.ne 2
+
+Note that the manycasting paradigm does not coincide
+with the anycast paradigm described in RFC-1546,
+which is designed to find a single server from a clique
+of servers providing the same service.
+The manycast paradigm is designed to find a plurality
+of redundant servers satisfying defined optimality criteria.
+.sp \n(Ppu
+.ne 2
+
+Manycasting can be used with either symmetric key
+or public key cryptography.
+The public key infrastructure (PKI)
+offers the best protection against compromised keys
+and is generally considered stronger, at least with relatively
+large key sizes.
+It is implemented using the Autokey protocol and
+the OpenSSL cryptographic library available from
+\f[C]http://www.openssl.org/\f[].
+The library can also be used with other NTPv4 modes
+as well and is highly recommended, especially for broadcast modes.
+.sp \n(Ppu
+.ne 2
+
+A persistent manycast client association is configured
+using the manycastclient command, which is similar to the
+server command but with a multicast (IPv4 class
+\f\*[B-Font]D\f[]
+or IPv6 prefix
+\f\*[B-Font]FF\f[])
+group address.
+The IANA has designated IPv4 address 224.1.1.1
+and IPv6 address FF05::101 (site local) for NTP.
+When more servers are needed, it broadcasts manycast
+client messages to this address at the minimum feasible rate
+and minimum feasible time-to-live (TTL) hops, depending
+on how many servers have already been found.
+There can be as many manycast client associations
+as different group address, each one serving as a template
+for a future ephemeral unicast client/server association.
+.sp \n(Ppu
+.ne 2
+
+Manycast servers configured with the
+\f\*[B-Font]manycastserver\f[]
+command listen on the specified group address for manycast
+client messages.
+Note the distinction between manycast client,
+which actively broadcasts messages, and manycast server,
+which passively responds to them.
+If a manycast server is
+in scope of the current TTL and is itself synchronized
+to a valid source and operating at a stratum level equal
+to or lower than the manycast client, it replies to the
+manycast client message with an ordinary unicast server message.
+.sp \n(Ppu
+.ne 2
+
+The manycast client receiving this message mobilizes
+an ephemeral client/server association according to the
+matching manycast client template, but only if cryptographically
+authenticated and the server stratum is less than or equal
+to the client stratum.
+Authentication is explicitly required
+and either symmetric key or public key (Autokey) can be used.
+Then, the client polls the server at its unicast address
+in burst mode in order to reliably set the host clock
+and validate the source.
+This normally results
+in a volley of eight client/server at 2-s intervals
+during which both the synchronization and cryptographic
+protocols run concurrently.
+Following the volley,
+the client runs the NTP intersection and clustering
+algorithms, which act to discard all but the "best"
+associations according to stratum and synchronization
+distance.
+The surviving associations then continue
+in ordinary client/server mode.
+.sp \n(Ppu
+.ne 2
+
+The manycast client polling strategy is designed to reduce
+as much as possible the volume of manycast client messages
+and the effects of implosion due to near-simultaneous
+arrival of manycast server messages.
+The strategy is determined by the
+\f\*[B-Font]manycastclient\f[],
+\f\*[B-Font]tos\f[]
+and
+\f\*[B-Font]ttl\f[]
+configuration commands.
+The manycast poll interval is
+normally eight times the system poll interval,
+which starts out at the
+\f\*[B-Font]minpoll\f[]
+value specified in the
+\f\*[B-Font]manycastclient\f[],
+command and, under normal circumstances, increments to the
+\f\*[B-Font]maxpolll\f[]
+value specified in this command.
+Initially, the TTL is
+set at the minimum hops specified by the ttl command.
+At each retransmission the TTL is increased until reaching
+the maximum hops specified by this command or a sufficient
+number client associations have been found.
+Further retransmissions use the same TTL.
+.sp \n(Ppu
+.ne 2
+
+The quality and reliability of the suite of associations
+discovered by the manycast client is determined by the NTP
+mitigation algorithms and the
+\f\*[B-Font]minclock\f[]
+and
+\f\*[B-Font]minsane\f[]
+values specified in the
+\f\*[B-Font]tos\f[]
+configuration command.
+At least
+\f\*[B-Font]minsane\f[]
+candidate servers must be available and the mitigation
+algorithms produce at least
+\f\*[B-Font]minclock\f[]
+survivors in order to synchronize the clock.
+Byzantine agreement principles require at least four
+candidates in order to correctly discard a single falseticker.
+For legacy purposes,
+\f\*[B-Font]minsane\f[]
+defaults to 1 and
+\f\*[B-Font]minclock\f[]
+defaults to 3.
+For manycast service
+\f\*[B-Font]minsane\f[]
+should be explicitly set to 4, assuming at least that
+number of servers are available.
+.sp \n(Ppu
+.ne 2
+
+If at least
+\f\*[B-Font]minclock\f[]
+servers are found, the manycast poll interval is immediately
+set to eight times
+\f\*[B-Font]maxpoll\f[].
+If less than
+\f\*[B-Font]minclock\f[]
+servers are found when the TTL has reached the maximum hops,
+the manycast poll interval is doubled.
+For each transmission
+after that, the poll interval is doubled again until
+reaching the maximum of eight times
+\f\*[B-Font]maxpoll\f[].
+Further transmissions use the same poll interval and
+TTL values.
+Note that while all this is going on,
+each client/server association found is operating normally
+it the system poll interval.
+.sp \n(Ppu
+.ne 2
+
+Administratively scoped multicast boundaries are normally
+specified by the network router configuration and,
+in the case of IPv6, the link/site scope prefix.
+By default, the increment for TTL hops is 32 starting
+from 31; however, the
+\f\*[B-Font]ttl\f[]
+configuration command can be
+used to modify the values to match the scope rules.
+.sp \n(Ppu
+.ne 2
+
+It is often useful to narrow the range of acceptable
+servers which can be found by manycast client associations.
+Because manycast servers respond only when the client
+stratum is equal to or greater than the server stratum,
+primary (stratum 1) servers fill find only primary servers
+in TTL range, which is probably the most common objective.
+However, unless configured otherwise, all manycast clients
+in TTL range will eventually find all primary servers
+in TTL range, which is probably not the most common
+objective in large networks.
+The
+\f\*[B-Font]tos\f[]
+command can be used to modify this behavior.
+Servers with stratum below
+\f\*[B-Font]floor\f[]
+or above
+\f\*[B-Font]ceiling\f[]
+specified in the
+\f\*[B-Font]tos\f[]
+command are strongly discouraged during the selection
+process; however, these servers may be temporally
+accepted if the number of servers within TTL range is
+less than
+\f\*[B-Font]minclock\f[].
+.sp \n(Ppu
+.ne 2
+
+The above actions occur for each manycast client message,
+which repeats at the designated poll interval.
+However, once the ephemeral client association is mobilized,
+subsequent manycast server replies are discarded,
+since that would result in a duplicate association.
+If during a poll interval the number of client associations
+falls below
+\f\*[B-Font]minclock\f[],
+all manycast client prototype associations are reset
+to the initial poll interval and TTL hops and operation
+resumes from the beginning.
+It is important to avoid
+frequent manycast client messages, since each one requires
+all manycast servers in TTL range to respond.
+The result could well be an implosion, either minor or major,
+depending on the number of servers in range.
+The recommended value for
+\f\*[B-Font]maxpoll\f[]
+is 12 (4,096 s).
+.sp \n(Ppu
+.ne 2
+
+It is possible and frequently useful to configure a host
+as both manycast client and manycast server.
+A number of hosts configured this way and sharing a common
+group address will automatically organize themselves
+in an optimum configuration based on stratum and
+synchronization distance.
+For example, consider an NTP
+subnet of two primary servers and a hundred or more
+dependent clients.
+With two exceptions, all servers
+and clients have identical configuration files including both
+\f\*[B-Font]multicastclient\f[]
+and
+\f\*[B-Font]multicastserver\f[]
+commands using, for instance, multicast group address
+239.1.1.1.
+The only exception is that each primary server
+configuration file must include commands for the primary
+reference source such as a GPS receiver.
+.sp \n(Ppu
+.ne 2
+
+The remaining configuration files for all secondary
+servers and clients have the same contents, except for the
+\f\*[B-Font]tos\f[]
+command, which is specific for each stratum level.
+For stratum 1 and stratum 2 servers, that command is
+not necessary.
+For stratum 3 and above servers the
+\f\*[B-Font]floor\f[]
+value is set to the intended stratum number.
+Thus, all stratum 3 configuration files are identical,
+all stratum 4 files are identical and so forth.
+.sp \n(Ppu
+.ne 2
+
+Once operations have stabilized in this scenario,
+the primary servers will find the primary reference source
+and each other, since they both operate at the same
+stratum (1), but not with any secondary server or client,
+since these operate at a higher stratum.
+The secondary
+servers will find the servers at the same stratum level.
+If one of the primary servers loses its GPS receiver,
+it will continue to operate as a client and other clients
+will time out the corresponding association and
+re-associate accordingly.
+.sp \n(Ppu
+.ne 2
+
+Some administrators prefer to avoid running
+\fCntpd\fR(1ntpdmdoc)\f[]
+continuously and run either
+\fCntpdate\fR(8)\f[]
+or
+\fCntpd\fR(1ntpdmdoc)\f[]
+\f\*[B-Font]\-q\f[]
+as a cron job.
+In either case the servers must be
+configured in advance and the program fails if none are
+available when the cron job runs.
+A really slick
+application of manycast is with
+\fCntpd\fR(1ntpdmdoc)\f[]
+\f\*[B-Font]\-q\f[].
+The program wakes up, scans the local landscape looking
+for the usual suspects, selects the best from among
+the rascals, sets the clock and then departs.
+Servers do not have to be configured in advance and
+all clients throughout the network can have the same
+configuration file.
+.SS Manycast Interactions with Autokey
+Each time a manycast client sends a client mode packet
+to a multicast group address, all manycast servers
+in scope generate a reply including the host name
+and status word.
+The manycast clients then run
+the Autokey protocol, which collects and verifies
+all certificates involved.
+Following the burst interval
+all but three survivors are cast off,
+but the certificates remain in the local cache.
+It often happens that several complete signing trails
+from the client to the primary servers are collected in this way.
+.sp \n(Ppu
+.ne 2
+
+About once an hour or less often if the poll interval
+exceeds this, the client regenerates the Autokey key list.
+This is in general transparent in client/server mode.
+However, about once per day the server private value
+used to generate cookies is refreshed along with all
+manycast client associations.
+In this case all
+cryptographic values including certificates is refreshed.
+If a new certificate has been generated since
+the last refresh epoch, it will automatically revoke
+all prior certificates that happen to be in the
+certificate cache.
+At the same time, the manycast
+scheme starts all over from the beginning and
+the expanding ring shrinks to the minimum and increments
+from there while collecting all servers in scope.
+.SS Manycast Options
+.TP 7
+.NOP \f\*[B-Font]tos\f[] [\f\*[B-Font]ceiling\f[] \f\*[I-Font]ceiling\f[] | \f\*[B-Font]cohort\f[] { \f\*[B-Font]0\f[] | \f\*[B-Font]1\f[] } | \f\*[B-Font]floor\f[] \f\*[I-Font]floor\f[] | \f\*[B-Font]minclock\f[] \f\*[I-Font]minclock\f[] | \f\*[B-Font]minsane\f[] \f\*[I-Font]minsane\f[]]
+This command affects the clock selection and clustering
+algorithms.
+It can be used to select the quality and
+quantity of peers used to synchronize the system clock
+and is most useful in manycast mode.
+The variables operate
+as follows:
+.RS
+.TP 7
+.NOP \f\*[B-Font]ceiling\f[] \f\*[I-Font]ceiling\f[]
+Peers with strata above
+\f\*[B-Font]ceiling\f[]
+will be discarded if there are at least
+\f\*[B-Font]minclock\f[]
+peers remaining.
+This value defaults to 15, but can be changed
+to any number from 1 to 15.
+.TP 7
+.NOP \f\*[B-Font]cohort\f[] {0 | 1 }
+This is a binary flag which enables (0) or disables (1)
+manycast server replies to manycast clients with the same
+stratum level.
+This is useful to reduce implosions where
+large numbers of clients with the same stratum level
+are present.
+The default is to enable these replies.
+.TP 7
+.NOP \f\*[B-Font]floor\f[] \f\*[I-Font]floor\f[]
+Peers with strata below
+\f\*[B-Font]floor\f[]
+will be discarded if there are at least
+\f\*[B-Font]minclock\f[]
+peers remaining.
+This value defaults to 1, but can be changed
+to any number from 1 to 15.
+.TP 7
+.NOP \f\*[B-Font]minclock\f[] \f\*[I-Font]minclock\f[]
+The clustering algorithm repeatedly casts out outlyer
+associations until no more than
+\f\*[B-Font]minclock\f[]
+associations remain.
+This value defaults to 3,
+but can be changed to any number from 1 to the number of
+configured sources.
+.TP 7
+.NOP \f\*[B-Font]minsane\f[] \f\*[I-Font]minsane\f[]
+This is the minimum number of candidates available
+to the clock selection algorithm in order to produce
+one or more truechimers for the clustering algorithm.
+If fewer than this number are available, the clock is
+undisciplined and allowed to run free.
+The default is 1
+for legacy purposes.
+However, according to principles of
+Byzantine agreement,
+\f\*[B-Font]minsane\f[]
+should be at least 4 in order to detect and discard
+a single falseticker.
+.RE
+.TP 7
+.NOP \f\*[B-Font]ttl\f[] \f\*[I-Font]hop\f[] \f\*[I-Font]...\f[]
+This command specifies a list of TTL values in increasing
+order, up to 8 values can be specified.
+In manycast mode these values are used in turn
+in an expanding-ring search.
+The default is eight
+multiples of 32 starting at 31.
+.PP
+.SH Reference Clock Support
+The NTP Version 4 daemon supports some three dozen different radio,
+satellite and modem reference clocks plus a special pseudo-clock
+used for backup or when no other clock source is available.
+Detailed descriptions of individual device drivers and options can
+be found in the
+"Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+Additional information can be found in the pages linked
+there, including the
+"Debugging Hints for Reference Clock Drivers"
+and
+"How To Write a Reference Clock Driver"
+pages
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+In addition, support for a PPS
+signal is available as described in the
+"Pulse-per-second (PPS) Signal Interfacing"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+Many
+drivers support special line discipline/streams modules which can
+significantly improve the accuracy using the driver.
+These are
+described in the
+"Line Disciplines and Streams Drivers"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+.sp \n(Ppu
+.ne 2
+
+A reference clock will generally (though not always) be a radio
+timecode receiver which is synchronized to a source of standard
+time such as the services offered by the NRC in Canada and NIST and
+USNO in the US.
+The interface between the computer and the timecode
+receiver is device dependent, but is usually a serial port.
+A
+device driver specific to each reference clock must be selected and
+compiled in the distribution; however, most common radio, satellite
+and modem clocks are included by default.
+Note that an attempt to
+configure a reference clock when the driver has not been compiled
+or the hardware port has not been appropriately configured results
+in a scalding remark to the system log file, but is otherwise non
+hazardous.
+.sp \n(Ppu
+.ne 2
+
+For the purposes of configuration,
+\fCntpd\fR(1ntpdmdoc)\f[]
+treats
+reference clocks in a manner analogous to normal NTP peers as much
+as possible.
+Reference clocks are identified by a syntactically
+correct but invalid IP address, in order to distinguish them from
+normal NTP peers.
+Reference clock addresses are of the form
+\f[C]127.127.\f[]\f\*[I-Font]t\f[].\f\*[I-Font]u\f[],
+where
+\f\*[I-Font]t\f[]
+is an integer
+denoting the clock type and
+\f\*[I-Font]u\f[]
+indicates the unit
+number in the range 0-3.
+While it may seem overkill, it is in fact
+sometimes useful to configure multiple reference clocks of the same
+type, in which case the unit numbers must be unique.
+.sp \n(Ppu
+.ne 2
+
+The
+\f\*[B-Font]server\f[]
+command is used to configure a reference
+clock, where the
+\f\*[I-Font]address\f[]
+argument in that command
+is the clock address.
+The
+\f\*[B-Font]key\f[],
+\f\*[B-Font]version\f[]
+and
+\f\*[B-Font]ttl\f[]
+options are not used for reference clock support.
+The
+\f\*[B-Font]mode\f[]
+option is added for reference clock support, as
+described below.
+The
+\f\*[B-Font]prefer\f[]
+option can be useful to
+persuade the server to cherish a reference clock with somewhat more
+enthusiasm than other reference clocks or peers.
+Further
+information on this option can be found in the
+"Mitigation Rules and the prefer Keyword"
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[])
+page.
+The
+\f\*[B-Font]minpoll\f[]
+and
+\f\*[B-Font]maxpoll\f[]
+options have
+meaning only for selected clock drivers.
+See the individual clock
+driver document pages for additional information.
+.sp \n(Ppu
+.ne 2
+
+The
+\f\*[B-Font]fudge\f[]
+command is used to provide additional
+information for individual clock drivers and normally follows
+immediately after the
+\f\*[B-Font]server\f[]
+command.
+The
+\f\*[I-Font]address\f[]
+argument specifies the clock address.
+The
+\f\*[B-Font]refid\f[]
+and
+\f\*[B-Font]stratum\f[]
+options can be used to
+override the defaults for the device.
+There are two optional
+device-dependent time offsets and four flags that can be included
+in the
+\f\*[B-Font]fudge\f[]
+command as well.
+.sp \n(Ppu
+.ne 2
+
+The stratum number of a reference clock is by default zero.
+Since the
+\fCntpd\fR(1ntpdmdoc)\f[]
+daemon adds one to the stratum of each
+peer, a primary server ordinarily displays an external stratum of
+one.
+In order to provide engineered backups, it is often useful to
+specify the reference clock stratum as greater than zero.
+The
+\f\*[B-Font]stratum\f[]
+option is used for this purpose.
+Also, in cases
+involving both a reference clock and a pulse-per-second (PPS)
+discipline signal, it is useful to specify the reference clock
+identifier as other than the default, depending on the driver.
+The
+\f\*[B-Font]refid\f[]
+option is used for this purpose.
+Except where noted,
+these options apply to all clock drivers.
+.SS Reference Clock Commands
+.TP 7
+.NOP \f\*[B-Font]server\f[] \f[C]127.127.\f[]\f\*[I-Font]t\f[].\f\*[I-Font]u\f[] [\f\*[B-Font]prefer\f[]] [\f\*[B-Font]mode\f[] \f\*[I-Font]int\f[]] [\f\*[B-Font]minpoll\f[] \f\*[I-Font]int\f[]] [\f\*[B-Font]maxpoll\f[] \f\*[I-Font]int\f[]]
+This command can be used to configure reference clocks in
+special ways.
+The options are interpreted as follows:
+.RS
+.TP 7
+.NOP \f\*[B-Font]prefer\f[]
+Marks the reference clock as preferred.
+All other things being
+equal, this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+"Mitigation Rules and the prefer Keyword"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[])
+for further information.
+.TP 7
+.NOP \f\*[B-Font]mode\f[] \f\*[I-Font]int\f[]
+Specifies a mode number which is interpreted in a
+device-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+.TP 7
+.NOP \f\*[B-Font]minpoll\f[] \f\*[I-Font]int\f[]
+.TP 7
+.NOP \f\*[B-Font]maxpoll\f[] \f\*[I-Font]int\f[]
+These options specify the minimum and maximum polling interval
+for reference clock messages, as a power of 2 in seconds
+For
+most directly connected reference clocks, both
+\f\*[B-Font]minpoll\f[]
+and
+\f\*[B-Font]maxpoll\f[]
+default to 6 (64 s).
+For modem reference clocks,
+\f\*[B-Font]minpoll\f[]
+defaults to 10 (17.1 m) and
+\f\*[B-Font]maxpoll\f[]
+defaults to 14 (4.5 h).
+The allowable range is 4 (16 s) to 17 (36.4 h) inclusive.
+.RE
+.TP 7
+.NOP \f\*[B-Font]fudge\f[] \f[C]127.127.\f[]\f\*[I-Font]t\f[].\f\*[I-Font]u\f[] [\f\*[B-Font]time1\f[] \f\*[I-Font]sec\f[]] [\f\*[B-Font]time2\f[] \f\*[I-Font]sec\f[]] [\f\*[B-Font]stratum\f[] \f\*[I-Font]int\f[]] [\f\*[B-Font]refid\f[] \f\*[I-Font]string\f[]] [\f\*[B-Font]mode\f[] \f\*[I-Font]int\f[]] [\f\*[B-Font]flag1\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]] [\f\*[B-Font]flag2\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]] [\f\*[B-Font]flag3\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]] [\f\*[B-Font]flag4\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]]
+This command can be used to configure reference clocks in
+special ways.
+It must immediately follow the
+\f\*[B-Font]server\f[]
+command which configures the driver.
+Note that the same capability
+is possible at run time using the
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+program.
+The options are interpreted as
+follows:
+.RS
+.TP 7
+.NOP \f\*[B-Font]time1\f[] \f\*[I-Font]sec\f[]
+Specifies a constant to be added to the time offset produced by
+the driver, a fixed-point decimal number in seconds.
+This is used
+as a calibration constant to adjust the nominal time offset of a
+particular clock to agree with an external standard, such as a
+precision PPS signal.
+It also provides a way to correct a
+systematic error or bias due to serial port or operating system
+latencies, different cable lengths or receiver internal delay.
+The
+specified offset is in addition to the propagation delay provided
+by other means, such as internal DIPswitches.
+Where a calibration
+for an individual system and driver is available, an approximate
+correction is noted in the driver documentation pages.
+Note: in order to facilitate calibration when more than one
+radio clock or PPS signal is supported, a special calibration
+feature is available.
+It takes the form of an argument to the
+\f\*[B-Font]enable\f[]
+command described in
+\fIMiscellaneous\f[] \fIOptions\f[]
+page and operates as described in the
+"Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+.TP 7
+.NOP \f\*[B-Font]time2\f[] \f\*[I-Font]secs\f[]
+Specifies a fixed-point decimal number in seconds, which is
+interpreted in a driver-dependent way.
+See the descriptions of
+specific drivers in the
+"Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+.TP 7
+.NOP \f\*[B-Font]stratum\f[] \f\*[I-Font]int\f[]
+Specifies the stratum number assigned to the driver, an integer
+between 0 and 15.
+This number overrides the default stratum number
+ordinarily assigned by the driver itself, usually zero.
+.TP 7
+.NOP \f\*[B-Font]refid\f[] \f\*[I-Font]string\f[]
+Specifies an ASCII string of from one to four characters which
+defines the reference identifier used by the driver.
+This string
+overrides the default identifier ordinarily assigned by the driver
+itself.
+.TP 7
+.NOP \f\*[B-Font]mode\f[] \f\*[I-Font]int\f[]
+Specifies a mode number which is interpreted in a
+device-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+.TP 7
+.NOP \f\*[B-Font]flag1\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]
+.TP 7
+.NOP \f\*[B-Font]flag2\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]
+.TP 7
+.NOP \f\*[B-Font]flag3\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]
+.TP 7
+.NOP \f\*[B-Font]flag4\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]
+These four flags are used for customizing the clock driver.
+The
+interpretation of these values, and whether they are used at all,
+is a function of the particular clock driver.
+However, by
+convention
+\f\*[B-Font]flag4\f[]
+is used to enable recording monitoring
+data to the
+\f\*[B-Font]clockstats\f[]
+file configured with the
+\f\*[B-Font]filegen\f[]
+command.
+Further information on the
+\f\*[B-Font]filegen\f[]
+command can be found in
+\fIMonitoring\f[] \fIOptions\f[].
+.RE
+.PP
+.SH Miscellaneous Options
+.TP 7
+.NOP \f\*[B-Font]broadcastdelay\f[] \f\*[I-Font]seconds\f[]
+The broadcast and multicast modes require a special calibration
+to determine the network delay between the local and remote
+servers.
+Ordinarily, this is done automatically by the initial
+protocol exchanges between the client and server.
+In some cases,
+the calibration procedure may fail due to network or server access
+controls, for example.
+This command specifies the default delay to
+be used under these circumstances.
+Typically (for Ethernet), a
+number between 0.003 and 0.007 seconds is appropriate.
+The default
+when this command is not used is 0.004 seconds.
+.TP 7
+.NOP \f\*[B-Font]calldelay\f[] \f\*[I-Font]delay\f[]
+This option controls the delay in seconds between the first and second
+packets sent in burst or iburst mode to allow additional time for a modem
+or ISDN call to complete.
+.TP 7
+.NOP \f\*[B-Font]driftfile\f[] \f\*[I-Font]driftfile\f[]
+This command specifies the complete path and name of the file used to
+record the frequency of the local clock oscillator.
+This is the same
+operation as the
+\f\*[B-Font]\-f\f[]
+command line option.
+If the file exists, it is read at
+startup in order to set the initial frequency and then updated once per
+hour with the current frequency computed by the daemon.
+If the file name is
+specified, but the file itself does not exist, the starts with an initial
+frequency of zero and creates the file when writing it for the first time.
+If this command is not given, the daemon will always start with an initial
+frequency of zero.
+.sp \n(Ppu
+.ne 2
+
+The file format consists of a single line containing a single
+floating point number, which records the frequency offset measured
+in parts-per-million (PPM).
+The file is updated by first writing
+the current drift value into a temporary file and then renaming
+this file to replace the old version.
+This implies that
+\fCntpd\fR(1ntpdmdoc)\f[]
+must have write permission for the directory the
+drift file is located in, and that file system links, symbolic or
+otherwise, should be avoided.
+.TP 7
+.NOP \f\*[B-Font]enable\f[] [\f\*[B-Font]auth\f[] | \f\*[B-Font]bclient\f[] | \f\*[B-Font]calibrate\f[] | \f\*[B-Font]kernel\f[] | \f\*[B-Font]mode7\f[] | \f\*[B-Font]monitor\f[] | \f\*[B-Font]ntp\f[] | \f\*[B-Font]stats\f[]]
+.TP 7
+.NOP \f\*[B-Font]disable\f[] [\f\*[B-Font]auth\f[] | \f\*[B-Font]bclient\f[] | \f\*[B-Font]calibrate\f[] | \f\*[B-Font]kernel\f[] | \f\*[B-Font]mode7\f[] | \f\*[B-Font]monitor\f[] | \f\*[B-Font]ntp\f[] | \f\*[B-Font]stats\f[]]
+Provides a way to enable or disable various server options.
+Flags not mentioned are unaffected.
+Note that all of these flags
+can be controlled remotely using the
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+utility program.
+.RS
+.TP 7
+.NOP \f\*[B-Font]auth\f[]
+Enables the server to synchronize with unconfigured peers only if the
+peer has been correctly authenticated using either public key or
+private key cryptography.
+The default for this flag is
+\f\*[B-Font]enable\f[].
+.TP 7
+.NOP \f\*[B-Font]bclient\f[]
+Enables the server to listen for a message from a broadcast or
+multicast server, as in the
+\f\*[B-Font]multicastclient\f[]
+command with default
+address.
+The default for this flag is
+\f\*[B-Font]disable\f[].
+.TP 7
+.NOP \f\*[B-Font]calibrate\f[]
+Enables the calibrate feature for reference clocks.
+The default for
+this flag is
+\f\*[B-Font]disable\f[].
+.TP 7
+.NOP \f\*[B-Font]kernel\f[]
+Enables the kernel time discipline, if available.
+The default for this
+flag is
+\f\*[B-Font]enable\f[]
+if support is available, otherwise
+\f\*[B-Font]disable\f[].
+.TP 7
+.NOP \f\*[B-Font]mode7\f[]
+Enables processing of NTP mode 7 implementation-specific requests
+which are used by the deprecated
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+program.
+The default for this flag is disable.
+This flag is excluded from runtime configuration using
+\fCntpq\fR(1ntpqmdoc)\f[].
+The
+\fCntpq\fR(1ntpqmdoc)\f[]
+program provides the same capabilities as
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+using standard mode 6 requests.
+.TP 7
+.NOP \f\*[B-Font]monitor\f[]
+Enables the monitoring facility.
+See the
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+program
+and the
+\f\*[B-Font]monlist\f[]
+command or further information.
+The
+default for this flag is
+\f\*[B-Font]enable\f[].
+.TP 7
+.NOP \f\*[B-Font]ntp\f[]
+Enables time and frequency discipline.
+In effect, this switch opens and
+closes the feedback loop, which is useful for testing.
+The default for
+this flag is
+\f\*[B-Font]enable\f[].
+.TP 7
+.NOP \f\*[B-Font]stats\f[]
+Enables the statistics facility.
+See the
+\fIMonitoring\f[] \fIOptions\f[]
+section for further information.
+The default for this flag is
+\f\*[B-Font]disable\f[].
+.RE
+.TP 7
+.NOP \f\*[B-Font]includefile\f[] \f\*[I-Font]includefile\f[]
+This command allows additional configuration commands
+to be included from a separate file.
+Include files may
+be nested to a depth of five; upon reaching the end of any
+include file, command processing resumes in the previous
+configuration file.
+This option is useful for sites that run
+\fCntpd\fR(1ntpdmdoc)\f[]
+on multiple hosts, with (mostly) common options (e.g., a
+restriction list).
+.TP 7
+.NOP \f\*[B-Font]logconfig\f[] \f\*[I-Font]configkeyword\f[]
+This command controls the amount and type of output written to
+the system
+\fCsyslog\fR(3)\f[]
+facility or the alternate
+\f\*[B-Font]logfile\f[]
+log file.
+By default, all output is turned on.
+All
+\f\*[I-Font]configkeyword\f[]
+keywords can be prefixed with
+\[oq]=\[cq],
+\[oq]+\[cq]
+and
+\[oq]\-\[cq],
+where
+\[oq]=\[cq]
+sets the
+\fCsyslog\fR(3)\f[]
+priority mask,
+\[oq]+\[cq]
+adds and
+\[oq]\-\[cq]
+removes
+messages.
+\fCsyslog\fR(3)\f[]
+messages can be controlled in four
+classes
+(\f\*[B-Font]clock\f[], \f\*[B-Font]peer\f[], \f\*[B-Font]sys\f[] and \f\*[B-Font]sync\f[]).
+Within these classes four types of messages can be
+controlled: informational messages
+(\f\*[B-Font]info\f[]),
+event messages
+(\f\*[B-Font]events\f[]),
+statistics messages
+(\f\*[B-Font]statistics\f[])
+and
+status messages
+(\f\*[B-Font]status\f[]).
+.sp \n(Ppu
+.ne 2
+
+Configuration keywords are formed by concatenating the message class with
+the event class.
+The
+\f\*[B-Font]all\f[]
+prefix can be used instead of a message class.
+A
+message class may also be followed by the
+\f\*[B-Font]all\f[]
+keyword to enable/disable all
+messages of the respective message class.Thus, a minimal log configuration
+could look like this:
+.br
+.in +4
+.nf
+logconfig =syncstatus +sysevents
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+This would just list the synchronizations state of
+\fCntpd\fR(1ntpdmdoc)\f[]
+and the major system events.
+For a simple reference server, the
+following minimum message configuration could be useful:
+.br
+.in +4
+.nf
+logconfig =syncall +clockall
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+This configuration will list all clock information and
+synchronization information.
+All other events and messages about
+peers, system events and so on is suppressed.
+.TP 7
+.NOP \f\*[B-Font]logfile\f[] \f\*[I-Font]logfile\f[]
+This command specifies the location of an alternate log file to
+be used instead of the default system
+\fCsyslog\fR(3)\f[]
+facility.
+This is the same operation as the \-l command line option.
+.TP 7
+.NOP \f\*[B-Font]setvar\f[] \f\*[I-Font]variable\f[] [\f\*[B-Font]default\f[]]
+This command adds an additional system variable.
+These
+variables can be used to distribute additional information such as
+the access policy.
+If the variable of the form
+\fIname\f[]\fI=\f[]\f\*[I-Font]value\f[]
+is followed by the
+\f\*[B-Font]default\f[]
+keyword, the
+variable will be listed as part of the default system variables
+(\fCntpq\fR(1ntpqmdoc)\f[] \f\*[B-Font]rv\f[] command)).
+These additional variables serve
+informational purposes only.
+They are not related to the protocol
+other that they can be listed.
+The known protocol variables will
+always override any variables defined via the
+\f\*[B-Font]setvar\f[]
+mechanism.
+There are three special variables that contain the names
+of all variable of the same group.
+The
+\fIsys_var_list\f[]
+holds
+the names of all system variables.
+The
+\fIpeer_var_list\f[]
+holds
+the names of all peer variables and the
+\fIclock_var_list\f[]
+holds the names of the reference clock variables.
+.TP 7
+.NOP \f\*[B-Font]tinker\f[] [\f\*[B-Font]allan\f[] \f\*[I-Font]allan\f[] | \f\*[B-Font]dispersion\f[] \f\*[I-Font]dispersion\f[] | \f\*[B-Font]freq\f[] \f\*[I-Font]freq\f[] | \f\*[B-Font]huffpuff\f[] \f\*[I-Font]huffpuff\f[] | \f\*[B-Font]panic\f[] \f\*[I-Font]panic\f[] | \f\*[B-Font]step\f[] \f\*[I-Font]srep\f[] | \f\*[B-Font]stepout\f[] \f\*[I-Font]stepout\f[]]
+This command can be used to alter several system variables in
+very exceptional circumstances.
+It should occur in the
+configuration file before any other configuration options.
+The
+default values of these variables have been carefully optimized for
+a wide range of network speeds and reliability expectations.
+In
+general, they interact in intricate ways that are hard to predict
+and some combinations can result in some very nasty behavior.
+Very
+rarely is it necessary to change the default values; but, some
+folks cannot resist twisting the knobs anyway and this command is
+for them.
+Emphasis added: twisters are on their own and can expect
+no help from the support group.
+.sp \n(Ppu
+.ne 2
+
+The variables operate as follows:
+.RS
+.TP 7
+.NOP \f\*[B-Font]allan\f[] \f\*[I-Font]allan\f[]
+The argument becomes the new value for the minimum Allan
+intercept, which is a parameter of the PLL/FLL clock discipline
+algorithm.
+The value in log2 seconds defaults to 7 (1024 s), which is also the lower
+limit.
+.TP 7
+.NOP \f\*[B-Font]dispersion\f[] \f\*[I-Font]dispersion\f[]
+The argument becomes the new value for the dispersion increase rate,
+normally .000015 s/s.
+.TP 7
+.NOP \f\*[B-Font]freq\f[] \f\*[I-Font]freq\f[]
+The argument becomes the initial value of the frequency offset in
+parts-per-million.
+This overrides the value in the frequency file, if
+present, and avoids the initial training state if it is not.
+.TP 7
+.NOP \f\*[B-Font]huffpuff\f[] \f\*[I-Font]huffpuff\f[]
+The argument becomes the new value for the experimental
+huff-n'-puff filter span, which determines the most recent interval
+the algorithm will search for a minimum delay.
+The lower limit is
+900 s (15 m), but a more reasonable value is 7200 (2 hours).
+There
+is no default, since the filter is not enabled unless this command
+is given.
+.TP 7
+.NOP \f\*[B-Font]panic\f[] \f\*[I-Font]panic\f[]
+The argument is the panic threshold, normally 1000 s.
+If set to zero,
+the panic sanity check is disabled and a clock offset of any value will
+be accepted.
+.TP 7
+.NOP \f\*[B-Font]step\f[] \f\*[I-Font]step\f[]
+The argument is the step threshold, which by default is 0.128 s.
+It can
+be set to any positive number in seconds.
+If set to zero, step
+adjustments will never occur.
+Note: The kernel time discipline is
+disabled if the step threshold is set to zero or greater than the
+default.
+.TP 7
+.NOP \f\*[B-Font]stepout\f[] \f\*[I-Font]stepout\f[]
+The argument is the stepout timeout, which by default is 900 s.
+It can
+be set to any positive number in seconds.
+If set to zero, the stepout
+pulses will not be suppressed.
+.RE
+.TP 7
+.NOP \f\*[B-Font]rlimit\f[] [\f\*[B-Font]memlock\f[] \f\*[I-Font]Nmegabytes\f[] | \f\*[B-Font]stacksize\f[] \f\*[I-Font]N4kPages\f[] \f\*[B-Font]filenum\f[] \f\*[I-Font]Nfiledescriptors\f[]]
+.RS
+.TP 7
+.NOP \f\*[B-Font]memlock\f[] \f\*[I-Font]Nmegabytes\f[]
+Specify the number of megabytes of memory that can be allocated.
+Probably only available under Linux, this option is useful
+when dropping root (the
+\f\*[B-Font]\-i\f[]
+option).
+The default is 32 megabytes. Setting this to zero will prevent any attemp to lock memory.
+.TP 7
+.NOP \f\*[B-Font]stacksize\f[] \f\*[I-Font]N4kPages\f[]
+Specifies the maximum size of the process stack on systems with the
+.TP 7
+.NOP \f\*[B-Font]filenum\f[] \f\*[I-Font]Nfiledescriptors\f[]
+Specifies the maximum number of file descriptors ntpd may have open at once. Defaults to the system default.
+\fBmlockall\fR()\f[]
+function.
+Defaults to 50 4k pages (200 4k pages in OpenBSD).
+.RE
+.TP 7
+.NOP \f\*[B-Font]trap\f[] \f\*[I-Font]host_address\f[] [\f\*[B-Font]port\f[] \f\*[I-Font]port_number\f[]] [\f\*[B-Font]interface\f[] \f\*[I-Font]interface_address\f[]]
+This command configures a trap receiver at the given host
+address and port number for sending messages with the specified
+local interface address.
+If the port number is unspecified, a value
+of 18447 is used.
+If the interface address is not specified, the
+message is sent with a source address of the local interface the
+message is sent through.
+Note that on a multihomed host the
+interface used may vary from time to time with routing changes.
+.sp \n(Ppu
+.ne 2
+
+The trap receiver will generally log event messages and other
+information from the server in a log file.
+While such monitor
+programs may also request their own trap dynamically, configuring a
+trap receiver will ensure that no messages are lost when the server
+is started.
+.TP 7
+.NOP \f\*[B-Font]hop\f[] \f\*[I-Font]...\f[]
+This command specifies a list of TTL values in increasing order, up to 8
+values can be specified.
+In manycast mode these values are used in turn in
+an expanding-ring search.
+The default is eight multiples of 32 starting at
+31.
+.PP
+.SH "OPTIONS"
+.TP
+.NOP \f\*[B-Font]\-\-help\f[]
+Display usage information and exit.
+.TP
+.NOP \f\*[B-Font]\-\-more-help\f[]
+Pass the extended usage information through a pager.
+.TP
+.NOP \f\*[B-Font]\-\-version\f[] [{\f\*[I-Font]v|c|n\f[]}]
+Output version of program and exit. The default mode is `v', a simple
+version. The `c' mode will print copyright information and `n' will
+print the full copyright notice.
+.PP
+.SH "OPTION PRESETS"
+Any option that is not marked as \fInot presettable\fP may be preset
+by loading values from environment variables named:
+.nf
+ \fBNTP_CONF_<option-name>\fP or \fBNTP_CONF\fP
+.fi
+.ad
+.SH "ENVIRONMENT"
+See \fBOPTION PRESETS\fP for configuration environment variables.
+.SH FILES
+.TP 15
+.NOP \fI/etc/ntp.conf\f[]
+the default name of the configuration file
+.br
+.ns
+.TP 15
+.NOP \fIntp.keys\f[]
+private MD5 keys
+.br
+.ns
+.TP 15
+.NOP \fIntpkey\f[]
+RSA private key
+.br
+.ns
+.TP 15
+.NOP \fIntpkey_\f[]\f\*[I-Font]host\f[]
+RSA public key
+.br
+.ns
+.TP 15
+.NOP \fIntp_dh\f[]
+Diffie-Hellman agreement parameters
+.PP
+.SH "EXIT STATUS"
+One of the following exit values will be returned:
+.TP
+.NOP 0 " (EXIT_SUCCESS)"
+Successful program execution.
+.TP
+.NOP 1 " (EXIT_FAILURE)"
+The operation failed or the command syntax was not valid.
+.TP
+.NOP 70 " (EX_SOFTWARE)"
+libopts had an internal operational error. Please report
+it to autogen-users@lists.sourceforge.net. Thank you.
+.PP
+.SH "SEE ALSO"
+\fCntpd\fR(1ntpdmdoc)\f[],
+\fCntpdc\fR(1ntpdcmdoc)\f[],
+\fCntpq\fR(1ntpqmdoc)\f[]
+.sp \n(Ppu
+.ne 2
+
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+\f[C]http://www.ntp.org/\f[].
+A snapshot of this documentation is available in HTML format in
+\fI/usr/share/doc/ntp\f[].
+David L. Mills,
+\fINetwork Time Protocol (Version 4)\fR,
+RFC5905
+.PP
+
+.SH "AUTHORS"
+The University of Delaware
+.SH "COPYRIGHT"
+Copyright (C) 1970-2014 The University of Delaware all rights reserved.
+This program is released under the terms of the NTP license, <http://ntp.org/license>.
+.SH BUGS
+The syntax checking is not picky; some combinations of
+ridiculous and even hilarious options and modes may not be
+detected.
+.sp \n(Ppu
+.ne 2
+
+The
+\fIntpkey_\f[]\f\*[I-Font]host\f[]
+files are really digital
+certificates.
+These should be obtained via secure directory
+services when they become universally available.
+.sp \n(Ppu
+.ne 2
+
+Please send bug reports to: http://bugs.ntp.org, bugs@ntp.org
+.SH NOTES
+This document was derived from FreeBSD.
+.sp \n(Ppu
+.ne 2
+
+This manual page was \fIAutoGen\fP-erated from the \fBntp.conf\fP
+option definitions.
diff --git a/ntpd/ntp.conf.5mdoc b/ntpd/ntp.conf.5mdoc
new file mode 100644
index 0000000..998a274
--- /dev/null
+++ b/ntpd/ntp.conf.5mdoc
@@ -0,0 +1,2808 @@
+.Dd December 2 2014
+.Dt NTP_CONF 5mdoc File Formats
+.Os
+.\" EDIT THIS FILE WITH CAUTION (ntp.mdoc)
+.\"
+.\" It has been AutoGen-ed December 2, 2014 at 08:56:58 AM by AutoGen 5.18.5pre4
+.\" From the definitions ntp.conf.def
+.\" and the template file agmdoc-cmd.tpl
+.Sh NAME
+.Nm ntp.conf
+.Nd Network Time Protocol (NTP) daemon configuration file format
+.Sh SYNOPSIS
+.Nm
+.Op Fl \-option\-name
+.Op Fl \-option\-name Ar value
+.Pp
+All arguments must be options.
+.Pp
+.Sh DESCRIPTION
+The
+.Nm
+configuration file is read at initial startup by the
+.Xr ntpd 1ntpdmdoc
+daemon in order to specify the synchronization sources,
+modes and other related information.
+Usually, it is installed in the
+.Pa /etc
+directory,
+but could be installed elsewhere
+(see the daemon's
+.Fl c
+command line option).
+.Pp
+The file format is similar to other
+.Ux
+configuration files.
+Comments begin with a
+.Ql #
+character and extend to the end of the line;
+blank lines are ignored.
+Configuration commands consist of an initial keyword
+followed by a list of arguments,
+some of which may be optional, separated by whitespace.
+Commands may not be continued over multiple lines.
+Arguments may be host names,
+host addresses written in numeric, dotted\-quad form,
+integers, floating point numbers (when specifying times in seconds)
+and text strings.
+.Pp
+The rest of this page describes the configuration and control options.
+The
+.Qq Notes on Configuring NTP and Setting up an NTP Subnet
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+contains an extended discussion of these options.
+In addition to the discussion of general
+.Sx Configuration Options ,
+there are sections describing the following supported functionality
+and the options used to control it:
+.Bl -bullet -offset indent
+.It
+.Sx Authentication Support
+.It
+.Sx Monitoring Support
+.It
+.Sx Access Control Support
+.It
+.Sx Automatic NTP Configuration Options
+.It
+.Sx Reference Clock Support
+.It
+.Sx Miscellaneous Options
+.El
+.Pp
+Following these is a section describing
+.Sx Miscellaneous Options .
+While there is a rich set of options available,
+the only required option is one or more
+.Ic pool ,
+.Ic server ,
+.Ic peer ,
+.Ic broadcast
+or
+.Ic manycastclient
+commands.
+.Sh Configuration Support
+Following is a description of the configuration commands in
+NTPv4.
+These commands have the same basic functions as in NTPv3 and
+in some cases new functions and new arguments.
+There are two
+classes of commands, configuration commands that configure a
+persistent association with a remote server or peer or reference
+clock, and auxiliary commands that specify environmental variables
+that control various related operations.
+.Ss Configuration Commands
+The various modes are determined by the command keyword and the
+type of the required IP address.
+Addresses are classed by type as
+(s) a remote server or peer (IPv4 class A, B and C), (b) the
+broadcast address of a local interface, (m) a multicast address (IPv4
+class D), or (r) a reference clock address (127.127.x.x).
+Note that
+only those options applicable to each command are listed below.
+Use
+of options not listed may not be caught as an error, but may result
+in some weird and even destructive behavior.
+.Pp
+If the Basic Socket Interface Extensions for IPv6 (RFC\-2553)
+is detected, support for the IPv6 address family is generated
+in addition to the default support of the IPv4 address family.
+In a few cases, including the reslist billboard generated
+by ntpdc, IPv6 addresses are automatically generated.
+IPv6 addresses can be identified by the presence of colons
+.Dq \&:
+in the address field.
+IPv6 addresses can be used almost everywhere where
+IPv4 addresses can be used,
+with the exception of reference clock addresses,
+which are always IPv4.
+.Pp
+Note that in contexts where a host name is expected, a
+.Fl 4
+qualifier preceding
+the host name forces DNS resolution to the IPv4 namespace,
+while a
+.Fl 6
+qualifier forces DNS resolution to the IPv6 namespace.
+See IPv6 references for the
+equivalent classes for that address family.
+.Bl -tag -width indent
+.It Xo Ic pool Ar address
+.Op Cm burst
+.Op Cm iburst
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Xc
+.It Xo Ic server Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm burst
+.Op Cm iburst
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Xc
+.It Xo Ic peer Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Xc
+.It Xo Ic broadcast Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm ttl Ar ttl
+.Xc
+.It Xo Ic manycastclient Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Op Cm ttl Ar ttl
+.Xc
+.El
+.Pp
+These five commands specify the time server name or address to
+be used and the mode in which to operate.
+The
+.Ar address
+can be
+either a DNS name or an IP address in dotted\-quad notation.
+Additional information on association behavior can be found in the
+.Qq Association Management
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.Bl -tag -width indent
+.It Ic pool
+For type s addresses, this command mobilizes a persistent
+client mode association with a number of remote servers.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+.It Ic server
+For type s and r addresses, this command mobilizes a persistent
+client mode association with the specified remote server or local
+radio clock.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+This command should
+.Em not
+be used for type
+b or m addresses.
+.It Ic peer
+For type s addresses (only), this command mobilizes a
+persistent symmetric\-active mode association with the specified
+remote peer.
+In this mode the local clock can be synchronized to
+the remote peer or the remote peer can be synchronized to the local
+clock.
+This is useful in a network of servers where, depending on
+various failure scenarios, either the local or remote peer may be
+the better source of time.
+This command should NOT be used for type
+b, m or r addresses.
+.It Ic broadcast
+For type b and m addresses (only), this
+command mobilizes a persistent broadcast mode association.
+Multiple
+commands can be used to specify multiple local broadcast interfaces
+(subnets) and/or multiple multicast groups.
+Note that local
+broadcast messages go only to the interface associated with the
+subnet specified, but multicast messages go to all interfaces.
+In broadcast mode the local server sends periodic broadcast
+messages to a client population at the
+.Ar address
+specified, which is usually the broadcast address on (one of) the
+local network(s) or a multicast address assigned to NTP.
+The IANA
+has assigned the multicast group address IPv4 224.0.1.1 and
+IPv6 ff05::101 (site local) exclusively to
+NTP, but other nonconflicting addresses can be used to contain the
+messages within administrative boundaries.
+Ordinarily, this
+specification applies only to the local server operating as a
+sender; for operation as a broadcast client, see the
+.Ic broadcastclient
+or
+.Ic multicastclient
+commands
+below.
+.It Ic manycastclient
+For type m addresses (only), this command mobilizes a
+manycast client mode association for the multicast address
+specified.
+In this case a specific address must be supplied which
+matches the address used on the
+.Ic manycastserver
+command for
+the designated manycast servers.
+The NTP multicast address
+224.0.1.1 assigned by the IANA should NOT be used, unless specific
+means are taken to avoid spraying large areas of the Internet with
+these messages and causing a possibly massive implosion of replies
+at the sender.
+The
+.Ic manycastserver
+command specifies that the local server
+is to operate in client mode with the remote servers that are
+discovered as the result of broadcast/multicast messages.
+The
+client broadcasts a request message to the group address associated
+with the specified
+.Ar address
+and specifically enabled
+servers respond to these messages.
+The client selects the servers
+providing the best time and continues as with the
+.Ic server
+command.
+The remaining servers are discarded as if never
+heard.
+.El
+.Pp
+Options:
+.Bl -tag -width indent
+.It Cm autokey
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the autokey scheme
+described in
+.Sx Authentication Options .
+.It Cm burst
+when the server is reachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first and second packets
+can be changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to improve timekeeping quality
+with the
+.Ic server
+command and s addresses.
+.It Cm iburst
+When the server is unreachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first two packets can be
+changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to speed the initial synchronization
+acquisition with the
+.Ic server
+command and s addresses and when
+.Xr ntpd 1ntpdmdoc
+is started with the
+.Fl q
+option.
+.It Cm key Ar key
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the specified
+.Ar key
+identifier with values from 1 to 65534, inclusive.
+The
+default is to include no encryption field.
+.It Cm minpoll Ar minpoll
+.It Cm maxpoll Ar maxpoll
+These options specify the minimum and maximum poll intervals
+for NTP messages, as a power of 2 in seconds
+The maximum poll
+interval defaults to 10 (1,024 s), but can be increased by the
+.Cm maxpoll
+option to an upper limit of 17 (36.4 h).
+The
+minimum poll interval defaults to 6 (64 s), but can be decreased by
+the
+.Cm minpoll
+option to a lower limit of 4 (16 s).
+.It Cm noselect
+Marks the server as unused, except for display purposes.
+The server is discarded by the selection algroithm.
+.It Cm prefer
+Marks the server as preferred.
+All other things being equal,
+this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+.Qq Mitigation Rules and the prefer Keyword
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+for further information.
+.It Cm ttl Ar ttl
+This option is used only with broadcast server and manycast
+client modes.
+It specifies the time\-to\-live
+.Ar ttl
+to
+use on broadcast server and multicast server and the maximum
+.Ar ttl
+for the expanding ring search with manycast
+client packets.
+Selection of the proper value, which defaults to
+127, is something of a black art and should be coordinated with the
+network administrator.
+.It Cm version Ar version
+Specifies the version number to be used for outgoing NTP
+packets.
+Versions 1\-4 are the choices, with version 4 the
+default.
+.El
+.Ss Auxiliary Commands
+.Bl -tag -width indent
+.It Ic broadcastclient
+This command enables reception of broadcast server messages to
+any local interface (type b) address.
+Upon receiving a message for
+the first time, the broadcast client measures the nominal server
+propagation delay using a brief client/server exchange with the
+server, then enters the broadcast client mode, in which it
+synchronizes to succeeding broadcast messages.
+Note that, in order
+to avoid accidental or malicious disruption in this mode, both the
+server and client should operate using symmetric\-key or public\-key
+authentication as described in
+.Sx Authentication Options .
+.It Ic manycastserver Ar address ...
+This command enables reception of manycast client messages to
+the multicast group address(es) (type m) specified.
+At least one
+address is required, but the NTP multicast address 224.0.1.1
+assigned by the IANA should NOT be used, unless specific means are
+taken to limit the span of the reply and avoid a possibly massive
+implosion at the original sender.
+Note that, in order to avoid
+accidental or malicious disruption in this mode, both the server
+and client should operate using symmetric\-key or public\-key
+authentication as described in
+.Sx Authentication Options .
+.It Ic multicastclient Ar address ...
+This command enables reception of multicast server messages to
+the multicast group address(es) (type m) specified.
+Upon receiving
+a message for the first time, the multicast client measures the
+nominal server propagation delay using a brief client/server
+exchange with the server, then enters the broadcast client mode, in
+which it synchronizes to succeeding multicast messages.
+Note that,
+in order to avoid accidental or malicious disruption in this mode,
+both the server and client should operate using symmetric\-key or
+public\-key authentication as described in
+.Sx Authentication Options .
+.El
+.Sh Authentication Support
+Authentication support allows the NTP client to verify that the
+server is in fact known and trusted and not an intruder intending
+accidentally or on purpose to masquerade as that server.
+The NTPv3
+specification RFC\-1305 defines a scheme which provides
+cryptographic authentication of received NTP packets.
+Originally,
+this was done using the Data Encryption Standard (DES) algorithm
+operating in Cipher Block Chaining (CBC) mode, commonly called
+DES\-CBC.
+Subsequently, this was replaced by the RSA Message Digest
+5 (MD5) algorithm using a private key, commonly called keyed\-MD5.
+Either algorithm computes a message digest, or one\-way hash, which
+can be used to verify the server has the correct private key and
+key identifier.
+.Pp
+NTPv4 retains the NTPv3 scheme, properly described as symmetric key
+cryptography and, in addition, provides a new Autokey scheme
+based on public key cryptography.
+Public key cryptography is generally considered more secure
+than symmetric key cryptography, since the security is based
+on a private value which is generated by each server and
+never revealed.
+With Autokey all key distribution and
+management functions involve only public values, which
+considerably simplifies key distribution and storage.
+Public key management is based on X.509 certificates,
+which can be provided by commercial services or
+produced by utility programs in the OpenSSL software library
+or the NTPv4 distribution.
+.Pp
+While the algorithms for symmetric key cryptography are
+included in the NTPv4 distribution, public key cryptography
+requires the OpenSSL software library to be installed
+before building the NTP distribution.
+Directions for doing that
+are on the Building and Installing the Distribution page.
+.Pp
+Authentication is configured separately for each association
+using the
+.Cm key
+or
+.Cm autokey
+subcommand on the
+.Ic peer ,
+.Ic server ,
+.Ic broadcast
+and
+.Ic manycastclient
+configuration commands as described in
+.Sx Configuration Options
+page.
+The authentication
+options described below specify the locations of the key files,
+if other than default, which symmetric keys are trusted
+and the interval between various operations, if other than default.
+.Pp
+Authentication is always enabled,
+although ineffective if not configured as
+described below.
+If a NTP packet arrives
+including a message authentication
+code (MAC), it is accepted only if it
+passes all cryptographic checks.
+The
+checks require correct key ID, key value
+and message digest.
+If the packet has
+been modified in any way or replayed
+by an intruder, it will fail one or more
+of these checks and be discarded.
+Furthermore, the Autokey scheme requires a
+preliminary protocol exchange to obtain
+the server certificate, verify its
+credentials and initialize the protocol
+.Pp
+The
+.Cm auth
+flag controls whether new associations or
+remote configuration commands require cryptographic authentication.
+This flag can be set or reset by the
+.Ic enable
+and
+.Ic disable
+commands and also by remote
+configuration commands sent by a
+.Xr ntpdc 1ntpdcmdoc
+program running in
+another machine.
+If this flag is enabled, which is the default
+case, new broadcast client and symmetric passive associations and
+remote configuration commands must be cryptographically
+authenticated using either symmetric key or public key cryptography.
+If this
+flag is disabled, these operations are effective
+even if not cryptographic
+authenticated.
+It should be understood
+that operating with the
+.Ic auth
+flag disabled invites a significant vulnerability
+where a rogue hacker can
+masquerade as a falseticker and seriously
+disrupt system timekeeping.
+It is
+important to note that this flag has no purpose
+other than to allow or disallow
+a new association in response to new broadcast
+and symmetric active messages
+and remote configuration commands and, in particular,
+the flag has no effect on
+the authentication process itself.
+.Pp
+An attractive alternative where multicast support is available
+is manycast mode, in which clients periodically troll
+for servers as described in the
+.Sx Automatic NTP Configuration Options
+page.
+Either symmetric key or public key
+cryptographic authentication can be used in this mode.
+The principle advantage
+of manycast mode is that potential servers need not be
+configured in advance,
+since the client finds them during regular operation,
+and the configuration
+files for all clients can be identical.
+.Pp
+The security model and protocol schemes for
+both symmetric key and public key
+cryptography are summarized below;
+further details are in the briefings, papers
+and reports at the NTP project page linked from
+.Li http://www.ntp.org/ .
+.Ss Symmetric\-Key Cryptography
+The original RFC\-1305 specification allows any one of possibly
+65,534 keys, each distinguished by a 32\-bit key identifier, to
+authenticate an association.
+The servers and clients involved must
+agree on the key and key identifier to
+authenticate NTP packets.
+Keys and
+related information are specified in a key
+file, usually called
+.Pa ntp.keys ,
+which must be distributed and stored using
+secure means beyond the scope of the NTP protocol itself.
+Besides the keys used
+for ordinary NTP associations,
+additional keys can be used as passwords for the
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+utility programs.
+.Pp
+When
+.Xr ntpd 1ntpdmdoc
+is first started, it reads the key file specified in the
+.Ic keys
+configuration command and installs the keys
+in the key cache.
+However,
+individual keys must be activated with the
+.Ic trusted
+command before use.
+This
+allows, for instance, the installation of possibly
+several batches of keys and
+then activating or deactivating each batch
+remotely using
+.Xr ntpdc 1ntpdcmdoc .
+This also provides a revocation capability that can be used
+if a key becomes compromised.
+The
+.Ic requestkey
+command selects the key used as the password for the
+.Xr ntpdc 1ntpdcmdoc
+utility, while the
+.Ic controlkey
+command selects the key used as the password for the
+.Xr ntpq 1ntpqmdoc
+utility.
+.Ss Public Key Cryptography
+NTPv4 supports the original NTPv3 symmetric key scheme
+described in RFC\-1305 and in addition the Autokey protocol,
+which is based on public key cryptography.
+The Autokey Version 2 protocol described on the Autokey Protocol
+page verifies packet integrity using MD5 message digests
+and verifies the source with digital signatures and any of several
+digest/signature schemes.
+Optional identity schemes described on the Identity Schemes
+page and based on cryptographic challenge/response algorithms
+are also available.
+Using all of these schemes provides strong security against
+replay with or without modification, spoofing, masquerade
+and most forms of clogging attacks.
+.\" .Pp
+.\" The cryptographic means necessary for all Autokey operations
+.\" is provided by the OpenSSL software library.
+.\" This library is available from http://www.openssl.org/
+.\" and can be installed using the procedures outlined
+.\" in the Building and Installing the Distribution page.
+.\" Once installed,
+.\" the configure and build
+.\" process automatically detects the library and links
+.\" the library routines required.
+.Pp
+The Autokey protocol has several modes of operation
+corresponding to the various NTP modes supported.
+Most modes use a special cookie which can be
+computed independently by the client and server,
+but encrypted in transmission.
+All modes use in addition a variant of the S\-KEY scheme,
+in which a pseudo\-random key list is generated and used
+in reverse order.
+These schemes are described along with an executive summary,
+current status, briefing slides and reading list on the
+.Sx Autonomous Authentication
+page.
+.Pp
+The specific cryptographic environment used by Autokey servers
+and clients is determined by a set of files
+and soft links generated by the
+.Xr ntp\-keygen 1ntpkeygenmdoc
+program.
+This includes a required host key file,
+required certificate file and optional sign key file,
+leapsecond file and identity scheme files.
+The
+digest/signature scheme is specified in the X.509 certificate
+along with the matching sign key.
+There are several schemes
+available in the OpenSSL software library, each identified
+by a specific string such as
+.Cm md5WithRSAEncryption ,
+which stands for the MD5 message digest with RSA
+encryption scheme.
+The current NTP distribution supports
+all the schemes in the OpenSSL library, including
+those based on RSA and DSA digital signatures.
+.Pp
+NTP secure groups can be used to define cryptographic compartments
+and security hierarchies.
+It is important that every host
+in the group be able to construct a certificate trail to one
+or more trusted hosts in the same group.
+Each group
+host runs the Autokey protocol to obtain the certificates
+for all hosts along the trail to one or more trusted hosts.
+This requires the configuration file in all hosts to be
+engineered so that, even under anticipated failure conditions,
+the NTP subnet will form such that every group host can find
+a trail to at least one trusted host.
+.Ss Naming and Addressing
+It is important to note that Autokey does not use DNS to
+resolve addresses, since DNS can't be completely trusted
+until the name servers have synchronized clocks.
+The cryptographic name used by Autokey to bind the host identity
+credentials and cryptographic values must be independent
+of interface, network and any other naming convention.
+The name appears in the host certificate in either or both
+the subject and issuer fields, so protection against
+DNS compromise is essential.
+.Pp
+By convention, the name of an Autokey host is the name returned
+by the Unix
+.Xr gethostname 2
+system call or equivalent in other systems.
+By the system design
+model, there are no provisions to allow alternate names or aliases.
+However, this is not to say that DNS aliases, different names
+for each interface, etc., are constrained in any way.
+.Pp
+It is also important to note that Autokey verifies authenticity
+using the host name, network address and public keys,
+all of which are bound together by the protocol specifically
+to deflect masquerade attacks.
+For this reason Autokey
+includes the source and destinatino IP addresses in message digest
+computations and so the same addresses must be available
+at both the server and client.
+For this reason operation
+with network address translation schemes is not possible.
+This reflects the intended robust security model where government
+and corporate NTP servers are operated outside firewall perimeters.
+.Ss Operation
+A specific combination of authentication scheme (none,
+symmetric key, public key) and identity scheme is called
+a cryptotype, although not all combinations are compatible.
+There may be management configurations where the clients,
+servers and peers may not all support the same cryptotypes.
+A secure NTPv4 subnet can be configured in many ways while
+keeping in mind the principles explained above and
+in this section.
+Note however that some cryptotype
+combinations may successfully interoperate with each other,
+but may not represent good security practice.
+.Pp
+The cryptotype of an association is determined at the time
+of mobilization, either at configuration time or some time
+later when a message of appropriate cryptotype arrives.
+When mobilized by a
+.Ic server
+or
+.Ic peer
+configuration command and no
+.Ic key
+or
+.Ic autokey
+subcommands are present, the association is not
+authenticated; if the
+.Ic key
+subcommand is present, the association is authenticated
+using the symmetric key ID specified; if the
+.Ic autokey
+subcommand is present, the association is authenticated
+using Autokey.
+.Pp
+When multiple identity schemes are supported in the Autokey
+protocol, the first message exchange determines which one is used.
+The client request message contains bits corresponding
+to which schemes it has available.
+The server response message
+contains bits corresponding to which schemes it has available.
+Both server and client match the received bits with their own
+and select a common scheme.
+.Pp
+Following the principle that time is a public value,
+a server responds to any client packet that matches
+its cryptotype capabilities.
+Thus, a server receiving
+an unauthenticated packet will respond with an unauthenticated
+packet, while the same server receiving a packet of a cryptotype
+it supports will respond with packets of that cryptotype.
+However, unconfigured broadcast or manycast client
+associations or symmetric passive associations will not be
+mobilized unless the server supports a cryptotype compatible
+with the first packet received.
+By default, unauthenticated associations will not be mobilized
+unless overridden in a decidedly dangerous way.
+.Pp
+Some examples may help to reduce confusion.
+Client Alice has no specific cryptotype selected.
+Server Bob has both a symmetric key file and minimal Autokey files.
+Alice's unauthenticated messages arrive at Bob, who replies with
+unauthenticated messages.
+Cathy has a copy of Bob's symmetric
+key file and has selected key ID 4 in messages to Bob.
+Bob verifies the message with his key ID 4.
+If it's the
+same key and the message is verified, Bob sends Cathy a reply
+authenticated with that key.
+If verification fails,
+Bob sends Cathy a thing called a crypto\-NAK, which tells her
+something broke.
+She can see the evidence using the
+.Xr ntpq 1ntpqmdoc
+program.
+.Pp
+Denise has rolled her own host key and certificate.
+She also uses one of the identity schemes as Bob.
+She sends the first Autokey message to Bob and they
+both dance the protocol authentication and identity steps.
+If all comes out okay, Denise and Bob continue as described above.
+.Pp
+It should be clear from the above that Bob can support
+all the girls at the same time, as long as he has compatible
+authentication and identity credentials.
+Now, Bob can act just like the girls in his own choice of servers;
+he can run multiple configured associations with multiple different
+servers (or the same server, although that might not be useful).
+But, wise security policy might preclude some cryptotype
+combinations; for instance, running an identity scheme
+with one server and no authentication with another might not be wise.
+.Ss Key Management
+The cryptographic values used by the Autokey protocol are
+incorporated as a set of files generated by the
+.Xr ntp\-keygen 1ntpkeygenmdoc
+utility program, including symmetric key, host key and
+public certificate files, as well as sign key, identity parameters
+and leapseconds files.
+Alternatively, host and sign keys and
+certificate files can be generated by the OpenSSL utilities
+and certificates can be imported from public certificate
+authorities.
+Note that symmetric keys are necessary for the
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+utility programs.
+The remaining files are necessary only for the
+Autokey protocol.
+.Pp
+Certificates imported from OpenSSL or public certificate
+authorities have certian limitations.
+The certificate should be in ASN.1 syntax, X.509 Version 3
+format and encoded in PEM, which is the same format
+used by OpenSSL.
+The overall length of the certificate encoded
+in ASN.1 must not exceed 1024 bytes.
+The subject distinguished
+name field (CN) is the fully qualified name of the host
+on which it is used; the remaining subject fields are ignored.
+The certificate extension fields must not contain either
+a subject key identifier or a issuer key identifier field;
+however, an extended key usage field for a trusted host must
+contain the value
+.Cm trustRoot ; .
+Other extension fields are ignored.
+.Ss Authentication Commands
+.Bl -tag -width indent
+.It Ic autokey Op Ar logsec
+Specifies the interval between regenerations of the session key
+list used with the Autokey protocol.
+Note that the size of the key
+list for each association depends on this interval and the current
+poll interval.
+The default value is 12 (4096 s or about 1.1 hours).
+For poll intervals above the specified interval, a session key list
+with a single entry will be regenerated for every message
+sent.
+.It Ic controlkey Ar key
+Specifies the key identifier to use with the
+.Xr ntpq 1ntpqmdoc
+utility, which uses the standard
+protocol defined in RFC\-1305.
+The
+.Ar key
+argument is
+the key identifier for a trusted key, where the value can be in the
+range 1 to 65,534, inclusive.
+.It Xo Ic crypto
+.Op Cm cert Ar file
+.Op Cm leap Ar file
+.Op Cm randfile Ar file
+.Op Cm host Ar file
+.Op Cm sign Ar file
+.Op Cm gq Ar file
+.Op Cm gqpar Ar file
+.Op Cm iffpar Ar file
+.Op Cm mvpar Ar file
+.Op Cm pw Ar password
+.Xc
+This command requires the OpenSSL library.
+It activates public key
+cryptography, selects the message digest and signature
+encryption scheme and loads the required private and public
+values described above.
+If one or more files are left unspecified,
+the default names are used as described above.
+Unless the complete path and name of the file are specified, the
+location of a file is relative to the keys directory specified
+in the
+.Ic keysdir
+command or default
+.Pa /usr/local/etc .
+Following are the subcommands:
+.Bl -tag -width indent
+.It Cm cert Ar file
+Specifies the location of the required host public certificate file.
+This overrides the link
+.Pa ntpkey_cert_ Ns Ar hostname
+in the keys directory.
+.It Cm gqpar Ar file
+Specifies the location of the optional GQ parameters file.
+This
+overrides the link
+.Pa ntpkey_gq_ Ns Ar hostname
+in the keys directory.
+.It Cm host Ar file
+Specifies the location of the required host key file.
+This overrides
+the link
+.Pa ntpkey_key_ Ns Ar hostname
+in the keys directory.
+.It Cm iffpar Ar file
+Specifies the location of the optional IFF parameters file.This
+overrides the link
+.Pa ntpkey_iff_ Ns Ar hostname
+in the keys directory.
+.It Cm leap Ar file
+Specifies the location of the optional leapsecond file.
+This overrides the link
+.Pa ntpkey_leap
+in the keys directory.
+.It Cm mvpar Ar file
+Specifies the location of the optional MV parameters file.
+This
+overrides the link
+.Pa ntpkey_mv_ Ns Ar hostname
+in the keys directory.
+.It Cm pw Ar password
+Specifies the password to decrypt files containing private keys and
+identity parameters.
+This is required only if these files have been
+encrypted.
+.It Cm randfile Ar file
+Specifies the location of the random seed file used by the OpenSSL
+library.
+The defaults are described in the main text above.
+.It Cm sign Ar file
+Specifies the location of the optional sign key file.
+This overrides
+the link
+.Pa ntpkey_sign_ Ns Ar hostname
+in the keys directory.
+If this file is
+not found, the host key is also the sign key.
+.El
+.It Ic keys Ar keyfile
+Specifies the complete path and location of the MD5 key file
+containing the keys and key identifiers used by
+.Xr ntpd 1ntpdmdoc ,
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+when operating with symmetric key cryptography.
+This is the same operation as the
+.Fl k
+command line option.
+.It Ic keysdir Ar path
+This command specifies the default directory path for
+cryptographic keys, parameters and certificates.
+The default is
+.Pa /usr/local/etc/ .
+.It Ic requestkey Ar key
+Specifies the key identifier to use with the
+.Xr ntpdc 1ntpdcmdoc
+utility program, which uses a
+proprietary protocol specific to this implementation of
+.Xr ntpd 1ntpdmdoc .
+The
+.Ar key
+argument is a key identifier
+for the trusted key, where the value can be in the range 1 to
+65,534, inclusive.
+.It Ic revoke Ar logsec
+Specifies the interval between re\-randomization of certain
+cryptographic values used by the Autokey scheme, as a power of 2 in
+seconds.
+These values need to be updated frequently in order to
+deflect brute\-force attacks on the algorithms of the scheme;
+however, updating some values is a relatively expensive operation.
+The default interval is 16 (65,536 s or about 18 hours).
+For poll
+intervals above the specified interval, the values will be updated
+for every message sent.
+.It Ic trustedkey Ar key ...
+Specifies the key identifiers which are trusted for the
+purposes of authenticating peers with symmetric key cryptography,
+as well as keys used by the
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+programs.
+The authentication procedures require that both the local
+and remote servers share the same key and key identifier for this
+purpose, although different keys can be used with different
+servers.
+The
+.Ar key
+arguments are 32\-bit unsigned
+integers with values from 1 to 65,534.
+.El
+.Ss Error Codes
+The following error codes are reported via the NTP control
+and monitoring protocol trap mechanism.
+.Bl -tag -width indent
+.It 101
+.Pq bad field format or length
+The packet has invalid version, length or format.
+.It 102
+.Pq bad timestamp
+The packet timestamp is the same or older than the most recent received.
+This could be due to a replay or a server clock time step.
+.It 103
+.Pq bad filestamp
+The packet filestamp is the same or older than the most recent received.
+This could be due to a replay or a key file generation error.
+.It 104
+.Pq bad or missing public key
+The public key is missing, has incorrect format or is an unsupported type.
+.It 105
+.Pq unsupported digest type
+The server requires an unsupported digest/signature scheme.
+.It 106
+.Pq mismatched digest types
+Not used.
+.It 107
+.Pq bad signature length
+The signature length does not match the current public key.
+.It 108
+.Pq signature not verified
+The message fails the signature check.
+It could be bogus or signed by a
+different private key.
+.It 109
+.Pq certificate not verified
+The certificate is invalid or signed with the wrong key.
+.It 110
+.Pq certificate not verified
+The certificate is not yet valid or has expired or the signature could not
+be verified.
+.It 111
+.Pq bad or missing cookie
+The cookie is missing, corrupted or bogus.
+.It 112
+.Pq bad or missing leapseconds table
+The leapseconds table is missing, corrupted or bogus.
+.It 113
+.Pq bad or missing certificate
+The certificate is missing, corrupted or bogus.
+.It 114
+.Pq bad or missing identity
+The identity key is missing, corrupt or bogus.
+.El
+.Sh Monitoring Support
+.Xr ntpd 1ntpdmdoc
+includes a comprehensive monitoring facility suitable
+for continuous, long term recording of server and client
+timekeeping performance.
+See the
+.Ic statistics
+command below
+for a listing and example of each type of statistics currently
+supported.
+Statistic files are managed using file generation sets
+and scripts in the
+.Pa ./scripts
+directory of this distribution.
+Using
+these facilities and
+.Ux
+.Xr cron 8
+jobs, the data can be
+automatically summarized and archived for retrospective analysis.
+.Ss Monitoring Commands
+.Bl -tag -width indent
+.It Ic statistics Ar name ...
+Enables writing of statistics records.
+Currently, eight kinds of
+.Ar name
+statistics are supported.
+.Bl -tag -width indent
+.It Cm clockstats
+Enables recording of clock driver statistics information.
+Each update
+received from a clock driver appends a line of the following form to
+the file generation set named
+.Cm clockstats :
+.Bd -literal
+49213 525.624 127.127.4.1 93 226 00:08:29.606 D
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the
+clock address in dotted\-quad notation.
+The final field shows the last
+timecode received from the clock in decoded ASCII format, where
+meaningful.
+In some clock drivers a good deal of additional information
+can be gathered and displayed as well.
+See information specific to each
+clock for further details.
+.It Cm cryptostats
+This option requires the OpenSSL cryptographic software library.
+It
+enables recording of cryptographic public key protocol information.
+Each message received by the protocol module appends a line of the
+following form to the file generation set named
+.Cm cryptostats :
+.Bd -literal
+49213 525.624 127.127.4.1 message
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the peer
+address in dotted\-quad notation, The final message field includes the
+message type and certain ancillary information.
+See the
+.Sx Authentication Options
+section for further information.
+.It Cm loopstats
+Enables recording of loop filter statistics information.
+Each
+update of the local clock outputs a line of the following form to
+the file generation set named
+.Cm loopstats :
+.Bd -literal
+50935 75440.031 0.000006019 13.778190 0.000351733 0.0133806
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next five fields
+show time offset (seconds), frequency offset (parts per million \-
+PPM), RMS jitter (seconds), Allan deviation (PPM) and clock
+discipline time constant.
+.It Cm peerstats
+Enables recording of peer statistics information.
+This includes
+statistics records of all peers of a NTP server and of special
+signals, where present and configured.
+Each valid update appends a
+line of the following form to the current element of a file
+generation set named
+.Cm peerstats :
+.Bd -literal
+48773 10847.650 127.127.4.1 9714 \-0.001605376 0.000000000 0.001424877 0.000958674
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the peer address in dotted\-quad notation and status,
+respectively.
+The status field is encoded in hex in the format
+described in Appendix A of the NTP specification RFC 1305.
+The final four fields show the offset,
+delay, dispersion and RMS jitter, all in seconds.
+.It Cm rawstats
+Enables recording of raw\-timestamp statistics information.
+This
+includes statistics records of all peers of a NTP server and of
+special signals, where present and configured.
+Each NTP message
+received from a peer or clock driver appends a line of the
+following form to the file generation set named
+.Cm rawstats :
+.Bd -literal
+50928 2132.543 128.4.1.1 128.4.1.20 3102453281.584327000 3102453281.58622800031 02453332.540806000 3102453332.541458000
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the remote peer or clock address followed by the local address
+in dotted\-quad notation.
+The final four fields show the originate,
+receive, transmit and final NTP timestamps in order.
+The timestamp
+values are as received and before processing by the various data
+smoothing and mitigation algorithms.
+.It Cm sysstats
+Enables recording of ntpd statistics counters on a periodic basis.
+Each
+hour a line of the following form is appended to the file generation
+set named
+.Cm sysstats :
+.Bd -literal
+50928 2132.543 36000 81965 0 9546 56 71793 512 540 10 147
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The remaining ten fields show
+the statistics counter values accumulated since the last generated
+line.
+.Bl -tag -width indent
+.It Time since restart Cm 36000
+Time in hours since the system was last rebooted.
+.It Packets received Cm 81965
+Total number of packets received.
+.It Packets processed Cm 0
+Number of packets received in response to previous packets sent
+.It Current version Cm 9546
+Number of packets matching the current NTP version.
+.It Previous version Cm 56
+Number of packets matching the previous NTP version.
+.It Bad version Cm 71793
+Number of packets matching neither NTP version.
+.It Access denied Cm 512
+Number of packets denied access for any reason.
+.It Bad length or format Cm 540
+Number of packets with invalid length, format or port number.
+.It Bad authentication Cm 10
+Number of packets not verified as authentic.
+.It Rate exceeded Cm 147
+Number of packets discarded due to rate limitation.
+.El
+.It Cm statsdir Ar directory_path
+Indicates the full path of a directory where statistics files
+should be created (see below).
+This keyword allows
+the (otherwise constant)
+.Cm filegen
+filename prefix to be modified for file generation sets, which
+is useful for handling statistics logs.
+.It Cm filegen Ar name Xo
+.Op Cm file Ar filename
+.Op Cm type Ar typename
+.Op Cm link | nolink
+.Op Cm enable | disable
+.Xc
+Configures setting of generation file set name.
+Generation
+file sets provide a means for handling files that are
+continuously growing during the lifetime of a server.
+Server statistics are a typical example for such files.
+Generation file sets provide access to a set of files used
+to store the actual data.
+At any time at most one element
+of the set is being written to.
+The type given specifies
+when and how data will be directed to a new element of the set.
+This way, information stored in elements of a file set
+that are currently unused are available for administrational
+operations without the risk of disturbing the operation of ntpd.
+(Most important: they can be removed to free space for new data
+produced.)
+.Pp
+Note that this command can be sent from the
+.Xr ntpdc 1ntpdcmdoc
+program running at a remote location.
+.Bl -tag -width indent
+.It Cm name
+This is the type of the statistics records, as shown in the
+.Cm statistics
+command.
+.It Cm file Ar filename
+This is the file name for the statistics records.
+Filenames of set
+members are built from three concatenated elements
+.Ar Cm prefix ,
+.Ar Cm filename
+and
+.Ar Cm suffix :
+.Bl -tag -width indent
+.It Cm prefix
+This is a constant filename path.
+It is not subject to
+modifications via the
+.Ar filegen
+option.
+It is defined by the
+server, usually specified as a compile\-time constant.
+It may,
+however, be configurable for individual file generation sets
+via other commands.
+For example, the prefix used with
+.Ar loopstats
+and
+.Ar peerstats
+generation can be configured using the
+.Ar statsdir
+option explained above.
+.It Cm filename
+This string is directly concatenated to the prefix mentioned
+above (no intervening
+.Ql / ) .
+This can be modified using
+the file argument to the
+.Ar filegen
+statement.
+No
+.Pa ..
+elements are
+allowed in this component to prevent filenames referring to
+parts outside the filesystem hierarchy denoted by
+.Ar prefix .
+.It Cm suffix
+This part is reflects individual elements of a file set.
+It is
+generated according to the type of a file set.
+.El
+.It Cm type Ar typename
+A file generation set is characterized by its type.
+The following
+types are supported:
+.Bl -tag -width indent
+.It Cm none
+The file set is actually a single plain file.
+.It Cm pid
+One element of file set is used per incarnation of a ntpd
+server.
+This type does not perform any changes to file set
+members during runtime, however it provides an easy way of
+separating files belonging to different
+.Xr ntpd 1ntpdmdoc
+server incarnations.
+The set member filename is built by appending a
+.Ql \&.
+to concatenated
+.Ar prefix
+and
+.Ar filename
+strings, and
+appending the decimal representation of the process ID of the
+.Xr ntpd 1ntpdmdoc
+server process.
+.It Cm day
+One file generation set element is created per day.
+A day is
+defined as the period between 00:00 and 24:00 UTC.
+The file set
+member suffix consists of a
+.Ql \&.
+and a day specification in
+the form
+.Cm YYYYMMdd .
+.Cm YYYY
+is a 4\-digit year number (e.g., 1992).
+.Cm MM
+is a two digit month number.
+.Cm dd
+is a two digit day number.
+Thus, all information written at 10 December 1992 would end up
+in a file named
+.Ar prefix
+.Ar filename Ns .19921210 .
+.It Cm week
+Any file set member contains data related to a certain week of
+a year.
+The term week is defined by computing day\-of\-year
+modulo 7.
+Elements of such a file generation set are
+distinguished by appending the following suffix to the file set
+filename base: A dot, a 4\-digit year number, the letter
+.Cm W ,
+and a 2\-digit week number.
+For example, information from January,
+10th 1992 would end up in a file with suffix
+.No . Ns Ar 1992W1 .
+.It Cm month
+One generation file set element is generated per month.
+The
+file name suffix consists of a dot, a 4\-digit year number, and
+a 2\-digit month.
+.It Cm year
+One generation file element is generated per year.
+The filename
+suffix consists of a dot and a 4 digit year number.
+.It Cm age
+This type of file generation sets changes to a new element of
+the file set every 24 hours of server operation.
+The filename
+suffix consists of a dot, the letter
+.Cm a ,
+and an 8\-digit number.
+This number is taken to be the number of seconds the server is
+running at the start of the corresponding 24\-hour period.
+Information is only written to a file generation by specifying
+.Cm enable ;
+output is prevented by specifying
+.Cm disable .
+.El
+.It Cm link | nolink
+It is convenient to be able to access the current element of a file
+generation set by a fixed name.
+This feature is enabled by
+specifying
+.Cm link
+and disabled using
+.Cm nolink .
+If link is specified, a
+hard link from the current file set element to a file without
+suffix is created.
+When there is already a file with this name and
+the number of links of this file is one, it is renamed appending a
+dot, the letter
+.Cm C ,
+and the pid of the ntpd server process.
+When the
+number of links is greater than one, the file is unlinked.
+This
+allows the current file to be accessed by a constant name.
+.It Cm enable \&| Cm disable
+Enables or disables the recording function.
+.El
+.El
+.El
+.Sh Access Control Support
+The
+.Xr ntpd 1ntpdmdoc
+daemon implements a general purpose address/mask based restriction
+list.
+The list contains address/match entries sorted first
+by increasing address values and and then by increasing mask values.
+A match occurs when the bitwise AND of the mask and the packet
+source address is equal to the bitwise AND of the mask and
+address in the list.
+The list is searched in order with the
+last match found defining the restriction flags associated
+with the entry.
+Additional information and examples can be found in the
+.Qq Notes on Configuring NTP and Setting up a NTP Subnet
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.Pp
+The restriction facility was implemented in conformance
+with the access policies for the original NSFnet backbone
+time servers.
+Later the facility was expanded to deflect
+cryptographic and clogging attacks.
+While this facility may
+be useful for keeping unwanted or broken or malicious clients
+from congesting innocent servers, it should not be considered
+an alternative to the NTP authentication facilities.
+Source address based restrictions are easily circumvented
+by a determined cracker.
+.Pp
+Clients can be denied service because they are explicitly
+included in the restrict list created by the restrict command
+or implicitly as the result of cryptographic or rate limit
+violations.
+Cryptographic violations include certificate
+or identity verification failure; rate limit violations generally
+result from defective NTP implementations that send packets
+at abusive rates.
+Some violations cause denied service
+only for the offending packet, others cause denied service
+for a timed period and others cause the denied service for
+an indefinate period.
+When a client or network is denied access
+for an indefinate period, the only way at present to remove
+the restrictions is by restarting the server.
+.Ss The Kiss\-of\-Death Packet
+Ordinarily, packets denied service are simply dropped with no
+further action except incrementing statistics counters.
+Sometimes a
+more proactive response is needed, such as a server message that
+explicitly requests the client to stop sending and leave a message
+for the system operator.
+A special packet format has been created
+for this purpose called the "kiss\-of\-death" (KoD) packet.
+KoD packets have the leap bits set unsynchronized and stratum set
+to zero and the reference identifier field set to a four\-byte
+ASCII code.
+If the
+.Cm noserve
+or
+.Cm notrust
+flag of the matching restrict list entry is set,
+the code is "DENY"; if the
+.Cm limited
+flag is set and the rate limit
+is exceeded, the code is "RATE".
+Finally, if a cryptographic violation occurs, the code is "CRYP".
+.Pp
+A client receiving a KoD performs a set of sanity checks to
+minimize security exposure, then updates the stratum and
+reference identifier peer variables, sets the access
+denied (TEST4) bit in the peer flash variable and sends
+a message to the log.
+As long as the TEST4 bit is set,
+the client will send no further packets to the server.
+The only way at present to recover from this condition is
+to restart the protocol at both the client and server.
+This
+happens automatically at the client when the association times out.
+It will happen at the server only if the server operator cooperates.
+.Ss Access Control Commands
+.Bl -tag -width indent
+.It Xo Ic discard
+.Op Cm average Ar avg
+.Op Cm minimum Ar min
+.Op Cm monitor Ar prob
+.Xc
+Set the parameters of the
+.Cm limited
+facility which protects the server from
+client abuse.
+The
+.Cm average
+subcommand specifies the minimum average packet
+spacing, while the
+.Cm minimum
+subcommand specifies the minimum packet spacing.
+Packets that violate these minima are discarded
+and a kiss\-o'\-death packet returned if enabled.
+The default
+minimum average and minimum are 5 and 2, respectively.
+The monitor subcommand specifies the probability of discard
+for packets that overflow the rate\-control window.
+.It Xo Ic restrict address
+.Op Cm mask Ar mask
+.Op Ar flag ...
+.Xc
+The
+.Ar address
+argument expressed in
+dotted\-quad form is the address of a host or network.
+Alternatively, the
+.Ar address
+argument can be a valid host DNS name.
+The
+.Ar mask
+argument expressed in dotted\-quad form defaults to
+.Cm 255.255.255.255 ,
+meaning that the
+.Ar address
+is treated as the address of an individual host.
+A default entry (address
+.Cm 0.0.0.0 ,
+mask
+.Cm 0.0.0.0 )
+is always included and is always the first entry in the list.
+Note that text string
+.Cm default ,
+with no mask option, may
+be used to indicate the default entry.
+In the current implementation,
+.Cm flag
+always
+restricts access, i.e., an entry with no flags indicates that free
+access to the server is to be given.
+The flags are not orthogonal,
+in that more restrictive flags will often make less restrictive
+ones redundant.
+The flags can generally be classed into two
+categories, those which restrict time service and those which
+restrict informational queries and attempts to do run\-time
+reconfiguration of the server.
+One or more of the following flags
+may be specified:
+.Bl -tag -width indent
+.It Cm ignore
+Deny packets of all kinds, including
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+queries.
+.It Cm kod
+If this flag is set when an access violation occurs, a kiss\-o'\-death
+(KoD) packet is sent.
+KoD packets are rate limited to no more than one
+per second.
+If another KoD packet occurs within one second after the
+last one, the packet is dropped.
+.It Cm limited
+Deny service if the packet spacing violates the lower limits specified
+in the discard command.
+A history of clients is kept using the
+monitoring capability of
+.Xr ntpd 1ntpdmdoc .
+Thus, monitoring is always active as
+long as there is a restriction entry with the
+.Cm limited
+flag.
+.It Cm lowpriotrap
+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.
+.It Cm nomodify
+Deny
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+queries which attempt to modify the state of the
+server (i.e., run time reconfiguration).
+Queries which return
+information are permitted.
+.It Cm noquery
+Deny
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+queries.
+Time service is not affected.
+.It Cm nopeer
+Deny packets which would result in mobilizing a new association.
+This
+includes broadcast and symmetric active packets when a configured
+association does not exist.
+It also includes
+.Cm pool
+associations, so if you want to use servers from a
+.Cm pool
+directive and also want to use
+.Cm nopeer
+by default, you'll want a
+.Cm "restrict source ..." line as well that does
+.It not
+include the
+.Cm nopeer
+directive.
+.It Cm noserve
+Deny all packets except
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+queries.
+.It Cm notrap
+Decline to provide mode 6 control message trap service to matching
+hosts.
+The trap service is a subsystem of the ntpdq control message
+protocol which is intended for use by remote event logging programs.
+.It Cm notrust
+Deny service unless the packet is cryptographically authenticated.
+.It Cm ntpport
+This is actually a match algorithm modifier, rather than a
+restriction flag.
+Its presence causes the restriction entry to be
+matched only if the source port in the packet is the standard NTP
+UDP port (123).
+Both
+.Cm ntpport
+and
+.Cm non\-ntpport
+may
+be specified.
+The
+.Cm ntpport
+is considered more specific and
+is sorted later in the list.
+.It Cm version
+Deny packets that do not match the current NTP version.
+.El
+.Pp
+Default restriction list entries with the flags ignore, interface,
+ntpport, for each of the local host's interface addresses are
+inserted into the table at startup to prevent the server
+from attempting to synchronize to its own time.
+A default entry is also always present, though if it is
+otherwise unconfigured; no flags are associated
+with the default entry (i.e., everything besides your own
+NTP server is unrestricted).
+.El
+.Sh Automatic NTP Configuration Options
+.Ss Manycasting
+Manycasting is a automatic discovery and configuration paradigm
+new to NTPv4.
+It is intended as a means for a multicast client
+to troll the nearby network neighborhood to find cooperating
+manycast servers, validate them using cryptographic means
+and evaluate their time values with respect to other servers
+that might be lurking in the vicinity.
+The intended result is that each manycast client mobilizes
+client associations with some number of the "best"
+of the nearby manycast servers, yet automatically reconfigures
+to sustain this number of servers should one or another fail.
+.Pp
+Note that the manycasting paradigm does not coincide
+with the anycast paradigm described in RFC\-1546,
+which is designed to find a single server from a clique
+of servers providing the same service.
+The manycast paradigm is designed to find a plurality
+of redundant servers satisfying defined optimality criteria.
+.Pp
+Manycasting can be used with either symmetric key
+or public key cryptography.
+The public key infrastructure (PKI)
+offers the best protection against compromised keys
+and is generally considered stronger, at least with relatively
+large key sizes.
+It is implemented using the Autokey protocol and
+the OpenSSL cryptographic library available from
+.Li http://www.openssl.org/ .
+The library can also be used with other NTPv4 modes
+as well and is highly recommended, especially for broadcast modes.
+.Pp
+A persistent manycast client association is configured
+using the manycastclient command, which is similar to the
+server command but with a multicast (IPv4 class
+.Cm D
+or IPv6 prefix
+.Cm FF )
+group address.
+The IANA has designated IPv4 address 224.1.1.1
+and IPv6 address FF05::101 (site local) for NTP.
+When more servers are needed, it broadcasts manycast
+client messages to this address at the minimum feasible rate
+and minimum feasible time\-to\-live (TTL) hops, depending
+on how many servers have already been found.
+There can be as many manycast client associations
+as different group address, each one serving as a template
+for a future ephemeral unicast client/server association.
+.Pp
+Manycast servers configured with the
+.Ic manycastserver
+command listen on the specified group address for manycast
+client messages.
+Note the distinction between manycast client,
+which actively broadcasts messages, and manycast server,
+which passively responds to them.
+If a manycast server is
+in scope of the current TTL and is itself synchronized
+to a valid source and operating at a stratum level equal
+to or lower than the manycast client, it replies to the
+manycast client message with an ordinary unicast server message.
+.Pp
+The manycast client receiving this message mobilizes
+an ephemeral client/server association according to the
+matching manycast client template, but only if cryptographically
+authenticated and the server stratum is less than or equal
+to the client stratum.
+Authentication is explicitly required
+and either symmetric key or public key (Autokey) can be used.
+Then, the client polls the server at its unicast address
+in burst mode in order to reliably set the host clock
+and validate the source.
+This normally results
+in a volley of eight client/server at 2\-s intervals
+during which both the synchronization and cryptographic
+protocols run concurrently.
+Following the volley,
+the client runs the NTP intersection and clustering
+algorithms, which act to discard all but the "best"
+associations according to stratum and synchronization
+distance.
+The surviving associations then continue
+in ordinary client/server mode.
+.Pp
+The manycast client polling strategy is designed to reduce
+as much as possible the volume of manycast client messages
+and the effects of implosion due to near\-simultaneous
+arrival of manycast server messages.
+The strategy is determined by the
+.Ic manycastclient ,
+.Ic tos
+and
+.Ic ttl
+configuration commands.
+The manycast poll interval is
+normally eight times the system poll interval,
+which starts out at the
+.Cm minpoll
+value specified in the
+.Ic manycastclient ,
+command and, under normal circumstances, increments to the
+.Cm maxpolll
+value specified in this command.
+Initially, the TTL is
+set at the minimum hops specified by the ttl command.
+At each retransmission the TTL is increased until reaching
+the maximum hops specified by this command or a sufficient
+number client associations have been found.
+Further retransmissions use the same TTL.
+.Pp
+The quality and reliability of the suite of associations
+discovered by the manycast client is determined by the NTP
+mitigation algorithms and the
+.Cm minclock
+and
+.Cm minsane
+values specified in the
+.Ic tos
+configuration command.
+At least
+.Cm minsane
+candidate servers must be available and the mitigation
+algorithms produce at least
+.Cm minclock
+survivors in order to synchronize the clock.
+Byzantine agreement principles require at least four
+candidates in order to correctly discard a single falseticker.
+For legacy purposes,
+.Cm minsane
+defaults to 1 and
+.Cm minclock
+defaults to 3.
+For manycast service
+.Cm minsane
+should be explicitly set to 4, assuming at least that
+number of servers are available.
+.Pp
+If at least
+.Cm minclock
+servers are found, the manycast poll interval is immediately
+set to eight times
+.Cm maxpoll .
+If less than
+.Cm minclock
+servers are found when the TTL has reached the maximum hops,
+the manycast poll interval is doubled.
+For each transmission
+after that, the poll interval is doubled again until
+reaching the maximum of eight times
+.Cm maxpoll .
+Further transmissions use the same poll interval and
+TTL values.
+Note that while all this is going on,
+each client/server association found is operating normally
+it the system poll interval.
+.Pp
+Administratively scoped multicast boundaries are normally
+specified by the network router configuration and,
+in the case of IPv6, the link/site scope prefix.
+By default, the increment for TTL hops is 32 starting
+from 31; however, the
+.Ic ttl
+configuration command can be
+used to modify the values to match the scope rules.
+.Pp
+It is often useful to narrow the range of acceptable
+servers which can be found by manycast client associations.
+Because manycast servers respond only when the client
+stratum is equal to or greater than the server stratum,
+primary (stratum 1) servers fill find only primary servers
+in TTL range, which is probably the most common objective.
+However, unless configured otherwise, all manycast clients
+in TTL range will eventually find all primary servers
+in TTL range, which is probably not the most common
+objective in large networks.
+The
+.Ic tos
+command can be used to modify this behavior.
+Servers with stratum below
+.Cm floor
+or above
+.Cm ceiling
+specified in the
+.Ic tos
+command are strongly discouraged during the selection
+process; however, these servers may be temporally
+accepted if the number of servers within TTL range is
+less than
+.Cm minclock .
+.Pp
+The above actions occur for each manycast client message,
+which repeats at the designated poll interval.
+However, once the ephemeral client association is mobilized,
+subsequent manycast server replies are discarded,
+since that would result in a duplicate association.
+If during a poll interval the number of client associations
+falls below
+.Cm minclock ,
+all manycast client prototype associations are reset
+to the initial poll interval and TTL hops and operation
+resumes from the beginning.
+It is important to avoid
+frequent manycast client messages, since each one requires
+all manycast servers in TTL range to respond.
+The result could well be an implosion, either minor or major,
+depending on the number of servers in range.
+The recommended value for
+.Cm maxpoll
+is 12 (4,096 s).
+.Pp
+It is possible and frequently useful to configure a host
+as both manycast client and manycast server.
+A number of hosts configured this way and sharing a common
+group address will automatically organize themselves
+in an optimum configuration based on stratum and
+synchronization distance.
+For example, consider an NTP
+subnet of two primary servers and a hundred or more
+dependent clients.
+With two exceptions, all servers
+and clients have identical configuration files including both
+.Ic multicastclient
+and
+.Ic multicastserver
+commands using, for instance, multicast group address
+239.1.1.1.
+The only exception is that each primary server
+configuration file must include commands for the primary
+reference source such as a GPS receiver.
+.Pp
+The remaining configuration files for all secondary
+servers and clients have the same contents, except for the
+.Ic tos
+command, which is specific for each stratum level.
+For stratum 1 and stratum 2 servers, that command is
+not necessary.
+For stratum 3 and above servers the
+.Cm floor
+value is set to the intended stratum number.
+Thus, all stratum 3 configuration files are identical,
+all stratum 4 files are identical and so forth.
+.Pp
+Once operations have stabilized in this scenario,
+the primary servers will find the primary reference source
+and each other, since they both operate at the same
+stratum (1), but not with any secondary server or client,
+since these operate at a higher stratum.
+The secondary
+servers will find the servers at the same stratum level.
+If one of the primary servers loses its GPS receiver,
+it will continue to operate as a client and other clients
+will time out the corresponding association and
+re\-associate accordingly.
+.Pp
+Some administrators prefer to avoid running
+.Xr ntpd 1ntpdmdoc
+continuously and run either
+.Xr ntpdate 8
+or
+.Xr ntpd 1ntpdmdoc
+.Fl q
+as a cron job.
+In either case the servers must be
+configured in advance and the program fails if none are
+available when the cron job runs.
+A really slick
+application of manycast is with
+.Xr ntpd 1ntpdmdoc
+.Fl q .
+The program wakes up, scans the local landscape looking
+for the usual suspects, selects the best from among
+the rascals, sets the clock and then departs.
+Servers do not have to be configured in advance and
+all clients throughout the network can have the same
+configuration file.
+.Ss Manycast Interactions with Autokey
+Each time a manycast client sends a client mode packet
+to a multicast group address, all manycast servers
+in scope generate a reply including the host name
+and status word.
+The manycast clients then run
+the Autokey protocol, which collects and verifies
+all certificates involved.
+Following the burst interval
+all but three survivors are cast off,
+but the certificates remain in the local cache.
+It often happens that several complete signing trails
+from the client to the primary servers are collected in this way.
+.Pp
+About once an hour or less often if the poll interval
+exceeds this, the client regenerates the Autokey key list.
+This is in general transparent in client/server mode.
+However, about once per day the server private value
+used to generate cookies is refreshed along with all
+manycast client associations.
+In this case all
+cryptographic values including certificates is refreshed.
+If a new certificate has been generated since
+the last refresh epoch, it will automatically revoke
+all prior certificates that happen to be in the
+certificate cache.
+At the same time, the manycast
+scheme starts all over from the beginning and
+the expanding ring shrinks to the minimum and increments
+from there while collecting all servers in scope.
+.Ss Manycast Options
+.Bl -tag -width indent
+.It Xo Ic tos
+.Oo
+.Cm ceiling Ar ceiling |
+.Cm cohort { 0 | 1 } |
+.Cm floor Ar floor |
+.Cm minclock Ar minclock |
+.Cm minsane Ar minsane
+.Oc
+.Xc
+This command affects the clock selection and clustering
+algorithms.
+It can be used to select the quality and
+quantity of peers used to synchronize the system clock
+and is most useful in manycast mode.
+The variables operate
+as follows:
+.Bl -tag -width indent
+.It Cm ceiling Ar ceiling
+Peers with strata above
+.Cm ceiling
+will be discarded if there are at least
+.Cm minclock
+peers remaining.
+This value defaults to 15, but can be changed
+to any number from 1 to 15.
+.It Cm cohort Bro 0 | 1 Brc
+This is a binary flag which enables (0) or disables (1)
+manycast server replies to manycast clients with the same
+stratum level.
+This is useful to reduce implosions where
+large numbers of clients with the same stratum level
+are present.
+The default is to enable these replies.
+.It Cm floor Ar floor
+Peers with strata below
+.Cm floor
+will be discarded if there are at least
+.Cm minclock
+peers remaining.
+This value defaults to 1, but can be changed
+to any number from 1 to 15.
+.It Cm minclock Ar minclock
+The clustering algorithm repeatedly casts out outlyer
+associations until no more than
+.Cm minclock
+associations remain.
+This value defaults to 3,
+but can be changed to any number from 1 to the number of
+configured sources.
+.It Cm minsane Ar minsane
+This is the minimum number of candidates available
+to the clock selection algorithm in order to produce
+one or more truechimers for the clustering algorithm.
+If fewer than this number are available, the clock is
+undisciplined and allowed to run free.
+The default is 1
+for legacy purposes.
+However, according to principles of
+Byzantine agreement,
+.Cm minsane
+should be at least 4 in order to detect and discard
+a single falseticker.
+.El
+.It Cm ttl Ar hop ...
+This command specifies a list of TTL values in increasing
+order, up to 8 values can be specified.
+In manycast mode these values are used in turn
+in an expanding\-ring search.
+The default is eight
+multiples of 32 starting at 31.
+.El
+.Sh Reference Clock Support
+The NTP Version 4 daemon supports some three dozen different radio,
+satellite and modem reference clocks plus a special pseudo\-clock
+used for backup or when no other clock source is available.
+Detailed descriptions of individual device drivers and options can
+be found in the
+.Qq Reference Clock Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+Additional information can be found in the pages linked
+there, including the
+.Qq Debugging Hints for Reference Clock Drivers
+and
+.Qq How To Write a Reference Clock Driver
+pages
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+In addition, support for a PPS
+signal is available as described in the
+.Qq Pulse\-per\-second (PPS) Signal Interfacing
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+Many
+drivers support special line discipline/streams modules which can
+significantly improve the accuracy using the driver.
+These are
+described in the
+.Qq Line Disciplines and Streams Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.Pp
+A reference clock will generally (though not always) be a radio
+timecode receiver which is synchronized to a source of standard
+time such as the services offered by the NRC in Canada and NIST and
+USNO in the US.
+The interface between the computer and the timecode
+receiver is device dependent, but is usually a serial port.
+A
+device driver specific to each reference clock must be selected and
+compiled in the distribution; however, most common radio, satellite
+and modem clocks are included by default.
+Note that an attempt to
+configure a reference clock when the driver has not been compiled
+or the hardware port has not been appropriately configured results
+in a scalding remark to the system log file, but is otherwise non
+hazardous.
+.Pp
+For the purposes of configuration,
+.Xr ntpd 1ntpdmdoc
+treats
+reference clocks in a manner analogous to normal NTP peers as much
+as possible.
+Reference clocks are identified by a syntactically
+correct but invalid IP address, in order to distinguish them from
+normal NTP peers.
+Reference clock addresses are of the form
+.Sm off
+.Li 127.127. Ar t . Ar u ,
+.Sm on
+where
+.Ar t
+is an integer
+denoting the clock type and
+.Ar u
+indicates the unit
+number in the range 0\-3.
+While it may seem overkill, it is in fact
+sometimes useful to configure multiple reference clocks of the same
+type, in which case the unit numbers must be unique.
+.Pp
+The
+.Ic server
+command is used to configure a reference
+clock, where the
+.Ar address
+argument in that command
+is the clock address.
+The
+.Cm key ,
+.Cm version
+and
+.Cm ttl
+options are not used for reference clock support.
+The
+.Cm mode
+option is added for reference clock support, as
+described below.
+The
+.Cm prefer
+option can be useful to
+persuade the server to cherish a reference clock with somewhat more
+enthusiasm than other reference clocks or peers.
+Further
+information on this option can be found in the
+.Qq Mitigation Rules and the prefer Keyword
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+page.
+The
+.Cm minpoll
+and
+.Cm maxpoll
+options have
+meaning only for selected clock drivers.
+See the individual clock
+driver document pages for additional information.
+.Pp
+The
+.Ic fudge
+command is used to provide additional
+information for individual clock drivers and normally follows
+immediately after the
+.Ic server
+command.
+The
+.Ar address
+argument specifies the clock address.
+The
+.Cm refid
+and
+.Cm stratum
+options can be used to
+override the defaults for the device.
+There are two optional
+device\-dependent time offsets and four flags that can be included
+in the
+.Ic fudge
+command as well.
+.Pp
+The stratum number of a reference clock is by default zero.
+Since the
+.Xr ntpd 1ntpdmdoc
+daemon adds one to the stratum of each
+peer, a primary server ordinarily displays an external stratum of
+one.
+In order to provide engineered backups, it is often useful to
+specify the reference clock stratum as greater than zero.
+The
+.Cm stratum
+option is used for this purpose.
+Also, in cases
+involving both a reference clock and a pulse\-per\-second (PPS)
+discipline signal, it is useful to specify the reference clock
+identifier as other than the default, depending on the driver.
+The
+.Cm refid
+option is used for this purpose.
+Except where noted,
+these options apply to all clock drivers.
+.Ss Reference Clock Commands
+.Bl -tag -width indent
+.It Xo Ic server
+.Sm off
+.Li 127.127. Ar t . Ar u
+.Sm on
+.Op Cm prefer
+.Op Cm mode Ar int
+.Op Cm minpoll Ar int
+.Op Cm maxpoll Ar int
+.Xc
+This command can be used to configure reference clocks in
+special ways.
+The options are interpreted as follows:
+.Bl -tag -width indent
+.It Cm prefer
+Marks the reference clock as preferred.
+All other things being
+equal, this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+.Qq Mitigation Rules and the prefer Keyword
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+for further information.
+.It Cm mode Ar int
+Specifies a mode number which is interpreted in a
+device\-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+.It Cm minpoll Ar int
+.It Cm maxpoll Ar int
+These options specify the minimum and maximum polling interval
+for reference clock messages, as a power of 2 in seconds
+For
+most directly connected reference clocks, both
+.Cm minpoll
+and
+.Cm maxpoll
+default to 6 (64 s).
+For modem reference clocks,
+.Cm minpoll
+defaults to 10 (17.1 m) and
+.Cm maxpoll
+defaults to 14 (4.5 h).
+The allowable range is 4 (16 s) to 17 (36.4 h) inclusive.
+.El
+.It Xo Ic fudge
+.Sm off
+.Li 127.127. Ar t . Ar u
+.Sm on
+.Op Cm time1 Ar sec
+.Op Cm time2 Ar sec
+.Op Cm stratum Ar int
+.Op Cm refid Ar string
+.Op Cm mode Ar int
+.Op Cm flag1 Cm 0 \&| Cm 1
+.Op Cm flag2 Cm 0 \&| Cm 1
+.Op Cm flag3 Cm 0 \&| Cm 1
+.Op Cm flag4 Cm 0 \&| Cm 1
+.Xc
+This command can be used to configure reference clocks in
+special ways.
+It must immediately follow the
+.Ic server
+command which configures the driver.
+Note that the same capability
+is possible at run time using the
+.Xr ntpdc 1ntpdcmdoc
+program.
+The options are interpreted as
+follows:
+.Bl -tag -width indent
+.It Cm time1 Ar sec
+Specifies a constant to be added to the time offset produced by
+the driver, a fixed\-point decimal number in seconds.
+This is used
+as a calibration constant to adjust the nominal time offset of a
+particular clock to agree with an external standard, such as a
+precision PPS signal.
+It also provides a way to correct a
+systematic error or bias due to serial port or operating system
+latencies, different cable lengths or receiver internal delay.
+The
+specified offset is in addition to the propagation delay provided
+by other means, such as internal DIPswitches.
+Where a calibration
+for an individual system and driver is available, an approximate
+correction is noted in the driver documentation pages.
+Note: in order to facilitate calibration when more than one
+radio clock or PPS signal is supported, a special calibration
+feature is available.
+It takes the form of an argument to the
+.Ic enable
+command described in
+.Sx Miscellaneous Options
+page and operates as described in the
+.Qq Reference Clock Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.It Cm time2 Ar secs
+Specifies a fixed\-point decimal number in seconds, which is
+interpreted in a driver\-dependent way.
+See the descriptions of
+specific drivers in the
+.Qq Reference Clock Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.It Cm stratum Ar int
+Specifies the stratum number assigned to the driver, an integer
+between 0 and 15.
+This number overrides the default stratum number
+ordinarily assigned by the driver itself, usually zero.
+.It Cm refid Ar string
+Specifies an ASCII string of from one to four characters which
+defines the reference identifier used by the driver.
+This string
+overrides the default identifier ordinarily assigned by the driver
+itself.
+.It Cm mode Ar int
+Specifies a mode number which is interpreted in a
+device\-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+.It Cm flag1 Cm 0 \&| Cm 1
+.It Cm flag2 Cm 0 \&| Cm 1
+.It Cm flag3 Cm 0 \&| Cm 1
+.It Cm flag4 Cm 0 \&| Cm 1
+These four flags are used for customizing the clock driver.
+The
+interpretation of these values, and whether they are used at all,
+is a function of the particular clock driver.
+However, by
+convention
+.Cm flag4
+is used to enable recording monitoring
+data to the
+.Cm clockstats
+file configured with the
+.Ic filegen
+command.
+Further information on the
+.Ic filegen
+command can be found in
+.Sx Monitoring Options .
+.El
+.El
+.Sh Miscellaneous Options
+.Bl -tag -width indent
+.It Ic broadcastdelay Ar seconds
+The broadcast and multicast modes require a special calibration
+to determine the network delay between the local and remote
+servers.
+Ordinarily, this is done automatically by the initial
+protocol exchanges between the client and server.
+In some cases,
+the calibration procedure may fail due to network or server access
+controls, for example.
+This command specifies the default delay to
+be used under these circumstances.
+Typically (for Ethernet), a
+number between 0.003 and 0.007 seconds is appropriate.
+The default
+when this command is not used is 0.004 seconds.
+.It Ic calldelay Ar delay
+This option controls the delay in seconds between the first and second
+packets sent in burst or iburst mode to allow additional time for a modem
+or ISDN call to complete.
+.It Ic driftfile Ar driftfile
+This command specifies the complete path and name of the file used to
+record the frequency of the local clock oscillator.
+This is the same
+operation as the
+.Fl f
+command line option.
+If the file exists, it is read at
+startup in order to set the initial frequency and then updated once per
+hour with the current frequency computed by the daemon.
+If the file name is
+specified, but the file itself does not exist, the starts with an initial
+frequency of zero and creates the file when writing it for the first time.
+If this command is not given, the daemon will always start with an initial
+frequency of zero.
+.Pp
+The file format consists of a single line containing a single
+floating point number, which records the frequency offset measured
+in parts\-per\-million (PPM).
+The file is updated by first writing
+the current drift value into a temporary file and then renaming
+this file to replace the old version.
+This implies that
+.Xr ntpd 1ntpdmdoc
+must have write permission for the directory the
+drift file is located in, and that file system links, symbolic or
+otherwise, should be avoided.
+.It Xo Ic enable
+.Oo
+.Cm auth | Cm bclient |
+.Cm calibrate | Cm kernel |
+.Cm mode7 | monitor |
+.Cm ntp | Cm stats
+.Oc
+.Xc
+.It Xo Ic disable
+.Oo
+.Cm auth | Cm bclient |
+.Cm calibrate | Cm kernel |
+.Cm mode7 | monitor |
+.Cm ntp | Cm stats
+.Oc
+.Xc
+Provides a way to enable or disable various server options.
+Flags not mentioned are unaffected.
+Note that all of these flags
+can be controlled remotely using the
+.Xr ntpdc 1ntpdcmdoc
+utility program.
+.Bl -tag -width indent
+.It Cm auth
+Enables the server to synchronize with unconfigured peers only if the
+peer has been correctly authenticated using either public key or
+private key cryptography.
+The default for this flag is
+.Ic enable .
+.It Cm bclient
+Enables the server to listen for a message from a broadcast or
+multicast server, as in the
+.Ic multicastclient
+command with default
+address.
+The default for this flag is
+.Ic disable .
+.It Cm calibrate
+Enables the calibrate feature for reference clocks.
+The default for
+this flag is
+.Ic disable .
+.It Cm kernel
+Enables the kernel time discipline, if available.
+The default for this
+flag is
+.Ic enable
+if support is available, otherwise
+.Ic disable .
+.It Cm mode7
+Enables processing of NTP mode 7 implementation\-specific requests
+which are used by the deprecated
+.Xr ntpdc 1ntpdcmdoc
+program.
+The default for this flag is disable.
+This flag is excluded from runtime configuration using
+.Xr ntpq 1ntpqmdoc .
+The
+.Xr ntpq 1ntpqmdoc
+program provides the same capabilities as
+.Xr ntpdc 1ntpdcmdoc
+using standard mode 6 requests.
+.It Cm monitor
+Enables the monitoring facility.
+See the
+.Xr ntpdc 1ntpdcmdoc
+program
+and the
+.Ic monlist
+command or further information.
+The
+default for this flag is
+.Ic enable .
+.It Cm ntp
+Enables time and frequency discipline.
+In effect, this switch opens and
+closes the feedback loop, which is useful for testing.
+The default for
+this flag is
+.Ic enable .
+.It Cm stats
+Enables the statistics facility.
+See the
+.Sx Monitoring Options
+section for further information.
+The default for this flag is
+.Ic disable .
+.El
+.It Ic includefile Ar includefile
+This command allows additional configuration commands
+to be included from a separate file.
+Include files may
+be nested to a depth of five; upon reaching the end of any
+include file, command processing resumes in the previous
+configuration file.
+This option is useful for sites that run
+.Xr ntpd 1ntpdmdoc
+on multiple hosts, with (mostly) common options (e.g., a
+restriction list).
+.It Ic logconfig Ar configkeyword
+This command controls the amount and type of output written to
+the system
+.Xr syslog 3
+facility or the alternate
+.Ic logfile
+log file.
+By default, all output is turned on.
+All
+.Ar configkeyword
+keywords can be prefixed with
+.Ql = ,
+.Ql +
+and
+.Ql \- ,
+where
+.Ql =
+sets the
+.Xr syslog 3
+priority mask,
+.Ql +
+adds and
+.Ql \-
+removes
+messages.
+.Xr syslog 3
+messages can be controlled in four
+classes
+.Po
+.Cm clock ,
+.Cm peer ,
+.Cm sys
+and
+.Cm sync
+.Pc .
+Within these classes four types of messages can be
+controlled: informational messages
+.Po
+.Cm info
+.Pc ,
+event messages
+.Po
+.Cm events
+.Pc ,
+statistics messages
+.Po
+.Cm statistics
+.Pc
+and
+status messages
+.Po
+.Cm status
+.Pc .
+.Pp
+Configuration keywords are formed by concatenating the message class with
+the event class.
+The
+.Cm all
+prefix can be used instead of a message class.
+A
+message class may also be followed by the
+.Cm all
+keyword to enable/disable all
+messages of the respective message class.Thus, a minimal log configuration
+could look like this:
+.Bd -literal
+logconfig =syncstatus +sysevents
+.Ed
+.Pp
+This would just list the synchronizations state of
+.Xr ntpd 1ntpdmdoc
+and the major system events.
+For a simple reference server, the
+following minimum message configuration could be useful:
+.Bd -literal
+logconfig =syncall +clockall
+.Ed
+.Pp
+This configuration will list all clock information and
+synchronization information.
+All other events and messages about
+peers, system events and so on is suppressed.
+.It Ic logfile Ar logfile
+This command specifies the location of an alternate log file to
+be used instead of the default system
+.Xr syslog 3
+facility.
+This is the same operation as the \-l command line option.
+.It Ic setvar Ar variable Op Cm default
+This command adds an additional system variable.
+These
+variables can be used to distribute additional information such as
+the access policy.
+If the variable of the form
+.Sm off
+.Va name = Ar value
+.Sm on
+is followed by the
+.Cm default
+keyword, the
+variable will be listed as part of the default system variables
+.Po
+.Xr ntpq 1ntpqmdoc
+.Ic rv
+command
+.Pc ) .
+These additional variables serve
+informational purposes only.
+They are not related to the protocol
+other that they can be listed.
+The known protocol variables will
+always override any variables defined via the
+.Ic setvar
+mechanism.
+There are three special variables that contain the names
+of all variable of the same group.
+The
+.Va sys_var_list
+holds
+the names of all system variables.
+The
+.Va peer_var_list
+holds
+the names of all peer variables and the
+.Va clock_var_list
+holds the names of the reference clock variables.
+.It Xo Ic tinker
+.Oo
+.Cm allan Ar allan |
+.Cm dispersion Ar dispersion |
+.Cm freq Ar freq |
+.Cm huffpuff Ar huffpuff |
+.Cm panic Ar panic |
+.Cm step Ar srep |
+.Cm stepout Ar stepout
+.Oc
+.Xc
+This command can be used to alter several system variables in
+very exceptional circumstances.
+It should occur in the
+configuration file before any other configuration options.
+The
+default values of these variables have been carefully optimized for
+a wide range of network speeds and reliability expectations.
+In
+general, they interact in intricate ways that are hard to predict
+and some combinations can result in some very nasty behavior.
+Very
+rarely is it necessary to change the default values; but, some
+folks cannot resist twisting the knobs anyway and this command is
+for them.
+Emphasis added: twisters are on their own and can expect
+no help from the support group.
+.Pp
+The variables operate as follows:
+.Bl -tag -width indent
+.It Cm allan Ar allan
+The argument becomes the new value for the minimum Allan
+intercept, which is a parameter of the PLL/FLL clock discipline
+algorithm.
+The value in log2 seconds defaults to 7 (1024 s), which is also the lower
+limit.
+.It Cm dispersion Ar dispersion
+The argument becomes the new value for the dispersion increase rate,
+normally .000015 s/s.
+.It Cm freq Ar freq
+The argument becomes the initial value of the frequency offset in
+parts\-per\-million.
+This overrides the value in the frequency file, if
+present, and avoids the initial training state if it is not.
+.It Cm huffpuff Ar huffpuff
+The argument becomes the new value for the experimental
+huff\-n'\-puff filter span, which determines the most recent interval
+the algorithm will search for a minimum delay.
+The lower limit is
+900 s (15 m), but a more reasonable value is 7200 (2 hours).
+There
+is no default, since the filter is not enabled unless this command
+is given.
+.It Cm panic Ar panic
+The argument is the panic threshold, normally 1000 s.
+If set to zero,
+the panic sanity check is disabled and a clock offset of any value will
+be accepted.
+.It Cm step Ar step
+The argument is the step threshold, which by default is 0.128 s.
+It can
+be set to any positive number in seconds.
+If set to zero, step
+adjustments will never occur.
+Note: The kernel time discipline is
+disabled if the step threshold is set to zero or greater than the
+default.
+.It Cm stepout Ar stepout
+The argument is the stepout timeout, which by default is 900 s.
+It can
+be set to any positive number in seconds.
+If set to zero, the stepout
+pulses will not be suppressed.
+.El
+.It Xo Ic rlimit
+.Oo
+.Cm memlock Ar Nmegabytes |
+.Cm stacksize Ar N4kPages
+.Cm filenum Ar Nfiledescriptors
+.Oc
+.Xc
+.Bl -tag -width indent
+.It Cm memlock Ar Nmegabytes
+Specify the number of megabytes of memory that can be allocated.
+Probably only available under Linux, this option is useful
+when dropping root (the
+.Fl i
+option).
+The default is 32 megabytes. Setting this to zero will prevent any attemp to lock memory.
+.It Cm stacksize Ar N4kPages
+Specifies the maximum size of the process stack on systems with the
+.It Cm filenum Ar Nfiledescriptors
+Specifies the maximum number of file descriptors ntpd may have open at once. Defaults to the system default.
+.Fn mlockall
+function.
+Defaults to 50 4k pages (200 4k pages in OpenBSD).
+.El
+.It Xo Ic trap Ar host_address
+.Op Cm port Ar port_number
+.Op Cm interface Ar interface_address
+.Xc
+This command configures a trap receiver at the given host
+address and port number for sending messages with the specified
+local interface address.
+If the port number is unspecified, a value
+of 18447 is used.
+If the interface address is not specified, the
+message is sent with a source address of the local interface the
+message is sent through.
+Note that on a multihomed host the
+interface used may vary from time to time with routing changes.
+.Pp
+The trap receiver will generally log event messages and other
+information from the server in a log file.
+While such monitor
+programs may also request their own trap dynamically, configuring a
+trap receiver will ensure that no messages are lost when the server
+is started.
+.It Cm hop Ar ...
+This command specifies a list of TTL values in increasing order, up to 8
+values can be specified.
+In manycast mode these values are used in turn in
+an expanding\-ring search.
+The default is eight multiples of 32 starting at
+31.
+.El
+.Sh "OPTIONS"
+.Bl -tag
+.It Fl \-help
+Display usage information and exit.
+.It Fl \-more\-help
+Pass the extended usage information through a pager.
+.It Fl \-version Op Brq Ar v|c|n
+Output version of program and exit. The default mode is `v', a simple
+version. The `c' mode will print copyright information and `n' will
+print the full copyright notice.
+.El
+.Sh "OPTION PRESETS"
+Any option that is not marked as \fInot presettable\fP may be preset
+by loading values from environment variables named:
+.nf
+ \fBNTP_CONF_<option\-name>\fP or \fBNTP_CONF\fP
+.fi
+.ad
+.Sh "ENVIRONMENT"
+See \fBOPTION PRESETS\fP for configuration environment variables.
+.Sh FILES
+.Bl -tag -width /etc/ntp.drift -compact
+.It Pa /etc/ntp.conf
+the default name of the configuration file
+.It Pa ntp.keys
+private MD5 keys
+.It Pa ntpkey
+RSA private key
+.It Pa ntpkey_ Ns Ar host
+RSA public key
+.It Pa ntp_dh
+Diffie\-Hellman agreement parameters
+.El
+.Sh "EXIT STATUS"
+One of the following exit values will be returned:
+.Bl -tag
+.It 0 " (EXIT_SUCCESS)"
+Successful program execution.
+.It 1 " (EXIT_FAILURE)"
+The operation failed or the command syntax was not valid.
+.It 70 " (EX_SOFTWARE)"
+libopts had an internal operational error. Please report
+it to autogen\-users@lists.sourceforge.net. Thank you.
+.El
+.Sh "SEE ALSO"
+.Xr ntpd 1ntpdmdoc ,
+.Xr ntpdc 1ntpdcmdoc ,
+.Xr ntpq 1ntpqmdoc
+.Pp
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+.Li http://www.ntp.org/ .
+A snapshot of this documentation is available in HTML format in
+.Pa /usr/share/doc/ntp .
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 4)
+.%O RFC5905
+.Re
+.Sh "AUTHORS"
+The University of Delaware
+.Sh "COPYRIGHT"
+Copyright (C) 1970\-2014 The University of Delaware all rights reserved.
+This program is released under the terms of the NTP license, <http://ntp.org/license>.
+.Sh BUGS
+The syntax checking is not picky; some combinations of
+ridiculous and even hilarious options and modes may not be
+detected.
+.Pp
+The
+.Pa ntpkey_ Ns Ar host
+files are really digital
+certificates.
+These should be obtained via secure directory
+services when they become universally available.
+.Pp
+Please send bug reports to: http://bugs.ntp.org, bugs@ntp.org
+.Sh NOTES
+This document was derived from FreeBSD.
+.Pp
+This manual page was \fIAutoGen\fP\-erated from the \fBntp.conf\fP
+option definitions.
diff --git a/ntpd/ntp.conf.def b/ntpd/ntp.conf.def
new file mode 100644
index 0000000..5a5af21
--- /dev/null
+++ b/ntpd/ntp.conf.def
@@ -0,0 +1,2795 @@
+/* -*- Mode: Text -*- */
+
+autogen definitions options;
+
+#include copyright.def
+
+// We want the synopsis to be "/etc/ntp.conf" but we need the prog-name
+// to be ntp.conf - the latter is also how autogen produces the output
+// file name.
+prog-name = "ntp.conf";
+file-path = "/etc/ntp.conf";
+prog-title = "Network Time Protocol (NTP) daemon configuration file format";
+
+/* explain: Additional information whenever the usage routine is invoked */
+explain = <<- _END_EXPLAIN
+ _END_EXPLAIN;
+
+doc-section = {
+ ds-type = 'DESCRIPTION';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_PROG_MDOC_DESCRIP
+The
+.Nm
+configuration file is read at initial startup by the
+.Xr ntpd 1ntpdmdoc
+daemon in order to specify the synchronization sources,
+modes and other related information.
+Usually, it is installed in the
+.Pa /etc
+directory,
+but could be installed elsewhere
+(see the daemon's
+.Fl c
+command line option).
+.Pp
+The file format is similar to other
+.Ux
+configuration files.
+Comments begin with a
+.Ql #
+character and extend to the end of the line;
+blank lines are ignored.
+Configuration commands consist of an initial keyword
+followed by a list of arguments,
+some of which may be optional, separated by whitespace.
+Commands may not be continued over multiple lines.
+Arguments may be host names,
+host addresses written in numeric, dotted-quad form,
+integers, floating point numbers (when specifying times in seconds)
+and text strings.
+.Pp
+The rest of this page describes the configuration and control options.
+The
+.Qq Notes on Configuring NTP and Setting up an NTP Subnet
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+contains an extended discussion of these options.
+In addition to the discussion of general
+.Sx Configuration Options ,
+there are sections describing the following supported functionality
+and the options used to control it:
+.Bl -bullet -offset indent
+.It
+.Sx Authentication Support
+.It
+.Sx Monitoring Support
+.It
+.Sx Access Control Support
+.It
+.Sx Automatic NTP Configuration Options
+.It
+.Sx Reference Clock Support
+.It
+.Sx Miscellaneous Options
+.El
+.Pp
+Following these is a section describing
+.Sx Miscellaneous Options .
+While there is a rich set of options available,
+the only required option is one or more
+.Ic pool ,
+.Ic server ,
+.Ic peer ,
+.Ic broadcast
+or
+.Ic manycastclient
+commands.
+.Sh Configuration Support
+Following is a description of the configuration commands in
+NTPv4.
+These commands have the same basic functions as in NTPv3 and
+in some cases new functions and new arguments.
+There are two
+classes of commands, configuration commands that configure a
+persistent association with a remote server or peer or reference
+clock, and auxiliary commands that specify environmental variables
+that control various related operations.
+.Ss Configuration Commands
+The various modes are determined by the command keyword and the
+type of the required IP address.
+Addresses are classed by type as
+(s) a remote server or peer (IPv4 class A, B and C), (b) the
+broadcast address of a local interface, (m) a multicast address (IPv4
+class D), or (r) a reference clock address (127.127.x.x).
+Note that
+only those options applicable to each command are listed below.
+Use
+of options not listed may not be caught as an error, but may result
+in some weird and even destructive behavior.
+.Pp
+If the Basic Socket Interface Extensions for IPv6 (RFC-2553)
+is detected, support for the IPv6 address family is generated
+in addition to the default support of the IPv4 address family.
+In a few cases, including the reslist billboard generated
+by ntpdc, IPv6 addresses are automatically generated.
+IPv6 addresses can be identified by the presence of colons
+.Dq \&:
+in the address field.
+IPv6 addresses can be used almost everywhere where
+IPv4 addresses can be used,
+with the exception of reference clock addresses,
+which are always IPv4.
+.Pp
+Note that in contexts where a host name is expected, a
+.Fl 4
+qualifier preceding
+the host name forces DNS resolution to the IPv4 namespace,
+while a
+.Fl 6
+qualifier forces DNS resolution to the IPv6 namespace.
+See IPv6 references for the
+equivalent classes for that address family.
+.Bl -tag -width indent
+.It Xo Ic pool Ar address
+.Op Cm burst
+.Op Cm iburst
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Xc
+.It Xo Ic server Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm burst
+.Op Cm iburst
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Xc
+.It Xo Ic peer Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Xc
+.It Xo Ic broadcast Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm ttl Ar ttl
+.Xc
+.It Xo Ic manycastclient Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Op Cm ttl Ar ttl
+.Xc
+.El
+.Pp
+These five commands specify the time server name or address to
+be used and the mode in which to operate.
+The
+.Ar address
+can be
+either a DNS name or an IP address in dotted-quad notation.
+Additional information on association behavior can be found in the
+.Qq Association Management
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.Bl -tag -width indent
+.It Ic pool
+For type s addresses, this command mobilizes a persistent
+client mode association with a number of remote servers.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+.It Ic server
+For type s and r addresses, this command mobilizes a persistent
+client mode association with the specified remote server or local
+radio clock.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+This command should
+.Em not
+be used for type
+b or m addresses.
+.It Ic peer
+For type s addresses (only), this command mobilizes a
+persistent symmetric-active mode association with the specified
+remote peer.
+In this mode the local clock can be synchronized to
+the remote peer or the remote peer can be synchronized to the local
+clock.
+This is useful in a network of servers where, depending on
+various failure scenarios, either the local or remote peer may be
+the better source of time.
+This command should NOT be used for type
+b, m or r addresses.
+.It Ic broadcast
+For type b and m addresses (only), this
+command mobilizes a persistent broadcast mode association.
+Multiple
+commands can be used to specify multiple local broadcast interfaces
+(subnets) and/or multiple multicast groups.
+Note that local
+broadcast messages go only to the interface associated with the
+subnet specified, but multicast messages go to all interfaces.
+In broadcast mode the local server sends periodic broadcast
+messages to a client population at the
+.Ar address
+specified, which is usually the broadcast address on (one of) the
+local network(s) or a multicast address assigned to NTP.
+The IANA
+has assigned the multicast group address IPv4 224.0.1.1 and
+IPv6 ff05::101 (site local) exclusively to
+NTP, but other nonconflicting addresses can be used to contain the
+messages within administrative boundaries.
+Ordinarily, this
+specification applies only to the local server operating as a
+sender; for operation as a broadcast client, see the
+.Ic broadcastclient
+or
+.Ic multicastclient
+commands
+below.
+.It Ic manycastclient
+For type m addresses (only), this command mobilizes a
+manycast client mode association for the multicast address
+specified.
+In this case a specific address must be supplied which
+matches the address used on the
+.Ic manycastserver
+command for
+the designated manycast servers.
+The NTP multicast address
+224.0.1.1 assigned by the IANA should NOT be used, unless specific
+means are taken to avoid spraying large areas of the Internet with
+these messages and causing a possibly massive implosion of replies
+at the sender.
+The
+.Ic manycastserver
+command specifies that the local server
+is to operate in client mode with the remote servers that are
+discovered as the result of broadcast/multicast messages.
+The
+client broadcasts a request message to the group address associated
+with the specified
+.Ar address
+and specifically enabled
+servers respond to these messages.
+The client selects the servers
+providing the best time and continues as with the
+.Ic server
+command.
+The remaining servers are discarded as if never
+heard.
+.El
+.Pp
+Options:
+.Bl -tag -width indent
+.It Cm autokey
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the autokey scheme
+described in
+.Sx Authentication Options .
+.It Cm burst
+when the server is reachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first and second packets
+can be changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to improve timekeeping quality
+with the
+.Ic server
+command and s addresses.
+.It Cm iburst
+When the server is unreachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first two packets can be
+changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to speed the initial synchronization
+acquisition with the
+.Ic server
+command and s addresses and when
+.Xr ntpd 1ntpdmdoc
+is started with the
+.Fl q
+option.
+.It Cm key Ar key
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the specified
+.Ar key
+identifier with values from 1 to 65534, inclusive.
+The
+default is to include no encryption field.
+.It Cm minpoll Ar minpoll
+.It Cm maxpoll Ar maxpoll
+These options specify the minimum and maximum poll intervals
+for NTP messages, as a power of 2 in seconds
+The maximum poll
+interval defaults to 10 (1,024 s), but can be increased by the
+.Cm maxpoll
+option to an upper limit of 17 (36.4 h).
+The
+minimum poll interval defaults to 6 (64 s), but can be decreased by
+the
+.Cm minpoll
+option to a lower limit of 4 (16 s).
+.It Cm noselect
+Marks the server as unused, except for display purposes.
+The server is discarded by the selection algroithm.
+.It Cm prefer
+Marks the server as preferred.
+All other things being equal,
+this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+.Qq Mitigation Rules and the prefer Keyword
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+for further information.
+.It Cm ttl Ar ttl
+This option is used only with broadcast server and manycast
+client modes.
+It specifies the time-to-live
+.Ar ttl
+to
+use on broadcast server and multicast server and the maximum
+.Ar ttl
+for the expanding ring search with manycast
+client packets.
+Selection of the proper value, which defaults to
+127, is something of a black art and should be coordinated with the
+network administrator.
+.It Cm version Ar version
+Specifies the version number to be used for outgoing NTP
+packets.
+Versions 1-4 are the choices, with version 4 the
+default.
+.El
+.Ss Auxiliary Commands
+.Bl -tag -width indent
+.It Ic broadcastclient
+This command enables reception of broadcast server messages to
+any local interface (type b) address.
+Upon receiving a message for
+the first time, the broadcast client measures the nominal server
+propagation delay using a brief client/server exchange with the
+server, then enters the broadcast client mode, in which it
+synchronizes to succeeding broadcast messages.
+Note that, in order
+to avoid accidental or malicious disruption in this mode, both the
+server and client should operate using symmetric-key or public-key
+authentication as described in
+.Sx Authentication Options .
+.It Ic manycastserver Ar address ...
+This command enables reception of manycast client messages to
+the multicast group address(es) (type m) specified.
+At least one
+address is required, but the NTP multicast address 224.0.1.1
+assigned by the IANA should NOT be used, unless specific means are
+taken to limit the span of the reply and avoid a possibly massive
+implosion at the original sender.
+Note that, in order to avoid
+accidental or malicious disruption in this mode, both the server
+and client should operate using symmetric-key or public-key
+authentication as described in
+.Sx Authentication Options .
+.It Ic multicastclient Ar address ...
+This command enables reception of multicast server messages to
+the multicast group address(es) (type m) specified.
+Upon receiving
+a message for the first time, the multicast client measures the
+nominal server propagation delay using a brief client/server
+exchange with the server, then enters the broadcast client mode, in
+which it synchronizes to succeeding multicast messages.
+Note that,
+in order to avoid accidental or malicious disruption in this mode,
+both the server and client should operate using symmetric-key or
+public-key authentication as described in
+.Sx Authentication Options .
+.El
+.Sh Authentication Support
+Authentication support allows the NTP client to verify that the
+server is in fact known and trusted and not an intruder intending
+accidentally or on purpose to masquerade as that server.
+The NTPv3
+specification RFC-1305 defines a scheme which provides
+cryptographic authentication of received NTP packets.
+Originally,
+this was done using the Data Encryption Standard (DES) algorithm
+operating in Cipher Block Chaining (CBC) mode, commonly called
+DES-CBC.
+Subsequently, this was replaced by the RSA Message Digest
+5 (MD5) algorithm using a private key, commonly called keyed-MD5.
+Either algorithm computes a message digest, or one-way hash, which
+can be used to verify the server has the correct private key and
+key identifier.
+.Pp
+NTPv4 retains the NTPv3 scheme, properly described as symmetric key
+cryptography and, in addition, provides a new Autokey scheme
+based on public key cryptography.
+Public key cryptography is generally considered more secure
+than symmetric key cryptography, since the security is based
+on a private value which is generated by each server and
+never revealed.
+With Autokey all key distribution and
+management functions involve only public values, which
+considerably simplifies key distribution and storage.
+Public key management is based on X.509 certificates,
+which can be provided by commercial services or
+produced by utility programs in the OpenSSL software library
+or the NTPv4 distribution.
+.Pp
+While the algorithms for symmetric key cryptography are
+included in the NTPv4 distribution, public key cryptography
+requires the OpenSSL software library to be installed
+before building the NTP distribution.
+Directions for doing that
+are on the Building and Installing the Distribution page.
+.Pp
+Authentication is configured separately for each association
+using the
+.Cm key
+or
+.Cm autokey
+subcommand on the
+.Ic peer ,
+.Ic server ,
+.Ic broadcast
+and
+.Ic manycastclient
+configuration commands as described in
+.Sx Configuration Options
+page.
+The authentication
+options described below specify the locations of the key files,
+if other than default, which symmetric keys are trusted
+and the interval between various operations, if other than default.
+.Pp
+Authentication is always enabled,
+although ineffective if not configured as
+described below.
+If a NTP packet arrives
+including a message authentication
+code (MAC), it is accepted only if it
+passes all cryptographic checks.
+The
+checks require correct key ID, key value
+and message digest.
+If the packet has
+been modified in any way or replayed
+by an intruder, it will fail one or more
+of these checks and be discarded.
+Furthermore, the Autokey scheme requires a
+preliminary protocol exchange to obtain
+the server certificate, verify its
+credentials and initialize the protocol
+.Pp
+The
+.Cm auth
+flag controls whether new associations or
+remote configuration commands require cryptographic authentication.
+This flag can be set or reset by the
+.Ic enable
+and
+.Ic disable
+commands and also by remote
+configuration commands sent by a
+.Xr ntpdc 1ntpdcmdoc
+program running in
+another machine.
+If this flag is enabled, which is the default
+case, new broadcast client and symmetric passive associations and
+remote configuration commands must be cryptographically
+authenticated using either symmetric key or public key cryptography.
+If this
+flag is disabled, these operations are effective
+even if not cryptographic
+authenticated.
+It should be understood
+that operating with the
+.Ic auth
+flag disabled invites a significant vulnerability
+where a rogue hacker can
+masquerade as a falseticker and seriously
+disrupt system timekeeping.
+It is
+important to note that this flag has no purpose
+other than to allow or disallow
+a new association in response to new broadcast
+and symmetric active messages
+and remote configuration commands and, in particular,
+the flag has no effect on
+the authentication process itself.
+.Pp
+An attractive alternative where multicast support is available
+is manycast mode, in which clients periodically troll
+for servers as described in the
+.Sx Automatic NTP Configuration Options
+page.
+Either symmetric key or public key
+cryptographic authentication can be used in this mode.
+The principle advantage
+of manycast mode is that potential servers need not be
+configured in advance,
+since the client finds them during regular operation,
+and the configuration
+files for all clients can be identical.
+.Pp
+The security model and protocol schemes for
+both symmetric key and public key
+cryptography are summarized below;
+further details are in the briefings, papers
+and reports at the NTP project page linked from
+.Li http://www.ntp.org/ .
+.Ss Symmetric-Key Cryptography
+The original RFC-1305 specification allows any one of possibly
+65,534 keys, each distinguished by a 32-bit key identifier, to
+authenticate an association.
+The servers and clients involved must
+agree on the key and key identifier to
+authenticate NTP packets.
+Keys and
+related information are specified in a key
+file, usually called
+.Pa ntp.keys ,
+which must be distributed and stored using
+secure means beyond the scope of the NTP protocol itself.
+Besides the keys used
+for ordinary NTP associations,
+additional keys can be used as passwords for the
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+utility programs.
+.Pp
+When
+.Xr ntpd 1ntpdmdoc
+is first started, it reads the key file specified in the
+.Ic keys
+configuration command and installs the keys
+in the key cache.
+However,
+individual keys must be activated with the
+.Ic trusted
+command before use.
+This
+allows, for instance, the installation of possibly
+several batches of keys and
+then activating or deactivating each batch
+remotely using
+.Xr ntpdc 1ntpdcmdoc .
+This also provides a revocation capability that can be used
+if a key becomes compromised.
+The
+.Ic requestkey
+command selects the key used as the password for the
+.Xr ntpdc 1ntpdcmdoc
+utility, while the
+.Ic controlkey
+command selects the key used as the password for the
+.Xr ntpq 1ntpqmdoc
+utility.
+.Ss Public Key Cryptography
+NTPv4 supports the original NTPv3 symmetric key scheme
+described in RFC-1305 and in addition the Autokey protocol,
+which is based on public key cryptography.
+The Autokey Version 2 protocol described on the Autokey Protocol
+page verifies packet integrity using MD5 message digests
+and verifies the source with digital signatures and any of several
+digest/signature schemes.
+Optional identity schemes described on the Identity Schemes
+page and based on cryptographic challenge/response algorithms
+are also available.
+Using all of these schemes provides strong security against
+replay with or without modification, spoofing, masquerade
+and most forms of clogging attacks.
+.\" .Pp
+.\" The cryptographic means necessary for all Autokey operations
+.\" is provided by the OpenSSL software library.
+.\" This library is available from http://www.openssl.org/
+.\" and can be installed using the procedures outlined
+.\" in the Building and Installing the Distribution page.
+.\" Once installed,
+.\" the configure and build
+.\" process automatically detects the library and links
+.\" the library routines required.
+.Pp
+The Autokey protocol has several modes of operation
+corresponding to the various NTP modes supported.
+Most modes use a special cookie which can be
+computed independently by the client and server,
+but encrypted in transmission.
+All modes use in addition a variant of the S-KEY scheme,
+in which a pseudo-random key list is generated and used
+in reverse order.
+These schemes are described along with an executive summary,
+current status, briefing slides and reading list on the
+.Sx Autonomous Authentication
+page.
+.Pp
+The specific cryptographic environment used by Autokey servers
+and clients is determined by a set of files
+and soft links generated by the
+.Xr ntp-keygen 1ntpkeygenmdoc
+program.
+This includes a required host key file,
+required certificate file and optional sign key file,
+leapsecond file and identity scheme files.
+The
+digest/signature scheme is specified in the X.509 certificate
+along with the matching sign key.
+There are several schemes
+available in the OpenSSL software library, each identified
+by a specific string such as
+.Cm md5WithRSAEncryption ,
+which stands for the MD5 message digest with RSA
+encryption scheme.
+The current NTP distribution supports
+all the schemes in the OpenSSL library, including
+those based on RSA and DSA digital signatures.
+.Pp
+NTP secure groups can be used to define cryptographic compartments
+and security hierarchies.
+It is important that every host
+in the group be able to construct a certificate trail to one
+or more trusted hosts in the same group.
+Each group
+host runs the Autokey protocol to obtain the certificates
+for all hosts along the trail to one or more trusted hosts.
+This requires the configuration file in all hosts to be
+engineered so that, even under anticipated failure conditions,
+the NTP subnet will form such that every group host can find
+a trail to at least one trusted host.
+.Ss Naming and Addressing
+It is important to note that Autokey does not use DNS to
+resolve addresses, since DNS can't be completely trusted
+until the name servers have synchronized clocks.
+The cryptographic name used by Autokey to bind the host identity
+credentials and cryptographic values must be independent
+of interface, network and any other naming convention.
+The name appears in the host certificate in either or both
+the subject and issuer fields, so protection against
+DNS compromise is essential.
+.Pp
+By convention, the name of an Autokey host is the name returned
+by the Unix
+.Xr gethostname 2
+system call or equivalent in other systems.
+By the system design
+model, there are no provisions to allow alternate names or aliases.
+However, this is not to say that DNS aliases, different names
+for each interface, etc., are constrained in any way.
+.Pp
+It is also important to note that Autokey verifies authenticity
+using the host name, network address and public keys,
+all of which are bound together by the protocol specifically
+to deflect masquerade attacks.
+For this reason Autokey
+includes the source and destinatino IP addresses in message digest
+computations and so the same addresses must be available
+at both the server and client.
+For this reason operation
+with network address translation schemes is not possible.
+This reflects the intended robust security model where government
+and corporate NTP servers are operated outside firewall perimeters.
+.Ss Operation
+A specific combination of authentication scheme (none,
+symmetric key, public key) and identity scheme is called
+a cryptotype, although not all combinations are compatible.
+There may be management configurations where the clients,
+servers and peers may not all support the same cryptotypes.
+A secure NTPv4 subnet can be configured in many ways while
+keeping in mind the principles explained above and
+in this section.
+Note however that some cryptotype
+combinations may successfully interoperate with each other,
+but may not represent good security practice.
+.Pp
+The cryptotype of an association is determined at the time
+of mobilization, either at configuration time or some time
+later when a message of appropriate cryptotype arrives.
+When mobilized by a
+.Ic server
+or
+.Ic peer
+configuration command and no
+.Ic key
+or
+.Ic autokey
+subcommands are present, the association is not
+authenticated; if the
+.Ic key
+subcommand is present, the association is authenticated
+using the symmetric key ID specified; if the
+.Ic autokey
+subcommand is present, the association is authenticated
+using Autokey.
+.Pp
+When multiple identity schemes are supported in the Autokey
+protocol, the first message exchange determines which one is used.
+The client request message contains bits corresponding
+to which schemes it has available.
+The server response message
+contains bits corresponding to which schemes it has available.
+Both server and client match the received bits with their own
+and select a common scheme.
+.Pp
+Following the principle that time is a public value,
+a server responds to any client packet that matches
+its cryptotype capabilities.
+Thus, a server receiving
+an unauthenticated packet will respond with an unauthenticated
+packet, while the same server receiving a packet of a cryptotype
+it supports will respond with packets of that cryptotype.
+However, unconfigured broadcast or manycast client
+associations or symmetric passive associations will not be
+mobilized unless the server supports a cryptotype compatible
+with the first packet received.
+By default, unauthenticated associations will not be mobilized
+unless overridden in a decidedly dangerous way.
+.Pp
+Some examples may help to reduce confusion.
+Client Alice has no specific cryptotype selected.
+Server Bob has both a symmetric key file and minimal Autokey files.
+Alice's unauthenticated messages arrive at Bob, who replies with
+unauthenticated messages.
+Cathy has a copy of Bob's symmetric
+key file and has selected key ID 4 in messages to Bob.
+Bob verifies the message with his key ID 4.
+If it's the
+same key and the message is verified, Bob sends Cathy a reply
+authenticated with that key.
+If verification fails,
+Bob sends Cathy a thing called a crypto-NAK, which tells her
+something broke.
+She can see the evidence using the
+.Xr ntpq 1ntpqmdoc
+program.
+.Pp
+Denise has rolled her own host key and certificate.
+She also uses one of the identity schemes as Bob.
+She sends the first Autokey message to Bob and they
+both dance the protocol authentication and identity steps.
+If all comes out okay, Denise and Bob continue as described above.
+.Pp
+It should be clear from the above that Bob can support
+all the girls at the same time, as long as he has compatible
+authentication and identity credentials.
+Now, Bob can act just like the girls in his own choice of servers;
+he can run multiple configured associations with multiple different
+servers (or the same server, although that might not be useful).
+But, wise security policy might preclude some cryptotype
+combinations; for instance, running an identity scheme
+with one server and no authentication with another might not be wise.
+.Ss Key Management
+The cryptographic values used by the Autokey protocol are
+incorporated as a set of files generated by the
+.Xr ntp-keygen 1ntpkeygenmdoc
+utility program, including symmetric key, host key and
+public certificate files, as well as sign key, identity parameters
+and leapseconds files.
+Alternatively, host and sign keys and
+certificate files can be generated by the OpenSSL utilities
+and certificates can be imported from public certificate
+authorities.
+Note that symmetric keys are necessary for the
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+utility programs.
+The remaining files are necessary only for the
+Autokey protocol.
+.Pp
+Certificates imported from OpenSSL or public certificate
+authorities have certian limitations.
+The certificate should be in ASN.1 syntax, X.509 Version 3
+format and encoded in PEM, which is the same format
+used by OpenSSL.
+The overall length of the certificate encoded
+in ASN.1 must not exceed 1024 bytes.
+The subject distinguished
+name field (CN) is the fully qualified name of the host
+on which it is used; the remaining subject fields are ignored.
+The certificate extension fields must not contain either
+a subject key identifier or a issuer key identifier field;
+however, an extended key usage field for a trusted host must
+contain the value
+.Cm trustRoot ; .
+Other extension fields are ignored.
+.Ss Authentication Commands
+.Bl -tag -width indent
+.It Ic autokey Op Ar logsec
+Specifies the interval between regenerations of the session key
+list used with the Autokey protocol.
+Note that the size of the key
+list for each association depends on this interval and the current
+poll interval.
+The default value is 12 (4096 s or about 1.1 hours).
+For poll intervals above the specified interval, a session key list
+with a single entry will be regenerated for every message
+sent.
+.It Ic controlkey Ar key
+Specifies the key identifier to use with the
+.Xr ntpq 1ntpqmdoc
+utility, which uses the standard
+protocol defined in RFC-1305.
+The
+.Ar key
+argument is
+the key identifier for a trusted key, where the value can be in the
+range 1 to 65,534, inclusive.
+.It Xo Ic crypto
+.Op Cm cert Ar file
+.Op Cm leap Ar file
+.Op Cm randfile Ar file
+.Op Cm host Ar file
+.Op Cm sign Ar file
+.Op Cm gq Ar file
+.Op Cm gqpar Ar file
+.Op Cm iffpar Ar file
+.Op Cm mvpar Ar file
+.Op Cm pw Ar password
+.Xc
+This command requires the OpenSSL library.
+It activates public key
+cryptography, selects the message digest and signature
+encryption scheme and loads the required private and public
+values described above.
+If one or more files are left unspecified,
+the default names are used as described above.
+Unless the complete path and name of the file are specified, the
+location of a file is relative to the keys directory specified
+in the
+.Ic keysdir
+command or default
+.Pa /usr/local/etc .
+Following are the subcommands:
+.Bl -tag -width indent
+.It Cm cert Ar file
+Specifies the location of the required host public certificate file.
+This overrides the link
+.Pa ntpkey_cert_ Ns Ar hostname
+in the keys directory.
+.It Cm gqpar Ar file
+Specifies the location of the optional GQ parameters file.
+This
+overrides the link
+.Pa ntpkey_gq_ Ns Ar hostname
+in the keys directory.
+.It Cm host Ar file
+Specifies the location of the required host key file.
+This overrides
+the link
+.Pa ntpkey_key_ Ns Ar hostname
+in the keys directory.
+.It Cm iffpar Ar file
+Specifies the location of the optional IFF parameters file.This
+overrides the link
+.Pa ntpkey_iff_ Ns Ar hostname
+in the keys directory.
+.It Cm leap Ar file
+Specifies the location of the optional leapsecond file.
+This overrides the link
+.Pa ntpkey_leap
+in the keys directory.
+.It Cm mvpar Ar file
+Specifies the location of the optional MV parameters file.
+This
+overrides the link
+.Pa ntpkey_mv_ Ns Ar hostname
+in the keys directory.
+.It Cm pw Ar password
+Specifies the password to decrypt files containing private keys and
+identity parameters.
+This is required only if these files have been
+encrypted.
+.It Cm randfile Ar file
+Specifies the location of the random seed file used by the OpenSSL
+library.
+The defaults are described in the main text above.
+.It Cm sign Ar file
+Specifies the location of the optional sign key file.
+This overrides
+the link
+.Pa ntpkey_sign_ Ns Ar hostname
+in the keys directory.
+If this file is
+not found, the host key is also the sign key.
+.El
+.It Ic keys Ar keyfile
+Specifies the complete path and location of the MD5 key file
+containing the keys and key identifiers used by
+.Xr ntpd 1ntpdmdoc ,
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+when operating with symmetric key cryptography.
+This is the same operation as the
+.Fl k
+command line option.
+.It Ic keysdir Ar path
+This command specifies the default directory path for
+cryptographic keys, parameters and certificates.
+The default is
+.Pa /usr/local/etc/ .
+.It Ic requestkey Ar key
+Specifies the key identifier to use with the
+.Xr ntpdc 1ntpdcmdoc
+utility program, which uses a
+proprietary protocol specific to this implementation of
+.Xr ntpd 1ntpdmdoc .
+The
+.Ar key
+argument is a key identifier
+for the trusted key, where the value can be in the range 1 to
+65,534, inclusive.
+.It Ic revoke Ar logsec
+Specifies the interval between re-randomization of certain
+cryptographic values used by the Autokey scheme, as a power of 2 in
+seconds.
+These values need to be updated frequently in order to
+deflect brute-force attacks on the algorithms of the scheme;
+however, updating some values is a relatively expensive operation.
+The default interval is 16 (65,536 s or about 18 hours).
+For poll
+intervals above the specified interval, the values will be updated
+for every message sent.
+.It Ic trustedkey Ar key ...
+Specifies the key identifiers which are trusted for the
+purposes of authenticating peers with symmetric key cryptography,
+as well as keys used by the
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+programs.
+The authentication procedures require that both the local
+and remote servers share the same key and key identifier for this
+purpose, although different keys can be used with different
+servers.
+The
+.Ar key
+arguments are 32-bit unsigned
+integers with values from 1 to 65,534.
+.El
+.Ss Error Codes
+The following error codes are reported via the NTP control
+and monitoring protocol trap mechanism.
+.Bl -tag -width indent
+.It 101
+.Pq bad field format or length
+The packet has invalid version, length or format.
+.It 102
+.Pq bad timestamp
+The packet timestamp is the same or older than the most recent received.
+This could be due to a replay or a server clock time step.
+.It 103
+.Pq bad filestamp
+The packet filestamp is the same or older than the most recent received.
+This could be due to a replay or a key file generation error.
+.It 104
+.Pq bad or missing public key
+The public key is missing, has incorrect format or is an unsupported type.
+.It 105
+.Pq unsupported digest type
+The server requires an unsupported digest/signature scheme.
+.It 106
+.Pq mismatched digest types
+Not used.
+.It 107
+.Pq bad signature length
+The signature length does not match the current public key.
+.It 108
+.Pq signature not verified
+The message fails the signature check.
+It could be bogus or signed by a
+different private key.
+.It 109
+.Pq certificate not verified
+The certificate is invalid or signed with the wrong key.
+.It 110
+.Pq certificate not verified
+The certificate is not yet valid or has expired or the signature could not
+be verified.
+.It 111
+.Pq bad or missing cookie
+The cookie is missing, corrupted or bogus.
+.It 112
+.Pq bad or missing leapseconds table
+The leapseconds table is missing, corrupted or bogus.
+.It 113
+.Pq bad or missing certificate
+The certificate is missing, corrupted or bogus.
+.It 114
+.Pq bad or missing identity
+The identity key is missing, corrupt or bogus.
+.El
+.Sh Monitoring Support
+.Xr ntpd 1ntpdmdoc
+includes a comprehensive monitoring facility suitable
+for continuous, long term recording of server and client
+timekeeping performance.
+See the
+.Ic statistics
+command below
+for a listing and example of each type of statistics currently
+supported.
+Statistic files are managed using file generation sets
+and scripts in the
+.Pa ./scripts
+directory of this distribution.
+Using
+these facilities and
+.Ux
+.Xr cron 8
+jobs, the data can be
+automatically summarized and archived for retrospective analysis.
+.Ss Monitoring Commands
+.Bl -tag -width indent
+.It Ic statistics Ar name ...
+Enables writing of statistics records.
+Currently, eight kinds of
+.Ar name
+statistics are supported.
+.Bl -tag -width indent
+.It Cm clockstats
+Enables recording of clock driver statistics information.
+Each update
+received from a clock driver appends a line of the following form to
+the file generation set named
+.Cm clockstats :
+.Bd -literal
+49213 525.624 127.127.4.1 93 226 00:08:29.606 D
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the
+clock address in dotted-quad notation.
+The final field shows the last
+timecode received from the clock in decoded ASCII format, where
+meaningful.
+In some clock drivers a good deal of additional information
+can be gathered and displayed as well.
+See information specific to each
+clock for further details.
+.It Cm cryptostats
+This option requires the OpenSSL cryptographic software library.
+It
+enables recording of cryptographic public key protocol information.
+Each message received by the protocol module appends a line of the
+following form to the file generation set named
+.Cm cryptostats :
+.Bd -literal
+49213 525.624 127.127.4.1 message
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the peer
+address in dotted-quad notation, The final message field includes the
+message type and certain ancillary information.
+See the
+.Sx Authentication Options
+section for further information.
+.It Cm loopstats
+Enables recording of loop filter statistics information.
+Each
+update of the local clock outputs a line of the following form to
+the file generation set named
+.Cm loopstats :
+.Bd -literal
+50935 75440.031 0.000006019 13.778190 0.000351733 0.0133806
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next five fields
+show time offset (seconds), frequency offset (parts per million -
+PPM), RMS jitter (seconds), Allan deviation (PPM) and clock
+discipline time constant.
+.It Cm peerstats
+Enables recording of peer statistics information.
+This includes
+statistics records of all peers of a NTP server and of special
+signals, where present and configured.
+Each valid update appends a
+line of the following form to the current element of a file
+generation set named
+.Cm peerstats :
+.Bd -literal
+48773 10847.650 127.127.4.1 9714 -0.001605376 0.000000000 0.001424877 0.000958674
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the peer address in dotted-quad notation and status,
+respectively.
+The status field is encoded in hex in the format
+described in Appendix A of the NTP specification RFC 1305.
+The final four fields show the offset,
+delay, dispersion and RMS jitter, all in seconds.
+.It Cm rawstats
+Enables recording of raw-timestamp statistics information.
+This
+includes statistics records of all peers of a NTP server and of
+special signals, where present and configured.
+Each NTP message
+received from a peer or clock driver appends a line of the
+following form to the file generation set named
+.Cm rawstats :
+.Bd -literal
+50928 2132.543 128.4.1.1 128.4.1.20 3102453281.584327000 3102453281.58622800031 02453332.540806000 3102453332.541458000
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the remote peer or clock address followed by the local address
+in dotted-quad notation.
+The final four fields show the originate,
+receive, transmit and final NTP timestamps in order.
+The timestamp
+values are as received and before processing by the various data
+smoothing and mitigation algorithms.
+.It Cm sysstats
+Enables recording of ntpd statistics counters on a periodic basis.
+Each
+hour a line of the following form is appended to the file generation
+set named
+.Cm sysstats :
+.Bd -literal
+50928 2132.543 36000 81965 0 9546 56 71793 512 540 10 147
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The remaining ten fields show
+the statistics counter values accumulated since the last generated
+line.
+.Bl -tag -width indent
+.It Time since restart Cm 36000
+Time in hours since the system was last rebooted.
+.It Packets received Cm 81965
+Total number of packets received.
+.It Packets processed Cm 0
+Number of packets received in response to previous packets sent
+.It Current version Cm 9546
+Number of packets matching the current NTP version.
+.It Previous version Cm 56
+Number of packets matching the previous NTP version.
+.It Bad version Cm 71793
+Number of packets matching neither NTP version.
+.It Access denied Cm 512
+Number of packets denied access for any reason.
+.It Bad length or format Cm 540
+Number of packets with invalid length, format or port number.
+.It Bad authentication Cm 10
+Number of packets not verified as authentic.
+.It Rate exceeded Cm 147
+Number of packets discarded due to rate limitation.
+.El
+.It Cm statsdir Ar directory_path
+Indicates the full path of a directory where statistics files
+should be created (see below).
+This keyword allows
+the (otherwise constant)
+.Cm filegen
+filename prefix to be modified for file generation sets, which
+is useful for handling statistics logs.
+.It Cm filegen Ar name Xo
+.Op Cm file Ar filename
+.Op Cm type Ar typename
+.Op Cm link | nolink
+.Op Cm enable | disable
+.Xc
+Configures setting of generation file set name.
+Generation
+file sets provide a means for handling files that are
+continuously growing during the lifetime of a server.
+Server statistics are a typical example for such files.
+Generation file sets provide access to a set of files used
+to store the actual data.
+At any time at most one element
+of the set is being written to.
+The type given specifies
+when and how data will be directed to a new element of the set.
+This way, information stored in elements of a file set
+that are currently unused are available for administrational
+operations without the risk of disturbing the operation of ntpd.
+(Most important: they can be removed to free space for new data
+produced.)
+.Pp
+Note that this command can be sent from the
+.Xr ntpdc 1ntpdcmdoc
+program running at a remote location.
+.Bl -tag -width indent
+.It Cm name
+This is the type of the statistics records, as shown in the
+.Cm statistics
+command.
+.It Cm file Ar filename
+This is the file name for the statistics records.
+Filenames of set
+members are built from three concatenated elements
+.Ar Cm prefix ,
+.Ar Cm filename
+and
+.Ar Cm suffix :
+.Bl -tag -width indent
+.It Cm prefix
+This is a constant filename path.
+It is not subject to
+modifications via the
+.Ar filegen
+option.
+It is defined by the
+server, usually specified as a compile-time constant.
+It may,
+however, be configurable for individual file generation sets
+via other commands.
+For example, the prefix used with
+.Ar loopstats
+and
+.Ar peerstats
+generation can be configured using the
+.Ar statsdir
+option explained above.
+.It Cm filename
+This string is directly concatenated to the prefix mentioned
+above (no intervening
+.Ql / ) .
+This can be modified using
+the file argument to the
+.Ar filegen
+statement.
+No
+.Pa ..
+elements are
+allowed in this component to prevent filenames referring to
+parts outside the filesystem hierarchy denoted by
+.Ar prefix .
+.It Cm suffix
+This part is reflects individual elements of a file set.
+It is
+generated according to the type of a file set.
+.El
+.It Cm type Ar typename
+A file generation set is characterized by its type.
+The following
+types are supported:
+.Bl -tag -width indent
+.It Cm none
+The file set is actually a single plain file.
+.It Cm pid
+One element of file set is used per incarnation of a ntpd
+server.
+This type does not perform any changes to file set
+members during runtime, however it provides an easy way of
+separating files belonging to different
+.Xr ntpd 1ntpdmdoc
+server incarnations.
+The set member filename is built by appending a
+.Ql \&.
+to concatenated
+.Ar prefix
+and
+.Ar filename
+strings, and
+appending the decimal representation of the process ID of the
+.Xr ntpd 1ntpdmdoc
+server process.
+.It Cm day
+One file generation set element is created per day.
+A day is
+defined as the period between 00:00 and 24:00 UTC.
+The file set
+member suffix consists of a
+.Ql \&.
+and a day specification in
+the form
+.Cm YYYYMMdd .
+.Cm YYYY
+is a 4-digit year number (e.g., 1992).
+.Cm MM
+is a two digit month number.
+.Cm dd
+is a two digit day number.
+Thus, all information written at 10 December 1992 would end up
+in a file named
+.Ar prefix
+.Ar filename Ns .19921210 .
+.It Cm week
+Any file set member contains data related to a certain week of
+a year.
+The term week is defined by computing day-of-year
+modulo 7.
+Elements of such a file generation set are
+distinguished by appending the following suffix to the file set
+filename base: A dot, a 4-digit year number, the letter
+.Cm W ,
+and a 2-digit week number.
+For example, information from January,
+10th 1992 would end up in a file with suffix
+.No . Ns Ar 1992W1 .
+.It Cm month
+One generation file set element is generated per month.
+The
+file name suffix consists of a dot, a 4-digit year number, and
+a 2-digit month.
+.It Cm year
+One generation file element is generated per year.
+The filename
+suffix consists of a dot and a 4 digit year number.
+.It Cm age
+This type of file generation sets changes to a new element of
+the file set every 24 hours of server operation.
+The filename
+suffix consists of a dot, the letter
+.Cm a ,
+and an 8-digit number.
+This number is taken to be the number of seconds the server is
+running at the start of the corresponding 24-hour period.
+Information is only written to a file generation by specifying
+.Cm enable ;
+output is prevented by specifying
+.Cm disable .
+.El
+.It Cm link | nolink
+It is convenient to be able to access the current element of a file
+generation set by a fixed name.
+This feature is enabled by
+specifying
+.Cm link
+and disabled using
+.Cm nolink .
+If link is specified, a
+hard link from the current file set element to a file without
+suffix is created.
+When there is already a file with this name and
+the number of links of this file is one, it is renamed appending a
+dot, the letter
+.Cm C ,
+and the pid of the ntpd server process.
+When the
+number of links is greater than one, the file is unlinked.
+This
+allows the current file to be accessed by a constant name.
+.It Cm enable \&| Cm disable
+Enables or disables the recording function.
+.El
+.El
+.El
+.Sh Access Control Support
+The
+.Xr ntpd 1ntpdmdoc
+daemon implements a general purpose address/mask based restriction
+list.
+The list contains address/match entries sorted first
+by increasing address values and and then by increasing mask values.
+A match occurs when the bitwise AND of the mask and the packet
+source address is equal to the bitwise AND of the mask and
+address in the list.
+The list is searched in order with the
+last match found defining the restriction flags associated
+with the entry.
+Additional information and examples can be found in the
+.Qq Notes on Configuring NTP and Setting up a NTP Subnet
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.Pp
+The restriction facility was implemented in conformance
+with the access policies for the original NSFnet backbone
+time servers.
+Later the facility was expanded to deflect
+cryptographic and clogging attacks.
+While this facility may
+be useful for keeping unwanted or broken or malicious clients
+from congesting innocent servers, it should not be considered
+an alternative to the NTP authentication facilities.
+Source address based restrictions are easily circumvented
+by a determined cracker.
+.Pp
+Clients can be denied service because they are explicitly
+included in the restrict list created by the restrict command
+or implicitly as the result of cryptographic or rate limit
+violations.
+Cryptographic violations include certificate
+or identity verification failure; rate limit violations generally
+result from defective NTP implementations that send packets
+at abusive rates.
+Some violations cause denied service
+only for the offending packet, others cause denied service
+for a timed period and others cause the denied service for
+an indefinate period.
+When a client or network is denied access
+for an indefinate period, the only way at present to remove
+the restrictions is by restarting the server.
+.Ss The Kiss-of-Death Packet
+Ordinarily, packets denied service are simply dropped with no
+further action except incrementing statistics counters.
+Sometimes a
+more proactive response is needed, such as a server message that
+explicitly requests the client to stop sending and leave a message
+for the system operator.
+A special packet format has been created
+for this purpose called the "kiss-of-death" (KoD) packet.
+KoD packets have the leap bits set unsynchronized and stratum set
+to zero and the reference identifier field set to a four-byte
+ASCII code.
+If the
+.Cm noserve
+or
+.Cm notrust
+flag of the matching restrict list entry is set,
+the code is "DENY"; if the
+.Cm limited
+flag is set and the rate limit
+is exceeded, the code is "RATE".
+Finally, if a cryptographic violation occurs, the code is "CRYP".
+.Pp
+A client receiving a KoD performs a set of sanity checks to
+minimize security exposure, then updates the stratum and
+reference identifier peer variables, sets the access
+denied (TEST4) bit in the peer flash variable and sends
+a message to the log.
+As long as the TEST4 bit is set,
+the client will send no further packets to the server.
+The only way at present to recover from this condition is
+to restart the protocol at both the client and server.
+This
+happens automatically at the client when the association times out.
+It will happen at the server only if the server operator cooperates.
+.Ss Access Control Commands
+.Bl -tag -width indent
+.It Xo Ic discard
+.Op Cm average Ar avg
+.Op Cm minimum Ar min
+.Op Cm monitor Ar prob
+.Xc
+Set the parameters of the
+.Cm limited
+facility which protects the server from
+client abuse.
+The
+.Cm average
+subcommand specifies the minimum average packet
+spacing, while the
+.Cm minimum
+subcommand specifies the minimum packet spacing.
+Packets that violate these minima are discarded
+and a kiss-o'-death packet returned if enabled.
+The default
+minimum average and minimum are 5 and 2, respectively.
+The monitor subcommand specifies the probability of discard
+for packets that overflow the rate-control window.
+.It Xo Ic restrict address
+.Op Cm mask Ar mask
+.Op Ar flag ...
+.Xc
+The
+.Ar address
+argument expressed in
+dotted-quad form is the address of a host or network.
+Alternatively, the
+.Ar address
+argument can be a valid host DNS name.
+The
+.Ar mask
+argument expressed in dotted-quad form defaults to
+.Cm 255.255.255.255 ,
+meaning that the
+.Ar address
+is treated as the address of an individual host.
+A default entry (address
+.Cm 0.0.0.0 ,
+mask
+.Cm 0.0.0.0 )
+is always included and is always the first entry in the list.
+Note that text string
+.Cm default ,
+with no mask option, may
+be used to indicate the default entry.
+In the current implementation,
+.Cm flag
+always
+restricts access, i.e., an entry with no flags indicates that free
+access to the server is to be given.
+The flags are not orthogonal,
+in that more restrictive flags will often make less restrictive
+ones redundant.
+The flags can generally be classed into two
+categories, those which restrict time service and those which
+restrict informational queries and attempts to do run-time
+reconfiguration of the server.
+One or more of the following flags
+may be specified:
+.Bl -tag -width indent
+.It Cm ignore
+Deny packets of all kinds, including
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+queries.
+.It Cm kod
+If this flag is set when an access violation occurs, a kiss-o'-death
+(KoD) packet is sent.
+KoD packets are rate limited to no more than one
+per second.
+If another KoD packet occurs within one second after the
+last one, the packet is dropped.
+.It Cm limited
+Deny service if the packet spacing violates the lower limits specified
+in the discard command.
+A history of clients is kept using the
+monitoring capability of
+.Xr ntpd 1ntpdmdoc .
+Thus, monitoring is always active as
+long as there is a restriction entry with the
+.Cm limited
+flag.
+.It Cm lowpriotrap
+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.
+.It Cm nomodify
+Deny
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+queries which attempt to modify the state of the
+server (i.e., run time reconfiguration).
+Queries which return
+information are permitted.
+.It Cm noquery
+Deny
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+queries.
+Time service is not affected.
+.It Cm nopeer
+Deny packets which would result in mobilizing a new association.
+This
+includes broadcast and symmetric active packets when a configured
+association does not exist.
+It also includes
+.Cm pool
+associations, so if you want to use servers from a
+.Cm pool
+directive and also want to use
+.Cm nopeer
+by default, you'll want a
+.Cm "restrict source ..." line as well that does
+.It not
+include the
+.Cm nopeer
+directive.
+.It Cm noserve
+Deny all packets except
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+queries.
+.It Cm notrap
+Decline to provide mode 6 control message trap service to matching
+hosts.
+The trap service is a subsystem of the ntpdq control message
+protocol which is intended for use by remote event logging programs.
+.It Cm notrust
+Deny service unless the packet is cryptographically authenticated.
+.It Cm ntpport
+This is actually a match algorithm modifier, rather than a
+restriction flag.
+Its presence causes the restriction entry to be
+matched only if the source port in the packet is the standard NTP
+UDP port (123).
+Both
+.Cm ntpport
+and
+.Cm non-ntpport
+may
+be specified.
+The
+.Cm ntpport
+is considered more specific and
+is sorted later in the list.
+.It Cm version
+Deny packets that do not match the current NTP version.
+.El
+.Pp
+Default restriction list entries with the flags ignore, interface,
+ntpport, for each of the local host's interface addresses are
+inserted into the table at startup to prevent the server
+from attempting to synchronize to its own time.
+A default entry is also always present, though if it is
+otherwise unconfigured; no flags are associated
+with the default entry (i.e., everything besides your own
+NTP server is unrestricted).
+.El
+.Sh Automatic NTP Configuration Options
+.Ss Manycasting
+Manycasting is a automatic discovery and configuration paradigm
+new to NTPv4.
+It is intended as a means for a multicast client
+to troll the nearby network neighborhood to find cooperating
+manycast servers, validate them using cryptographic means
+and evaluate their time values with respect to other servers
+that might be lurking in the vicinity.
+The intended result is that each manycast client mobilizes
+client associations with some number of the "best"
+of the nearby manycast servers, yet automatically reconfigures
+to sustain this number of servers should one or another fail.
+.Pp
+Note that the manycasting paradigm does not coincide
+with the anycast paradigm described in RFC-1546,
+which is designed to find a single server from a clique
+of servers providing the same service.
+The manycast paradigm is designed to find a plurality
+of redundant servers satisfying defined optimality criteria.
+.Pp
+Manycasting can be used with either symmetric key
+or public key cryptography.
+The public key infrastructure (PKI)
+offers the best protection against compromised keys
+and is generally considered stronger, at least with relatively
+large key sizes.
+It is implemented using the Autokey protocol and
+the OpenSSL cryptographic library available from
+.Li http://www.openssl.org/ .
+The library can also be used with other NTPv4 modes
+as well and is highly recommended, especially for broadcast modes.
+.Pp
+A persistent manycast client association is configured
+using the manycastclient command, which is similar to the
+server command but with a multicast (IPv4 class
+.Cm D
+or IPv6 prefix
+.Cm FF )
+group address.
+The IANA has designated IPv4 address 224.1.1.1
+and IPv6 address FF05::101 (site local) for NTP.
+When more servers are needed, it broadcasts manycast
+client messages to this address at the minimum feasible rate
+and minimum feasible time-to-live (TTL) hops, depending
+on how many servers have already been found.
+There can be as many manycast client associations
+as different group address, each one serving as a template
+for a future ephemeral unicast client/server association.
+.Pp
+Manycast servers configured with the
+.Ic manycastserver
+command listen on the specified group address for manycast
+client messages.
+Note the distinction between manycast client,
+which actively broadcasts messages, and manycast server,
+which passively responds to them.
+If a manycast server is
+in scope of the current TTL and is itself synchronized
+to a valid source and operating at a stratum level equal
+to or lower than the manycast client, it replies to the
+manycast client message with an ordinary unicast server message.
+.Pp
+The manycast client receiving this message mobilizes
+an ephemeral client/server association according to the
+matching manycast client template, but only if cryptographically
+authenticated and the server stratum is less than or equal
+to the client stratum.
+Authentication is explicitly required
+and either symmetric key or public key (Autokey) can be used.
+Then, the client polls the server at its unicast address
+in burst mode in order to reliably set the host clock
+and validate the source.
+This normally results
+in a volley of eight client/server at 2-s intervals
+during which both the synchronization and cryptographic
+protocols run concurrently.
+Following the volley,
+the client runs the NTP intersection and clustering
+algorithms, which act to discard all but the "best"
+associations according to stratum and synchronization
+distance.
+The surviving associations then continue
+in ordinary client/server mode.
+.Pp
+The manycast client polling strategy is designed to reduce
+as much as possible the volume of manycast client messages
+and the effects of implosion due to near-simultaneous
+arrival of manycast server messages.
+The strategy is determined by the
+.Ic manycastclient ,
+.Ic tos
+and
+.Ic ttl
+configuration commands.
+The manycast poll interval is
+normally eight times the system poll interval,
+which starts out at the
+.Cm minpoll
+value specified in the
+.Ic manycastclient ,
+command and, under normal circumstances, increments to the
+.Cm maxpolll
+value specified in this command.
+Initially, the TTL is
+set at the minimum hops specified by the ttl command.
+At each retransmission the TTL is increased until reaching
+the maximum hops specified by this command or a sufficient
+number client associations have been found.
+Further retransmissions use the same TTL.
+.Pp
+The quality and reliability of the suite of associations
+discovered by the manycast client is determined by the NTP
+mitigation algorithms and the
+.Cm minclock
+and
+.Cm minsane
+values specified in the
+.Ic tos
+configuration command.
+At least
+.Cm minsane
+candidate servers must be available and the mitigation
+algorithms produce at least
+.Cm minclock
+survivors in order to synchronize the clock.
+Byzantine agreement principles require at least four
+candidates in order to correctly discard a single falseticker.
+For legacy purposes,
+.Cm minsane
+defaults to 1 and
+.Cm minclock
+defaults to 3.
+For manycast service
+.Cm minsane
+should be explicitly set to 4, assuming at least that
+number of servers are available.
+.Pp
+If at least
+.Cm minclock
+servers are found, the manycast poll interval is immediately
+set to eight times
+.Cm maxpoll .
+If less than
+.Cm minclock
+servers are found when the TTL has reached the maximum hops,
+the manycast poll interval is doubled.
+For each transmission
+after that, the poll interval is doubled again until
+reaching the maximum of eight times
+.Cm maxpoll .
+Further transmissions use the same poll interval and
+TTL values.
+Note that while all this is going on,
+each client/server association found is operating normally
+it the system poll interval.
+.Pp
+Administratively scoped multicast boundaries are normally
+specified by the network router configuration and,
+in the case of IPv6, the link/site scope prefix.
+By default, the increment for TTL hops is 32 starting
+from 31; however, the
+.Ic ttl
+configuration command can be
+used to modify the values to match the scope rules.
+.Pp
+It is often useful to narrow the range of acceptable
+servers which can be found by manycast client associations.
+Because manycast servers respond only when the client
+stratum is equal to or greater than the server stratum,
+primary (stratum 1) servers fill find only primary servers
+in TTL range, which is probably the most common objective.
+However, unless configured otherwise, all manycast clients
+in TTL range will eventually find all primary servers
+in TTL range, which is probably not the most common
+objective in large networks.
+The
+.Ic tos
+command can be used to modify this behavior.
+Servers with stratum below
+.Cm floor
+or above
+.Cm ceiling
+specified in the
+.Ic tos
+command are strongly discouraged during the selection
+process; however, these servers may be temporally
+accepted if the number of servers within TTL range is
+less than
+.Cm minclock .
+.Pp
+The above actions occur for each manycast client message,
+which repeats at the designated poll interval.
+However, once the ephemeral client association is mobilized,
+subsequent manycast server replies are discarded,
+since that would result in a duplicate association.
+If during a poll interval the number of client associations
+falls below
+.Cm minclock ,
+all manycast client prototype associations are reset
+to the initial poll interval and TTL hops and operation
+resumes from the beginning.
+It is important to avoid
+frequent manycast client messages, since each one requires
+all manycast servers in TTL range to respond.
+The result could well be an implosion, either minor or major,
+depending on the number of servers in range.
+The recommended value for
+.Cm maxpoll
+is 12 (4,096 s).
+.Pp
+It is possible and frequently useful to configure a host
+as both manycast client and manycast server.
+A number of hosts configured this way and sharing a common
+group address will automatically organize themselves
+in an optimum configuration based on stratum and
+synchronization distance.
+For example, consider an NTP
+subnet of two primary servers and a hundred or more
+dependent clients.
+With two exceptions, all servers
+and clients have identical configuration files including both
+.Ic multicastclient
+and
+.Ic multicastserver
+commands using, for instance, multicast group address
+239.1.1.1.
+The only exception is that each primary server
+configuration file must include commands for the primary
+reference source such as a GPS receiver.
+.Pp
+The remaining configuration files for all secondary
+servers and clients have the same contents, except for the
+.Ic tos
+command, which is specific for each stratum level.
+For stratum 1 and stratum 2 servers, that command is
+not necessary.
+For stratum 3 and above servers the
+.Cm floor
+value is set to the intended stratum number.
+Thus, all stratum 3 configuration files are identical,
+all stratum 4 files are identical and so forth.
+.Pp
+Once operations have stabilized in this scenario,
+the primary servers will find the primary reference source
+and each other, since they both operate at the same
+stratum (1), but not with any secondary server or client,
+since these operate at a higher stratum.
+The secondary
+servers will find the servers at the same stratum level.
+If one of the primary servers loses its GPS receiver,
+it will continue to operate as a client and other clients
+will time out the corresponding association and
+re-associate accordingly.
+.Pp
+Some administrators prefer to avoid running
+.Xr ntpd 1ntpdmdoc
+continuously and run either
+.Xr ntpdate 8
+or
+.Xr ntpd 1ntpdmdoc
+.Fl q
+as a cron job.
+In either case the servers must be
+configured in advance and the program fails if none are
+available when the cron job runs.
+A really slick
+application of manycast is with
+.Xr ntpd 1ntpdmdoc
+.Fl q .
+The program wakes up, scans the local landscape looking
+for the usual suspects, selects the best from among
+the rascals, sets the clock and then departs.
+Servers do not have to be configured in advance and
+all clients throughout the network can have the same
+configuration file.
+.Ss Manycast Interactions with Autokey
+Each time a manycast client sends a client mode packet
+to a multicast group address, all manycast servers
+in scope generate a reply including the host name
+and status word.
+The manycast clients then run
+the Autokey protocol, which collects and verifies
+all certificates involved.
+Following the burst interval
+all but three survivors are cast off,
+but the certificates remain in the local cache.
+It often happens that several complete signing trails
+from the client to the primary servers are collected in this way.
+.Pp
+About once an hour or less often if the poll interval
+exceeds this, the client regenerates the Autokey key list.
+This is in general transparent in client/server mode.
+However, about once per day the server private value
+used to generate cookies is refreshed along with all
+manycast client associations.
+In this case all
+cryptographic values including certificates is refreshed.
+If a new certificate has been generated since
+the last refresh epoch, it will automatically revoke
+all prior certificates that happen to be in the
+certificate cache.
+At the same time, the manycast
+scheme starts all over from the beginning and
+the expanding ring shrinks to the minimum and increments
+from there while collecting all servers in scope.
+.Ss Manycast Options
+.Bl -tag -width indent
+.It Xo Ic tos
+.Oo
+.Cm ceiling Ar ceiling |
+.Cm cohort { 0 | 1 } |
+.Cm floor Ar floor |
+.Cm minclock Ar minclock |
+.Cm minsane Ar minsane
+.Oc
+.Xc
+This command affects the clock selection and clustering
+algorithms.
+It can be used to select the quality and
+quantity of peers used to synchronize the system clock
+and is most useful in manycast mode.
+The variables operate
+as follows:
+.Bl -tag -width indent
+.It Cm ceiling Ar ceiling
+Peers with strata above
+.Cm ceiling
+will be discarded if there are at least
+.Cm minclock
+peers remaining.
+This value defaults to 15, but can be changed
+to any number from 1 to 15.
+.It Cm cohort Bro 0 | 1 Brc
+This is a binary flag which enables (0) or disables (1)
+manycast server replies to manycast clients with the same
+stratum level.
+This is useful to reduce implosions where
+large numbers of clients with the same stratum level
+are present.
+The default is to enable these replies.
+.It Cm floor Ar floor
+Peers with strata below
+.Cm floor
+will be discarded if there are at least
+.Cm minclock
+peers remaining.
+This value defaults to 1, but can be changed
+to any number from 1 to 15.
+.It Cm minclock Ar minclock
+The clustering algorithm repeatedly casts out outlyer
+associations until no more than
+.Cm minclock
+associations remain.
+This value defaults to 3,
+but can be changed to any number from 1 to the number of
+configured sources.
+.It Cm minsane Ar minsane
+This is the minimum number of candidates available
+to the clock selection algorithm in order to produce
+one or more truechimers for the clustering algorithm.
+If fewer than this number are available, the clock is
+undisciplined and allowed to run free.
+The default is 1
+for legacy purposes.
+However, according to principles of
+Byzantine agreement,
+.Cm minsane
+should be at least 4 in order to detect and discard
+a single falseticker.
+.El
+.It Cm ttl Ar hop ...
+This command specifies a list of TTL values in increasing
+order, up to 8 values can be specified.
+In manycast mode these values are used in turn
+in an expanding-ring search.
+The default is eight
+multiples of 32 starting at 31.
+.El
+.Sh Reference Clock Support
+The NTP Version 4 daemon supports some three dozen different radio,
+satellite and modem reference clocks plus a special pseudo-clock
+used for backup or when no other clock source is available.
+Detailed descriptions of individual device drivers and options can
+be found in the
+.Qq Reference Clock Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+Additional information can be found in the pages linked
+there, including the
+.Qq Debugging Hints for Reference Clock Drivers
+and
+.Qq How To Write a Reference Clock Driver
+pages
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+In addition, support for a PPS
+signal is available as described in the
+.Qq Pulse-per-second (PPS) Signal Interfacing
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+Many
+drivers support special line discipline/streams modules which can
+significantly improve the accuracy using the driver.
+These are
+described in the
+.Qq Line Disciplines and Streams Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.Pp
+A reference clock will generally (though not always) be a radio
+timecode receiver which is synchronized to a source of standard
+time such as the services offered by the NRC in Canada and NIST and
+USNO in the US.
+The interface between the computer and the timecode
+receiver is device dependent, but is usually a serial port.
+A
+device driver specific to each reference clock must be selected and
+compiled in the distribution; however, most common radio, satellite
+and modem clocks are included by default.
+Note that an attempt to
+configure a reference clock when the driver has not been compiled
+or the hardware port has not been appropriately configured results
+in a scalding remark to the system log file, but is otherwise non
+hazardous.
+.Pp
+For the purposes of configuration,
+.Xr ntpd 1ntpdmdoc
+treats
+reference clocks in a manner analogous to normal NTP peers as much
+as possible.
+Reference clocks are identified by a syntactically
+correct but invalid IP address, in order to distinguish them from
+normal NTP peers.
+Reference clock addresses are of the form
+.Sm off
+.Li 127.127. Ar t . Ar u ,
+.Sm on
+where
+.Ar t
+is an integer
+denoting the clock type and
+.Ar u
+indicates the unit
+number in the range 0-3.
+While it may seem overkill, it is in fact
+sometimes useful to configure multiple reference clocks of the same
+type, in which case the unit numbers must be unique.
+.Pp
+The
+.Ic server
+command is used to configure a reference
+clock, where the
+.Ar address
+argument in that command
+is the clock address.
+The
+.Cm key ,
+.Cm version
+and
+.Cm ttl
+options are not used for reference clock support.
+The
+.Cm mode
+option is added for reference clock support, as
+described below.
+The
+.Cm prefer
+option can be useful to
+persuade the server to cherish a reference clock with somewhat more
+enthusiasm than other reference clocks or peers.
+Further
+information on this option can be found in the
+.Qq Mitigation Rules and the prefer Keyword
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+page.
+The
+.Cm minpoll
+and
+.Cm maxpoll
+options have
+meaning only for selected clock drivers.
+See the individual clock
+driver document pages for additional information.
+.Pp
+The
+.Ic fudge
+command is used to provide additional
+information for individual clock drivers and normally follows
+immediately after the
+.Ic server
+command.
+The
+.Ar address
+argument specifies the clock address.
+The
+.Cm refid
+and
+.Cm stratum
+options can be used to
+override the defaults for the device.
+There are two optional
+device-dependent time offsets and four flags that can be included
+in the
+.Ic fudge
+command as well.
+.Pp
+The stratum number of a reference clock is by default zero.
+Since the
+.Xr ntpd 1ntpdmdoc
+daemon adds one to the stratum of each
+peer, a primary server ordinarily displays an external stratum of
+one.
+In order to provide engineered backups, it is often useful to
+specify the reference clock stratum as greater than zero.
+The
+.Cm stratum
+option is used for this purpose.
+Also, in cases
+involving both a reference clock and a pulse-per-second (PPS)
+discipline signal, it is useful to specify the reference clock
+identifier as other than the default, depending on the driver.
+The
+.Cm refid
+option is used for this purpose.
+Except where noted,
+these options apply to all clock drivers.
+.Ss Reference Clock Commands
+.Bl -tag -width indent
+.It Xo Ic server
+.Sm off
+.Li 127.127. Ar t . Ar u
+.Sm on
+.Op Cm prefer
+.Op Cm mode Ar int
+.Op Cm minpoll Ar int
+.Op Cm maxpoll Ar int
+.Xc
+This command can be used to configure reference clocks in
+special ways.
+The options are interpreted as follows:
+.Bl -tag -width indent
+.It Cm prefer
+Marks the reference clock as preferred.
+All other things being
+equal, this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+.Qq Mitigation Rules and the prefer Keyword
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+for further information.
+.It Cm mode Ar int
+Specifies a mode number which is interpreted in a
+device-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+.It Cm minpoll Ar int
+.It Cm maxpoll Ar int
+These options specify the minimum and maximum polling interval
+for reference clock messages, as a power of 2 in seconds
+For
+most directly connected reference clocks, both
+.Cm minpoll
+and
+.Cm maxpoll
+default to 6 (64 s).
+For modem reference clocks,
+.Cm minpoll
+defaults to 10 (17.1 m) and
+.Cm maxpoll
+defaults to 14 (4.5 h).
+The allowable range is 4 (16 s) to 17 (36.4 h) inclusive.
+.El
+.It Xo Ic fudge
+.Sm off
+.Li 127.127. Ar t . Ar u
+.Sm on
+.Op Cm time1 Ar sec
+.Op Cm time2 Ar sec
+.Op Cm stratum Ar int
+.Op Cm refid Ar string
+.Op Cm mode Ar int
+.Op Cm flag1 Cm 0 \&| Cm 1
+.Op Cm flag2 Cm 0 \&| Cm 1
+.Op Cm flag3 Cm 0 \&| Cm 1
+.Op Cm flag4 Cm 0 \&| Cm 1
+.Xc
+This command can be used to configure reference clocks in
+special ways.
+It must immediately follow the
+.Ic server
+command which configures the driver.
+Note that the same capability
+is possible at run time using the
+.Xr ntpdc 1ntpdcmdoc
+program.
+The options are interpreted as
+follows:
+.Bl -tag -width indent
+.It Cm time1 Ar sec
+Specifies a constant to be added to the time offset produced by
+the driver, a fixed-point decimal number in seconds.
+This is used
+as a calibration constant to adjust the nominal time offset of a
+particular clock to agree with an external standard, such as a
+precision PPS signal.
+It also provides a way to correct a
+systematic error or bias due to serial port or operating system
+latencies, different cable lengths or receiver internal delay.
+The
+specified offset is in addition to the propagation delay provided
+by other means, such as internal DIPswitches.
+Where a calibration
+for an individual system and driver is available, an approximate
+correction is noted in the driver documentation pages.
+Note: in order to facilitate calibration when more than one
+radio clock or PPS signal is supported, a special calibration
+feature is available.
+It takes the form of an argument to the
+.Ic enable
+command described in
+.Sx Miscellaneous Options
+page and operates as described in the
+.Qq Reference Clock Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.It Cm time2 Ar secs
+Specifies a fixed-point decimal number in seconds, which is
+interpreted in a driver-dependent way.
+See the descriptions of
+specific drivers in the
+.Qq Reference Clock Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.It Cm stratum Ar int
+Specifies the stratum number assigned to the driver, an integer
+between 0 and 15.
+This number overrides the default stratum number
+ordinarily assigned by the driver itself, usually zero.
+.It Cm refid Ar string
+Specifies an ASCII string of from one to four characters which
+defines the reference identifier used by the driver.
+This string
+overrides the default identifier ordinarily assigned by the driver
+itself.
+.It Cm mode Ar int
+Specifies a mode number which is interpreted in a
+device-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+.It Cm flag1 Cm 0 \&| Cm 1
+.It Cm flag2 Cm 0 \&| Cm 1
+.It Cm flag3 Cm 0 \&| Cm 1
+.It Cm flag4 Cm 0 \&| Cm 1
+These four flags are used for customizing the clock driver.
+The
+interpretation of these values, and whether they are used at all,
+is a function of the particular clock driver.
+However, by
+convention
+.Cm flag4
+is used to enable recording monitoring
+data to the
+.Cm clockstats
+file configured with the
+.Ic filegen
+command.
+Further information on the
+.Ic filegen
+command can be found in
+.Sx Monitoring Options .
+.El
+.El
+.Sh Miscellaneous Options
+.Bl -tag -width indent
+.It Ic broadcastdelay Ar seconds
+The broadcast and multicast modes require a special calibration
+to determine the network delay between the local and remote
+servers.
+Ordinarily, this is done automatically by the initial
+protocol exchanges between the client and server.
+In some cases,
+the calibration procedure may fail due to network or server access
+controls, for example.
+This command specifies the default delay to
+be used under these circumstances.
+Typically (for Ethernet), a
+number between 0.003 and 0.007 seconds is appropriate.
+The default
+when this command is not used is 0.004 seconds.
+.It Ic calldelay Ar delay
+This option controls the delay in seconds between the first and second
+packets sent in burst or iburst mode to allow additional time for a modem
+or ISDN call to complete.
+.It Ic driftfile Ar driftfile
+This command specifies the complete path and name of the file used to
+record the frequency of the local clock oscillator.
+This is the same
+operation as the
+.Fl f
+command line option.
+If the file exists, it is read at
+startup in order to set the initial frequency and then updated once per
+hour with the current frequency computed by the daemon.
+If the file name is
+specified, but the file itself does not exist, the starts with an initial
+frequency of zero and creates the file when writing it for the first time.
+If this command is not given, the daemon will always start with an initial
+frequency of zero.
+.Pp
+The file format consists of a single line containing a single
+floating point number, which records the frequency offset measured
+in parts-per-million (PPM).
+The file is updated by first writing
+the current drift value into a temporary file and then renaming
+this file to replace the old version.
+This implies that
+.Xr ntpd 1ntpdmdoc
+must have write permission for the directory the
+drift file is located in, and that file system links, symbolic or
+otherwise, should be avoided.
+.It Xo Ic enable
+.Oo
+.Cm auth | Cm bclient |
+.Cm calibrate | Cm kernel |
+.Cm mode7 | monitor |
+.Cm ntp | Cm stats
+.Oc
+.Xc
+.It Xo Ic disable
+.Oo
+.Cm auth | Cm bclient |
+.Cm calibrate | Cm kernel |
+.Cm mode7 | monitor |
+.Cm ntp | Cm stats
+.Oc
+.Xc
+Provides a way to enable or disable various server options.
+Flags not mentioned are unaffected.
+Note that all of these flags
+can be controlled remotely using the
+.Xr ntpdc 1ntpdcmdoc
+utility program.
+.Bl -tag -width indent
+.It Cm auth
+Enables the server to synchronize with unconfigured peers only if the
+peer has been correctly authenticated using either public key or
+private key cryptography.
+The default for this flag is
+.Ic enable .
+.It Cm bclient
+Enables the server to listen for a message from a broadcast or
+multicast server, as in the
+.Ic multicastclient
+command with default
+address.
+The default for this flag is
+.Ic disable .
+.It Cm calibrate
+Enables the calibrate feature for reference clocks.
+The default for
+this flag is
+.Ic disable .
+.It Cm kernel
+Enables the kernel time discipline, if available.
+The default for this
+flag is
+.Ic enable
+if support is available, otherwise
+.Ic disable .
+.It Cm mode7
+Enables processing of NTP mode 7 implementation-specific requests
+which are used by the deprecated
+.Xr ntpdc 1ntpdcmdoc
+program.
+The default for this flag is disable.
+This flag is excluded from runtime configuration using
+.Xr ntpq 1ntpqmdoc .
+The
+.Xr ntpq 1ntpqmdoc
+program provides the same capabilities as
+.Xr ntpdc 1ntpdcmdoc
+using standard mode 6 requests.
+.It Cm monitor
+Enables the monitoring facility.
+See the
+.Xr ntpdc 1ntpdcmdoc
+program
+and the
+.Ic monlist
+command or further information.
+The
+default for this flag is
+.Ic enable .
+.It Cm ntp
+Enables time and frequency discipline.
+In effect, this switch opens and
+closes the feedback loop, which is useful for testing.
+The default for
+this flag is
+.Ic enable .
+.It Cm stats
+Enables the statistics facility.
+See the
+.Sx Monitoring Options
+section for further information.
+The default for this flag is
+.Ic disable .
+.El
+.It Ic includefile Ar includefile
+This command allows additional configuration commands
+to be included from a separate file.
+Include files may
+be nested to a depth of five; upon reaching the end of any
+include file, command processing resumes in the previous
+configuration file.
+This option is useful for sites that run
+.Xr ntpd 1ntpdmdoc
+on multiple hosts, with (mostly) common options (e.g., a
+restriction list).
+.It Ic logconfig Ar configkeyword
+This command controls the amount and type of output written to
+the system
+.Xr syslog 3
+facility or the alternate
+.Ic logfile
+log file.
+By default, all output is turned on.
+All
+.Ar configkeyword
+keywords can be prefixed with
+.Ql = ,
+.Ql +
+and
+.Ql - ,
+where
+.Ql =
+sets the
+.Xr syslog 3
+priority mask,
+.Ql +
+adds and
+.Ql -
+removes
+messages.
+.Xr syslog 3
+messages can be controlled in four
+classes
+.Po
+.Cm clock ,
+.Cm peer ,
+.Cm sys
+and
+.Cm sync
+.Pc .
+Within these classes four types of messages can be
+controlled: informational messages
+.Po
+.Cm info
+.Pc ,
+event messages
+.Po
+.Cm events
+.Pc ,
+statistics messages
+.Po
+.Cm statistics
+.Pc
+and
+status messages
+.Po
+.Cm status
+.Pc .
+.Pp
+Configuration keywords are formed by concatenating the message class with
+the event class.
+The
+.Cm all
+prefix can be used instead of a message class.
+A
+message class may also be followed by the
+.Cm all
+keyword to enable/disable all
+messages of the respective message class.Thus, a minimal log configuration
+could look like this:
+.Bd -literal
+logconfig =syncstatus +sysevents
+.Ed
+.Pp
+This would just list the synchronizations state of
+.Xr ntpd 1ntpdmdoc
+and the major system events.
+For a simple reference server, the
+following minimum message configuration could be useful:
+.Bd -literal
+logconfig =syncall +clockall
+.Ed
+.Pp
+This configuration will list all clock information and
+synchronization information.
+All other events and messages about
+peers, system events and so on is suppressed.
+.It Ic logfile Ar logfile
+This command specifies the location of an alternate log file to
+be used instead of the default system
+.Xr syslog 3
+facility.
+This is the same operation as the -l command line option.
+.It Ic setvar Ar variable Op Cm default
+This command adds an additional system variable.
+These
+variables can be used to distribute additional information such as
+the access policy.
+If the variable of the form
+.Sm off
+.Va name = Ar value
+.Sm on
+is followed by the
+.Cm default
+keyword, the
+variable will be listed as part of the default system variables
+.Po
+.Xr ntpq 1ntpqmdoc
+.Ic rv
+command
+.Pc ) .
+These additional variables serve
+informational purposes only.
+They are not related to the protocol
+other that they can be listed.
+The known protocol variables will
+always override any variables defined via the
+.Ic setvar
+mechanism.
+There are three special variables that contain the names
+of all variable of the same group.
+The
+.Va sys_var_list
+holds
+the names of all system variables.
+The
+.Va peer_var_list
+holds
+the names of all peer variables and the
+.Va clock_var_list
+holds the names of the reference clock variables.
+.It Xo Ic tinker
+.Oo
+.Cm allan Ar allan |
+.Cm dispersion Ar dispersion |
+.Cm freq Ar freq |
+.Cm huffpuff Ar huffpuff |
+.Cm panic Ar panic |
+.Cm step Ar srep |
+.Cm stepout Ar stepout
+.Oc
+.Xc
+This command can be used to alter several system variables in
+very exceptional circumstances.
+It should occur in the
+configuration file before any other configuration options.
+The
+default values of these variables have been carefully optimized for
+a wide range of network speeds and reliability expectations.
+In
+general, they interact in intricate ways that are hard to predict
+and some combinations can result in some very nasty behavior.
+Very
+rarely is it necessary to change the default values; but, some
+folks cannot resist twisting the knobs anyway and this command is
+for them.
+Emphasis added: twisters are on their own and can expect
+no help from the support group.
+.Pp
+The variables operate as follows:
+.Bl -tag -width indent
+.It Cm allan Ar allan
+The argument becomes the new value for the minimum Allan
+intercept, which is a parameter of the PLL/FLL clock discipline
+algorithm.
+The value in log2 seconds defaults to 7 (1024 s), which is also the lower
+limit.
+.It Cm dispersion Ar dispersion
+The argument becomes the new value for the dispersion increase rate,
+normally .000015 s/s.
+.It Cm freq Ar freq
+The argument becomes the initial value of the frequency offset in
+parts-per-million.
+This overrides the value in the frequency file, if
+present, and avoids the initial training state if it is not.
+.It Cm huffpuff Ar huffpuff
+The argument becomes the new value for the experimental
+huff-n'-puff filter span, which determines the most recent interval
+the algorithm will search for a minimum delay.
+The lower limit is
+900 s (15 m), but a more reasonable value is 7200 (2 hours).
+There
+is no default, since the filter is not enabled unless this command
+is given.
+.It Cm panic Ar panic
+The argument is the panic threshold, normally 1000 s.
+If set to zero,
+the panic sanity check is disabled and a clock offset of any value will
+be accepted.
+.It Cm step Ar step
+The argument is the step threshold, which by default is 0.128 s.
+It can
+be set to any positive number in seconds.
+If set to zero, step
+adjustments will never occur.
+Note: The kernel time discipline is
+disabled if the step threshold is set to zero or greater than the
+default.
+.It Cm stepout Ar stepout
+The argument is the stepout timeout, which by default is 900 s.
+It can
+be set to any positive number in seconds.
+If set to zero, the stepout
+pulses will not be suppressed.
+.El
+.It Xo Ic rlimit
+.Oo
+.Cm memlock Ar Nmegabytes |
+.Cm stacksize Ar N4kPages
+.Cm filenum Ar Nfiledescriptors
+.Oc
+.Xc
+.Bl -tag -width indent
+.It Cm memlock Ar Nmegabytes
+Specify the number of megabytes of memory that can be allocated.
+Probably only available under Linux, this option is useful
+when dropping root (the
+.Fl i
+option).
+The default is 32 megabytes. Setting this to zero will prevent any attemp to lock memory.
+.It Cm stacksize Ar N4kPages
+Specifies the maximum size of the process stack on systems with the
+.It Cm filenum Ar Nfiledescriptors
+Specifies the maximum number of file descriptors ntpd may have open at once. Defaults to the system default.
+.Fn mlockall
+function.
+Defaults to 50 4k pages (200 4k pages in OpenBSD).
+.El
+.It Xo Ic trap Ar host_address
+.Op Cm port Ar port_number
+.Op Cm interface Ar interface_address
+.Xc
+This command configures a trap receiver at the given host
+address and port number for sending messages with the specified
+local interface address.
+If the port number is unspecified, a value
+of 18447 is used.
+If the interface address is not specified, the
+message is sent with a source address of the local interface the
+message is sent through.
+Note that on a multihomed host the
+interface used may vary from time to time with routing changes.
+.Pp
+The trap receiver will generally log event messages and other
+information from the server in a log file.
+While such monitor
+programs may also request their own trap dynamically, configuring a
+trap receiver will ensure that no messages are lost when the server
+is started.
+.It Cm hop Ar ...
+This command specifies a list of TTL values in increasing order, up to 8
+values can be specified.
+In manycast mode these values are used in turn in
+an expanding-ring search.
+The default is eight multiples of 32 starting at
+31.
+.El
+ _END_PROG_MDOC_DESCRIP;
+};
+
+doc-section = {
+ ds-type = 'FILES';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_MDOC_FILES
+.Bl -tag -width /etc/ntp.drift -compact
+.It Pa /etc/ntp.conf
+the default name of the configuration file
+.It Pa ntp.keys
+private MD5 keys
+.It Pa ntpkey
+RSA private key
+.It Pa ntpkey_ Ns Ar host
+RSA public key
+.It Pa ntp_dh
+Diffie-Hellman agreement parameters
+.El
+ _END_MDOC_FILES;
+};
+
+doc-section = {
+ ds-type = 'SEE ALSO';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_MDOC_SEE_ALSO
+.Xr ntpd 1ntpdmdoc ,
+.Xr ntpdc 1ntpdcmdoc ,
+.Xr ntpq 1ntpqmdoc
+.Pp
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+.Li http://www.ntp.org/ .
+A snapshot of this documentation is available in HTML format in
+.Pa /usr/share/doc/ntp .
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 4)
+.%O RFC5905
+.Re
+ _END_MDOC_SEE_ALSO;
+};
+
+doc-section = {
+ ds-type = 'BUGS';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_MDOC_BUGS
+The syntax checking is not picky; some combinations of
+ridiculous and even hilarious options and modes may not be
+detected.
+.Pp
+The
+.Pa ntpkey_ Ns Ar host
+files are really digital
+certificates.
+These should be obtained via secure directory
+services when they become universally available.
+ _END_MDOC_BUGS;
+};
+
+doc-section = {
+ ds-type = 'NOTES';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_MDOC_NOTES
+This document was derived from FreeBSD.
+ _END_MDOC_NOTES;
+};
diff --git a/ntpd/ntp.conf.html b/ntpd/ntp.conf.html
new file mode 100644
index 0000000..15632da
--- /dev/null
+++ b/ntpd/ntp.conf.html
@@ -0,0 +1,2625 @@
+<html lang="en">
+<head>
+<title>NTP Configuration File User's Manual</title>
+<meta http-equiv="Content-Type" content="text/html">
+<meta name="description" content="NTP Configuration File User's Manual">
+<meta name="generator" content="makeinfo 4.7">
+<link title="Top" rel="top" href="#Top">
+<link href="http://www.gnu.org/software/texinfo/" rel="generator-home" title="Texinfo Homepage">
+<meta http-equiv="Content-Style-Type" content="text/css">
+<style type="text/css"><!--
+ pre.display { font-family:inherit }
+ pre.format { font-family:inherit }
+ pre.smalldisplay { font-family:inherit; font-size:smaller }
+ pre.smallformat { font-family:inherit; font-size:smaller }
+ pre.smallexample { font-size:smaller }
+ pre.smalllisp { font-size:smaller }
+ span.sc { font-variant:small-caps }
+ span.roman { font-family: serif; font-weight: normal; }
+--></style>
+</head>
+<body>
+<h1 class="settitle">NTP Configuration File User's Manual</h1>
+<div class="node">
+<p><hr>
+<a name="Top"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntp_002econf-Description">ntp.conf Description</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#dir">(dir)</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#dir">(dir)</a>
+<br>
+</div>
+
+<h2 class="unnumbered">NTP's Configuration File User Manual</h2>
+
+<p>This document describes the configuration file for the NTP Project's
+<code>ntpd</code> program.
+
+ <p>This document applies to version 4.2.7p482 of <code>ntp.conf</code>.
+
+ <div class="shortcontents">
+<h2>Short Contents</h2>
+<ul>
+<a href="#Top">NTP's Configuration File User Manual</a>
+</ul>
+</div>
+
+<ul class="menu">
+<li><a accesskey="1" href="#ntp_002econf-Description">ntp.conf Description</a>
+<li><a accesskey="2" href="#ntp_002econf-Notes">ntp.conf Notes</a>
+</ul>
+
+<div class="node">
+<p><hr>
+<a name="ntp_002econf-Description"></a>Previous:&nbsp;<a rel="previous" accesskey="p" href="#Top">Top</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
+<br>
+</div>
+
+<!-- node-name, next, previous, up -->
+<h3 class="section">Description</h3>
+
+<p>The behavior of <code>ntpd</code> can be changed by a configuration file,
+by default <code>ntp.conf</code>.
+
+<div class="node">
+<p><hr>
+<a name="ntp_002econf-Notes"></a>
+<br>
+</div>
+
+<h3 class="section">Notes about ntp.conf</h3>
+
+<p><a name="index-ntp_002econf-1"></a><a name="index-Network-Time-Protocol-_0028NTP_0029-daemon-configuration-file-format-2"></a>
+
+ <p>The
+<code>ntp.conf</code>
+configuration file is read at initial startup by the
+<code>ntpd(1ntpdmdoc)</code>
+daemon in order to specify the synchronization sources,
+modes and other related information.
+Usually, it is installed in the
+<span class="file">/etc</span>
+directory,
+but could be installed elsewhere
+(see the daemon's
+<code>-c</code>
+command line option).
+
+ <p>The file format is similar to other
+<span class="sc">unix</span>
+configuration files.
+Comments begin with a
+#
+character and extend to the end of the line;
+blank lines are ignored.
+Configuration commands consist of an initial keyword
+followed by a list of arguments,
+some of which may be optional, separated by whitespace.
+Commands may not be continued over multiple lines.
+Arguments may be host names,
+host addresses written in numeric, dotted-quad form,
+integers, floating point numbers (when specifying times in seconds)
+and text strings.
+
+ <p>The rest of this page describes the configuration and control options.
+The
+"Notes on Configuring NTP and Setting up an NTP Subnet"
+page
+(available as part of the HTML documentation
+provided in
+<span class="file">/usr/share/doc/ntp</span>)
+contains an extended discussion of these options.
+In addition to the discussion of general
+<a href="#Configuration-Options">Configuration Options</a>,
+there are sections describing the following supported functionality
+and the options used to control it:
+ <ul>
+<li><a href="#Authentication-Support">Authentication Support</a>
+<li><a href="#Monitoring-Support">Monitoring Support</a>
+<li><a href="#Access-Control-Support">Access Control Support</a>
+<li><a href="#Automatic-NTP-Configuration-Options">Automatic NTP Configuration Options</a>
+<li><a href="#Reference-Clock-Support">Reference Clock Support</a>
+<li><a href="#Miscellaneous-Options">Miscellaneous Options</a>
+</ul>
+
+ <p>Following these is a section describing
+<a href="#Miscellaneous-Options">Miscellaneous Options</a>.
+While there is a rich set of options available,
+the only required option is one or more
+<code>pool</code>,
+<code>server</code>,
+<code>peer</code>,
+<code>broadcast</code>
+or
+<code>manycastclient</code>
+commands.
+<div class="node">
+<p><hr>
+<a name="Configuration-Support"></a>
+<br>
+</div>
+
+<h4 class="subsection">Configuration Support</h4>
+
+<p>Following is a description of the configuration commands in
+NTPv4.
+These commands have the same basic functions as in NTPv3 and
+in some cases new functions and new arguments.
+There are two
+classes of commands, configuration commands that configure a
+persistent association with a remote server or peer or reference
+clock, and auxiliary commands that specify environmental variables
+that control various related operations.
+
+<h5 class="subsubsection">Configuration Commands</h5>
+
+<p>The various modes are determined by the command keyword and the
+type of the required IP address.
+Addresses are classed by type as
+(s) a remote server or peer (IPv4 class A, B and C), (b) the
+broadcast address of a local interface, (m) a multicast address (IPv4
+class D), or (r) a reference clock address (127.127.x.x).
+Note that
+only those options applicable to each command are listed below.
+Use
+of options not listed may not be caught as an error, but may result
+in some weird and even destructive behavior.
+
+ <p>If the Basic Socket Interface Extensions for IPv6 (RFC-2553)
+is detected, support for the IPv6 address family is generated
+in addition to the default support of the IPv4 address family.
+In a few cases, including the reslist billboard generated
+by ntpdc, IPv6 addresses are automatically generated.
+IPv6 addresses can be identified by the presence of colons
+:
+in the address field.
+IPv6 addresses can be used almost everywhere where
+IPv4 addresses can be used,
+with the exception of reference clock addresses,
+which are always IPv4.
+
+ <p>Note that in contexts where a host name is expected, a
+<code>-4</code>
+qualifier preceding
+the host name forces DNS resolution to the IPv4 namespace,
+while a
+<code>-6</code>
+qualifier forces DNS resolution to the IPv6 namespace.
+See IPv6 references for the
+equivalent classes for that address family.
+ <dl>
+<dt><code>pool</code> <kbd>address</kbd> <code>[burst]</code> <code>[iburst]</code> <code>[version </code><kbd>version</kbd><code>]</code> <code>[prefer]</code> <code>[minpoll </code><kbd>minpoll</kbd><code>]</code> <code>[maxpoll </code><kbd>maxpoll</kbd><code>]</code><br><dt><code>server</code> <kbd>address</kbd> <code>[key </code><kbd>key</kbd> <kbd>|</kbd><code> autokey]</code> <code>[burst]</code> <code>[iburst]</code> <code>[version </code><kbd>version</kbd><code>]</code> <code>[prefer]</code> <code>[minpoll </code><kbd>minpoll</kbd><code>]</code> <code>[maxpoll </code><kbd>maxpoll</kbd><code>]</code><br><dt><code>peer</code> <kbd>address</kbd> <code>[key </code><kbd>key</kbd> <kbd>|</kbd><code> autokey]</code> <code>[version </code><kbd>version</kbd><code>]</code> <code>[prefer]</code> <code>[minpoll </code><kbd>minpoll</kbd><code>]</code> <code>[maxpoll </code><kbd>maxpoll</kbd><code>]</code><br><dt><code>broadcast</code> <kbd>address</kbd> <code>[key </code><kbd>key</kbd> <kbd>|</kbd><code> autokey]</code> <code>[version </code><kbd>version</kbd><code>]</code> <code>[prefer]</code> <code>[minpoll </code><kbd>minpoll</kbd><code>]</code> <code>[ttl </code><kbd>ttl</kbd><code>]</code><br><dt><code>manycastclient</code> <kbd>address</kbd> <code>[key </code><kbd>key</kbd> <kbd>|</kbd><code> autokey]</code> <code>[version </code><kbd>version</kbd><code>]</code> <code>[prefer]</code> <code>[minpoll </code><kbd>minpoll</kbd><code>]</code> <code>[maxpoll </code><kbd>maxpoll</kbd><code>]</code> <code>[ttl </code><kbd>ttl</kbd><code>]</code><dd></dl>
+
+ <p>These five commands specify the time server name or address to
+be used and the mode in which to operate.
+The
+<kbd>address</kbd>
+can be
+either a DNS name or an IP address in dotted-quad notation.
+Additional information on association behavior can be found in the
+"Association Management"
+page
+(available as part of the HTML documentation
+provided in
+<span class="file">/usr/share/doc/ntp</span>).
+ <dl>
+<dt><code>pool</code><dd>For type s addresses, this command mobilizes a persistent
+client mode association with a number of remote servers.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+<br><dt><code>server</code><dd>For type s and r addresses, this command mobilizes a persistent
+client mode association with the specified remote server or local
+radio clock.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+This command should
+<em>not</em>
+be used for type
+b or m addresses.
+<br><dt><code>peer</code><dd>For type s addresses (only), this command mobilizes a
+persistent symmetric-active mode association with the specified
+remote peer.
+In this mode the local clock can be synchronized to
+the remote peer or the remote peer can be synchronized to the local
+clock.
+This is useful in a network of servers where, depending on
+various failure scenarios, either the local or remote peer may be
+the better source of time.
+This command should NOT be used for type
+b, m or r addresses.
+<br><dt><code>broadcast</code><dd>For type b and m addresses (only), this
+command mobilizes a persistent broadcast mode association.
+Multiple
+commands can be used to specify multiple local broadcast interfaces
+(subnets) and/or multiple multicast groups.
+Note that local
+broadcast messages go only to the interface associated with the
+subnet specified, but multicast messages go to all interfaces.
+In broadcast mode the local server sends periodic broadcast
+messages to a client population at the
+<kbd>address</kbd>
+specified, which is usually the broadcast address on (one of) the
+local network(s) or a multicast address assigned to NTP.
+The IANA
+has assigned the multicast group address IPv4 224.0.1.1 and
+IPv6 ff05::101 (site local) exclusively to
+NTP, but other nonconflicting addresses can be used to contain the
+messages within administrative boundaries.
+Ordinarily, this
+specification applies only to the local server operating as a
+sender; for operation as a broadcast client, see the
+<code>broadcastclient</code>
+or
+<code>multicastclient</code>
+commands
+below.
+<br><dt><code>manycastclient</code><dd>For type m addresses (only), this command mobilizes a
+manycast client mode association for the multicast address
+specified.
+In this case a specific address must be supplied which
+matches the address used on the
+<code>manycastserver</code>
+command for
+the designated manycast servers.
+The NTP multicast address
+224.0.1.1 assigned by the IANA should NOT be used, unless specific
+means are taken to avoid spraying large areas of the Internet with
+these messages and causing a possibly massive implosion of replies
+at the sender.
+The
+<code>manycastserver</code>
+command specifies that the local server
+is to operate in client mode with the remote servers that are
+discovered as the result of broadcast/multicast messages.
+The
+client broadcasts a request message to the group address associated
+with the specified
+<kbd>address</kbd>
+and specifically enabled
+servers respond to these messages.
+The client selects the servers
+providing the best time and continues as with the
+<code>server</code>
+command.
+The remaining servers are discarded as if never
+heard.
+</dl>
+
+ <p>Options:
+ <dl>
+<dt><code>autokey</code><dd>All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the autokey scheme
+described in
+<a href="#Authentication-Options">Authentication Options</a>.
+<br><dt><code>burst</code><dd>when the server is reachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first and second packets
+can be changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to improve timekeeping quality
+with the
+<code>server</code>
+command and s addresses.
+<br><dt><code>iburst</code><dd>When the server is unreachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first two packets can be
+changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to speed the initial synchronization
+acquisition with the
+<code>server</code>
+command and s addresses and when
+<code>ntpd(1ntpdmdoc)</code>
+is started with the
+<code>-q</code>
+option.
+<br><dt><code>key</code> <kbd>key</kbd><dd>All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the specified
+<kbd>key</kbd>
+identifier with values from 1 to 65534, inclusive.
+The
+default is to include no encryption field.
+<br><dt><code>minpoll</code> <kbd>minpoll</kbd><br><dt><code>maxpoll</code> <kbd>maxpoll</kbd><dd>These options specify the minimum and maximum poll intervals
+for NTP messages, as a power of 2 in seconds
+The maximum poll
+interval defaults to 10 (1,024 s), but can be increased by the
+<code>maxpoll</code>
+option to an upper limit of 17 (36.4 h).
+The
+minimum poll interval defaults to 6 (64 s), but can be decreased by
+the
+<code>minpoll</code>
+option to a lower limit of 4 (16 s).
+<br><dt><code>noselect</code><dd>Marks the server as unused, except for display purposes.
+The server is discarded by the selection algroithm.
+<br><dt><code>prefer</code><dd>Marks the server as preferred.
+All other things being equal,
+this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+"Mitigation Rules and the prefer Keyword"
+page
+(available as part of the HTML documentation
+provided in
+<span class="file">/usr/share/doc/ntp</span>)
+for further information.
+<br><dt><code>ttl</code> <kbd>ttl</kbd><dd>This option is used only with broadcast server and manycast
+client modes.
+It specifies the time-to-live
+<kbd>ttl</kbd>
+to
+use on broadcast server and multicast server and the maximum
+<kbd>ttl</kbd>
+for the expanding ring search with manycast
+client packets.
+Selection of the proper value, which defaults to
+127, is something of a black art and should be coordinated with the
+network administrator.
+<br><dt><code>version</code> <kbd>version</kbd><dd>Specifies the version number to be used for outgoing NTP
+packets.
+Versions 1-4 are the choices, with version 4 the
+default.
+</dl>
+
+<h5 class="subsubsection">Auxiliary Commands</h5>
+
+ <dl>
+<dt><code>broadcastclient</code><dd>This command enables reception of broadcast server messages to
+any local interface (type b) address.
+Upon receiving a message for
+the first time, the broadcast client measures the nominal server
+propagation delay using a brief client/server exchange with the
+server, then enters the broadcast client mode, in which it
+synchronizes to succeeding broadcast messages.
+Note that, in order
+to avoid accidental or malicious disruption in this mode, both the
+server and client should operate using symmetric-key or public-key
+authentication as described in
+<a href="#Authentication-Options">Authentication Options</a>.
+<br><dt><code>manycastserver</code> <kbd>address</kbd> <kbd>...</kbd><dd>This command enables reception of manycast client messages to
+the multicast group address(es) (type m) specified.
+At least one
+address is required, but the NTP multicast address 224.0.1.1
+assigned by the IANA should NOT be used, unless specific means are
+taken to limit the span of the reply and avoid a possibly massive
+implosion at the original sender.
+Note that, in order to avoid
+accidental or malicious disruption in this mode, both the server
+and client should operate using symmetric-key or public-key
+authentication as described in
+<a href="#Authentication-Options">Authentication Options</a>.
+<br><dt><code>multicastclient</code> <kbd>address</kbd> <kbd>...</kbd><dd>This command enables reception of multicast server messages to
+the multicast group address(es) (type m) specified.
+Upon receiving
+a message for the first time, the multicast client measures the
+nominal server propagation delay using a brief client/server
+exchange with the server, then enters the broadcast client mode, in
+which it synchronizes to succeeding multicast messages.
+Note that,
+in order to avoid accidental or malicious disruption in this mode,
+both the server and client should operate using symmetric-key or
+public-key authentication as described in
+<a href="#Authentication-Options">Authentication Options</a>.
+</dl>
+<div class="node">
+<p><hr>
+<a name="Authentication-Support"></a>
+<br>
+</div>
+
+<h4 class="subsection">Authentication Support</h4>
+
+<p>Authentication support allows the NTP client to verify that the
+server is in fact known and trusted and not an intruder intending
+accidentally or on purpose to masquerade as that server.
+The NTPv3
+specification RFC-1305 defines a scheme which provides
+cryptographic authentication of received NTP packets.
+Originally,
+this was done using the Data Encryption Standard (DES) algorithm
+operating in Cipher Block Chaining (CBC) mode, commonly called
+DES-CBC.
+Subsequently, this was replaced by the RSA Message Digest
+5 (MD5) algorithm using a private key, commonly called keyed-MD5.
+Either algorithm computes a message digest, or one-way hash, which
+can be used to verify the server has the correct private key and
+key identifier.
+
+ <p>NTPv4 retains the NTPv3 scheme, properly described as symmetric key
+cryptography and, in addition, provides a new Autokey scheme
+based on public key cryptography.
+Public key cryptography is generally considered more secure
+than symmetric key cryptography, since the security is based
+on a private value which is generated by each server and
+never revealed.
+With Autokey all key distribution and
+management functions involve only public values, which
+considerably simplifies key distribution and storage.
+Public key management is based on X.509 certificates,
+which can be provided by commercial services or
+produced by utility programs in the OpenSSL software library
+or the NTPv4 distribution.
+
+ <p>While the algorithms for symmetric key cryptography are
+included in the NTPv4 distribution, public key cryptography
+requires the OpenSSL software library to be installed
+before building the NTP distribution.
+Directions for doing that
+are on the Building and Installing the Distribution page.
+
+ <p>Authentication is configured separately for each association
+using the
+<code>key</code>
+or
+<code>autokey</code>
+subcommand on the
+<code>peer</code>,
+<code>server</code>,
+<code>broadcast</code>
+and
+<code>manycastclient</code>
+configuration commands as described in
+<a href="#Configuration-Options">Configuration Options</a>
+page.
+The authentication
+options described below specify the locations of the key files,
+if other than default, which symmetric keys are trusted
+and the interval between various operations, if other than default.
+
+ <p>Authentication is always enabled,
+although ineffective if not configured as
+described below.
+If a NTP packet arrives
+including a message authentication
+code (MAC), it is accepted only if it
+passes all cryptographic checks.
+The
+checks require correct key ID, key value
+and message digest.
+If the packet has
+been modified in any way or replayed
+by an intruder, it will fail one or more
+of these checks and be discarded.
+Furthermore, the Autokey scheme requires a
+preliminary protocol exchange to obtain
+the server certificate, verify its
+credentials and initialize the protocol
+
+ <p>The
+<code>auth</code>
+flag controls whether new associations or
+remote configuration commands require cryptographic authentication.
+This flag can be set or reset by the
+<code>enable</code>
+and
+<code>disable</code>
+commands and also by remote
+configuration commands sent by a
+<code>ntpdc(1ntpdcmdoc)</code>
+program running in
+another machine.
+If this flag is enabled, which is the default
+case, new broadcast client and symmetric passive associations and
+remote configuration commands must be cryptographically
+authenticated using either symmetric key or public key cryptography.
+If this
+flag is disabled, these operations are effective
+even if not cryptographic
+authenticated.
+It should be understood
+that operating with the
+<code>auth</code>
+flag disabled invites a significant vulnerability
+where a rogue hacker can
+masquerade as a falseticker and seriously
+disrupt system timekeeping.
+It is
+important to note that this flag has no purpose
+other than to allow or disallow
+a new association in response to new broadcast
+and symmetric active messages
+and remote configuration commands and, in particular,
+the flag has no effect on
+the authentication process itself.
+
+ <p>An attractive alternative where multicast support is available
+is manycast mode, in which clients periodically troll
+for servers as described in the
+<a href="#Automatic-NTP-Configuration-Options">Automatic NTP Configuration Options</a>
+page.
+Either symmetric key or public key
+cryptographic authentication can be used in this mode.
+The principle advantage
+of manycast mode is that potential servers need not be
+configured in advance,
+since the client finds them during regular operation,
+and the configuration
+files for all clients can be identical.
+
+ <p>The security model and protocol schemes for
+both symmetric key and public key
+cryptography are summarized below;
+further details are in the briefings, papers
+and reports at the NTP project page linked from
+<code>http://www.ntp.org/</code>.
+
+<h5 class="subsubsection">Symmetric-Key Cryptography</h5>
+
+<p>The original RFC-1305 specification allows any one of possibly
+65,534 keys, each distinguished by a 32-bit key identifier, to
+authenticate an association.
+The servers and clients involved must
+agree on the key and key identifier to
+authenticate NTP packets.
+Keys and
+related information are specified in a key
+file, usually called
+<span class="file">ntp.keys</span>,
+which must be distributed and stored using
+secure means beyond the scope of the NTP protocol itself.
+Besides the keys used
+for ordinary NTP associations,
+additional keys can be used as passwords for the
+<code>ntpq(1ntpqmdoc)</code>
+and
+<code>ntpdc(1ntpdcmdoc)</code>
+utility programs.
+
+ <p>When
+<code>ntpd(1ntpdmdoc)</code>
+is first started, it reads the key file specified in the
+<code>keys</code>
+configuration command and installs the keys
+in the key cache.
+However,
+individual keys must be activated with the
+<code>trusted</code>
+command before use.
+This
+allows, for instance, the installation of possibly
+several batches of keys and
+then activating or deactivating each batch
+remotely using
+<code>ntpdc(1ntpdcmdoc)</code>.
+This also provides a revocation capability that can be used
+if a key becomes compromised.
+The
+<code>requestkey</code>
+command selects the key used as the password for the
+<code>ntpdc(1ntpdcmdoc)</code>
+utility, while the
+<code>controlkey</code>
+command selects the key used as the password for the
+<code>ntpq(1ntpqmdoc)</code>
+utility.
+
+<h5 class="subsubsection">Public Key Cryptography</h5>
+
+<p>NTPv4 supports the original NTPv3 symmetric key scheme
+described in RFC-1305 and in addition the Autokey protocol,
+which is based on public key cryptography.
+The Autokey Version 2 protocol described on the Autokey Protocol
+page verifies packet integrity using MD5 message digests
+and verifies the source with digital signatures and any of several
+digest/signature schemes.
+Optional identity schemes described on the Identity Schemes
+page and based on cryptographic challenge/response algorithms
+are also available.
+Using all of these schemes provides strong security against
+replay with or without modification, spoofing, masquerade
+and most forms of clogging attacks.
+
+ <p>The Autokey protocol has several modes of operation
+corresponding to the various NTP modes supported.
+Most modes use a special cookie which can be
+computed independently by the client and server,
+but encrypted in transmission.
+All modes use in addition a variant of the S-KEY scheme,
+in which a pseudo-random key list is generated and used
+in reverse order.
+These schemes are described along with an executive summary,
+current status, briefing slides and reading list on the
+<a href="#Autonomous-Authentication">Autonomous Authentication</a>
+page.
+
+ <p>The specific cryptographic environment used by Autokey servers
+and clients is determined by a set of files
+and soft links generated by the
+<code>ntp-keygen(1ntpkeygenmdoc)</code>
+program.
+This includes a required host key file,
+required certificate file and optional sign key file,
+leapsecond file and identity scheme files.
+The
+digest/signature scheme is specified in the X.509 certificate
+along with the matching sign key.
+There are several schemes
+available in the OpenSSL software library, each identified
+by a specific string such as
+<code>md5WithRSAEncryption</code>,
+which stands for the MD5 message digest with RSA
+encryption scheme.
+The current NTP distribution supports
+all the schemes in the OpenSSL library, including
+those based on RSA and DSA digital signatures.
+
+ <p>NTP secure groups can be used to define cryptographic compartments
+and security hierarchies.
+It is important that every host
+in the group be able to construct a certificate trail to one
+or more trusted hosts in the same group.
+Each group
+host runs the Autokey protocol to obtain the certificates
+for all hosts along the trail to one or more trusted hosts.
+This requires the configuration file in all hosts to be
+engineered so that, even under anticipated failure conditions,
+the NTP subnet will form such that every group host can find
+a trail to at least one trusted host.
+
+<h5 class="subsubsection">Naming and Addressing</h5>
+
+<p>It is important to note that Autokey does not use DNS to
+resolve addresses, since DNS can't be completely trusted
+until the name servers have synchronized clocks.
+The cryptographic name used by Autokey to bind the host identity
+credentials and cryptographic values must be independent
+of interface, network and any other naming convention.
+The name appears in the host certificate in either or both
+the subject and issuer fields, so protection against
+DNS compromise is essential.
+
+ <p>By convention, the name of an Autokey host is the name returned
+by the Unix
+<code>gethostname(2)</code>
+system call or equivalent in other systems.
+By the system design
+model, there are no provisions to allow alternate names or aliases.
+However, this is not to say that DNS aliases, different names
+for each interface, etc., are constrained in any way.
+
+ <p>It is also important to note that Autokey verifies authenticity
+using the host name, network address and public keys,
+all of which are bound together by the protocol specifically
+to deflect masquerade attacks.
+For this reason Autokey
+includes the source and destinatino IP addresses in message digest
+computations and so the same addresses must be available
+at both the server and client.
+For this reason operation
+with network address translation schemes is not possible.
+This reflects the intended robust security model where government
+and corporate NTP servers are operated outside firewall perimeters.
+
+<h5 class="subsubsection">Operation</h5>
+
+<p>A specific combination of authentication scheme (none,
+symmetric key, public key) and identity scheme is called
+a cryptotype, although not all combinations are compatible.
+There may be management configurations where the clients,
+servers and peers may not all support the same cryptotypes.
+A secure NTPv4 subnet can be configured in many ways while
+keeping in mind the principles explained above and
+in this section.
+Note however that some cryptotype
+combinations may successfully interoperate with each other,
+but may not represent good security practice.
+
+ <p>The cryptotype of an association is determined at the time
+of mobilization, either at configuration time or some time
+later when a message of appropriate cryptotype arrives.
+When mobilized by a
+<code>server</code>
+or
+<code>peer</code>
+configuration command and no
+<code>key</code>
+or
+<code>autokey</code>
+subcommands are present, the association is not
+authenticated; if the
+<code>key</code>
+subcommand is present, the association is authenticated
+using the symmetric key ID specified; if the
+<code>autokey</code>
+subcommand is present, the association is authenticated
+using Autokey.
+
+ <p>When multiple identity schemes are supported in the Autokey
+protocol, the first message exchange determines which one is used.
+The client request message contains bits corresponding
+to which schemes it has available.
+The server response message
+contains bits corresponding to which schemes it has available.
+Both server and client match the received bits with their own
+and select a common scheme.
+
+ <p>Following the principle that time is a public value,
+a server responds to any client packet that matches
+its cryptotype capabilities.
+Thus, a server receiving
+an unauthenticated packet will respond with an unauthenticated
+packet, while the same server receiving a packet of a cryptotype
+it supports will respond with packets of that cryptotype.
+However, unconfigured broadcast or manycast client
+associations or symmetric passive associations will not be
+mobilized unless the server supports a cryptotype compatible
+with the first packet received.
+By default, unauthenticated associations will not be mobilized
+unless overridden in a decidedly dangerous way.
+
+ <p>Some examples may help to reduce confusion.
+Client Alice has no specific cryptotype selected.
+Server Bob has both a symmetric key file and minimal Autokey files.
+Alice's unauthenticated messages arrive at Bob, who replies with
+unauthenticated messages.
+Cathy has a copy of Bob's symmetric
+key file and has selected key ID 4 in messages to Bob.
+Bob verifies the message with his key ID 4.
+If it's the
+same key and the message is verified, Bob sends Cathy a reply
+authenticated with that key.
+If verification fails,
+Bob sends Cathy a thing called a crypto-NAK, which tells her
+something broke.
+She can see the evidence using the
+<code>ntpq(1ntpqmdoc)</code>
+program.
+
+ <p>Denise has rolled her own host key and certificate.
+She also uses one of the identity schemes as Bob.
+She sends the first Autokey message to Bob and they
+both dance the protocol authentication and identity steps.
+If all comes out okay, Denise and Bob continue as described above.
+
+ <p>It should be clear from the above that Bob can support
+all the girls at the same time, as long as he has compatible
+authentication and identity credentials.
+Now, Bob can act just like the girls in his own choice of servers;
+he can run multiple configured associations with multiple different
+servers (or the same server, although that might not be useful).
+But, wise security policy might preclude some cryptotype
+combinations; for instance, running an identity scheme
+with one server and no authentication with another might not be wise.
+
+<h5 class="subsubsection">Key Management</h5>
+
+<p>The cryptographic values used by the Autokey protocol are
+incorporated as a set of files generated by the
+<code>ntp-keygen(1ntpkeygenmdoc)</code>
+utility program, including symmetric key, host key and
+public certificate files, as well as sign key, identity parameters
+and leapseconds files.
+Alternatively, host and sign keys and
+certificate files can be generated by the OpenSSL utilities
+and certificates can be imported from public certificate
+authorities.
+Note that symmetric keys are necessary for the
+<code>ntpq(1ntpqmdoc)</code>
+and
+<code>ntpdc(1ntpdcmdoc)</code>
+utility programs.
+The remaining files are necessary only for the
+Autokey protocol.
+
+ <p>Certificates imported from OpenSSL or public certificate
+authorities have certian limitations.
+The certificate should be in ASN.1 syntax, X.509 Version 3
+format and encoded in PEM, which is the same format
+used by OpenSSL.
+The overall length of the certificate encoded
+in ASN.1 must not exceed 1024 bytes.
+The subject distinguished
+name field (CN) is the fully qualified name of the host
+on which it is used; the remaining subject fields are ignored.
+The certificate extension fields must not contain either
+a subject key identifier or a issuer key identifier field;
+however, an extended key usage field for a trusted host must
+contain the value
+<code>trustRoot</code>;.
+Other extension fields are ignored.
+
+<h5 class="subsubsection">Authentication Commands</h5>
+
+ <dl>
+<dt><code>autokey</code> <code>[</code><kbd>logsec</kbd><code>]</code><dd>Specifies the interval between regenerations of the session key
+list used with the Autokey protocol.
+Note that the size of the key
+list for each association depends on this interval and the current
+poll interval.
+The default value is 12 (4096 s or about 1.1 hours).
+For poll intervals above the specified interval, a session key list
+with a single entry will be regenerated for every message
+sent.
+<br><dt><code>controlkey</code> <kbd>key</kbd><dd>Specifies the key identifier to use with the
+<code>ntpq(1ntpqmdoc)</code>
+utility, which uses the standard
+protocol defined in RFC-1305.
+The
+<kbd>key</kbd>
+argument is
+the key identifier for a trusted key, where the value can be in the
+range 1 to 65,534, inclusive.
+<br><dt><code>crypto</code> <code>[cert </code><kbd>file</kbd><code>]</code> <code>[leap </code><kbd>file</kbd><code>]</code> <code>[randfile </code><kbd>file</kbd><code>]</code> <code>[host </code><kbd>file</kbd><code>]</code> <code>[sign </code><kbd>file</kbd><code>]</code> <code>[gq </code><kbd>file</kbd><code>]</code> <code>[gqpar </code><kbd>file</kbd><code>]</code> <code>[iffpar </code><kbd>file</kbd><code>]</code> <code>[mvpar </code><kbd>file</kbd><code>]</code> <code>[pw </code><kbd>password</kbd><code>]</code><dd>This command requires the OpenSSL library.
+It activates public key
+cryptography, selects the message digest and signature
+encryption scheme and loads the required private and public
+values described above.
+If one or more files are left unspecified,
+the default names are used as described above.
+Unless the complete path and name of the file are specified, the
+location of a file is relative to the keys directory specified
+in the
+<code>keysdir</code>
+command or default
+<span class="file">/usr/local/etc</span>.
+Following are the subcommands:
+ <dl>
+<dt><code>cert</code> <kbd>file</kbd><dd>Specifies the location of the required host public certificate file.
+This overrides the link
+<span class="file">ntpkey_cert_</span><kbd>hostname</kbd>
+in the keys directory.
+<br><dt><code>gqpar</code> <kbd>file</kbd><dd>Specifies the location of the optional GQ parameters file.
+This
+overrides the link
+<span class="file">ntpkey_gq_</span><kbd>hostname</kbd>
+in the keys directory.
+<br><dt><code>host</code> <kbd>file</kbd><dd>Specifies the location of the required host key file.
+This overrides
+the link
+<span class="file">ntpkey_key_</span><kbd>hostname</kbd>
+in the keys directory.
+<br><dt><code>iffpar</code> <kbd>file</kbd><dd>Specifies the location of the optional IFF parameters file.This
+overrides the link
+<span class="file">ntpkey_iff_</span><kbd>hostname</kbd>
+in the keys directory.
+<br><dt><code>leap</code> <kbd>file</kbd><dd>Specifies the location of the optional leapsecond file.
+This overrides the link
+<span class="file">ntpkey_leap</span>
+in the keys directory.
+<br><dt><code>mvpar</code> <kbd>file</kbd><dd>Specifies the location of the optional MV parameters file.
+This
+overrides the link
+<span class="file">ntpkey_mv_</span><kbd>hostname</kbd>
+in the keys directory.
+<br><dt><code>pw</code> <kbd>password</kbd><dd>Specifies the password to decrypt files containing private keys and
+identity parameters.
+This is required only if these files have been
+encrypted.
+<br><dt><code>randfile</code> <kbd>file</kbd><dd>Specifies the location of the random seed file used by the OpenSSL
+library.
+The defaults are described in the main text above.
+<br><dt><code>sign</code> <kbd>file</kbd><dd>Specifies the location of the optional sign key file.
+This overrides
+the link
+<span class="file">ntpkey_sign_</span><kbd>hostname</kbd>
+in the keys directory.
+If this file is
+not found, the host key is also the sign key.
+</dl>
+ <br><dt><code>keys</code> <kbd>keyfile</kbd><dd>Specifies the complete path and location of the MD5 key file
+containing the keys and key identifiers used by
+<code>ntpd(1ntpdmdoc)</code>,
+<code>ntpq(1ntpqmdoc)</code>
+and
+<code>ntpdc(1ntpdcmdoc)</code>
+when operating with symmetric key cryptography.
+This is the same operation as the
+<code>-k</code>
+command line option.
+<br><dt><code>keysdir</code> <kbd>path</kbd><dd>This command specifies the default directory path for
+cryptographic keys, parameters and certificates.
+The default is
+<span class="file">/usr/local/etc/</span>.
+<br><dt><code>requestkey</code> <kbd>key</kbd><dd>Specifies the key identifier to use with the
+<code>ntpdc(1ntpdcmdoc)</code>
+utility program, which uses a
+proprietary protocol specific to this implementation of
+<code>ntpd(1ntpdmdoc)</code>.
+The
+<kbd>key</kbd>
+argument is a key identifier
+for the trusted key, where the value can be in the range 1 to
+65,534, inclusive.
+<br><dt><code>revoke</code> <kbd>logsec</kbd><dd>Specifies the interval between re-randomization of certain
+cryptographic values used by the Autokey scheme, as a power of 2 in
+seconds.
+These values need to be updated frequently in order to
+deflect brute-force attacks on the algorithms of the scheme;
+however, updating some values is a relatively expensive operation.
+The default interval is 16 (65,536 s or about 18 hours).
+For poll
+intervals above the specified interval, the values will be updated
+for every message sent.
+<br><dt><code>trustedkey</code> <kbd>key</kbd> <kbd>...</kbd><dd>Specifies the key identifiers which are trusted for the
+purposes of authenticating peers with symmetric key cryptography,
+as well as keys used by the
+<code>ntpq(1ntpqmdoc)</code>
+and
+<code>ntpdc(1ntpdcmdoc)</code>
+programs.
+The authentication procedures require that both the local
+and remote servers share the same key and key identifier for this
+purpose, although different keys can be used with different
+servers.
+The
+<kbd>key</kbd>
+arguments are 32-bit unsigned
+integers with values from 1 to 65,534.
+</dl>
+
+<h5 class="subsubsection">Error Codes</h5>
+
+<p>The following error codes are reported via the NTP control
+and monitoring protocol trap mechanism.
+ <dl>
+<dt>101<dd>(bad field format or length)
+The packet has invalid version, length or format.
+<br><dt>102<dd>(bad timestamp)
+The packet timestamp is the same or older than the most recent received.
+This could be due to a replay or a server clock time step.
+<br><dt>103<dd>(bad filestamp)
+The packet filestamp is the same or older than the most recent received.
+This could be due to a replay or a key file generation error.
+<br><dt>104<dd>(bad or missing public key)
+The public key is missing, has incorrect format or is an unsupported type.
+<br><dt>105<dd>(unsupported digest type)
+The server requires an unsupported digest/signature scheme.
+<br><dt>106<dd>(mismatched digest types)
+Not used.
+<br><dt>107<dd>(bad signature length)
+The signature length does not match the current public key.
+<br><dt>108<dd>(signature not verified)
+The message fails the signature check.
+It could be bogus or signed by a
+different private key.
+<br><dt>109<dd>(certificate not verified)
+The certificate is invalid or signed with the wrong key.
+<br><dt>110<dd>(certificate not verified)
+The certificate is not yet valid or has expired or the signature could not
+be verified.
+<br><dt>111<dd>(bad or missing cookie)
+The cookie is missing, corrupted or bogus.
+<br><dt>112<dd>(bad or missing leapseconds table)
+The leapseconds table is missing, corrupted or bogus.
+<br><dt>113<dd>(bad or missing certificate)
+The certificate is missing, corrupted or bogus.
+<br><dt>114<dd>(bad or missing identity)
+The identity key is missing, corrupt or bogus.
+</dl>
+ <div class="node">
+<p><hr>
+<a name="Monitoring-Support"></a>
+<br>
+</div>
+
+<h4 class="subsection">Monitoring Support</h4>
+
+<p><code>ntpd(1ntpdmdoc)</code>
+includes a comprehensive monitoring facility suitable
+for continuous, long term recording of server and client
+timekeeping performance.
+See the
+<code>statistics</code>
+command below
+for a listing and example of each type of statistics currently
+supported.
+Statistic files are managed using file generation sets
+and scripts in the
+<span class="file">./scripts</span>
+directory of this distribution.
+Using
+these facilities and
+<span class="sc">unix</span>
+<code>cron(8)</code>
+jobs, the data can be
+automatically summarized and archived for retrospective analysis.
+
+<h5 class="subsubsection">Monitoring Commands</h5>
+
+ <dl>
+<dt><code>statistics</code> <kbd>name</kbd> <kbd>...</kbd><dd>Enables writing of statistics records.
+Currently, eight kinds of
+<kbd>name</kbd>
+statistics are supported.
+ <dl>
+<dt><code>clockstats</code><dd>Enables recording of clock driver statistics information.
+Each update
+received from a clock driver appends a line of the following form to
+the file generation set named
+<code>clockstats</code>:
+<pre class="verbatim">
+ 49213 525.624 127.127.4.1 93 226 00:08:29.606 D
+ </pre>
+
+ <p>The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the
+clock address in dotted-quad notation.
+The final field shows the last
+timecode received from the clock in decoded ASCII format, where
+meaningful.
+In some clock drivers a good deal of additional information
+can be gathered and displayed as well.
+See information specific to each
+clock for further details.
+<br><dt><code>cryptostats</code><dd>This option requires the OpenSSL cryptographic software library.
+It
+enables recording of cryptographic public key protocol information.
+Each message received by the protocol module appends a line of the
+following form to the file generation set named
+<code>cryptostats</code>:
+<pre class="verbatim">
+ 49213 525.624 127.127.4.1 message
+ </pre>
+
+ <p>The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the peer
+address in dotted-quad notation, The final message field includes the
+message type and certain ancillary information.
+See the
+<a href="#Authentication-Options">Authentication Options</a>
+section for further information.
+<br><dt><code>loopstats</code><dd>Enables recording of loop filter statistics information.
+Each
+update of the local clock outputs a line of the following form to
+the file generation set named
+<code>loopstats</code>:
+<pre class="verbatim">
+ 50935 75440.031 0.000006019 13.778190 0.000351733 0.0133806
+ </pre>
+
+ <p>The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next five fields
+show time offset (seconds), frequency offset (parts per million -
+PPM), RMS jitter (seconds), Allan deviation (PPM) and clock
+discipline time constant.
+<br><dt><code>peerstats</code><dd>Enables recording of peer statistics information.
+This includes
+statistics records of all peers of a NTP server and of special
+signals, where present and configured.
+Each valid update appends a
+line of the following form to the current element of a file
+generation set named
+<code>peerstats</code>:
+<pre class="verbatim">
+ 48773 10847.650 127.127.4.1 9714 -0.001605376 0.000000000 0.001424877 0.000958674
+ </pre>
+
+ <p>The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the peer address in dotted-quad notation and status,
+respectively.
+The status field is encoded in hex in the format
+described in Appendix A of the NTP specification RFC 1305.
+The final four fields show the offset,
+delay, dispersion and RMS jitter, all in seconds.
+<br><dt><code>rawstats</code><dd>Enables recording of raw-timestamp statistics information.
+This
+includes statistics records of all peers of a NTP server and of
+special signals, where present and configured.
+Each NTP message
+received from a peer or clock driver appends a line of the
+following form to the file generation set named
+<code>rawstats</code>:
+<pre class="verbatim">
+ 50928 2132.543 128.4.1.1 128.4.1.20 3102453281.584327000 3102453281.58622800031 02453332.540806000 3102453332.541458000
+ </pre>
+
+ <p>The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the remote peer or clock address followed by the local address
+in dotted-quad notation.
+The final four fields show the originate,
+receive, transmit and final NTP timestamps in order.
+The timestamp
+values are as received and before processing by the various data
+smoothing and mitigation algorithms.
+<br><dt><code>sysstats</code><dd>Enables recording of ntpd statistics counters on a periodic basis.
+Each
+hour a line of the following form is appended to the file generation
+set named
+<code>sysstats</code>:
+<pre class="verbatim">
+ 50928 2132.543 36000 81965 0 9546 56 71793 512 540 10 147
+ </pre>
+
+ <p>The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The remaining ten fields show
+the statistics counter values accumulated since the last generated
+line.
+ <dl>
+<dt>Time since restart <code>36000</code><dd>Time in hours since the system was last rebooted.
+<br><dt>Packets received <code>81965</code><dd>Total number of packets received.
+<br><dt>Packets processed <code>0</code><dd>Number of packets received in response to previous packets sent
+<br><dt>Current version <code>9546</code><dd>Number of packets matching the current NTP version.
+<br><dt>Previous version <code>56</code><dd>Number of packets matching the previous NTP version.
+<br><dt>Bad version <code>71793</code><dd>Number of packets matching neither NTP version.
+<br><dt>Access denied <code>512</code><dd>Number of packets denied access for any reason.
+<br><dt>Bad length or format <code>540</code><dd>Number of packets with invalid length, format or port number.
+<br><dt>Bad authentication <code>10</code><dd>Number of packets not verified as authentic.
+<br><dt>Rate exceeded <code>147</code><dd>Number of packets discarded due to rate limitation.
+</dl>
+ <br><dt><code>statsdir</code> <kbd>directory_path</kbd><dd>Indicates the full path of a directory where statistics files
+should be created (see below).
+This keyword allows
+the (otherwise constant)
+<code>filegen</code>
+filename prefix to be modified for file generation sets, which
+is useful for handling statistics logs.
+<br><dt><code>filegen</code> <kbd>name</kbd> <code>[file </code><kbd>filename</kbd><code>]</code> <code>[type </code><kbd>typename</kbd><code>]</code> <code>[link | nolink]</code> <code>[enable | disable]</code><dd>Configures setting of generation file set name.
+Generation
+file sets provide a means for handling files that are
+continuously growing during the lifetime of a server.
+Server statistics are a typical example for such files.
+Generation file sets provide access to a set of files used
+to store the actual data.
+At any time at most one element
+of the set is being written to.
+The type given specifies
+when and how data will be directed to a new element of the set.
+This way, information stored in elements of a file set
+that are currently unused are available for administrational
+operations without the risk of disturbing the operation of ntpd.
+(Most important: they can be removed to free space for new data
+produced.)
+
+ <p>Note that this command can be sent from the
+<code>ntpdc(1ntpdcmdoc)</code>
+program running at a remote location.
+ <dl>
+<dt><code>name</code><dd>This is the type of the statistics records, as shown in the
+<code>statistics</code>
+command.
+<br><dt><code>file</code> <kbd>filename</kbd><dd>This is the file name for the statistics records.
+Filenames of set
+members are built from three concatenated elements
+<code>prefix</code>,
+<code>filename</code>
+and
+<code>suffix</code>:
+ <dl>
+<dt><code>prefix</code><dd>This is a constant filename path.
+It is not subject to
+modifications via the
+<kbd>filegen</kbd>
+option.
+It is defined by the
+server, usually specified as a compile-time constant.
+It may,
+however, be configurable for individual file generation sets
+via other commands.
+For example, the prefix used with
+<kbd>loopstats</kbd>
+and
+<kbd>peerstats</kbd>
+generation can be configured using the
+<kbd>statsdir</kbd>
+option explained above.
+<br><dt><code>filename</code><dd>This string is directly concatenated to the prefix mentioned
+above (no intervening
+/).
+This can be modified using
+the file argument to the
+<kbd>filegen</kbd>
+statement.
+No
+<span class="file">..</span>
+elements are
+allowed in this component to prevent filenames referring to
+parts outside the filesystem hierarchy denoted by
+<kbd>prefix</kbd>.
+<br><dt><code>suffix</code><dd>This part is reflects individual elements of a file set.
+It is
+generated according to the type of a file set.
+</dl>
+ <br><dt><code>type</code> <kbd>typename</kbd><dd>A file generation set is characterized by its type.
+The following
+types are supported:
+ <dl>
+<dt><code>none</code><dd>The file set is actually a single plain file.
+<br><dt><code>pid</code><dd>One element of file set is used per incarnation of a ntpd
+server.
+This type does not perform any changes to file set
+members during runtime, however it provides an easy way of
+separating files belonging to different
+<code>ntpd(1ntpdmdoc)</code>
+server incarnations.
+The set member filename is built by appending a
+.
+to concatenated
+<kbd>prefix</kbd>
+and
+<kbd>filename</kbd>
+strings, and
+appending the decimal representation of the process ID of the
+<code>ntpd(1ntpdmdoc)</code>
+server process.
+<br><dt><code>day</code><dd>One file generation set element is created per day.
+A day is
+defined as the period between 00:00 and 24:00 UTC.
+The file set
+member suffix consists of a
+.
+and a day specification in
+the form
+<code>YYYYMMdd</code>.
+<code>YYYY</code>
+is a 4-digit year number (e.g., 1992).
+<code>MM</code>
+is a two digit month number.
+<code>dd</code>
+is a two digit day number.
+Thus, all information written at 10 December 1992 would end up
+in a file named
+<kbd>prefix</kbd>
+<kbd>filename</kbd>.19921210.
+<br><dt><code>week</code><dd>Any file set member contains data related to a certain week of
+a year.
+The term week is defined by computing day-of-year
+modulo 7.
+Elements of such a file generation set are
+distinguished by appending the following suffix to the file set
+filename base: A dot, a 4-digit year number, the letter
+<code>W</code>,
+and a 2-digit week number.
+For example, information from January,
+10th 1992 would end up in a file with suffix
+.No . Ns Ar 1992W1 .
+<br><dt><code>month</code><dd>One generation file set element is generated per month.
+The
+file name suffix consists of a dot, a 4-digit year number, and
+a 2-digit month.
+<br><dt><code>year</code><dd>One generation file element is generated per year.
+The filename
+suffix consists of a dot and a 4 digit year number.
+<br><dt><code>age</code><dd>This type of file generation sets changes to a new element of
+the file set every 24 hours of server operation.
+The filename
+suffix consists of a dot, the letter
+<code>a</code>,
+and an 8-digit number.
+This number is taken to be the number of seconds the server is
+running at the start of the corresponding 24-hour period.
+Information is only written to a file generation by specifying
+<code>enable</code>;
+output is prevented by specifying
+<code>disable</code>.
+</dl>
+ <br><dt><code>link</code> | <code>nolink</code><dd>It is convenient to be able to access the current element of a file
+generation set by a fixed name.
+This feature is enabled by
+specifying
+<code>link</code>
+and disabled using
+<code>nolink</code>.
+If link is specified, a
+hard link from the current file set element to a file without
+suffix is created.
+When there is already a file with this name and
+the number of links of this file is one, it is renamed appending a
+dot, the letter
+<code>C</code>,
+and the pid of the ntpd server process.
+When the
+number of links is greater than one, the file is unlinked.
+This
+allows the current file to be accessed by a constant name.
+<br><dt><code>enable</code> <code>|</code> <code>disable</code><dd>Enables or disables the recording function.
+</dl>
+ </dl>
+ </dl>
+<div class="node">
+<p><hr>
+<a name="Access-Control-Support"></a>
+<br>
+</div>
+
+<h4 class="subsection">Access Control Support</h4>
+
+<p>The
+<code>ntpd(1ntpdmdoc)</code>
+daemon implements a general purpose address/mask based restriction
+list.
+The list contains address/match entries sorted first
+by increasing address values and and then by increasing mask values.
+A match occurs when the bitwise AND of the mask and the packet
+source address is equal to the bitwise AND of the mask and
+address in the list.
+The list is searched in order with the
+last match found defining the restriction flags associated
+with the entry.
+Additional information and examples can be found in the
+"Notes on Configuring NTP and Setting up a NTP Subnet"
+page
+(available as part of the HTML documentation
+provided in
+<span class="file">/usr/share/doc/ntp</span>).
+
+ <p>The restriction facility was implemented in conformance
+with the access policies for the original NSFnet backbone
+time servers.
+Later the facility was expanded to deflect
+cryptographic and clogging attacks.
+While this facility may
+be useful for keeping unwanted or broken or malicious clients
+from congesting innocent servers, it should not be considered
+an alternative to the NTP authentication facilities.
+Source address based restrictions are easily circumvented
+by a determined cracker.
+
+ <p>Clients can be denied service because they are explicitly
+included in the restrict list created by the restrict command
+or implicitly as the result of cryptographic or rate limit
+violations.
+Cryptographic violations include certificate
+or identity verification failure; rate limit violations generally
+result from defective NTP implementations that send packets
+at abusive rates.
+Some violations cause denied service
+only for the offending packet, others cause denied service
+for a timed period and others cause the denied service for
+an indefinate period.
+When a client or network is denied access
+for an indefinate period, the only way at present to remove
+the restrictions is by restarting the server.
+
+<h5 class="subsubsection">The Kiss-of-Death Packet</h5>
+
+<p>Ordinarily, packets denied service are simply dropped with no
+further action except incrementing statistics counters.
+Sometimes a
+more proactive response is needed, such as a server message that
+explicitly requests the client to stop sending and leave a message
+for the system operator.
+A special packet format has been created
+for this purpose called the "kiss-of-death" (KoD) packet.
+KoD packets have the leap bits set unsynchronized and stratum set
+to zero and the reference identifier field set to a four-byte
+ASCII code.
+If the
+<code>noserve</code>
+or
+<code>notrust</code>
+flag of the matching restrict list entry is set,
+the code is "DENY"; if the
+<code>limited</code>
+flag is set and the rate limit
+is exceeded, the code is "RATE".
+Finally, if a cryptographic violation occurs, the code is "CRYP".
+
+ <p>A client receiving a KoD performs a set of sanity checks to
+minimize security exposure, then updates the stratum and
+reference identifier peer variables, sets the access
+denied (TEST4) bit in the peer flash variable and sends
+a message to the log.
+As long as the TEST4 bit is set,
+the client will send no further packets to the server.
+The only way at present to recover from this condition is
+to restart the protocol at both the client and server.
+This
+happens automatically at the client when the association times out.
+It will happen at the server only if the server operator cooperates.
+
+<h5 class="subsubsection">Access Control Commands</h5>
+
+ <dl>
+<dt><code>discard</code> <code>[average </code><kbd>avg</kbd><code>]</code> <code>[minimum </code><kbd>min</kbd><code>]</code> <code>[monitor </code><kbd>prob</kbd><code>]</code><dd>Set the parameters of the
+<code>limited</code>
+facility which protects the server from
+client abuse.
+The
+<code>average</code>
+subcommand specifies the minimum average packet
+spacing, while the
+<code>minimum</code>
+subcommand specifies the minimum packet spacing.
+Packets that violate these minima are discarded
+and a kiss-o'-death packet returned if enabled.
+The default
+minimum average and minimum are 5 and 2, respectively.
+The monitor subcommand specifies the probability of discard
+for packets that overflow the rate-control window.
+<br><dt><code>restrict</code> <code>address</code> <code>[mask </code><kbd>mask</kbd><code>]</code> <code>[</code><kbd>flag</kbd> <kbd>...</kbd><code>]</code><dd>The
+<kbd>address</kbd>
+argument expressed in
+dotted-quad form is the address of a host or network.
+Alternatively, the
+<kbd>address</kbd>
+argument can be a valid host DNS name.
+The
+<kbd>mask</kbd>
+argument expressed in dotted-quad form defaults to
+<code>255.255.255.255</code>,
+meaning that the
+<kbd>address</kbd>
+is treated as the address of an individual host.
+A default entry (address
+<code>0.0.0.0</code>,
+mask
+<code>0.0.0.0</code>)
+is always included and is always the first entry in the list.
+Note that text string
+<code>default</code>,
+with no mask option, may
+be used to indicate the default entry.
+In the current implementation,
+<code>flag</code>
+always
+restricts access, i.e., an entry with no flags indicates that free
+access to the server is to be given.
+The flags are not orthogonal,
+in that more restrictive flags will often make less restrictive
+ones redundant.
+The flags can generally be classed into two
+categories, those which restrict time service and those which
+restrict informational queries and attempts to do run-time
+reconfiguration of the server.
+One or more of the following flags
+may be specified:
+ <dl>
+<dt><code>ignore</code><dd>Deny packets of all kinds, including
+<code>ntpq(1ntpqmdoc)</code>
+and
+<code>ntpdc(1ntpdcmdoc)</code>
+queries.
+<br><dt><code>kod</code><dd>If this flag is set when an access violation occurs, a kiss-o'-death
+(KoD) packet is sent.
+KoD packets are rate limited to no more than one
+per second.
+If another KoD packet occurs within one second after the
+last one, the packet is dropped.
+<br><dt><code>limited</code><dd>Deny service if the packet spacing violates the lower limits specified
+in the discard command.
+A history of clients is kept using the
+monitoring capability of
+<code>ntpd(1ntpdmdoc)</code>.
+Thus, monitoring is always active as
+long as there is a restriction entry with the
+<code>limited</code>
+flag.
+<br><dt><code>lowpriotrap</code><dd>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.
+<br><dt><code>nomodify</code><dd>Deny
+<code>ntpq(1ntpqmdoc)</code>
+and
+<code>ntpdc(1ntpdcmdoc)</code>
+queries which attempt to modify the state of the
+server (i.e., run time reconfiguration).
+Queries which return
+information are permitted.
+<br><dt><code>noquery</code><dd>Deny
+<code>ntpq(1ntpqmdoc)</code>
+and
+<code>ntpdc(1ntpdcmdoc)</code>
+queries.
+Time service is not affected.
+<br><dt><code>nopeer</code><dd>Deny packets which would result in mobilizing a new association.
+This
+includes broadcast and symmetric active packets when a configured
+association does not exist.
+It also includes
+<code>pool</code>
+associations, so if you want to use servers from a
+<code>pool</code>
+directive and also want to use
+<code>nopeer</code>
+by default, you'll want a
+<code>restrict source ...</code> <code>line</code> <code>as</code> <code>well</code> <code>that</code> <code>does</code>
+<br><dt>not<dd>include the
+<code>nopeer</code>
+directive.
+<br><dt><code>noserve</code><dd>Deny all packets except
+<code>ntpq(1ntpqmdoc)</code>
+and
+<code>ntpdc(1ntpdcmdoc)</code>
+queries.
+<br><dt><code>notrap</code><dd>Decline to provide mode 6 control message trap service to matching
+hosts.
+The trap service is a subsystem of the ntpdq control message
+protocol which is intended for use by remote event logging programs.
+<br><dt><code>notrust</code><dd>Deny service unless the packet is cryptographically authenticated.
+<br><dt><code>ntpport</code><dd>This is actually a match algorithm modifier, rather than a
+restriction flag.
+Its presence causes the restriction entry to be
+matched only if the source port in the packet is the standard NTP
+UDP port (123).
+Both
+<code>ntpport</code>
+and
+<code>non-ntpport</code>
+may
+be specified.
+The
+<code>ntpport</code>
+is considered more specific and
+is sorted later in the list.
+<br><dt><code>version</code><dd>Deny packets that do not match the current NTP version.
+</dl>
+
+ <p>Default restriction list entries with the flags ignore, interface,
+ntpport, for each of the local host's interface addresses are
+inserted into the table at startup to prevent the server
+from attempting to synchronize to its own time.
+A default entry is also always present, though if it is
+otherwise unconfigured; no flags are associated
+with the default entry (i.e., everything besides your own
+NTP server is unrestricted).
+</dl>
+<div class="node">
+<p><hr>
+<a name="Automatic-NTP-Configuration-Options"></a>
+<br>
+</div>
+
+<h4 class="subsection">Automatic NTP Configuration Options</h4>
+
+<h5 class="subsubsection">Manycasting</h5>
+
+<p>Manycasting is a automatic discovery and configuration paradigm
+new to NTPv4.
+It is intended as a means for a multicast client
+to troll the nearby network neighborhood to find cooperating
+manycast servers, validate them using cryptographic means
+and evaluate their time values with respect to other servers
+that might be lurking in the vicinity.
+The intended result is that each manycast client mobilizes
+client associations with some number of the "best"
+of the nearby manycast servers, yet automatically reconfigures
+to sustain this number of servers should one or another fail.
+
+ <p>Note that the manycasting paradigm does not coincide
+with the anycast paradigm described in RFC-1546,
+which is designed to find a single server from a clique
+of servers providing the same service.
+The manycast paradigm is designed to find a plurality
+of redundant servers satisfying defined optimality criteria.
+
+ <p>Manycasting can be used with either symmetric key
+or public key cryptography.
+The public key infrastructure (PKI)
+offers the best protection against compromised keys
+and is generally considered stronger, at least with relatively
+large key sizes.
+It is implemented using the Autokey protocol and
+the OpenSSL cryptographic library available from
+<code>http://www.openssl.org/</code>.
+The library can also be used with other NTPv4 modes
+as well and is highly recommended, especially for broadcast modes.
+
+ <p>A persistent manycast client association is configured
+using the manycastclient command, which is similar to the
+server command but with a multicast (IPv4 class
+<code>D</code>
+or IPv6 prefix
+<code>FF</code>)
+group address.
+The IANA has designated IPv4 address 224.1.1.1
+and IPv6 address FF05::101 (site local) for NTP.
+When more servers are needed, it broadcasts manycast
+client messages to this address at the minimum feasible rate
+and minimum feasible time-to-live (TTL) hops, depending
+on how many servers have already been found.
+There can be as many manycast client associations
+as different group address, each one serving as a template
+for a future ephemeral unicast client/server association.
+
+ <p>Manycast servers configured with the
+<code>manycastserver</code>
+command listen on the specified group address for manycast
+client messages.
+Note the distinction between manycast client,
+which actively broadcasts messages, and manycast server,
+which passively responds to them.
+If a manycast server is
+in scope of the current TTL and is itself synchronized
+to a valid source and operating at a stratum level equal
+to or lower than the manycast client, it replies to the
+manycast client message with an ordinary unicast server message.
+
+ <p>The manycast client receiving this message mobilizes
+an ephemeral client/server association according to the
+matching manycast client template, but only if cryptographically
+authenticated and the server stratum is less than or equal
+to the client stratum.
+Authentication is explicitly required
+and either symmetric key or public key (Autokey) can be used.
+Then, the client polls the server at its unicast address
+in burst mode in order to reliably set the host clock
+and validate the source.
+This normally results
+in a volley of eight client/server at 2-s intervals
+during which both the synchronization and cryptographic
+protocols run concurrently.
+Following the volley,
+the client runs the NTP intersection and clustering
+algorithms, which act to discard all but the "best"
+associations according to stratum and synchronization
+distance.
+The surviving associations then continue
+in ordinary client/server mode.
+
+ <p>The manycast client polling strategy is designed to reduce
+as much as possible the volume of manycast client messages
+and the effects of implosion due to near-simultaneous
+arrival of manycast server messages.
+The strategy is determined by the
+<code>manycastclient</code>,
+<code>tos</code>
+and
+<code>ttl</code>
+configuration commands.
+The manycast poll interval is
+normally eight times the system poll interval,
+which starts out at the
+<code>minpoll</code>
+value specified in the
+<code>manycastclient</code>,
+command and, under normal circumstances, increments to the
+<code>maxpolll</code>
+value specified in this command.
+Initially, the TTL is
+set at the minimum hops specified by the ttl command.
+At each retransmission the TTL is increased until reaching
+the maximum hops specified by this command or a sufficient
+number client associations have been found.
+Further retransmissions use the same TTL.
+
+ <p>The quality and reliability of the suite of associations
+discovered by the manycast client is determined by the NTP
+mitigation algorithms and the
+<code>minclock</code>
+and
+<code>minsane</code>
+values specified in the
+<code>tos</code>
+configuration command.
+At least
+<code>minsane</code>
+candidate servers must be available and the mitigation
+algorithms produce at least
+<code>minclock</code>
+survivors in order to synchronize the clock.
+Byzantine agreement principles require at least four
+candidates in order to correctly discard a single falseticker.
+For legacy purposes,
+<code>minsane</code>
+defaults to 1 and
+<code>minclock</code>
+defaults to 3.
+For manycast service
+<code>minsane</code>
+should be explicitly set to 4, assuming at least that
+number of servers are available.
+
+ <p>If at least
+<code>minclock</code>
+servers are found, the manycast poll interval is immediately
+set to eight times
+<code>maxpoll</code>.
+If less than
+<code>minclock</code>
+servers are found when the TTL has reached the maximum hops,
+the manycast poll interval is doubled.
+For each transmission
+after that, the poll interval is doubled again until
+reaching the maximum of eight times
+<code>maxpoll</code>.
+Further transmissions use the same poll interval and
+TTL values.
+Note that while all this is going on,
+each client/server association found is operating normally
+it the system poll interval.
+
+ <p>Administratively scoped multicast boundaries are normally
+specified by the network router configuration and,
+in the case of IPv6, the link/site scope prefix.
+By default, the increment for TTL hops is 32 starting
+from 31; however, the
+<code>ttl</code>
+configuration command can be
+used to modify the values to match the scope rules.
+
+ <p>It is often useful to narrow the range of acceptable
+servers which can be found by manycast client associations.
+Because manycast servers respond only when the client
+stratum is equal to or greater than the server stratum,
+primary (stratum 1) servers fill find only primary servers
+in TTL range, which is probably the most common objective.
+However, unless configured otherwise, all manycast clients
+in TTL range will eventually find all primary servers
+in TTL range, which is probably not the most common
+objective in large networks.
+The
+<code>tos</code>
+command can be used to modify this behavior.
+Servers with stratum below
+<code>floor</code>
+or above
+<code>ceiling</code>
+specified in the
+<code>tos</code>
+command are strongly discouraged during the selection
+process; however, these servers may be temporally
+accepted if the number of servers within TTL range is
+less than
+<code>minclock</code>.
+
+ <p>The above actions occur for each manycast client message,
+which repeats at the designated poll interval.
+However, once the ephemeral client association is mobilized,
+subsequent manycast server replies are discarded,
+since that would result in a duplicate association.
+If during a poll interval the number of client associations
+falls below
+<code>minclock</code>,
+all manycast client prototype associations are reset
+to the initial poll interval and TTL hops and operation
+resumes from the beginning.
+It is important to avoid
+frequent manycast client messages, since each one requires
+all manycast servers in TTL range to respond.
+The result could well be an implosion, either minor or major,
+depending on the number of servers in range.
+The recommended value for
+<code>maxpoll</code>
+is 12 (4,096 s).
+
+ <p>It is possible and frequently useful to configure a host
+as both manycast client and manycast server.
+A number of hosts configured this way and sharing a common
+group address will automatically organize themselves
+in an optimum configuration based on stratum and
+synchronization distance.
+For example, consider an NTP
+subnet of two primary servers and a hundred or more
+dependent clients.
+With two exceptions, all servers
+and clients have identical configuration files including both
+<code>multicastclient</code>
+and
+<code>multicastserver</code>
+commands using, for instance, multicast group address
+239.1.1.1.
+The only exception is that each primary server
+configuration file must include commands for the primary
+reference source such as a GPS receiver.
+
+ <p>The remaining configuration files for all secondary
+servers and clients have the same contents, except for the
+<code>tos</code>
+command, which is specific for each stratum level.
+For stratum 1 and stratum 2 servers, that command is
+not necessary.
+For stratum 3 and above servers the
+<code>floor</code>
+value is set to the intended stratum number.
+Thus, all stratum 3 configuration files are identical,
+all stratum 4 files are identical and so forth.
+
+ <p>Once operations have stabilized in this scenario,
+the primary servers will find the primary reference source
+and each other, since they both operate at the same
+stratum (1), but not with any secondary server or client,
+since these operate at a higher stratum.
+The secondary
+servers will find the servers at the same stratum level.
+If one of the primary servers loses its GPS receiver,
+it will continue to operate as a client and other clients
+will time out the corresponding association and
+re-associate accordingly.
+
+ <p>Some administrators prefer to avoid running
+<code>ntpd(1ntpdmdoc)</code>
+continuously and run either
+<code>ntpdate(8)</code>
+or
+<code>ntpd(1ntpdmdoc)</code>
+<code>-q</code>
+as a cron job.
+In either case the servers must be
+configured in advance and the program fails if none are
+available when the cron job runs.
+A really slick
+application of manycast is with
+<code>ntpd(1ntpdmdoc)</code>
+<code>-q</code>.
+The program wakes up, scans the local landscape looking
+for the usual suspects, selects the best from among
+the rascals, sets the clock and then departs.
+Servers do not have to be configured in advance and
+all clients throughout the network can have the same
+configuration file.
+
+<h5 class="subsubsection">Manycast Interactions with Autokey</h5>
+
+<p>Each time a manycast client sends a client mode packet
+to a multicast group address, all manycast servers
+in scope generate a reply including the host name
+and status word.
+The manycast clients then run
+the Autokey protocol, which collects and verifies
+all certificates involved.
+Following the burst interval
+all but three survivors are cast off,
+but the certificates remain in the local cache.
+It often happens that several complete signing trails
+from the client to the primary servers are collected in this way.
+
+ <p>About once an hour or less often if the poll interval
+exceeds this, the client regenerates the Autokey key list.
+This is in general transparent in client/server mode.
+However, about once per day the server private value
+used to generate cookies is refreshed along with all
+manycast client associations.
+In this case all
+cryptographic values including certificates is refreshed.
+If a new certificate has been generated since
+the last refresh epoch, it will automatically revoke
+all prior certificates that happen to be in the
+certificate cache.
+At the same time, the manycast
+scheme starts all over from the beginning and
+the expanding ring shrinks to the minimum and increments
+from there while collecting all servers in scope.
+
+<h5 class="subsubsection">Manycast Options</h5>
+
+ <dl>
+<dt><code>tos</code> <code>[ceiling </code><kbd>ceiling</kbd><code> | cohort { 0 | 1 } | floor </code><kbd>floor</kbd><code> | minclock </code><kbd>minclock</kbd><code> | minsane </code><kbd>minsane</kbd><code>]</code><dd>This command affects the clock selection and clustering
+algorithms.
+It can be used to select the quality and
+quantity of peers used to synchronize the system clock
+and is most useful in manycast mode.
+The variables operate
+as follows:
+ <dl>
+<dt><code>ceiling</code> <kbd>ceiling</kbd><dd>Peers with strata above
+<code>ceiling</code>
+will be discarded if there are at least
+<code>minclock</code>
+peers remaining.
+This value defaults to 15, but can be changed
+to any number from 1 to 15.
+<br><dt><code>cohort</code> <code>{0 | 1}</code><dd>This is a binary flag which enables (0) or disables (1)
+manycast server replies to manycast clients with the same
+stratum level.
+This is useful to reduce implosions where
+large numbers of clients with the same stratum level
+are present.
+The default is to enable these replies.
+<br><dt><code>floor</code> <kbd>floor</kbd><dd>Peers with strata below
+<code>floor</code>
+will be discarded if there are at least
+<code>minclock</code>
+peers remaining.
+This value defaults to 1, but can be changed
+to any number from 1 to 15.
+<br><dt><code>minclock</code> <kbd>minclock</kbd><dd>The clustering algorithm repeatedly casts out outlyer
+associations until no more than
+<code>minclock</code>
+associations remain.
+This value defaults to 3,
+but can be changed to any number from 1 to the number of
+configured sources.
+<br><dt><code>minsane</code> <kbd>minsane</kbd><dd>This is the minimum number of candidates available
+to the clock selection algorithm in order to produce
+one or more truechimers for the clustering algorithm.
+If fewer than this number are available, the clock is
+undisciplined and allowed to run free.
+The default is 1
+for legacy purposes.
+However, according to principles of
+Byzantine agreement,
+<code>minsane</code>
+should be at least 4 in order to detect and discard
+a single falseticker.
+</dl>
+ <br><dt><code>ttl</code> <kbd>hop</kbd> <kbd>...</kbd><dd>This command specifies a list of TTL values in increasing
+order, up to 8 values can be specified.
+In manycast mode these values are used in turn
+in an expanding-ring search.
+The default is eight
+multiples of 32 starting at 31.
+</dl>
+<div class="node">
+<p><hr>
+<a name="Reference-Clock-Support"></a>
+<br>
+</div>
+
+<h4 class="subsection">Reference Clock Support</h4>
+
+<p>The NTP Version 4 daemon supports some three dozen different radio,
+satellite and modem reference clocks plus a special pseudo-clock
+used for backup or when no other clock source is available.
+Detailed descriptions of individual device drivers and options can
+be found in the
+"Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+<span class="file">/usr/share/doc/ntp</span>).
+Additional information can be found in the pages linked
+there, including the
+"Debugging Hints for Reference Clock Drivers"
+and
+"How To Write a Reference Clock Driver"
+pages
+(available as part of the HTML documentation
+provided in
+<span class="file">/usr/share/doc/ntp</span>).
+In addition, support for a PPS
+signal is available as described in the
+"Pulse-per-second (PPS) Signal Interfacing"
+page
+(available as part of the HTML documentation
+provided in
+<span class="file">/usr/share/doc/ntp</span>).
+Many
+drivers support special line discipline/streams modules which can
+significantly improve the accuracy using the driver.
+These are
+described in the
+"Line Disciplines and Streams Drivers"
+page
+(available as part of the HTML documentation
+provided in
+<span class="file">/usr/share/doc/ntp</span>).
+
+ <p>A reference clock will generally (though not always) be a radio
+timecode receiver which is synchronized to a source of standard
+time such as the services offered by the NRC in Canada and NIST and
+USNO in the US.
+The interface between the computer and the timecode
+receiver is device dependent, but is usually a serial port.
+A
+device driver specific to each reference clock must be selected and
+compiled in the distribution; however, most common radio, satellite
+and modem clocks are included by default.
+Note that an attempt to
+configure a reference clock when the driver has not been compiled
+or the hardware port has not been appropriately configured results
+in a scalding remark to the system log file, but is otherwise non
+hazardous.
+
+ <p>For the purposes of configuration,
+<code>ntpd(1ntpdmdoc)</code>
+treats
+reference clocks in a manner analogous to normal NTP peers as much
+as possible.
+Reference clocks are identified by a syntactically
+correct but invalid IP address, in order to distinguish them from
+normal NTP peers.
+Reference clock addresses are of the form
+<code>127.127.</code><kbd>t</kbd>.<kbd>u</kbd>,
+where
+<kbd>t</kbd>
+is an integer
+denoting the clock type and
+<kbd>u</kbd>
+indicates the unit
+number in the range 0-3.
+While it may seem overkill, it is in fact
+sometimes useful to configure multiple reference clocks of the same
+type, in which case the unit numbers must be unique.
+
+ <p>The
+<code>server</code>
+command is used to configure a reference
+clock, where the
+<kbd>address</kbd>
+argument in that command
+is the clock address.
+The
+<code>key</code>,
+<code>version</code>
+and
+<code>ttl</code>
+options are not used for reference clock support.
+The
+<code>mode</code>
+option is added for reference clock support, as
+described below.
+The
+<code>prefer</code>
+option can be useful to
+persuade the server to cherish a reference clock with somewhat more
+enthusiasm than other reference clocks or peers.
+Further
+information on this option can be found in the
+"Mitigation Rules and the prefer Keyword"
+(available as part of the HTML documentation
+provided in
+<span class="file">/usr/share/doc/ntp</span>)
+page.
+The
+<code>minpoll</code>
+and
+<code>maxpoll</code>
+options have
+meaning only for selected clock drivers.
+See the individual clock
+driver document pages for additional information.
+
+ <p>The
+<code>fudge</code>
+command is used to provide additional
+information for individual clock drivers and normally follows
+immediately after the
+<code>server</code>
+command.
+The
+<kbd>address</kbd>
+argument specifies the clock address.
+The
+<code>refid</code>
+and
+<code>stratum</code>
+options can be used to
+override the defaults for the device.
+There are two optional
+device-dependent time offsets and four flags that can be included
+in the
+<code>fudge</code>
+command as well.
+
+ <p>The stratum number of a reference clock is by default zero.
+Since the
+<code>ntpd(1ntpdmdoc)</code>
+daemon adds one to the stratum of each
+peer, a primary server ordinarily displays an external stratum of
+one.
+In order to provide engineered backups, it is often useful to
+specify the reference clock stratum as greater than zero.
+The
+<code>stratum</code>
+option is used for this purpose.
+Also, in cases
+involving both a reference clock and a pulse-per-second (PPS)
+discipline signal, it is useful to specify the reference clock
+identifier as other than the default, depending on the driver.
+The
+<code>refid</code>
+option is used for this purpose.
+Except where noted,
+these options apply to all clock drivers.
+
+<h5 class="subsubsection">Reference Clock Commands</h5>
+
+ <dl>
+<dt><code>server</code> <code>127.127.</code><kbd>t</kbd>.<kbd>u</kbd> <code>[prefer]</code> <code>[mode </code><kbd>int</kbd><code>]</code> <code>[minpoll </code><kbd>int</kbd><code>]</code> <code>[maxpoll </code><kbd>int</kbd><code>]</code><dd>This command can be used to configure reference clocks in
+special ways.
+The options are interpreted as follows:
+ <dl>
+<dt><code>prefer</code><dd>Marks the reference clock as preferred.
+All other things being
+equal, this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+"Mitigation Rules and the prefer Keyword"
+page
+(available as part of the HTML documentation
+provided in
+<span class="file">/usr/share/doc/ntp</span>)
+for further information.
+<br><dt><code>mode</code> <kbd>int</kbd><dd>Specifies a mode number which is interpreted in a
+device-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+<br><dt><code>minpoll</code> <kbd>int</kbd><br><dt><code>maxpoll</code> <kbd>int</kbd><dd>These options specify the minimum and maximum polling interval
+for reference clock messages, as a power of 2 in seconds
+For
+most directly connected reference clocks, both
+<code>minpoll</code>
+and
+<code>maxpoll</code>
+default to 6 (64 s).
+For modem reference clocks,
+<code>minpoll</code>
+defaults to 10 (17.1 m) and
+<code>maxpoll</code>
+defaults to 14 (4.5 h).
+The allowable range is 4 (16 s) to 17 (36.4 h) inclusive.
+</dl>
+ <br><dt><code>fudge</code> <code>127.127.</code><kbd>t</kbd>.<kbd>u</kbd> <code>[time1 </code><kbd>sec</kbd><code>]</code> <code>[time2 </code><kbd>sec</kbd><code>]</code> <code>[stratum </code><kbd>int</kbd><code>]</code> <code>[refid </code><kbd>string</kbd><code>]</code> <code>[mode </code><kbd>int</kbd><code>]</code> <code>[flag1 0 | 1]</code> <code>[flag2 0 | 1]</code> <code>[flag3 0 | 1]</code> <code>[flag4 0 | 1]</code><dd>This command can be used to configure reference clocks in
+special ways.
+It must immediately follow the
+<code>server</code>
+command which configures the driver.
+Note that the same capability
+is possible at run time using the
+<code>ntpdc(1ntpdcmdoc)</code>
+program.
+The options are interpreted as
+follows:
+ <dl>
+<dt><code>time1</code> <kbd>sec</kbd><dd>Specifies a constant to be added to the time offset produced by
+the driver, a fixed-point decimal number in seconds.
+This is used
+as a calibration constant to adjust the nominal time offset of a
+particular clock to agree with an external standard, such as a
+precision PPS signal.
+It also provides a way to correct a
+systematic error or bias due to serial port or operating system
+latencies, different cable lengths or receiver internal delay.
+The
+specified offset is in addition to the propagation delay provided
+by other means, such as internal DIPswitches.
+Where a calibration
+for an individual system and driver is available, an approximate
+correction is noted in the driver documentation pages.
+Note: in order to facilitate calibration when more than one
+radio clock or PPS signal is supported, a special calibration
+feature is available.
+It takes the form of an argument to the
+<code>enable</code>
+command described in
+<a href="#Miscellaneous-Options">Miscellaneous Options</a>
+page and operates as described in the
+"Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+<span class="file">/usr/share/doc/ntp</span>).
+<br><dt><code>time2</code> <kbd>secs</kbd><dd>Specifies a fixed-point decimal number in seconds, which is
+interpreted in a driver-dependent way.
+See the descriptions of
+specific drivers in the
+"Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+<span class="file">/usr/share/doc/ntp</span>).
+<br><dt><code>stratum</code> <kbd>int</kbd><dd>Specifies the stratum number assigned to the driver, an integer
+between 0 and 15.
+This number overrides the default stratum number
+ordinarily assigned by the driver itself, usually zero.
+<br><dt><code>refid</code> <kbd>string</kbd><dd>Specifies an ASCII string of from one to four characters which
+defines the reference identifier used by the driver.
+This string
+overrides the default identifier ordinarily assigned by the driver
+itself.
+<br><dt><code>mode</code> <kbd>int</kbd><dd>Specifies a mode number which is interpreted in a
+device-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+<br><dt><code>flag1</code> <code>0</code> <code>|</code> <code>1</code><br><dt><code>flag2</code> <code>0</code> <code>|</code> <code>1</code><br><dt><code>flag3</code> <code>0</code> <code>|</code> <code>1</code><br><dt><code>flag4</code> <code>0</code> <code>|</code> <code>1</code><dd>These four flags are used for customizing the clock driver.
+The
+interpretation of these values, and whether they are used at all,
+is a function of the particular clock driver.
+However, by
+convention
+<code>flag4</code>
+is used to enable recording monitoring
+data to the
+<code>clockstats</code>
+file configured with the
+<code>filegen</code>
+command.
+Further information on the
+<code>filegen</code>
+command can be found in
+<a href="#Monitoring-Options">Monitoring Options</a>.
+</dl>
+ </dl>
+<div class="node">
+<p><hr>
+<a name="Miscellaneous-Options"></a>
+<br>
+</div>
+
+<h4 class="subsection">Miscellaneous Options</h4>
+
+ <dl>
+<dt><code>broadcastdelay</code> <kbd>seconds</kbd><dd>The broadcast and multicast modes require a special calibration
+to determine the network delay between the local and remote
+servers.
+Ordinarily, this is done automatically by the initial
+protocol exchanges between the client and server.
+In some cases,
+the calibration procedure may fail due to network or server access
+controls, for example.
+This command specifies the default delay to
+be used under these circumstances.
+Typically (for Ethernet), a
+number between 0.003 and 0.007 seconds is appropriate.
+The default
+when this command is not used is 0.004 seconds.
+<br><dt><code>calldelay</code> <kbd>delay</kbd><dd>This option controls the delay in seconds between the first and second
+packets sent in burst or iburst mode to allow additional time for a modem
+or ISDN call to complete.
+<br><dt><code>driftfile</code> <kbd>driftfile</kbd><dd>This command specifies the complete path and name of the file used to
+record the frequency of the local clock oscillator.
+This is the same
+operation as the
+<code>-f</code>
+command line option.
+If the file exists, it is read at
+startup in order to set the initial frequency and then updated once per
+hour with the current frequency computed by the daemon.
+If the file name is
+specified, but the file itself does not exist, the starts with an initial
+frequency of zero and creates the file when writing it for the first time.
+If this command is not given, the daemon will always start with an initial
+frequency of zero.
+
+ <p>The file format consists of a single line containing a single
+floating point number, which records the frequency offset measured
+in parts-per-million (PPM).
+The file is updated by first writing
+the current drift value into a temporary file and then renaming
+this file to replace the old version.
+This implies that
+<code>ntpd(1ntpdmdoc)</code>
+must have write permission for the directory the
+drift file is located in, and that file system links, symbolic or
+otherwise, should be avoided.
+<br><dt><code>enable</code> <code>[auth | bclient | calibrate | kernel | mode7 | monitor | ntp | stats]</code><br><dt><code>disable</code> <code>[auth | bclient | calibrate | kernel | mode7 | monitor | ntp | stats]</code><dd>Provides a way to enable or disable various server options.
+Flags not mentioned are unaffected.
+Note that all of these flags
+can be controlled remotely using the
+<code>ntpdc(1ntpdcmdoc)</code>
+utility program.
+ <dl>
+<dt><code>auth</code><dd>Enables the server to synchronize with unconfigured peers only if the
+peer has been correctly authenticated using either public key or
+private key cryptography.
+The default for this flag is
+<code>enable</code>.
+<br><dt><code>bclient</code><dd>Enables the server to listen for a message from a broadcast or
+multicast server, as in the
+<code>multicastclient</code>
+command with default
+address.
+The default for this flag is
+<code>disable</code>.
+<br><dt><code>calibrate</code><dd>Enables the calibrate feature for reference clocks.
+The default for
+this flag is
+<code>disable</code>.
+<br><dt><code>kernel</code><dd>Enables the kernel time discipline, if available.
+The default for this
+flag is
+<code>enable</code>
+if support is available, otherwise
+<code>disable</code>.
+<br><dt><code>mode7</code><dd>Enables processing of NTP mode 7 implementation-specific requests
+which are used by the deprecated
+<code>ntpdc(1ntpdcmdoc)</code>
+program.
+The default for this flag is disable.
+This flag is excluded from runtime configuration using
+<code>ntpq(1ntpqmdoc)</code>.
+The
+<code>ntpq(1ntpqmdoc)</code>
+program provides the same capabilities as
+<code>ntpdc(1ntpdcmdoc)</code>
+using standard mode 6 requests.
+<br><dt><code>monitor</code><dd>Enables the monitoring facility.
+See the
+<code>ntpdc(1ntpdcmdoc)</code>
+program
+and the
+<code>monlist</code>
+command or further information.
+The
+default for this flag is
+<code>enable</code>.
+<br><dt><code>ntp</code><dd>Enables time and frequency discipline.
+In effect, this switch opens and
+closes the feedback loop, which is useful for testing.
+The default for
+this flag is
+<code>enable</code>.
+<br><dt><code>stats</code><dd>Enables the statistics facility.
+See the
+<a href="#Monitoring-Options">Monitoring Options</a>
+section for further information.
+The default for this flag is
+<code>disable</code>.
+</dl>
+ <br><dt><code>includefile</code> <kbd>includefile</kbd><dd>This command allows additional configuration commands
+to be included from a separate file.
+Include files may
+be nested to a depth of five; upon reaching the end of any
+include file, command processing resumes in the previous
+configuration file.
+This option is useful for sites that run
+<code>ntpd(1ntpdmdoc)</code>
+on multiple hosts, with (mostly) common options (e.g., a
+restriction list).
+<br><dt><code>logconfig</code> <kbd>configkeyword</kbd><dd>This command controls the amount and type of output written to
+the system
+<code>syslog(3)</code>
+facility or the alternate
+<code>logfile</code>
+log file.
+By default, all output is turned on.
+All
+<kbd>configkeyword</kbd>
+keywords can be prefixed with
+=,
++
+and
+-,
+where
+=
+sets the
+<code>syslog(3)</code>
+priority mask,
++
+adds and
+-
+removes
+messages.
+<code>syslog(3)</code>
+messages can be controlled in four
+classes
+(<code>clock</code>, <code>peer</code>, <code>sys</code> and <code>sync</code>).
+Within these classes four types of messages can be
+controlled: informational messages
+(<code>info</code>),
+event messages
+(<code>events</code>),
+statistics messages
+(<code>statistics</code>)
+and
+status messages
+(<code>status</code>).
+
+ <p>Configuration keywords are formed by concatenating the message class with
+the event class.
+The
+<code>all</code>
+prefix can be used instead of a message class.
+A
+message class may also be followed by the
+<code>all</code>
+keyword to enable/disable all
+messages of the respective message class.Thus, a minimal log configuration
+could look like this:
+<pre class="verbatim">
+ logconfig =syncstatus +sysevents
+</pre>
+
+ <p>This would just list the synchronizations state of
+<code>ntpd(1ntpdmdoc)</code>
+and the major system events.
+For a simple reference server, the
+following minimum message configuration could be useful:
+<pre class="verbatim">
+ logconfig =syncall +clockall
+</pre>
+
+ <p>This configuration will list all clock information and
+synchronization information.
+All other events and messages about
+peers, system events and so on is suppressed.
+<br><dt><code>logfile</code> <kbd>logfile</kbd><dd>This command specifies the location of an alternate log file to
+be used instead of the default system
+<code>syslog(3)</code>
+facility.
+This is the same operation as the -l command line option.
+<br><dt><code>setvar</code> <kbd>variable</kbd> <code>[default]</code><dd>This command adds an additional system variable.
+These
+variables can be used to distribute additional information such as
+the access policy.
+If the variable of the form
+<code>name</code><code>=</code><kbd>value</kbd>
+is followed by the
+<code>default</code>
+keyword, the
+variable will be listed as part of the default system variables
+(<code>rv</code> command)).
+These additional variables serve
+informational purposes only.
+They are not related to the protocol
+other that they can be listed.
+The known protocol variables will
+always override any variables defined via the
+<code>setvar</code>
+mechanism.
+There are three special variables that contain the names
+of all variable of the same group.
+The
+<code>sys_var_list</code>
+holds
+the names of all system variables.
+The
+<code>peer_var_list</code>
+holds
+the names of all peer variables and the
+<code>clock_var_list</code>
+holds the names of the reference clock variables.
+<br><dt><code>tinker</code> <code>[allan </code><kbd>allan</kbd><code> | dispersion </code><kbd>dispersion</kbd><code> | freq </code><kbd>freq</kbd><code> | huffpuff </code><kbd>huffpuff</kbd><code> | panic </code><kbd>panic</kbd><code> | step </code><kbd>srep</kbd><code> | stepout </code><kbd>stepout</kbd><code>]</code><dd>This command can be used to alter several system variables in
+very exceptional circumstances.
+It should occur in the
+configuration file before any other configuration options.
+The
+default values of these variables have been carefully optimized for
+a wide range of network speeds and reliability expectations.
+In
+general, they interact in intricate ways that are hard to predict
+and some combinations can result in some very nasty behavior.
+Very
+rarely is it necessary to change the default values; but, some
+folks cannot resist twisting the knobs anyway and this command is
+for them.
+Emphasis added: twisters are on their own and can expect
+no help from the support group.
+
+ <p>The variables operate as follows:
+ <dl>
+<dt><code>allan</code> <kbd>allan</kbd><dd>The argument becomes the new value for the minimum Allan
+intercept, which is a parameter of the PLL/FLL clock discipline
+algorithm.
+The value in log2 seconds defaults to 7 (1024 s), which is also the lower
+limit.
+<br><dt><code>dispersion</code> <kbd>dispersion</kbd><dd>The argument becomes the new value for the dispersion increase rate,
+normally .000015 s/s.
+<br><dt><code>freq</code> <kbd>freq</kbd><dd>The argument becomes the initial value of the frequency offset in
+parts-per-million.
+This overrides the value in the frequency file, if
+present, and avoids the initial training state if it is not.
+<br><dt><code>huffpuff</code> <kbd>huffpuff</kbd><dd>The argument becomes the new value for the experimental
+huff-n'-puff filter span, which determines the most recent interval
+the algorithm will search for a minimum delay.
+The lower limit is
+900 s (15 m), but a more reasonable value is 7200 (2 hours).
+There
+is no default, since the filter is not enabled unless this command
+is given.
+<br><dt><code>panic</code> <kbd>panic</kbd><dd>The argument is the panic threshold, normally 1000 s.
+If set to zero,
+the panic sanity check is disabled and a clock offset of any value will
+be accepted.
+<br><dt><code>step</code> <kbd>step</kbd><dd>The argument is the step threshold, which by default is 0.128 s.
+It can
+be set to any positive number in seconds.
+If set to zero, step
+adjustments will never occur.
+Note: The kernel time discipline is
+disabled if the step threshold is set to zero or greater than the
+default.
+<br><dt><code>stepout</code> <kbd>stepout</kbd><dd>The argument is the stepout timeout, which by default is 900 s.
+It can
+be set to any positive number in seconds.
+If set to zero, the stepout
+pulses will not be suppressed.
+</dl>
+ <br><dt><code>rlimit</code> <code>[memlock </code><kbd>Nmegabytes</kbd><code> | stacksize </code><kbd>N4kPages</kbd><code> filenum </code><kbd>Nfiledescriptors</kbd><code>]</code><dd>
+ <dl>
+<dt><code>memlock</code> <kbd>Nmegabytes</kbd><dd>Specify the number of megabytes of memory that can be allocated.
+Probably only available under Linux, this option is useful
+when dropping root (the
+<code>-i</code>
+option).
+The default is 32 megabytes. Setting this to zero will prevent any attemp to lock memory.
+<br><dt><code>stacksize</code> <kbd>N4kPages</kbd><dd>Specifies the maximum size of the process stack on systems with the
+<br><dt><code>filenum</code> <kbd>Nfiledescriptors</kbd><dd>Specifies the maximum number of file descriptors ntpd may have open at once. Defaults to the system default.
+<code>mlockall()</code>
+function.
+Defaults to 50 4k pages (200 4k pages in OpenBSD).
+</dl>
+ <br><dt><code>trap</code> <kbd>host_address</kbd> <code>[port </code><kbd>port_number</kbd><code>]</code> <code>[interface </code><kbd>interface_address</kbd><code>]</code><dd>This command configures a trap receiver at the given host
+address and port number for sending messages with the specified
+local interface address.
+If the port number is unspecified, a value
+of 18447 is used.
+If the interface address is not specified, the
+message is sent with a source address of the local interface the
+message is sent through.
+Note that on a multihomed host the
+interface used may vary from time to time with routing changes.
+
+ <p>The trap receiver will generally log event messages and other
+information from the server in a log file.
+While such monitor
+programs may also request their own trap dynamically, configuring a
+trap receiver will ensure that no messages are lost when the server
+is started.
+<br><dt><code>hop</code> <kbd>...</kbd><dd>This command specifies a list of TTL values in increasing order, up to 8
+values can be specified.
+In manycast mode these values are used in turn in
+an expanding-ring search.
+The default is eight multiples of 32 starting at
+31.
+</dl>
+
+ <p>This section was generated by <strong>AutoGen</strong>,
+using the <code>agtexi-cmd</code> template and the option descriptions for the <code>ntp.conf</code> program.
+This software is released under the NTP license, &lt;http://ntp.org/license&gt;.
+
+<ul class="menu">
+<li><a accesskey="1" href="#ntp_002econf-Files">ntp.conf Files</a>: Files
+<li><a accesskey="2" href="#ntp_002econf-See-Also">ntp.conf See Also</a>: See Also
+<li><a accesskey="3" href="#ntp_002econf-Bugs">ntp.conf Bugs</a>: Bugs
+<li><a accesskey="4" href="#ntp_002econf-Notes">ntp.conf Notes</a>: Notes
+</ul>
+
+<div class="node">
+<p><hr>
+<a name="ntp_002econf-Files"></a>
+<br>
+</div>
+
+<h4 class="subsection">ntp.conf Files</h4>
+
+ <dl>
+<dt><span class="file">/etc/ntp.conf</span><dd>the default name of the configuration file
+<br><dt><span class="file">ntp.keys</span><dd>private MD5 keys
+<br><dt><span class="file">ntpkey</span><dd>RSA private key
+<br><dt><span class="file">ntpkey_</span><kbd>host</kbd><dd>RSA public key
+<br><dt><span class="file">ntp_dh</span><dd>Diffie-Hellman agreement parameters
+</dl>
+<div class="node">
+<p><hr>
+<a name="ntp_002econf-See-Also"></a>
+<br>
+</div>
+
+<h4 class="subsection">ntp.conf See Also</h4>
+
+<p><code>ntpd(1ntpdmdoc)</code>,
+<code>ntpdc(1ntpdcmdoc)</code>,
+<code>ntpq(1ntpqmdoc)</code>
+
+ <p>In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+<code>http://www.ntp.org/</code>.
+A snapshot of this documentation is available in HTML format in
+<span class="file">/usr/share/doc/ntp</span>.
+<br>
+
+ <p><br>
+David L. Mills, <em>Network Time Protocol (Version 4)</em>, RFC5905
+<div class="node">
+<p><hr>
+<a name="ntp_002econf-Bugs"></a>
+<br>
+</div>
+
+<h4 class="subsection">ntp.conf Bugs</h4>
+
+<p>The syntax checking is not picky; some combinations of
+ridiculous and even hilarious options and modes may not be
+detected.
+
+ <p>The
+<span class="file">ntpkey_</span><kbd>host</kbd>
+files are really digital
+certificates.
+These should be obtained via secure directory
+services when they become universally available.
+<div class="node">
+<p><hr>
+<a name="ntp_002econf-Notes"></a>
+<br>
+</div>
+
+<h4 class="subsection">ntp.conf Notes</h4>
+
+<p>This document was derived from FreeBSD.
+
+</body></html>
+
diff --git a/ntpd/ntp.conf.man.in b/ntpd/ntp.conf.man.in
new file mode 100644
index 0000000..3261b6c
--- /dev/null
+++ b/ntpd/ntp.conf.man.in
@@ -0,0 +1,3004 @@
+.de1 NOP
+. it 1 an-trap
+. if \\n[.$] \,\\$*\/
+..
+.ie t \
+.ds B-Font [CB]
+.ds I-Font [CI]
+.ds R-Font [CR]
+.el \
+.ds B-Font B
+.ds I-Font I
+.ds R-Font R
+.TH ntp.conf 5 "02 Dec 2014" "4.2.7p482" "File Formats"
+.\"
+.\" EDIT THIS FILE WITH CAUTION (/tmp/.ag-7eaizK/ag-wfaayK)
+.\"
+.\" It has been AutoGen-ed December 2, 2014 at 08:56:37 AM by AutoGen 5.18.5pre4
+.\" From the definitions ntp.conf.def
+.\" and the template file agman-cmd.tpl
+.SH NAME
+\f\*[B-Font]ntp.conf\fP
+\- Network Time Protocol (NTP) daemon configuration file format
+.SH SYNOPSIS
+\f\*[B-Font]ntp.conf\fP
+[\f\*[B-Font]\-\-option-name\f[]]
+[\f\*[B-Font]\-\-option-name\f[] \f\*[I-Font]value\f[]]
+.sp \n(Ppu
+.ne 2
+
+All arguments must be options.
+.sp \n(Ppu
+.ne 2
+
+.SH DESCRIPTION
+The
+\f\*[B-Font]ntp.conf\fP
+configuration file is read at initial startup by the
+\fCntpd\fR(@NTPD_MS@)\f[]
+daemon in order to specify the synchronization sources,
+modes and other related information.
+Usually, it is installed in the
+\fI/etc\f[]
+directory,
+but could be installed elsewhere
+(see the daemon's
+\f\*[B-Font]\-c\f[]
+command line option).
+.sp \n(Ppu
+.ne 2
+
+The file format is similar to other
+UNIX
+configuration files.
+Comments begin with a
+\[oq]#\[cq]
+character and extend to the end of the line;
+blank lines are ignored.
+Configuration commands consist of an initial keyword
+followed by a list of arguments,
+some of which may be optional, separated by whitespace.
+Commands may not be continued over multiple lines.
+Arguments may be host names,
+host addresses written in numeric, dotted-quad form,
+integers, floating point numbers (when specifying times in seconds)
+and text strings.
+.sp \n(Ppu
+.ne 2
+
+The rest of this page describes the configuration and control options.
+The
+"Notes on Configuring NTP and Setting up an NTP Subnet"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[])
+contains an extended discussion of these options.
+In addition to the discussion of general
+\fIConfiguration\f[] \fIOptions\f[],
+there are sections describing the following supported functionality
+and the options used to control it:
+.IP \fB\(bu\fP 2
+\fIAuthentication\f[] \fISupport\f[]
+.IP \fB\(bu\fP 2
+\fIMonitoring\f[] \fISupport\f[]
+.IP \fB\(bu\fP 2
+\fIAccess\f[] \fIControl\f[] \fISupport\f[]
+.IP \fB\(bu\fP 2
+\fIAutomatic\f[] \fINTP\f[] \fIConfiguration\f[] \fIOptions\f[]
+.IP \fB\(bu\fP 2
+\fIReference\f[] \fIClock\f[] \fISupport\f[]
+.IP \fB\(bu\fP 2
+\fIMiscellaneous\f[] \fIOptions\f[]
+.PP
+.sp \n(Ppu
+.ne 2
+
+Following these is a section describing
+\fIMiscellaneous\f[] \fIOptions\f[].
+While there is a rich set of options available,
+the only required option is one or more
+\f\*[B-Font]pool\f[],
+\f\*[B-Font]server\f[],
+\f\*[B-Font]peer\f[],
+\f\*[B-Font]broadcast\f[]
+or
+\f\*[B-Font]manycastclient\f[]
+commands.
+.SH Configuration Support
+Following is a description of the configuration commands in
+NTPv4.
+These commands have the same basic functions as in NTPv3 and
+in some cases new functions and new arguments.
+There are two
+classes of commands, configuration commands that configure a
+persistent association with a remote server or peer or reference
+clock, and auxiliary commands that specify environmental variables
+that control various related operations.
+.SS Configuration Commands
+The various modes are determined by the command keyword and the
+type of the required IP address.
+Addresses are classed by type as
+(s) a remote server or peer (IPv4 class A, B and C), (b) the
+broadcast address of a local interface, (m) a multicast address (IPv4
+class D), or (r) a reference clock address (127.127.x.x).
+Note that
+only those options applicable to each command are listed below.
+Use
+of options not listed may not be caught as an error, but may result
+in some weird and even destructive behavior.
+.sp \n(Ppu
+.ne 2
+
+If the Basic Socket Interface Extensions for IPv6 (RFC-2553)
+is detected, support for the IPv6 address family is generated
+in addition to the default support of the IPv4 address family.
+In a few cases, including the reslist billboard generated
+by ntpdc, IPv6 addresses are automatically generated.
+IPv6 addresses can be identified by the presence of colons
+\*[Lq]\&:\*[Rq]
+in the address field.
+IPv6 addresses can be used almost everywhere where
+IPv4 addresses can be used,
+with the exception of reference clock addresses,
+which are always IPv4.
+.sp \n(Ppu
+.ne 2
+
+Note that in contexts where a host name is expected, a
+\f\*[B-Font]\-4\f[]
+qualifier preceding
+the host name forces DNS resolution to the IPv4 namespace,
+while a
+\f\*[B-Font]\-6\f[]
+qualifier forces DNS resolution to the IPv6 namespace.
+See IPv6 references for the
+equivalent classes for that address family.
+.TP 7
+.NOP \f\*[B-Font]pool\f[] \f\*[I-Font]address\f[] [\f\*[B-Font]burst\f[]] [\f\*[B-Font]iburst\f[]] [\f\*[B-Font]version\f[] \f\*[I-Font]version\f[]] [\f\*[B-Font]prefer\f[]] [\f\*[B-Font]minpoll\f[] \f\*[I-Font]minpoll\f[]] [\f\*[B-Font]maxpoll\f[] \f\*[I-Font]maxpoll\f[]]
+.TP 7
+.NOP \f\*[B-Font]server\f[] \f\*[I-Font]address\f[] [\f\*[B-Font]key\f[] \f\*[I-Font]key\f[] \f\*[I-Font]\&|\f[] \f\*[B-Font]autokey\f[]] [\f\*[B-Font]burst\f[]] [\f\*[B-Font]iburst\f[]] [\f\*[B-Font]version\f[] \f\*[I-Font]version\f[]] [\f\*[B-Font]prefer\f[]] [\f\*[B-Font]minpoll\f[] \f\*[I-Font]minpoll\f[]] [\f\*[B-Font]maxpoll\f[] \f\*[I-Font]maxpoll\f[]]
+.TP 7
+.NOP \f\*[B-Font]peer\f[] \f\*[I-Font]address\f[] [\f\*[B-Font]key\f[] \f\*[I-Font]key\f[] \f\*[I-Font]\&|\f[] \f\*[B-Font]autokey\f[]] [\f\*[B-Font]version\f[] \f\*[I-Font]version\f[]] [\f\*[B-Font]prefer\f[]] [\f\*[B-Font]minpoll\f[] \f\*[I-Font]minpoll\f[]] [\f\*[B-Font]maxpoll\f[] \f\*[I-Font]maxpoll\f[]]
+.TP 7
+.NOP \f\*[B-Font]broadcast\f[] \f\*[I-Font]address\f[] [\f\*[B-Font]key\f[] \f\*[I-Font]key\f[] \f\*[I-Font]\&|\f[] \f\*[B-Font]autokey\f[]] [\f\*[B-Font]version\f[] \f\*[I-Font]version\f[]] [\f\*[B-Font]prefer\f[]] [\f\*[B-Font]minpoll\f[] \f\*[I-Font]minpoll\f[]] [\f\*[B-Font]ttl\f[] \f\*[I-Font]ttl\f[]]
+.TP 7
+.NOP \f\*[B-Font]manycastclient\f[] \f\*[I-Font]address\f[] [\f\*[B-Font]key\f[] \f\*[I-Font]key\f[] \f\*[I-Font]\&|\f[] \f\*[B-Font]autokey\f[]] [\f\*[B-Font]version\f[] \f\*[I-Font]version\f[]] [\f\*[B-Font]prefer\f[]] [\f\*[B-Font]minpoll\f[] \f\*[I-Font]minpoll\f[]] [\f\*[B-Font]maxpoll\f[] \f\*[I-Font]maxpoll\f[]] [\f\*[B-Font]ttl\f[] \f\*[I-Font]ttl\f[]]
+.PP
+.sp \n(Ppu
+.ne 2
+
+These five commands specify the time server name or address to
+be used and the mode in which to operate.
+The
+\f\*[I-Font]address\f[]
+can be
+either a DNS name or an IP address in dotted-quad notation.
+Additional information on association behavior can be found in the
+"Association Management"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+.TP 7
+.NOP \f\*[B-Font]pool\f[]
+For type s addresses, this command mobilizes a persistent
+client mode association with a number of remote servers.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+.TP 7
+.NOP \f\*[B-Font]server\f[]
+For type s and r addresses, this command mobilizes a persistent
+client mode association with the specified remote server or local
+radio clock.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+This command should
+\fInot\f[]
+be used for type
+b or m addresses.
+.TP 7
+.NOP \f\*[B-Font]peer\f[]
+For type s addresses (only), this command mobilizes a
+persistent symmetric-active mode association with the specified
+remote peer.
+In this mode the local clock can be synchronized to
+the remote peer or the remote peer can be synchronized to the local
+clock.
+This is useful in a network of servers where, depending on
+various failure scenarios, either the local or remote peer may be
+the better source of time.
+This command should NOT be used for type
+b, m or r addresses.
+.TP 7
+.NOP \f\*[B-Font]broadcast\f[]
+For type b and m addresses (only), this
+command mobilizes a persistent broadcast mode association.
+Multiple
+commands can be used to specify multiple local broadcast interfaces
+(subnets) and/or multiple multicast groups.
+Note that local
+broadcast messages go only to the interface associated with the
+subnet specified, but multicast messages go to all interfaces.
+In broadcast mode the local server sends periodic broadcast
+messages to a client population at the
+\f\*[I-Font]address\f[]
+specified, which is usually the broadcast address on (one of) the
+local network(s) or a multicast address assigned to NTP.
+The IANA
+has assigned the multicast group address IPv4 224.0.1.1 and
+IPv6 ff05::101 (site local) exclusively to
+NTP, but other nonconflicting addresses can be used to contain the
+messages within administrative boundaries.
+Ordinarily, this
+specification applies only to the local server operating as a
+sender; for operation as a broadcast client, see the
+\f\*[B-Font]broadcastclient\f[]
+or
+\f\*[B-Font]multicastclient\f[]
+commands
+below.
+.TP 7
+.NOP \f\*[B-Font]manycastclient\f[]
+For type m addresses (only), this command mobilizes a
+manycast client mode association for the multicast address
+specified.
+In this case a specific address must be supplied which
+matches the address used on the
+\f\*[B-Font]manycastserver\f[]
+command for
+the designated manycast servers.
+The NTP multicast address
+224.0.1.1 assigned by the IANA should NOT be used, unless specific
+means are taken to avoid spraying large areas of the Internet with
+these messages and causing a possibly massive implosion of replies
+at the sender.
+The
+\f\*[B-Font]manycastserver\f[]
+command specifies that the local server
+is to operate in client mode with the remote servers that are
+discovered as the result of broadcast/multicast messages.
+The
+client broadcasts a request message to the group address associated
+with the specified
+\f\*[I-Font]address\f[]
+and specifically enabled
+servers respond to these messages.
+The client selects the servers
+providing the best time and continues as with the
+\f\*[B-Font]server\f[]
+command.
+The remaining servers are discarded as if never
+heard.
+.PP
+.sp \n(Ppu
+.ne 2
+
+Options:
+.TP 7
+.NOP \f\*[B-Font]autokey\f[]
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the autokey scheme
+described in
+\fIAuthentication\f[] \fIOptions\f[].
+.TP 7
+.NOP \f\*[B-Font]burst\f[]
+when the server is reachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first and second packets
+can be changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to improve timekeeping quality
+with the
+\f\*[B-Font]server\f[]
+command and s addresses.
+.TP 7
+.NOP \f\*[B-Font]iburst\f[]
+When the server is unreachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first two packets can be
+changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to speed the initial synchronization
+acquisition with the
+\f\*[B-Font]server\f[]
+command and s addresses and when
+\fCntpd\fR(@NTPD_MS@)\f[]
+is started with the
+\f\*[B-Font]\-q\f[]
+option.
+.TP 7
+.NOP \f\*[B-Font]key\f[] \f\*[I-Font]key\f[]
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the specified
+\f\*[I-Font]key\f[]
+identifier with values from 1 to 65534, inclusive.
+The
+default is to include no encryption field.
+.TP 7
+.NOP \f\*[B-Font]minpoll\f[] \f\*[I-Font]minpoll\f[]
+.TP 7
+.NOP \f\*[B-Font]maxpoll\f[] \f\*[I-Font]maxpoll\f[]
+These options specify the minimum and maximum poll intervals
+for NTP messages, as a power of 2 in seconds
+The maximum poll
+interval defaults to 10 (1,024 s), but can be increased by the
+\f\*[B-Font]maxpoll\f[]
+option to an upper limit of 17 (36.4 h).
+The
+minimum poll interval defaults to 6 (64 s), but can be decreased by
+the
+\f\*[B-Font]minpoll\f[]
+option to a lower limit of 4 (16 s).
+.TP 7
+.NOP \f\*[B-Font]noselect\f[]
+Marks the server as unused, except for display purposes.
+The server is discarded by the selection algroithm.
+.TP 7
+.NOP \f\*[B-Font]prefer\f[]
+Marks the server as preferred.
+All other things being equal,
+this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+"Mitigation Rules and the prefer Keyword"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[])
+for further information.
+.TP 7
+.NOP \f\*[B-Font]ttl\f[] \f\*[I-Font]ttl\f[]
+This option is used only with broadcast server and manycast
+client modes.
+It specifies the time-to-live
+\f\*[I-Font]ttl\f[]
+to
+use on broadcast server and multicast server and the maximum
+\f\*[I-Font]ttl\f[]
+for the expanding ring search with manycast
+client packets.
+Selection of the proper value, which defaults to
+127, is something of a black art and should be coordinated with the
+network administrator.
+.TP 7
+.NOP \f\*[B-Font]version\f[] \f\*[I-Font]version\f[]
+Specifies the version number to be used for outgoing NTP
+packets.
+Versions 1-4 are the choices, with version 4 the
+default.
+.PP
+.SS Auxiliary Commands
+.TP 7
+.NOP \f\*[B-Font]broadcastclient\f[]
+This command enables reception of broadcast server messages to
+any local interface (type b) address.
+Upon receiving a message for
+the first time, the broadcast client measures the nominal server
+propagation delay using a brief client/server exchange with the
+server, then enters the broadcast client mode, in which it
+synchronizes to succeeding broadcast messages.
+Note that, in order
+to avoid accidental or malicious disruption in this mode, both the
+server and client should operate using symmetric-key or public-key
+authentication as described in
+\fIAuthentication\f[] \fIOptions\f[].
+.TP 7
+.NOP \f\*[B-Font]manycastserver\f[] \f\*[I-Font]address\f[] \f\*[I-Font]...\f[]
+This command enables reception of manycast client messages to
+the multicast group address(es) (type m) specified.
+At least one
+address is required, but the NTP multicast address 224.0.1.1
+assigned by the IANA should NOT be used, unless specific means are
+taken to limit the span of the reply and avoid a possibly massive
+implosion at the original sender.
+Note that, in order to avoid
+accidental or malicious disruption in this mode, both the server
+and client should operate using symmetric-key or public-key
+authentication as described in
+\fIAuthentication\f[] \fIOptions\f[].
+.TP 7
+.NOP \f\*[B-Font]multicastclient\f[] \f\*[I-Font]address\f[] \f\*[I-Font]...\f[]
+This command enables reception of multicast server messages to
+the multicast group address(es) (type m) specified.
+Upon receiving
+a message for the first time, the multicast client measures the
+nominal server propagation delay using a brief client/server
+exchange with the server, then enters the broadcast client mode, in
+which it synchronizes to succeeding multicast messages.
+Note that,
+in order to avoid accidental or malicious disruption in this mode,
+both the server and client should operate using symmetric-key or
+public-key authentication as described in
+\fIAuthentication\f[] \fIOptions\f[].
+.PP
+.SH Authentication Support
+Authentication support allows the NTP client to verify that the
+server is in fact known and trusted and not an intruder intending
+accidentally or on purpose to masquerade as that server.
+The NTPv3
+specification RFC-1305 defines a scheme which provides
+cryptographic authentication of received NTP packets.
+Originally,
+this was done using the Data Encryption Standard (DES) algorithm
+operating in Cipher Block Chaining (CBC) mode, commonly called
+DES-CBC.
+Subsequently, this was replaced by the RSA Message Digest
+5 (MD5) algorithm using a private key, commonly called keyed-MD5.
+Either algorithm computes a message digest, or one-way hash, which
+can be used to verify the server has the correct private key and
+key identifier.
+.sp \n(Ppu
+.ne 2
+
+NTPv4 retains the NTPv3 scheme, properly described as symmetric key
+cryptography and, in addition, provides a new Autokey scheme
+based on public key cryptography.
+Public key cryptography is generally considered more secure
+than symmetric key cryptography, since the security is based
+on a private value which is generated by each server and
+never revealed.
+With Autokey all key distribution and
+management functions involve only public values, which
+considerably simplifies key distribution and storage.
+Public key management is based on X.509 certificates,
+which can be provided by commercial services or
+produced by utility programs in the OpenSSL software library
+or the NTPv4 distribution.
+.sp \n(Ppu
+.ne 2
+
+While the algorithms for symmetric key cryptography are
+included in the NTPv4 distribution, public key cryptography
+requires the OpenSSL software library to be installed
+before building the NTP distribution.
+Directions for doing that
+are on the Building and Installing the Distribution page.
+.sp \n(Ppu
+.ne 2
+
+Authentication is configured separately for each association
+using the
+\f\*[B-Font]key\f[]
+or
+\f\*[B-Font]autokey\f[]
+subcommand on the
+\f\*[B-Font]peer\f[],
+\f\*[B-Font]server\f[],
+\f\*[B-Font]broadcast\f[]
+and
+\f\*[B-Font]manycastclient\f[]
+configuration commands as described in
+\fIConfiguration\f[] \fIOptions\f[]
+page.
+The authentication
+options described below specify the locations of the key files,
+if other than default, which symmetric keys are trusted
+and the interval between various operations, if other than default.
+.sp \n(Ppu
+.ne 2
+
+Authentication is always enabled,
+although ineffective if not configured as
+described below.
+If a NTP packet arrives
+including a message authentication
+code (MAC), it is accepted only if it
+passes all cryptographic checks.
+The
+checks require correct key ID, key value
+and message digest.
+If the packet has
+been modified in any way or replayed
+by an intruder, it will fail one or more
+of these checks and be discarded.
+Furthermore, the Autokey scheme requires a
+preliminary protocol exchange to obtain
+the server certificate, verify its
+credentials and initialize the protocol
+.sp \n(Ppu
+.ne 2
+
+The
+\f\*[B-Font]auth\f[]
+flag controls whether new associations or
+remote configuration commands require cryptographic authentication.
+This flag can be set or reset by the
+\f\*[B-Font]enable\f[]
+and
+\f\*[B-Font]disable\f[]
+commands and also by remote
+configuration commands sent by a
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+program running in
+another machine.
+If this flag is enabled, which is the default
+case, new broadcast client and symmetric passive associations and
+remote configuration commands must be cryptographically
+authenticated using either symmetric key or public key cryptography.
+If this
+flag is disabled, these operations are effective
+even if not cryptographic
+authenticated.
+It should be understood
+that operating with the
+\f\*[B-Font]auth\f[]
+flag disabled invites a significant vulnerability
+where a rogue hacker can
+masquerade as a falseticker and seriously
+disrupt system timekeeping.
+It is
+important to note that this flag has no purpose
+other than to allow or disallow
+a new association in response to new broadcast
+and symmetric active messages
+and remote configuration commands and, in particular,
+the flag has no effect on
+the authentication process itself.
+.sp \n(Ppu
+.ne 2
+
+An attractive alternative where multicast support is available
+is manycast mode, in which clients periodically troll
+for servers as described in the
+\fIAutomatic\f[] \fINTP\f[] \fIConfiguration\f[] \fIOptions\f[]
+page.
+Either symmetric key or public key
+cryptographic authentication can be used in this mode.
+The principle advantage
+of manycast mode is that potential servers need not be
+configured in advance,
+since the client finds them during regular operation,
+and the configuration
+files for all clients can be identical.
+.sp \n(Ppu
+.ne 2
+
+The security model and protocol schemes for
+both symmetric key and public key
+cryptography are summarized below;
+further details are in the briefings, papers
+and reports at the NTP project page linked from
+\f[C]http://www.ntp.org/\f[].
+.SS Symmetric-Key Cryptography
+The original RFC-1305 specification allows any one of possibly
+65,534 keys, each distinguished by a 32-bit key identifier, to
+authenticate an association.
+The servers and clients involved must
+agree on the key and key identifier to
+authenticate NTP packets.
+Keys and
+related information are specified in a key
+file, usually called
+\fIntp.keys\f[],
+which must be distributed and stored using
+secure means beyond the scope of the NTP protocol itself.
+Besides the keys used
+for ordinary NTP associations,
+additional keys can be used as passwords for the
+\fCntpq\fR(@NTPQ_MS@)\f[]
+and
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+utility programs.
+.sp \n(Ppu
+.ne 2
+
+When
+\fCntpd\fR(@NTPD_MS@)\f[]
+is first started, it reads the key file specified in the
+\f\*[B-Font]keys\f[]
+configuration command and installs the keys
+in the key cache.
+However,
+individual keys must be activated with the
+\f\*[B-Font]trusted\f[]
+command before use.
+This
+allows, for instance, the installation of possibly
+several batches of keys and
+then activating or deactivating each batch
+remotely using
+\fCntpdc\fR(@NTPDC_MS@)\f[].
+This also provides a revocation capability that can be used
+if a key becomes compromised.
+The
+\f\*[B-Font]requestkey\f[]
+command selects the key used as the password for the
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+utility, while the
+\f\*[B-Font]controlkey\f[]
+command selects the key used as the password for the
+\fCntpq\fR(@NTPQ_MS@)\f[]
+utility.
+.SS Public Key Cryptography
+NTPv4 supports the original NTPv3 symmetric key scheme
+described in RFC-1305 and in addition the Autokey protocol,
+which is based on public key cryptography.
+The Autokey Version 2 protocol described on the Autokey Protocol
+page verifies packet integrity using MD5 message digests
+and verifies the source with digital signatures and any of several
+digest/signature schemes.
+Optional identity schemes described on the Identity Schemes
+page and based on cryptographic challenge/response algorithms
+are also available.
+Using all of these schemes provides strong security against
+replay with or without modification, spoofing, masquerade
+and most forms of clogging attacks.
+.\" .Pp
+.\" The cryptographic means necessary for all Autokey operations
+.\" is provided by the OpenSSL software library.
+.\" This library is available from http://www.openssl.org/
+.\" and can be installed using the procedures outlined
+.\" in the Building and Installing the Distribution page.
+.\" Once installed,
+.\" the configure and build
+.\" process automatically detects the library and links
+.\" the library routines required.
+.sp \n(Ppu
+.ne 2
+
+The Autokey protocol has several modes of operation
+corresponding to the various NTP modes supported.
+Most modes use a special cookie which can be
+computed independently by the client and server,
+but encrypted in transmission.
+All modes use in addition a variant of the S-KEY scheme,
+in which a pseudo-random key list is generated and used
+in reverse order.
+These schemes are described along with an executive summary,
+current status, briefing slides and reading list on the
+\fIAutonomous\f[] \fIAuthentication\f[]
+page.
+.sp \n(Ppu
+.ne 2
+
+The specific cryptographic environment used by Autokey servers
+and clients is determined by a set of files
+and soft links generated by the
+\fCntp-keygen\fR(1ntpkeygenmdoc)\f[]
+program.
+This includes a required host key file,
+required certificate file and optional sign key file,
+leapsecond file and identity scheme files.
+The
+digest/signature scheme is specified in the X.509 certificate
+along with the matching sign key.
+There are several schemes
+available in the OpenSSL software library, each identified
+by a specific string such as
+\f\*[B-Font]md5WithRSAEncryption\f[],
+which stands for the MD5 message digest with RSA
+encryption scheme.
+The current NTP distribution supports
+all the schemes in the OpenSSL library, including
+those based on RSA and DSA digital signatures.
+.sp \n(Ppu
+.ne 2
+
+NTP secure groups can be used to define cryptographic compartments
+and security hierarchies.
+It is important that every host
+in the group be able to construct a certificate trail to one
+or more trusted hosts in the same group.
+Each group
+host runs the Autokey protocol to obtain the certificates
+for all hosts along the trail to one or more trusted hosts.
+This requires the configuration file in all hosts to be
+engineered so that, even under anticipated failure conditions,
+the NTP subnet will form such that every group host can find
+a trail to at least one trusted host.
+.SS Naming and Addressing
+It is important to note that Autokey does not use DNS to
+resolve addresses, since DNS can't be completely trusted
+until the name servers have synchronized clocks.
+The cryptographic name used by Autokey to bind the host identity
+credentials and cryptographic values must be independent
+of interface, network and any other naming convention.
+The name appears in the host certificate in either or both
+the subject and issuer fields, so protection against
+DNS compromise is essential.
+.sp \n(Ppu
+.ne 2
+
+By convention, the name of an Autokey host is the name returned
+by the Unix
+\fCgethostname\fR(2)\f[]
+system call or equivalent in other systems.
+By the system design
+model, there are no provisions to allow alternate names or aliases.
+However, this is not to say that DNS aliases, different names
+for each interface, etc., are constrained in any way.
+.sp \n(Ppu
+.ne 2
+
+It is also important to note that Autokey verifies authenticity
+using the host name, network address and public keys,
+all of which are bound together by the protocol specifically
+to deflect masquerade attacks.
+For this reason Autokey
+includes the source and destinatino IP addresses in message digest
+computations and so the same addresses must be available
+at both the server and client.
+For this reason operation
+with network address translation schemes is not possible.
+This reflects the intended robust security model where government
+and corporate NTP servers are operated outside firewall perimeters.
+.SS Operation
+A specific combination of authentication scheme (none,
+symmetric key, public key) and identity scheme is called
+a cryptotype, although not all combinations are compatible.
+There may be management configurations where the clients,
+servers and peers may not all support the same cryptotypes.
+A secure NTPv4 subnet can be configured in many ways while
+keeping in mind the principles explained above and
+in this section.
+Note however that some cryptotype
+combinations may successfully interoperate with each other,
+but may not represent good security practice.
+.sp \n(Ppu
+.ne 2
+
+The cryptotype of an association is determined at the time
+of mobilization, either at configuration time or some time
+later when a message of appropriate cryptotype arrives.
+When mobilized by a
+\f\*[B-Font]server\f[]
+or
+\f\*[B-Font]peer\f[]
+configuration command and no
+\f\*[B-Font]key\f[]
+or
+\f\*[B-Font]autokey\f[]
+subcommands are present, the association is not
+authenticated; if the
+\f\*[B-Font]key\f[]
+subcommand is present, the association is authenticated
+using the symmetric key ID specified; if the
+\f\*[B-Font]autokey\f[]
+subcommand is present, the association is authenticated
+using Autokey.
+.sp \n(Ppu
+.ne 2
+
+When multiple identity schemes are supported in the Autokey
+protocol, the first message exchange determines which one is used.
+The client request message contains bits corresponding
+to which schemes it has available.
+The server response message
+contains bits corresponding to which schemes it has available.
+Both server and client match the received bits with their own
+and select a common scheme.
+.sp \n(Ppu
+.ne 2
+
+Following the principle that time is a public value,
+a server responds to any client packet that matches
+its cryptotype capabilities.
+Thus, a server receiving
+an unauthenticated packet will respond with an unauthenticated
+packet, while the same server receiving a packet of a cryptotype
+it supports will respond with packets of that cryptotype.
+However, unconfigured broadcast or manycast client
+associations or symmetric passive associations will not be
+mobilized unless the server supports a cryptotype compatible
+with the first packet received.
+By default, unauthenticated associations will not be mobilized
+unless overridden in a decidedly dangerous way.
+.sp \n(Ppu
+.ne 2
+
+Some examples may help to reduce confusion.
+Client Alice has no specific cryptotype selected.
+Server Bob has both a symmetric key file and minimal Autokey files.
+Alice's unauthenticated messages arrive at Bob, who replies with
+unauthenticated messages.
+Cathy has a copy of Bob's symmetric
+key file and has selected key ID 4 in messages to Bob.
+Bob verifies the message with his key ID 4.
+If it's the
+same key and the message is verified, Bob sends Cathy a reply
+authenticated with that key.
+If verification fails,
+Bob sends Cathy a thing called a crypto-NAK, which tells her
+something broke.
+She can see the evidence using the
+\fCntpq\fR(@NTPQ_MS@)\f[]
+program.
+.sp \n(Ppu
+.ne 2
+
+Denise has rolled her own host key and certificate.
+She also uses one of the identity schemes as Bob.
+She sends the first Autokey message to Bob and they
+both dance the protocol authentication and identity steps.
+If all comes out okay, Denise and Bob continue as described above.
+.sp \n(Ppu
+.ne 2
+
+It should be clear from the above that Bob can support
+all the girls at the same time, as long as he has compatible
+authentication and identity credentials.
+Now, Bob can act just like the girls in his own choice of servers;
+he can run multiple configured associations with multiple different
+servers (or the same server, although that might not be useful).
+But, wise security policy might preclude some cryptotype
+combinations; for instance, running an identity scheme
+with one server and no authentication with another might not be wise.
+.SS Key Management
+The cryptographic values used by the Autokey protocol are
+incorporated as a set of files generated by the
+\fCntp-keygen\fR(1ntpkeygenmdoc)\f[]
+utility program, including symmetric key, host key and
+public certificate files, as well as sign key, identity parameters
+and leapseconds files.
+Alternatively, host and sign keys and
+certificate files can be generated by the OpenSSL utilities
+and certificates can be imported from public certificate
+authorities.
+Note that symmetric keys are necessary for the
+\fCntpq\fR(@NTPQ_MS@)\f[]
+and
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+utility programs.
+The remaining files are necessary only for the
+Autokey protocol.
+.sp \n(Ppu
+.ne 2
+
+Certificates imported from OpenSSL or public certificate
+authorities have certian limitations.
+The certificate should be in ASN.1 syntax, X.509 Version 3
+format and encoded in PEM, which is the same format
+used by OpenSSL.
+The overall length of the certificate encoded
+in ASN.1 must not exceed 1024 bytes.
+The subject distinguished
+name field (CN) is the fully qualified name of the host
+on which it is used; the remaining subject fields are ignored.
+The certificate extension fields must not contain either
+a subject key identifier or a issuer key identifier field;
+however, an extended key usage field for a trusted host must
+contain the value
+\f\*[B-Font]trustRoot\f[];.
+Other extension fields are ignored.
+.SS Authentication Commands
+.TP 7
+.NOP \f\*[B-Font]autokey\f[] [\f\*[I-Font]logsec\f[]]
+Specifies the interval between regenerations of the session key
+list used with the Autokey protocol.
+Note that the size of the key
+list for each association depends on this interval and the current
+poll interval.
+The default value is 12 (4096 s or about 1.1 hours).
+For poll intervals above the specified interval, a session key list
+with a single entry will be regenerated for every message
+sent.
+.TP 7
+.NOP \f\*[B-Font]controlkey\f[] \f\*[I-Font]key\f[]
+Specifies the key identifier to use with the
+\fCntpq\fR(@NTPQ_MS@)\f[]
+utility, which uses the standard
+protocol defined in RFC-1305.
+The
+\f\*[I-Font]key\f[]
+argument is
+the key identifier for a trusted key, where the value can be in the
+range 1 to 65,534, inclusive.
+.TP 7
+.NOP \f\*[B-Font]crypto\f[] [\f\*[B-Font]cert\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]leap\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]randfile\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]host\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]sign\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]gq\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]gqpar\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]iffpar\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]mvpar\f[] \f\*[I-Font]file\f[]] [\f\*[B-Font]pw\f[] \f\*[I-Font]password\f[]]
+This command requires the OpenSSL library.
+It activates public key
+cryptography, selects the message digest and signature
+encryption scheme and loads the required private and public
+values described above.
+If one or more files are left unspecified,
+the default names are used as described above.
+Unless the complete path and name of the file are specified, the
+location of a file is relative to the keys directory specified
+in the
+\f\*[B-Font]keysdir\f[]
+command or default
+\fI/usr/local/etc\f[].
+Following are the subcommands:
+.RS
+.TP 7
+.NOP \f\*[B-Font]cert\f[] \f\*[I-Font]file\f[]
+Specifies the location of the required host public certificate file.
+This overrides the link
+\fIntpkey_cert_\f[]\f\*[I-Font]hostname\f[]
+in the keys directory.
+.TP 7
+.NOP \f\*[B-Font]gqpar\f[] \f\*[I-Font]file\f[]
+Specifies the location of the optional GQ parameters file.
+This
+overrides the link
+\fIntpkey_gq_\f[]\f\*[I-Font]hostname\f[]
+in the keys directory.
+.TP 7
+.NOP \f\*[B-Font]host\f[] \f\*[I-Font]file\f[]
+Specifies the location of the required host key file.
+This overrides
+the link
+\fIntpkey_key_\f[]\f\*[I-Font]hostname\f[]
+in the keys directory.
+.TP 7
+.NOP \f\*[B-Font]iffpar\f[] \f\*[I-Font]file\f[]
+Specifies the location of the optional IFF parameters file.This
+overrides the link
+\fIntpkey_iff_\f[]\f\*[I-Font]hostname\f[]
+in the keys directory.
+.TP 7
+.NOP \f\*[B-Font]leap\f[] \f\*[I-Font]file\f[]
+Specifies the location of the optional leapsecond file.
+This overrides the link
+\fIntpkey_leap\f[]
+in the keys directory.
+.TP 7
+.NOP \f\*[B-Font]mvpar\f[] \f\*[I-Font]file\f[]
+Specifies the location of the optional MV parameters file.
+This
+overrides the link
+\fIntpkey_mv_\f[]\f\*[I-Font]hostname\f[]
+in the keys directory.
+.TP 7
+.NOP \f\*[B-Font]pw\f[] \f\*[I-Font]password\f[]
+Specifies the password to decrypt files containing private keys and
+identity parameters.
+This is required only if these files have been
+encrypted.
+.TP 7
+.NOP \f\*[B-Font]randfile\f[] \f\*[I-Font]file\f[]
+Specifies the location of the random seed file used by the OpenSSL
+library.
+The defaults are described in the main text above.
+.TP 7
+.NOP \f\*[B-Font]sign\f[] \f\*[I-Font]file\f[]
+Specifies the location of the optional sign key file.
+This overrides
+the link
+\fIntpkey_sign_\f[]\f\*[I-Font]hostname\f[]
+in the keys directory.
+If this file is
+not found, the host key is also the sign key.
+.RE
+.TP 7
+.NOP \f\*[B-Font]keys\f[] \f\*[I-Font]keyfile\f[]
+Specifies the complete path and location of the MD5 key file
+containing the keys and key identifiers used by
+\fCntpd\fR(@NTPD_MS@)\f[],
+\fCntpq\fR(@NTPQ_MS@)\f[]
+and
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+when operating with symmetric key cryptography.
+This is the same operation as the
+\f\*[B-Font]\-k\f[]
+command line option.
+.TP 7
+.NOP \f\*[B-Font]keysdir\f[] \f\*[I-Font]path\f[]
+This command specifies the default directory path for
+cryptographic keys, parameters and certificates.
+The default is
+\fI/usr/local/etc/\f[].
+.TP 7
+.NOP \f\*[B-Font]requestkey\f[] \f\*[I-Font]key\f[]
+Specifies the key identifier to use with the
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+utility program, which uses a
+proprietary protocol specific to this implementation of
+\fCntpd\fR(@NTPD_MS@)\f[].
+The
+\f\*[I-Font]key\f[]
+argument is a key identifier
+for the trusted key, where the value can be in the range 1 to
+65,534, inclusive.
+.TP 7
+.NOP \f\*[B-Font]revoke\f[] \f\*[I-Font]logsec\f[]
+Specifies the interval between re-randomization of certain
+cryptographic values used by the Autokey scheme, as a power of 2 in
+seconds.
+These values need to be updated frequently in order to
+deflect brute-force attacks on the algorithms of the scheme;
+however, updating some values is a relatively expensive operation.
+The default interval is 16 (65,536 s or about 18 hours).
+For poll
+intervals above the specified interval, the values will be updated
+for every message sent.
+.TP 7
+.NOP \f\*[B-Font]trustedkey\f[] \f\*[I-Font]key\f[] \f\*[I-Font]...\f[]
+Specifies the key identifiers which are trusted for the
+purposes of authenticating peers with symmetric key cryptography,
+as well as keys used by the
+\fCntpq\fR(@NTPQ_MS@)\f[]
+and
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+programs.
+The authentication procedures require that both the local
+and remote servers share the same key and key identifier for this
+purpose, although different keys can be used with different
+servers.
+The
+\f\*[I-Font]key\f[]
+arguments are 32-bit unsigned
+integers with values from 1 to 65,534.
+.PP
+.SS Error Codes
+The following error codes are reported via the NTP control
+and monitoring protocol trap mechanism.
+.TP 7
+.NOP 101
+(bad field format or length)
+The packet has invalid version, length or format.
+.TP 7
+.NOP 102
+(bad timestamp)
+The packet timestamp is the same or older than the most recent received.
+This could be due to a replay or a server clock time step.
+.TP 7
+.NOP 103
+(bad filestamp)
+The packet filestamp is the same or older than the most recent received.
+This could be due to a replay or a key file generation error.
+.TP 7
+.NOP 104
+(bad or missing public key)
+The public key is missing, has incorrect format or is an unsupported type.
+.TP 7
+.NOP 105
+(unsupported digest type)
+The server requires an unsupported digest/signature scheme.
+.TP 7
+.NOP 106
+(mismatched digest types)
+Not used.
+.TP 7
+.NOP 107
+(bad signature length)
+The signature length does not match the current public key.
+.TP 7
+.NOP 108
+(signature not verified)
+The message fails the signature check.
+It could be bogus or signed by a
+different private key.
+.TP 7
+.NOP 109
+(certificate not verified)
+The certificate is invalid or signed with the wrong key.
+.TP 7
+.NOP 110
+(certificate not verified)
+The certificate is not yet valid or has expired or the signature could not
+be verified.
+.TP 7
+.NOP 111
+(bad or missing cookie)
+The cookie is missing, corrupted or bogus.
+.TP 7
+.NOP 112
+(bad or missing leapseconds table)
+The leapseconds table is missing, corrupted or bogus.
+.TP 7
+.NOP 113
+(bad or missing certificate)
+The certificate is missing, corrupted or bogus.
+.TP 7
+.NOP 114
+(bad or missing identity)
+The identity key is missing, corrupt or bogus.
+.PP
+.SH Monitoring Support
+\fCntpd\fR(@NTPD_MS@)\f[]
+includes a comprehensive monitoring facility suitable
+for continuous, long term recording of server and client
+timekeeping performance.
+See the
+\f\*[B-Font]statistics\f[]
+command below
+for a listing and example of each type of statistics currently
+supported.
+Statistic files are managed using file generation sets
+and scripts in the
+\fI./scripts\f[]
+directory of this distribution.
+Using
+these facilities and
+UNIX
+\fCcron\fR(8)\f[]
+jobs, the data can be
+automatically summarized and archived for retrospective analysis.
+.SS Monitoring Commands
+.TP 7
+.NOP \f\*[B-Font]statistics\f[] \f\*[I-Font]name\f[] \f\*[I-Font]...\f[]
+Enables writing of statistics records.
+Currently, eight kinds of
+\f\*[I-Font]name\f[]
+statistics are supported.
+.RS
+.TP 7
+.NOP \f\*[B-Font]clockstats\f[]
+Enables recording of clock driver statistics information.
+Each update
+received from a clock driver appends a line of the following form to
+the file generation set named
+\f\*[B-Font]clockstats\f[]:
+.br
+.in +4
+.nf
+49213 525.624 127.127.4.1 93 226 00:08:29.606 D
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the
+clock address in dotted-quad notation.
+The final field shows the last
+timecode received from the clock in decoded ASCII format, where
+meaningful.
+In some clock drivers a good deal of additional information
+can be gathered and displayed as well.
+See information specific to each
+clock for further details.
+.TP 7
+.NOP \f\*[B-Font]cryptostats\f[]
+This option requires the OpenSSL cryptographic software library.
+It
+enables recording of cryptographic public key protocol information.
+Each message received by the protocol module appends a line of the
+following form to the file generation set named
+\f\*[B-Font]cryptostats\f[]:
+.br
+.in +4
+.nf
+49213 525.624 127.127.4.1 message
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the peer
+address in dotted-quad notation, The final message field includes the
+message type and certain ancillary information.
+See the
+\fIAuthentication\f[] \fIOptions\f[]
+section for further information.
+.TP 7
+.NOP \f\*[B-Font]loopstats\f[]
+Enables recording of loop filter statistics information.
+Each
+update of the local clock outputs a line of the following form to
+the file generation set named
+\f\*[B-Font]loopstats\f[]:
+.br
+.in +4
+.nf
+50935 75440.031 0.000006019 13.778190 0.000351733 0.0133806
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next five fields
+show time offset (seconds), frequency offset (parts per million \-
+PPM), RMS jitter (seconds), Allan deviation (PPM) and clock
+discipline time constant.
+.TP 7
+.NOP \f\*[B-Font]peerstats\f[]
+Enables recording of peer statistics information.
+This includes
+statistics records of all peers of a NTP server and of special
+signals, where present and configured.
+Each valid update appends a
+line of the following form to the current element of a file
+generation set named
+\f\*[B-Font]peerstats\f[]:
+.br
+.in +4
+.nf
+48773 10847.650 127.127.4.1 9714 \-0.001605376 0.000000000 0.001424877 0.000958674
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the peer address in dotted-quad notation and status,
+respectively.
+The status field is encoded in hex in the format
+described in Appendix A of the NTP specification RFC 1305.
+The final four fields show the offset,
+delay, dispersion and RMS jitter, all in seconds.
+.TP 7
+.NOP \f\*[B-Font]rawstats\f[]
+Enables recording of raw-timestamp statistics information.
+This
+includes statistics records of all peers of a NTP server and of
+special signals, where present and configured.
+Each NTP message
+received from a peer or clock driver appends a line of the
+following form to the file generation set named
+\f\*[B-Font]rawstats\f[]:
+.br
+.in +4
+.nf
+50928 2132.543 128.4.1.1 128.4.1.20 3102453281.584327000 3102453281.58622800031 02453332.540806000 3102453332.541458000
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the remote peer or clock address followed by the local address
+in dotted-quad notation.
+The final four fields show the originate,
+receive, transmit and final NTP timestamps in order.
+The timestamp
+values are as received and before processing by the various data
+smoothing and mitigation algorithms.
+.TP 7
+.NOP \f\*[B-Font]sysstats\f[]
+Enables recording of ntpd statistics counters on a periodic basis.
+Each
+hour a line of the following form is appended to the file generation
+set named
+\f\*[B-Font]sysstats\f[]:
+.br
+.in +4
+.nf
+50928 2132.543 36000 81965 0 9546 56 71793 512 540 10 147
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The remaining ten fields show
+the statistics counter values accumulated since the last generated
+line.
+.RS
+.TP 7
+.NOP Time since restart \f\*[B-Font]36000\f[]
+Time in hours since the system was last rebooted.
+.TP 7
+.NOP Packets received \f\*[B-Font]81965\f[]
+Total number of packets received.
+.TP 7
+.NOP Packets processed \f\*[B-Font]0\f[]
+Number of packets received in response to previous packets sent
+.TP 7
+.NOP Current version \f\*[B-Font]9546\f[]
+Number of packets matching the current NTP version.
+.TP 7
+.NOP Previous version \f\*[B-Font]56\f[]
+Number of packets matching the previous NTP version.
+.TP 7
+.NOP Bad version \f\*[B-Font]71793\f[]
+Number of packets matching neither NTP version.
+.TP 7
+.NOP Access denied \f\*[B-Font]512\f[]
+Number of packets denied access for any reason.
+.TP 7
+.NOP Bad length or format \f\*[B-Font]540\f[]
+Number of packets with invalid length, format or port number.
+.TP 7
+.NOP Bad authentication \f\*[B-Font]10\f[]
+Number of packets not verified as authentic.
+.TP 7
+.NOP Rate exceeded \f\*[B-Font]147\f[]
+Number of packets discarded due to rate limitation.
+.RE
+.TP 7
+.NOP \f\*[B-Font]statsdir\f[] \f\*[I-Font]directory_path\f[]
+Indicates the full path of a directory where statistics files
+should be created (see below).
+This keyword allows
+the (otherwise constant)
+\f\*[B-Font]filegen\f[]
+filename prefix to be modified for file generation sets, which
+is useful for handling statistics logs.
+.TP 7
+.NOP \f\*[B-Font]filegen\f[] \f\*[I-Font]name\f[] [\f\*[B-Font]file\f[] \f\*[I-Font]filename\f[]] [\f\*[B-Font]type\f[] \f\*[I-Font]typename\f[]] [\f\*[B-Font]link\f[] | \f\*[B-Font]nolink\f[]] [\f\*[B-Font]enable\f[] | \f\*[B-Font]disable\f[]]
+Configures setting of generation file set name.
+Generation
+file sets provide a means for handling files that are
+continuously growing during the lifetime of a server.
+Server statistics are a typical example for such files.
+Generation file sets provide access to a set of files used
+to store the actual data.
+At any time at most one element
+of the set is being written to.
+The type given specifies
+when and how data will be directed to a new element of the set.
+This way, information stored in elements of a file set
+that are currently unused are available for administrational
+operations without the risk of disturbing the operation of ntpd.
+(Most important: they can be removed to free space for new data
+produced.)
+.sp \n(Ppu
+.ne 2
+
+Note that this command can be sent from the
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+program running at a remote location.
+.RS
+.TP 7
+.NOP \f\*[B-Font]name\f[]
+This is the type of the statistics records, as shown in the
+\f\*[B-Font]statistics\f[]
+command.
+.TP 7
+.NOP \f\*[B-Font]file\f[] \f\*[I-Font]filename\f[]
+This is the file name for the statistics records.
+Filenames of set
+members are built from three concatenated elements
+\f\*[B-Font]prefix\f[],
+\f\*[B-Font]filename\f[]
+and
+\f\*[B-Font]suffix\f[]:
+.RS
+.TP 7
+.NOP \f\*[B-Font]prefix\f[]
+This is a constant filename path.
+It is not subject to
+modifications via the
+\f\*[I-Font]filegen\f[]
+option.
+It is defined by the
+server, usually specified as a compile-time constant.
+It may,
+however, be configurable for individual file generation sets
+via other commands.
+For example, the prefix used with
+\f\*[I-Font]loopstats\f[]
+and
+\f\*[I-Font]peerstats\f[]
+generation can be configured using the
+\f\*[I-Font]statsdir\f[]
+option explained above.
+.TP 7
+.NOP \f\*[B-Font]filename\f[]
+This string is directly concatenated to the prefix mentioned
+above (no intervening
+\[oq]/\[cq]).
+This can be modified using
+the file argument to the
+\f\*[I-Font]filegen\f[]
+statement.
+No
+\fI..\f[]
+elements are
+allowed in this component to prevent filenames referring to
+parts outside the filesystem hierarchy denoted by
+\f\*[I-Font]prefix\f[].
+.TP 7
+.NOP \f\*[B-Font]suffix\f[]
+This part is reflects individual elements of a file set.
+It is
+generated according to the type of a file set.
+.RE
+.TP 7
+.NOP \f\*[B-Font]type\f[] \f\*[I-Font]typename\f[]
+A file generation set is characterized by its type.
+The following
+types are supported:
+.RS
+.TP 7
+.NOP \f\*[B-Font]none\f[]
+The file set is actually a single plain file.
+.TP 7
+.NOP \f\*[B-Font]pid\f[]
+One element of file set is used per incarnation of a ntpd
+server.
+This type does not perform any changes to file set
+members during runtime, however it provides an easy way of
+separating files belonging to different
+\fCntpd\fR(@NTPD_MS@)\f[]
+server incarnations.
+The set member filename is built by appending a
+\[oq]\&.\[cq]
+to concatenated
+\f\*[I-Font]prefix\f[]
+and
+\f\*[I-Font]filename\f[]
+strings, and
+appending the decimal representation of the process ID of the
+\fCntpd\fR(@NTPD_MS@)\f[]
+server process.
+.TP 7
+.NOP \f\*[B-Font]day\f[]
+One file generation set element is created per day.
+A day is
+defined as the period between 00:00 and 24:00 UTC.
+The file set
+member suffix consists of a
+\[oq]\&.\[cq]
+and a day specification in
+the form
+\f\*[B-Font]YYYYMMdd\f[].
+\f\*[B-Font]YYYY\f[]
+is a 4-digit year number (e.g., 1992).
+\f\*[B-Font]MM\f[]
+is a two digit month number.
+\f\*[B-Font]dd\f[]
+is a two digit day number.
+Thus, all information written at 10 December 1992 would end up
+in a file named
+\f\*[I-Font]prefix\f[]
+\f\*[I-Font]filename\f[].19921210.
+.TP 7
+.NOP \f\*[B-Font]week\f[]
+Any file set member contains data related to a certain week of
+a year.
+The term week is defined by computing day-of-year
+modulo 7.
+Elements of such a file generation set are
+distinguished by appending the following suffix to the file set
+filename base: A dot, a 4-digit year number, the letter
+\f\*[B-Font]W\f[],
+and a 2-digit week number.
+For example, information from January,
+10th 1992 would end up in a file with suffix
+.NOP. \f\*[I-Font]1992W1\f[].
+.TP 7
+.NOP \f\*[B-Font]month\f[]
+One generation file set element is generated per month.
+The
+file name suffix consists of a dot, a 4-digit year number, and
+a 2-digit month.
+.TP 7
+.NOP \f\*[B-Font]year\f[]
+One generation file element is generated per year.
+The filename
+suffix consists of a dot and a 4 digit year number.
+.TP 7
+.NOP \f\*[B-Font]age\f[]
+This type of file generation sets changes to a new element of
+the file set every 24 hours of server operation.
+The filename
+suffix consists of a dot, the letter
+\f\*[B-Font]a\f[],
+and an 8-digit number.
+This number is taken to be the number of seconds the server is
+running at the start of the corresponding 24-hour period.
+Information is only written to a file generation by specifying
+\f\*[B-Font]enable\f[];
+output is prevented by specifying
+\f\*[B-Font]disable\f[].
+.RE
+.TP 7
+.NOP \f\*[B-Font]link\f[] | \f\*[B-Font]nolink\f[]
+It is convenient to be able to access the current element of a file
+generation set by a fixed name.
+This feature is enabled by
+specifying
+\f\*[B-Font]link\f[]
+and disabled using
+\f\*[B-Font]nolink\f[].
+If link is specified, a
+hard link from the current file set element to a file without
+suffix is created.
+When there is already a file with this name and
+the number of links of this file is one, it is renamed appending a
+dot, the letter
+\f\*[B-Font]C\f[],
+and the pid of the ntpd server process.
+When the
+number of links is greater than one, the file is unlinked.
+This
+allows the current file to be accessed by a constant name.
+.TP 7
+.NOP \f\*[B-Font]enable\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]disable\f[]
+Enables or disables the recording function.
+.RE
+.RE
+.PP
+.SH Access Control Support
+The
+\fCntpd\fR(@NTPD_MS@)\f[]
+daemon implements a general purpose address/mask based restriction
+list.
+The list contains address/match entries sorted first
+by increasing address values and and then by increasing mask values.
+A match occurs when the bitwise AND of the mask and the packet
+source address is equal to the bitwise AND of the mask and
+address in the list.
+The list is searched in order with the
+last match found defining the restriction flags associated
+with the entry.
+Additional information and examples can be found in the
+"Notes on Configuring NTP and Setting up a NTP Subnet"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+.sp \n(Ppu
+.ne 2
+
+The restriction facility was implemented in conformance
+with the access policies for the original NSFnet backbone
+time servers.
+Later the facility was expanded to deflect
+cryptographic and clogging attacks.
+While this facility may
+be useful for keeping unwanted or broken or malicious clients
+from congesting innocent servers, it should not be considered
+an alternative to the NTP authentication facilities.
+Source address based restrictions are easily circumvented
+by a determined cracker.
+.sp \n(Ppu
+.ne 2
+
+Clients can be denied service because they are explicitly
+included in the restrict list created by the restrict command
+or implicitly as the result of cryptographic or rate limit
+violations.
+Cryptographic violations include certificate
+or identity verification failure; rate limit violations generally
+result from defective NTP implementations that send packets
+at abusive rates.
+Some violations cause denied service
+only for the offending packet, others cause denied service
+for a timed period and others cause the denied service for
+an indefinate period.
+When a client or network is denied access
+for an indefinate period, the only way at present to remove
+the restrictions is by restarting the server.
+.SS The Kiss-of-Death Packet
+Ordinarily, packets denied service are simply dropped with no
+further action except incrementing statistics counters.
+Sometimes a
+more proactive response is needed, such as a server message that
+explicitly requests the client to stop sending and leave a message
+for the system operator.
+A special packet format has been created
+for this purpose called the "kiss-of-death" (KoD) packet.
+KoD packets have the leap bits set unsynchronized and stratum set
+to zero and the reference identifier field set to a four-byte
+ASCII code.
+If the
+\f\*[B-Font]noserve\f[]
+or
+\f\*[B-Font]notrust\f[]
+flag of the matching restrict list entry is set,
+the code is "DENY"; if the
+\f\*[B-Font]limited\f[]
+flag is set and the rate limit
+is exceeded, the code is "RATE".
+Finally, if a cryptographic violation occurs, the code is "CRYP".
+.sp \n(Ppu
+.ne 2
+
+A client receiving a KoD performs a set of sanity checks to
+minimize security exposure, then updates the stratum and
+reference identifier peer variables, sets the access
+denied (TEST4) bit in the peer flash variable and sends
+a message to the log.
+As long as the TEST4 bit is set,
+the client will send no further packets to the server.
+The only way at present to recover from this condition is
+to restart the protocol at both the client and server.
+This
+happens automatically at the client when the association times out.
+It will happen at the server only if the server operator cooperates.
+.SS Access Control Commands
+.TP 7
+.NOP \f\*[B-Font]discard\f[] [\f\*[B-Font]average\f[] \f\*[I-Font]avg\f[]] [\f\*[B-Font]minimum\f[] \f\*[I-Font]min\f[]] [\f\*[B-Font]monitor\f[] \f\*[I-Font]prob\f[]]
+Set the parameters of the
+\f\*[B-Font]limited\f[]
+facility which protects the server from
+client abuse.
+The
+\f\*[B-Font]average\f[]
+subcommand specifies the minimum average packet
+spacing, while the
+\f\*[B-Font]minimum\f[]
+subcommand specifies the minimum packet spacing.
+Packets that violate these minima are discarded
+and a kiss-o'-death packet returned if enabled.
+The default
+minimum average and minimum are 5 and 2, respectively.
+The monitor subcommand specifies the probability of discard
+for packets that overflow the rate-control window.
+.TP 7
+.NOP \f\*[B-Font]restrict\f[] \f\*[B-Font]address\f[] [\f\*[B-Font]mask\f[] \f\*[I-Font]mask\f[]] [\f\*[I-Font]flag\f[] \f\*[I-Font]...\f[]]
+The
+\f\*[I-Font]address\f[]
+argument expressed in
+dotted-quad form is the address of a host or network.
+Alternatively, the
+\f\*[I-Font]address\f[]
+argument can be a valid host DNS name.
+The
+\f\*[I-Font]mask\f[]
+argument expressed in dotted-quad form defaults to
+\f\*[B-Font]255.255.255.255\f[],
+meaning that the
+\f\*[I-Font]address\f[]
+is treated as the address of an individual host.
+A default entry (address
+\f\*[B-Font]0.0.0.0\f[],
+mask
+\f\*[B-Font]0.0.0.0\f[])
+is always included and is always the first entry in the list.
+Note that text string
+\f\*[B-Font]default\f[],
+with no mask option, may
+be used to indicate the default entry.
+In the current implementation,
+\f\*[B-Font]flag\f[]
+always
+restricts access, i.e., an entry with no flags indicates that free
+access to the server is to be given.
+The flags are not orthogonal,
+in that more restrictive flags will often make less restrictive
+ones redundant.
+The flags can generally be classed into two
+categories, those which restrict time service and those which
+restrict informational queries and attempts to do run-time
+reconfiguration of the server.
+One or more of the following flags
+may be specified:
+.RS
+.TP 7
+.NOP \f\*[B-Font]ignore\f[]
+Deny packets of all kinds, including
+\fCntpq\fR(@NTPQ_MS@)\f[]
+and
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+queries.
+.TP 7
+.NOP \f\*[B-Font]kod\f[]
+If this flag is set when an access violation occurs, a kiss-o'-death
+(KoD) packet is sent.
+KoD packets are rate limited to no more than one
+per second.
+If another KoD packet occurs within one second after the
+last one, the packet is dropped.
+.TP 7
+.NOP \f\*[B-Font]limited\f[]
+Deny service if the packet spacing violates the lower limits specified
+in the discard command.
+A history of clients is kept using the
+monitoring capability of
+\fCntpd\fR(@NTPD_MS@)\f[].
+Thus, monitoring is always active as
+long as there is a restriction entry with the
+\f\*[B-Font]limited\f[]
+flag.
+.TP 7
+.NOP \f\*[B-Font]lowpriotrap\f[]
+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.
+.TP 7
+.NOP \f\*[B-Font]nomodify\f[]
+Deny
+\fCntpq\fR(@NTPQ_MS@)\f[]
+and
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+queries which attempt to modify the state of the
+server (i.e., run time reconfiguration).
+Queries which return
+information are permitted.
+.TP 7
+.NOP \f\*[B-Font]noquery\f[]
+Deny
+\fCntpq\fR(@NTPQ_MS@)\f[]
+and
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+queries.
+Time service is not affected.
+.TP 7
+.NOP \f\*[B-Font]nopeer\f[]
+Deny packets which would result in mobilizing a new association.
+This
+includes broadcast and symmetric active packets when a configured
+association does not exist.
+It also includes
+\f\*[B-Font]pool\f[]
+associations, so if you want to use servers from a
+\f\*[B-Font]pool\f[]
+directive and also want to use
+\f\*[B-Font]nopeer\f[]
+by default, you'll want a
+\f\*[B-Font]restrict source ...\f[] \f\*[B-Font]line\f[] \f\*[B-Font]as\f[] \f\*[B-Font]well\f[] \f\*[B-Font]that\f[] \f\*[B-Font]does\f[]
+.TP 7
+.NOP not
+include the
+\f\*[B-Font]nopeer\f[]
+directive.
+.TP 7
+.NOP \f\*[B-Font]noserve\f[]
+Deny all packets except
+\fCntpq\fR(@NTPQ_MS@)\f[]
+and
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+queries.
+.TP 7
+.NOP \f\*[B-Font]notrap\f[]
+Decline to provide mode 6 control message trap service to matching
+hosts.
+The trap service is a subsystem of the ntpdq control message
+protocol which is intended for use by remote event logging programs.
+.TP 7
+.NOP \f\*[B-Font]notrust\f[]
+Deny service unless the packet is cryptographically authenticated.
+.TP 7
+.NOP \f\*[B-Font]ntpport\f[]
+This is actually a match algorithm modifier, rather than a
+restriction flag.
+Its presence causes the restriction entry to be
+matched only if the source port in the packet is the standard NTP
+UDP port (123).
+Both
+\f\*[B-Font]ntpport\f[]
+and
+\f\*[B-Font]non-ntpport\f[]
+may
+be specified.
+The
+\f\*[B-Font]ntpport\f[]
+is considered more specific and
+is sorted later in the list.
+.TP 7
+.NOP \f\*[B-Font]version\f[]
+Deny packets that do not match the current NTP version.
+.RE
+.sp \n(Ppu
+.ne 2
+
+Default restriction list entries with the flags ignore, interface,
+ntpport, for each of the local host's interface addresses are
+inserted into the table at startup to prevent the server
+from attempting to synchronize to its own time.
+A default entry is also always present, though if it is
+otherwise unconfigured; no flags are associated
+with the default entry (i.e., everything besides your own
+NTP server is unrestricted).
+.PP
+.SH Automatic NTP Configuration Options
+.SS Manycasting
+Manycasting is a automatic discovery and configuration paradigm
+new to NTPv4.
+It is intended as a means for a multicast client
+to troll the nearby network neighborhood to find cooperating
+manycast servers, validate them using cryptographic means
+and evaluate their time values with respect to other servers
+that might be lurking in the vicinity.
+The intended result is that each manycast client mobilizes
+client associations with some number of the "best"
+of the nearby manycast servers, yet automatically reconfigures
+to sustain this number of servers should one or another fail.
+.sp \n(Ppu
+.ne 2
+
+Note that the manycasting paradigm does not coincide
+with the anycast paradigm described in RFC-1546,
+which is designed to find a single server from a clique
+of servers providing the same service.
+The manycast paradigm is designed to find a plurality
+of redundant servers satisfying defined optimality criteria.
+.sp \n(Ppu
+.ne 2
+
+Manycasting can be used with either symmetric key
+or public key cryptography.
+The public key infrastructure (PKI)
+offers the best protection against compromised keys
+and is generally considered stronger, at least with relatively
+large key sizes.
+It is implemented using the Autokey protocol and
+the OpenSSL cryptographic library available from
+\f[C]http://www.openssl.org/\f[].
+The library can also be used with other NTPv4 modes
+as well and is highly recommended, especially for broadcast modes.
+.sp \n(Ppu
+.ne 2
+
+A persistent manycast client association is configured
+using the manycastclient command, which is similar to the
+server command but with a multicast (IPv4 class
+\f\*[B-Font]D\f[]
+or IPv6 prefix
+\f\*[B-Font]FF\f[])
+group address.
+The IANA has designated IPv4 address 224.1.1.1
+and IPv6 address FF05::101 (site local) for NTP.
+When more servers are needed, it broadcasts manycast
+client messages to this address at the minimum feasible rate
+and minimum feasible time-to-live (TTL) hops, depending
+on how many servers have already been found.
+There can be as many manycast client associations
+as different group address, each one serving as a template
+for a future ephemeral unicast client/server association.
+.sp \n(Ppu
+.ne 2
+
+Manycast servers configured with the
+\f\*[B-Font]manycastserver\f[]
+command listen on the specified group address for manycast
+client messages.
+Note the distinction between manycast client,
+which actively broadcasts messages, and manycast server,
+which passively responds to them.
+If a manycast server is
+in scope of the current TTL and is itself synchronized
+to a valid source and operating at a stratum level equal
+to or lower than the manycast client, it replies to the
+manycast client message with an ordinary unicast server message.
+.sp \n(Ppu
+.ne 2
+
+The manycast client receiving this message mobilizes
+an ephemeral client/server association according to the
+matching manycast client template, but only if cryptographically
+authenticated and the server stratum is less than or equal
+to the client stratum.
+Authentication is explicitly required
+and either symmetric key or public key (Autokey) can be used.
+Then, the client polls the server at its unicast address
+in burst mode in order to reliably set the host clock
+and validate the source.
+This normally results
+in a volley of eight client/server at 2-s intervals
+during which both the synchronization and cryptographic
+protocols run concurrently.
+Following the volley,
+the client runs the NTP intersection and clustering
+algorithms, which act to discard all but the "best"
+associations according to stratum and synchronization
+distance.
+The surviving associations then continue
+in ordinary client/server mode.
+.sp \n(Ppu
+.ne 2
+
+The manycast client polling strategy is designed to reduce
+as much as possible the volume of manycast client messages
+and the effects of implosion due to near-simultaneous
+arrival of manycast server messages.
+The strategy is determined by the
+\f\*[B-Font]manycastclient\f[],
+\f\*[B-Font]tos\f[]
+and
+\f\*[B-Font]ttl\f[]
+configuration commands.
+The manycast poll interval is
+normally eight times the system poll interval,
+which starts out at the
+\f\*[B-Font]minpoll\f[]
+value specified in the
+\f\*[B-Font]manycastclient\f[],
+command and, under normal circumstances, increments to the
+\f\*[B-Font]maxpolll\f[]
+value specified in this command.
+Initially, the TTL is
+set at the minimum hops specified by the ttl command.
+At each retransmission the TTL is increased until reaching
+the maximum hops specified by this command or a sufficient
+number client associations have been found.
+Further retransmissions use the same TTL.
+.sp \n(Ppu
+.ne 2
+
+The quality and reliability of the suite of associations
+discovered by the manycast client is determined by the NTP
+mitigation algorithms and the
+\f\*[B-Font]minclock\f[]
+and
+\f\*[B-Font]minsane\f[]
+values specified in the
+\f\*[B-Font]tos\f[]
+configuration command.
+At least
+\f\*[B-Font]minsane\f[]
+candidate servers must be available and the mitigation
+algorithms produce at least
+\f\*[B-Font]minclock\f[]
+survivors in order to synchronize the clock.
+Byzantine agreement principles require at least four
+candidates in order to correctly discard a single falseticker.
+For legacy purposes,
+\f\*[B-Font]minsane\f[]
+defaults to 1 and
+\f\*[B-Font]minclock\f[]
+defaults to 3.
+For manycast service
+\f\*[B-Font]minsane\f[]
+should be explicitly set to 4, assuming at least that
+number of servers are available.
+.sp \n(Ppu
+.ne 2
+
+If at least
+\f\*[B-Font]minclock\f[]
+servers are found, the manycast poll interval is immediately
+set to eight times
+\f\*[B-Font]maxpoll\f[].
+If less than
+\f\*[B-Font]minclock\f[]
+servers are found when the TTL has reached the maximum hops,
+the manycast poll interval is doubled.
+For each transmission
+after that, the poll interval is doubled again until
+reaching the maximum of eight times
+\f\*[B-Font]maxpoll\f[].
+Further transmissions use the same poll interval and
+TTL values.
+Note that while all this is going on,
+each client/server association found is operating normally
+it the system poll interval.
+.sp \n(Ppu
+.ne 2
+
+Administratively scoped multicast boundaries are normally
+specified by the network router configuration and,
+in the case of IPv6, the link/site scope prefix.
+By default, the increment for TTL hops is 32 starting
+from 31; however, the
+\f\*[B-Font]ttl\f[]
+configuration command can be
+used to modify the values to match the scope rules.
+.sp \n(Ppu
+.ne 2
+
+It is often useful to narrow the range of acceptable
+servers which can be found by manycast client associations.
+Because manycast servers respond only when the client
+stratum is equal to or greater than the server stratum,
+primary (stratum 1) servers fill find only primary servers
+in TTL range, which is probably the most common objective.
+However, unless configured otherwise, all manycast clients
+in TTL range will eventually find all primary servers
+in TTL range, which is probably not the most common
+objective in large networks.
+The
+\f\*[B-Font]tos\f[]
+command can be used to modify this behavior.
+Servers with stratum below
+\f\*[B-Font]floor\f[]
+or above
+\f\*[B-Font]ceiling\f[]
+specified in the
+\f\*[B-Font]tos\f[]
+command are strongly discouraged during the selection
+process; however, these servers may be temporally
+accepted if the number of servers within TTL range is
+less than
+\f\*[B-Font]minclock\f[].
+.sp \n(Ppu
+.ne 2
+
+The above actions occur for each manycast client message,
+which repeats at the designated poll interval.
+However, once the ephemeral client association is mobilized,
+subsequent manycast server replies are discarded,
+since that would result in a duplicate association.
+If during a poll interval the number of client associations
+falls below
+\f\*[B-Font]minclock\f[],
+all manycast client prototype associations are reset
+to the initial poll interval and TTL hops and operation
+resumes from the beginning.
+It is important to avoid
+frequent manycast client messages, since each one requires
+all manycast servers in TTL range to respond.
+The result could well be an implosion, either minor or major,
+depending on the number of servers in range.
+The recommended value for
+\f\*[B-Font]maxpoll\f[]
+is 12 (4,096 s).
+.sp \n(Ppu
+.ne 2
+
+It is possible and frequently useful to configure a host
+as both manycast client and manycast server.
+A number of hosts configured this way and sharing a common
+group address will automatically organize themselves
+in an optimum configuration based on stratum and
+synchronization distance.
+For example, consider an NTP
+subnet of two primary servers and a hundred or more
+dependent clients.
+With two exceptions, all servers
+and clients have identical configuration files including both
+\f\*[B-Font]multicastclient\f[]
+and
+\f\*[B-Font]multicastserver\f[]
+commands using, for instance, multicast group address
+239.1.1.1.
+The only exception is that each primary server
+configuration file must include commands for the primary
+reference source such as a GPS receiver.
+.sp \n(Ppu
+.ne 2
+
+The remaining configuration files for all secondary
+servers and clients have the same contents, except for the
+\f\*[B-Font]tos\f[]
+command, which is specific for each stratum level.
+For stratum 1 and stratum 2 servers, that command is
+not necessary.
+For stratum 3 and above servers the
+\f\*[B-Font]floor\f[]
+value is set to the intended stratum number.
+Thus, all stratum 3 configuration files are identical,
+all stratum 4 files are identical and so forth.
+.sp \n(Ppu
+.ne 2
+
+Once operations have stabilized in this scenario,
+the primary servers will find the primary reference source
+and each other, since they both operate at the same
+stratum (1), but not with any secondary server or client,
+since these operate at a higher stratum.
+The secondary
+servers will find the servers at the same stratum level.
+If one of the primary servers loses its GPS receiver,
+it will continue to operate as a client and other clients
+will time out the corresponding association and
+re-associate accordingly.
+.sp \n(Ppu
+.ne 2
+
+Some administrators prefer to avoid running
+\fCntpd\fR(@NTPD_MS@)\f[]
+continuously and run either
+\fCntpdate\fR(8)\f[]
+or
+\fCntpd\fR(@NTPD_MS@)\f[]
+\f\*[B-Font]\-q\f[]
+as a cron job.
+In either case the servers must be
+configured in advance and the program fails if none are
+available when the cron job runs.
+A really slick
+application of manycast is with
+\fCntpd\fR(@NTPD_MS@)\f[]
+\f\*[B-Font]\-q\f[].
+The program wakes up, scans the local landscape looking
+for the usual suspects, selects the best from among
+the rascals, sets the clock and then departs.
+Servers do not have to be configured in advance and
+all clients throughout the network can have the same
+configuration file.
+.SS Manycast Interactions with Autokey
+Each time a manycast client sends a client mode packet
+to a multicast group address, all manycast servers
+in scope generate a reply including the host name
+and status word.
+The manycast clients then run
+the Autokey protocol, which collects and verifies
+all certificates involved.
+Following the burst interval
+all but three survivors are cast off,
+but the certificates remain in the local cache.
+It often happens that several complete signing trails
+from the client to the primary servers are collected in this way.
+.sp \n(Ppu
+.ne 2
+
+About once an hour or less often if the poll interval
+exceeds this, the client regenerates the Autokey key list.
+This is in general transparent in client/server mode.
+However, about once per day the server private value
+used to generate cookies is refreshed along with all
+manycast client associations.
+In this case all
+cryptographic values including certificates is refreshed.
+If a new certificate has been generated since
+the last refresh epoch, it will automatically revoke
+all prior certificates that happen to be in the
+certificate cache.
+At the same time, the manycast
+scheme starts all over from the beginning and
+the expanding ring shrinks to the minimum and increments
+from there while collecting all servers in scope.
+.SS Manycast Options
+.TP 7
+.NOP \f\*[B-Font]tos\f[] [\f\*[B-Font]ceiling\f[] \f\*[I-Font]ceiling\f[] | \f\*[B-Font]cohort\f[] { \f\*[B-Font]0\f[] | \f\*[B-Font]1\f[] } | \f\*[B-Font]floor\f[] \f\*[I-Font]floor\f[] | \f\*[B-Font]minclock\f[] \f\*[I-Font]minclock\f[] | \f\*[B-Font]minsane\f[] \f\*[I-Font]minsane\f[]]
+This command affects the clock selection and clustering
+algorithms.
+It can be used to select the quality and
+quantity of peers used to synchronize the system clock
+and is most useful in manycast mode.
+The variables operate
+as follows:
+.RS
+.TP 7
+.NOP \f\*[B-Font]ceiling\f[] \f\*[I-Font]ceiling\f[]
+Peers with strata above
+\f\*[B-Font]ceiling\f[]
+will be discarded if there are at least
+\f\*[B-Font]minclock\f[]
+peers remaining.
+This value defaults to 15, but can be changed
+to any number from 1 to 15.
+.TP 7
+.NOP \f\*[B-Font]cohort\f[] {0 | 1 }
+This is a binary flag which enables (0) or disables (1)
+manycast server replies to manycast clients with the same
+stratum level.
+This is useful to reduce implosions where
+large numbers of clients with the same stratum level
+are present.
+The default is to enable these replies.
+.TP 7
+.NOP \f\*[B-Font]floor\f[] \f\*[I-Font]floor\f[]
+Peers with strata below
+\f\*[B-Font]floor\f[]
+will be discarded if there are at least
+\f\*[B-Font]minclock\f[]
+peers remaining.
+This value defaults to 1, but can be changed
+to any number from 1 to 15.
+.TP 7
+.NOP \f\*[B-Font]minclock\f[] \f\*[I-Font]minclock\f[]
+The clustering algorithm repeatedly casts out outlyer
+associations until no more than
+\f\*[B-Font]minclock\f[]
+associations remain.
+This value defaults to 3,
+but can be changed to any number from 1 to the number of
+configured sources.
+.TP 7
+.NOP \f\*[B-Font]minsane\f[] \f\*[I-Font]minsane\f[]
+This is the minimum number of candidates available
+to the clock selection algorithm in order to produce
+one or more truechimers for the clustering algorithm.
+If fewer than this number are available, the clock is
+undisciplined and allowed to run free.
+The default is 1
+for legacy purposes.
+However, according to principles of
+Byzantine agreement,
+\f\*[B-Font]minsane\f[]
+should be at least 4 in order to detect and discard
+a single falseticker.
+.RE
+.TP 7
+.NOP \f\*[B-Font]ttl\f[] \f\*[I-Font]hop\f[] \f\*[I-Font]...\f[]
+This command specifies a list of TTL values in increasing
+order, up to 8 values can be specified.
+In manycast mode these values are used in turn
+in an expanding-ring search.
+The default is eight
+multiples of 32 starting at 31.
+.PP
+.SH Reference Clock Support
+The NTP Version 4 daemon supports some three dozen different radio,
+satellite and modem reference clocks plus a special pseudo-clock
+used for backup or when no other clock source is available.
+Detailed descriptions of individual device drivers and options can
+be found in the
+"Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+Additional information can be found in the pages linked
+there, including the
+"Debugging Hints for Reference Clock Drivers"
+and
+"How To Write a Reference Clock Driver"
+pages
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+In addition, support for a PPS
+signal is available as described in the
+"Pulse-per-second (PPS) Signal Interfacing"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+Many
+drivers support special line discipline/streams modules which can
+significantly improve the accuracy using the driver.
+These are
+described in the
+"Line Disciplines and Streams Drivers"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+.sp \n(Ppu
+.ne 2
+
+A reference clock will generally (though not always) be a radio
+timecode receiver which is synchronized to a source of standard
+time such as the services offered by the NRC in Canada and NIST and
+USNO in the US.
+The interface between the computer and the timecode
+receiver is device dependent, but is usually a serial port.
+A
+device driver specific to each reference clock must be selected and
+compiled in the distribution; however, most common radio, satellite
+and modem clocks are included by default.
+Note that an attempt to
+configure a reference clock when the driver has not been compiled
+or the hardware port has not been appropriately configured results
+in a scalding remark to the system log file, but is otherwise non
+hazardous.
+.sp \n(Ppu
+.ne 2
+
+For the purposes of configuration,
+\fCntpd\fR(@NTPD_MS@)\f[]
+treats
+reference clocks in a manner analogous to normal NTP peers as much
+as possible.
+Reference clocks are identified by a syntactically
+correct but invalid IP address, in order to distinguish them from
+normal NTP peers.
+Reference clock addresses are of the form
+\f[C]127.127.\f[]\f\*[I-Font]t\f[].\f\*[I-Font]u\f[],
+where
+\f\*[I-Font]t\f[]
+is an integer
+denoting the clock type and
+\f\*[I-Font]u\f[]
+indicates the unit
+number in the range 0-3.
+While it may seem overkill, it is in fact
+sometimes useful to configure multiple reference clocks of the same
+type, in which case the unit numbers must be unique.
+.sp \n(Ppu
+.ne 2
+
+The
+\f\*[B-Font]server\f[]
+command is used to configure a reference
+clock, where the
+\f\*[I-Font]address\f[]
+argument in that command
+is the clock address.
+The
+\f\*[B-Font]key\f[],
+\f\*[B-Font]version\f[]
+and
+\f\*[B-Font]ttl\f[]
+options are not used for reference clock support.
+The
+\f\*[B-Font]mode\f[]
+option is added for reference clock support, as
+described below.
+The
+\f\*[B-Font]prefer\f[]
+option can be useful to
+persuade the server to cherish a reference clock with somewhat more
+enthusiasm than other reference clocks or peers.
+Further
+information on this option can be found in the
+"Mitigation Rules and the prefer Keyword"
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[])
+page.
+The
+\f\*[B-Font]minpoll\f[]
+and
+\f\*[B-Font]maxpoll\f[]
+options have
+meaning only for selected clock drivers.
+See the individual clock
+driver document pages for additional information.
+.sp \n(Ppu
+.ne 2
+
+The
+\f\*[B-Font]fudge\f[]
+command is used to provide additional
+information for individual clock drivers and normally follows
+immediately after the
+\f\*[B-Font]server\f[]
+command.
+The
+\f\*[I-Font]address\f[]
+argument specifies the clock address.
+The
+\f\*[B-Font]refid\f[]
+and
+\f\*[B-Font]stratum\f[]
+options can be used to
+override the defaults for the device.
+There are two optional
+device-dependent time offsets and four flags that can be included
+in the
+\f\*[B-Font]fudge\f[]
+command as well.
+.sp \n(Ppu
+.ne 2
+
+The stratum number of a reference clock is by default zero.
+Since the
+\fCntpd\fR(@NTPD_MS@)\f[]
+daemon adds one to the stratum of each
+peer, a primary server ordinarily displays an external stratum of
+one.
+In order to provide engineered backups, it is often useful to
+specify the reference clock stratum as greater than zero.
+The
+\f\*[B-Font]stratum\f[]
+option is used for this purpose.
+Also, in cases
+involving both a reference clock and a pulse-per-second (PPS)
+discipline signal, it is useful to specify the reference clock
+identifier as other than the default, depending on the driver.
+The
+\f\*[B-Font]refid\f[]
+option is used for this purpose.
+Except where noted,
+these options apply to all clock drivers.
+.SS Reference Clock Commands
+.TP 7
+.NOP \f\*[B-Font]server\f[] \f[C]127.127.\f[]\f\*[I-Font]t\f[].\f\*[I-Font]u\f[] [\f\*[B-Font]prefer\f[]] [\f\*[B-Font]mode\f[] \f\*[I-Font]int\f[]] [\f\*[B-Font]minpoll\f[] \f\*[I-Font]int\f[]] [\f\*[B-Font]maxpoll\f[] \f\*[I-Font]int\f[]]
+This command can be used to configure reference clocks in
+special ways.
+The options are interpreted as follows:
+.RS
+.TP 7
+.NOP \f\*[B-Font]prefer\f[]
+Marks the reference clock as preferred.
+All other things being
+equal, this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+"Mitigation Rules and the prefer Keyword"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[])
+for further information.
+.TP 7
+.NOP \f\*[B-Font]mode\f[] \f\*[I-Font]int\f[]
+Specifies a mode number which is interpreted in a
+device-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+.TP 7
+.NOP \f\*[B-Font]minpoll\f[] \f\*[I-Font]int\f[]
+.TP 7
+.NOP \f\*[B-Font]maxpoll\f[] \f\*[I-Font]int\f[]
+These options specify the minimum and maximum polling interval
+for reference clock messages, as a power of 2 in seconds
+For
+most directly connected reference clocks, both
+\f\*[B-Font]minpoll\f[]
+and
+\f\*[B-Font]maxpoll\f[]
+default to 6 (64 s).
+For modem reference clocks,
+\f\*[B-Font]minpoll\f[]
+defaults to 10 (17.1 m) and
+\f\*[B-Font]maxpoll\f[]
+defaults to 14 (4.5 h).
+The allowable range is 4 (16 s) to 17 (36.4 h) inclusive.
+.RE
+.TP 7
+.NOP \f\*[B-Font]fudge\f[] \f[C]127.127.\f[]\f\*[I-Font]t\f[].\f\*[I-Font]u\f[] [\f\*[B-Font]time1\f[] \f\*[I-Font]sec\f[]] [\f\*[B-Font]time2\f[] \f\*[I-Font]sec\f[]] [\f\*[B-Font]stratum\f[] \f\*[I-Font]int\f[]] [\f\*[B-Font]refid\f[] \f\*[I-Font]string\f[]] [\f\*[B-Font]mode\f[] \f\*[I-Font]int\f[]] [\f\*[B-Font]flag1\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]] [\f\*[B-Font]flag2\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]] [\f\*[B-Font]flag3\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]] [\f\*[B-Font]flag4\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]]
+This command can be used to configure reference clocks in
+special ways.
+It must immediately follow the
+\f\*[B-Font]server\f[]
+command which configures the driver.
+Note that the same capability
+is possible at run time using the
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+program.
+The options are interpreted as
+follows:
+.RS
+.TP 7
+.NOP \f\*[B-Font]time1\f[] \f\*[I-Font]sec\f[]
+Specifies a constant to be added to the time offset produced by
+the driver, a fixed-point decimal number in seconds.
+This is used
+as a calibration constant to adjust the nominal time offset of a
+particular clock to agree with an external standard, such as a
+precision PPS signal.
+It also provides a way to correct a
+systematic error or bias due to serial port or operating system
+latencies, different cable lengths or receiver internal delay.
+The
+specified offset is in addition to the propagation delay provided
+by other means, such as internal DIPswitches.
+Where a calibration
+for an individual system and driver is available, an approximate
+correction is noted in the driver documentation pages.
+Note: in order to facilitate calibration when more than one
+radio clock or PPS signal is supported, a special calibration
+feature is available.
+It takes the form of an argument to the
+\f\*[B-Font]enable\f[]
+command described in
+\fIMiscellaneous\f[] \fIOptions\f[]
+page and operates as described in the
+"Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+.TP 7
+.NOP \f\*[B-Font]time2\f[] \f\*[I-Font]secs\f[]
+Specifies a fixed-point decimal number in seconds, which is
+interpreted in a driver-dependent way.
+See the descriptions of
+specific drivers in the
+"Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+.TP 7
+.NOP \f\*[B-Font]stratum\f[] \f\*[I-Font]int\f[]
+Specifies the stratum number assigned to the driver, an integer
+between 0 and 15.
+This number overrides the default stratum number
+ordinarily assigned by the driver itself, usually zero.
+.TP 7
+.NOP \f\*[B-Font]refid\f[] \f\*[I-Font]string\f[]
+Specifies an ASCII string of from one to four characters which
+defines the reference identifier used by the driver.
+This string
+overrides the default identifier ordinarily assigned by the driver
+itself.
+.TP 7
+.NOP \f\*[B-Font]mode\f[] \f\*[I-Font]int\f[]
+Specifies a mode number which is interpreted in a
+device-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+.TP 7
+.NOP \f\*[B-Font]flag1\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]
+.TP 7
+.NOP \f\*[B-Font]flag2\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]
+.TP 7
+.NOP \f\*[B-Font]flag3\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]
+.TP 7
+.NOP \f\*[B-Font]flag4\f[] \f\*[B-Font]0\f[] \f\*[B-Font]\&|\f[] \f\*[B-Font]1\f[]
+These four flags are used for customizing the clock driver.
+The
+interpretation of these values, and whether they are used at all,
+is a function of the particular clock driver.
+However, by
+convention
+\f\*[B-Font]flag4\f[]
+is used to enable recording monitoring
+data to the
+\f\*[B-Font]clockstats\f[]
+file configured with the
+\f\*[B-Font]filegen\f[]
+command.
+Further information on the
+\f\*[B-Font]filegen\f[]
+command can be found in
+\fIMonitoring\f[] \fIOptions\f[].
+.RE
+.PP
+.SH Miscellaneous Options
+.TP 7
+.NOP \f\*[B-Font]broadcastdelay\f[] \f\*[I-Font]seconds\f[]
+The broadcast and multicast modes require a special calibration
+to determine the network delay between the local and remote
+servers.
+Ordinarily, this is done automatically by the initial
+protocol exchanges between the client and server.
+In some cases,
+the calibration procedure may fail due to network or server access
+controls, for example.
+This command specifies the default delay to
+be used under these circumstances.
+Typically (for Ethernet), a
+number between 0.003 and 0.007 seconds is appropriate.
+The default
+when this command is not used is 0.004 seconds.
+.TP 7
+.NOP \f\*[B-Font]calldelay\f[] \f\*[I-Font]delay\f[]
+This option controls the delay in seconds between the first and second
+packets sent in burst or iburst mode to allow additional time for a modem
+or ISDN call to complete.
+.TP 7
+.NOP \f\*[B-Font]driftfile\f[] \f\*[I-Font]driftfile\f[]
+This command specifies the complete path and name of the file used to
+record the frequency of the local clock oscillator.
+This is the same
+operation as the
+\f\*[B-Font]\-f\f[]
+command line option.
+If the file exists, it is read at
+startup in order to set the initial frequency and then updated once per
+hour with the current frequency computed by the daemon.
+If the file name is
+specified, but the file itself does not exist, the starts with an initial
+frequency of zero and creates the file when writing it for the first time.
+If this command is not given, the daemon will always start with an initial
+frequency of zero.
+.sp \n(Ppu
+.ne 2
+
+The file format consists of a single line containing a single
+floating point number, which records the frequency offset measured
+in parts-per-million (PPM).
+The file is updated by first writing
+the current drift value into a temporary file and then renaming
+this file to replace the old version.
+This implies that
+\fCntpd\fR(@NTPD_MS@)\f[]
+must have write permission for the directory the
+drift file is located in, and that file system links, symbolic or
+otherwise, should be avoided.
+.TP 7
+.NOP \f\*[B-Font]enable\f[] [\f\*[B-Font]auth\f[] | \f\*[B-Font]bclient\f[] | \f\*[B-Font]calibrate\f[] | \f\*[B-Font]kernel\f[] | \f\*[B-Font]mode7\f[] | \f\*[B-Font]monitor\f[] | \f\*[B-Font]ntp\f[] | \f\*[B-Font]stats\f[]]
+.TP 7
+.NOP \f\*[B-Font]disable\f[] [\f\*[B-Font]auth\f[] | \f\*[B-Font]bclient\f[] | \f\*[B-Font]calibrate\f[] | \f\*[B-Font]kernel\f[] | \f\*[B-Font]mode7\f[] | \f\*[B-Font]monitor\f[] | \f\*[B-Font]ntp\f[] | \f\*[B-Font]stats\f[]]
+Provides a way to enable or disable various server options.
+Flags not mentioned are unaffected.
+Note that all of these flags
+can be controlled remotely using the
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+utility program.
+.RS
+.TP 7
+.NOP \f\*[B-Font]auth\f[]
+Enables the server to synchronize with unconfigured peers only if the
+peer has been correctly authenticated using either public key or
+private key cryptography.
+The default for this flag is
+\f\*[B-Font]enable\f[].
+.TP 7
+.NOP \f\*[B-Font]bclient\f[]
+Enables the server to listen for a message from a broadcast or
+multicast server, as in the
+\f\*[B-Font]multicastclient\f[]
+command with default
+address.
+The default for this flag is
+\f\*[B-Font]disable\f[].
+.TP 7
+.NOP \f\*[B-Font]calibrate\f[]
+Enables the calibrate feature for reference clocks.
+The default for
+this flag is
+\f\*[B-Font]disable\f[].
+.TP 7
+.NOP \f\*[B-Font]kernel\f[]
+Enables the kernel time discipline, if available.
+The default for this
+flag is
+\f\*[B-Font]enable\f[]
+if support is available, otherwise
+\f\*[B-Font]disable\f[].
+.TP 7
+.NOP \f\*[B-Font]mode7\f[]
+Enables processing of NTP mode 7 implementation-specific requests
+which are used by the deprecated
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+program.
+The default for this flag is disable.
+This flag is excluded from runtime configuration using
+\fCntpq\fR(@NTPQ_MS@)\f[].
+The
+\fCntpq\fR(@NTPQ_MS@)\f[]
+program provides the same capabilities as
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+using standard mode 6 requests.
+.TP 7
+.NOP \f\*[B-Font]monitor\f[]
+Enables the monitoring facility.
+See the
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+program
+and the
+\f\*[B-Font]monlist\f[]
+command or further information.
+The
+default for this flag is
+\f\*[B-Font]enable\f[].
+.TP 7
+.NOP \f\*[B-Font]ntp\f[]
+Enables time and frequency discipline.
+In effect, this switch opens and
+closes the feedback loop, which is useful for testing.
+The default for
+this flag is
+\f\*[B-Font]enable\f[].
+.TP 7
+.NOP \f\*[B-Font]stats\f[]
+Enables the statistics facility.
+See the
+\fIMonitoring\f[] \fIOptions\f[]
+section for further information.
+The default for this flag is
+\f\*[B-Font]disable\f[].
+.RE
+.TP 7
+.NOP \f\*[B-Font]includefile\f[] \f\*[I-Font]includefile\f[]
+This command allows additional configuration commands
+to be included from a separate file.
+Include files may
+be nested to a depth of five; upon reaching the end of any
+include file, command processing resumes in the previous
+configuration file.
+This option is useful for sites that run
+\fCntpd\fR(@NTPD_MS@)\f[]
+on multiple hosts, with (mostly) common options (e.g., a
+restriction list).
+.TP 7
+.NOP \f\*[B-Font]logconfig\f[] \f\*[I-Font]configkeyword\f[]
+This command controls the amount and type of output written to
+the system
+\fCsyslog\fR(3)\f[]
+facility or the alternate
+\f\*[B-Font]logfile\f[]
+log file.
+By default, all output is turned on.
+All
+\f\*[I-Font]configkeyword\f[]
+keywords can be prefixed with
+\[oq]=\[cq],
+\[oq]+\[cq]
+and
+\[oq]\-\[cq],
+where
+\[oq]=\[cq]
+sets the
+\fCsyslog\fR(3)\f[]
+priority mask,
+\[oq]+\[cq]
+adds and
+\[oq]\-\[cq]
+removes
+messages.
+\fCsyslog\fR(3)\f[]
+messages can be controlled in four
+classes
+(\f\*[B-Font]clock\f[], \f\*[B-Font]peer\f[], \f\*[B-Font]sys\f[] and \f\*[B-Font]sync\f[]).
+Within these classes four types of messages can be
+controlled: informational messages
+(\f\*[B-Font]info\f[]),
+event messages
+(\f\*[B-Font]events\f[]),
+statistics messages
+(\f\*[B-Font]statistics\f[])
+and
+status messages
+(\f\*[B-Font]status\f[]).
+.sp \n(Ppu
+.ne 2
+
+Configuration keywords are formed by concatenating the message class with
+the event class.
+The
+\f\*[B-Font]all\f[]
+prefix can be used instead of a message class.
+A
+message class may also be followed by the
+\f\*[B-Font]all\f[]
+keyword to enable/disable all
+messages of the respective message class.Thus, a minimal log configuration
+could look like this:
+.br
+.in +4
+.nf
+logconfig =syncstatus +sysevents
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+This would just list the synchronizations state of
+\fCntpd\fR(@NTPD_MS@)\f[]
+and the major system events.
+For a simple reference server, the
+following minimum message configuration could be useful:
+.br
+.in +4
+.nf
+logconfig =syncall +clockall
+.in -4
+.fi
+.sp \n(Ppu
+.ne 2
+
+This configuration will list all clock information and
+synchronization information.
+All other events and messages about
+peers, system events and so on is suppressed.
+.TP 7
+.NOP \f\*[B-Font]logfile\f[] \f\*[I-Font]logfile\f[]
+This command specifies the location of an alternate log file to
+be used instead of the default system
+\fCsyslog\fR(3)\f[]
+facility.
+This is the same operation as the \-l command line option.
+.TP 7
+.NOP \f\*[B-Font]setvar\f[] \f\*[I-Font]variable\f[] [\f\*[B-Font]default\f[]]
+This command adds an additional system variable.
+These
+variables can be used to distribute additional information such as
+the access policy.
+If the variable of the form
+\fIname\f[]\fI=\f[]\f\*[I-Font]value\f[]
+is followed by the
+\f\*[B-Font]default\f[]
+keyword, the
+variable will be listed as part of the default system variables
+(\fCntpq\fR(@NTPQ_MS@)\f[] \f\*[B-Font]rv\f[] command)).
+These additional variables serve
+informational purposes only.
+They are not related to the protocol
+other that they can be listed.
+The known protocol variables will
+always override any variables defined via the
+\f\*[B-Font]setvar\f[]
+mechanism.
+There are three special variables that contain the names
+of all variable of the same group.
+The
+\fIsys_var_list\f[]
+holds
+the names of all system variables.
+The
+\fIpeer_var_list\f[]
+holds
+the names of all peer variables and the
+\fIclock_var_list\f[]
+holds the names of the reference clock variables.
+.TP 7
+.NOP \f\*[B-Font]tinker\f[] [\f\*[B-Font]allan\f[] \f\*[I-Font]allan\f[] | \f\*[B-Font]dispersion\f[] \f\*[I-Font]dispersion\f[] | \f\*[B-Font]freq\f[] \f\*[I-Font]freq\f[] | \f\*[B-Font]huffpuff\f[] \f\*[I-Font]huffpuff\f[] | \f\*[B-Font]panic\f[] \f\*[I-Font]panic\f[] | \f\*[B-Font]step\f[] \f\*[I-Font]srep\f[] | \f\*[B-Font]stepout\f[] \f\*[I-Font]stepout\f[]]
+This command can be used to alter several system variables in
+very exceptional circumstances.
+It should occur in the
+configuration file before any other configuration options.
+The
+default values of these variables have been carefully optimized for
+a wide range of network speeds and reliability expectations.
+In
+general, they interact in intricate ways that are hard to predict
+and some combinations can result in some very nasty behavior.
+Very
+rarely is it necessary to change the default values; but, some
+folks cannot resist twisting the knobs anyway and this command is
+for them.
+Emphasis added: twisters are on their own and can expect
+no help from the support group.
+.sp \n(Ppu
+.ne 2
+
+The variables operate as follows:
+.RS
+.TP 7
+.NOP \f\*[B-Font]allan\f[] \f\*[I-Font]allan\f[]
+The argument becomes the new value for the minimum Allan
+intercept, which is a parameter of the PLL/FLL clock discipline
+algorithm.
+The value in log2 seconds defaults to 7 (1024 s), which is also the lower
+limit.
+.TP 7
+.NOP \f\*[B-Font]dispersion\f[] \f\*[I-Font]dispersion\f[]
+The argument becomes the new value for the dispersion increase rate,
+normally .000015 s/s.
+.TP 7
+.NOP \f\*[B-Font]freq\f[] \f\*[I-Font]freq\f[]
+The argument becomes the initial value of the frequency offset in
+parts-per-million.
+This overrides the value in the frequency file, if
+present, and avoids the initial training state if it is not.
+.TP 7
+.NOP \f\*[B-Font]huffpuff\f[] \f\*[I-Font]huffpuff\f[]
+The argument becomes the new value for the experimental
+huff-n'-puff filter span, which determines the most recent interval
+the algorithm will search for a minimum delay.
+The lower limit is
+900 s (15 m), but a more reasonable value is 7200 (2 hours).
+There
+is no default, since the filter is not enabled unless this command
+is given.
+.TP 7
+.NOP \f\*[B-Font]panic\f[] \f\*[I-Font]panic\f[]
+The argument is the panic threshold, normally 1000 s.
+If set to zero,
+the panic sanity check is disabled and a clock offset of any value will
+be accepted.
+.TP 7
+.NOP \f\*[B-Font]step\f[] \f\*[I-Font]step\f[]
+The argument is the step threshold, which by default is 0.128 s.
+It can
+be set to any positive number in seconds.
+If set to zero, step
+adjustments will never occur.
+Note: The kernel time discipline is
+disabled if the step threshold is set to zero or greater than the
+default.
+.TP 7
+.NOP \f\*[B-Font]stepout\f[] \f\*[I-Font]stepout\f[]
+The argument is the stepout timeout, which by default is 900 s.
+It can
+be set to any positive number in seconds.
+If set to zero, the stepout
+pulses will not be suppressed.
+.RE
+.TP 7
+.NOP \f\*[B-Font]rlimit\f[] [\f\*[B-Font]memlock\f[] \f\*[I-Font]Nmegabytes\f[] | \f\*[B-Font]stacksize\f[] \f\*[I-Font]N4kPages\f[] \f\*[B-Font]filenum\f[] \f\*[I-Font]Nfiledescriptors\f[]]
+.RS
+.TP 7
+.NOP \f\*[B-Font]memlock\f[] \f\*[I-Font]Nmegabytes\f[]
+Specify the number of megabytes of memory that can be allocated.
+Probably only available under Linux, this option is useful
+when dropping root (the
+\f\*[B-Font]\-i\f[]
+option).
+The default is 32 megabytes. Setting this to zero will prevent any attemp to lock memory.
+.TP 7
+.NOP \f\*[B-Font]stacksize\f[] \f\*[I-Font]N4kPages\f[]
+Specifies the maximum size of the process stack on systems with the
+.TP 7
+.NOP \f\*[B-Font]filenum\f[] \f\*[I-Font]Nfiledescriptors\f[]
+Specifies the maximum number of file descriptors ntpd may have open at once. Defaults to the system default.
+\fBmlockall\fR()\f[]
+function.
+Defaults to 50 4k pages (200 4k pages in OpenBSD).
+.RE
+.TP 7
+.NOP \f\*[B-Font]trap\f[] \f\*[I-Font]host_address\f[] [\f\*[B-Font]port\f[] \f\*[I-Font]port_number\f[]] [\f\*[B-Font]interface\f[] \f\*[I-Font]interface_address\f[]]
+This command configures a trap receiver at the given host
+address and port number for sending messages with the specified
+local interface address.
+If the port number is unspecified, a value
+of 18447 is used.
+If the interface address is not specified, the
+message is sent with a source address of the local interface the
+message is sent through.
+Note that on a multihomed host the
+interface used may vary from time to time with routing changes.
+.sp \n(Ppu
+.ne 2
+
+The trap receiver will generally log event messages and other
+information from the server in a log file.
+While such monitor
+programs may also request their own trap dynamically, configuring a
+trap receiver will ensure that no messages are lost when the server
+is started.
+.TP 7
+.NOP \f\*[B-Font]hop\f[] \f\*[I-Font]...\f[]
+This command specifies a list of TTL values in increasing order, up to 8
+values can be specified.
+In manycast mode these values are used in turn in
+an expanding-ring search.
+The default is eight multiples of 32 starting at
+31.
+.PP
+.SH "OPTIONS"
+.TP
+.NOP \f\*[B-Font]\-\-help\f[]
+Display usage information and exit.
+.TP
+.NOP \f\*[B-Font]\-\-more-help\f[]
+Pass the extended usage information through a pager.
+.TP
+.NOP \f\*[B-Font]\-\-version\f[] [{\f\*[I-Font]v|c|n\f[]}]
+Output version of program and exit. The default mode is `v', a simple
+version. The `c' mode will print copyright information and `n' will
+print the full copyright notice.
+.PP
+.SH "OPTION PRESETS"
+Any option that is not marked as \fInot presettable\fP may be preset
+by loading values from environment variables named:
+.nf
+ \fBNTP_CONF_<option-name>\fP or \fBNTP_CONF\fP
+.fi
+.ad
+.SH "ENVIRONMENT"
+See \fBOPTION PRESETS\fP for configuration environment variables.
+.SH FILES
+.TP 15
+.NOP \fI/etc/ntp.conf\f[]
+the default name of the configuration file
+.br
+.ns
+.TP 15
+.NOP \fIntp.keys\f[]
+private MD5 keys
+.br
+.ns
+.TP 15
+.NOP \fIntpkey\f[]
+RSA private key
+.br
+.ns
+.TP 15
+.NOP \fIntpkey_\f[]\f\*[I-Font]host\f[]
+RSA public key
+.br
+.ns
+.TP 15
+.NOP \fIntp_dh\f[]
+Diffie-Hellman agreement parameters
+.PP
+.SH "EXIT STATUS"
+One of the following exit values will be returned:
+.TP
+.NOP 0 " (EXIT_SUCCESS)"
+Successful program execution.
+.TP
+.NOP 1 " (EXIT_FAILURE)"
+The operation failed or the command syntax was not valid.
+.TP
+.NOP 70 " (EX_SOFTWARE)"
+libopts had an internal operational error. Please report
+it to autogen-users@lists.sourceforge.net. Thank you.
+.PP
+.SH "SEE ALSO"
+\fCntpd\fR(@NTPD_MS@)\f[],
+\fCntpdc\fR(@NTPDC_MS@)\f[],
+\fCntpq\fR(@NTPQ_MS@)\f[]
+.sp \n(Ppu
+.ne 2
+
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+\f[C]http://www.ntp.org/\f[].
+A snapshot of this documentation is available in HTML format in
+\fI/usr/share/doc/ntp\f[].
+David L. Mills,
+\fINetwork Time Protocol (Version 4)\fR,
+RFC5905
+.PP
+
+.SH "AUTHORS"
+The University of Delaware
+.SH "COPYRIGHT"
+Copyright (C) 1970-2014 The University of Delaware all rights reserved.
+This program is released under the terms of the NTP license, <http://ntp.org/license>.
+.SH BUGS
+The syntax checking is not picky; some combinations of
+ridiculous and even hilarious options and modes may not be
+detected.
+.sp \n(Ppu
+.ne 2
+
+The
+\fIntpkey_\f[]\f\*[I-Font]host\f[]
+files are really digital
+certificates.
+These should be obtained via secure directory
+services when they become universally available.
+.sp \n(Ppu
+.ne 2
+
+Please send bug reports to: http://bugs.ntp.org, bugs@ntp.org
+.SH NOTES
+This document was derived from FreeBSD.
+.sp \n(Ppu
+.ne 2
+
+This manual page was \fIAutoGen\fP-erated from the \fBntp.conf\fP
+option definitions.
diff --git a/ntpd/ntp.conf.mdoc.in b/ntpd/ntp.conf.mdoc.in
new file mode 100644
index 0000000..3a236d5
--- /dev/null
+++ b/ntpd/ntp.conf.mdoc.in
@@ -0,0 +1,2808 @@
+.Dd December 2 2014
+.Dt NTP_CONF 5 File Formats
+.Os
+.\" EDIT THIS FILE WITH CAUTION (ntp.mdoc)
+.\"
+.\" It has been AutoGen-ed December 2, 2014 at 08:56:58 AM by AutoGen 5.18.5pre4
+.\" From the definitions ntp.conf.def
+.\" and the template file agmdoc-cmd.tpl
+.Sh NAME
+.Nm ntp.conf
+.Nd Network Time Protocol (NTP) daemon configuration file format
+.Sh SYNOPSIS
+.Nm
+.Op Fl \-option\-name
+.Op Fl \-option\-name Ar value
+.Pp
+All arguments must be options.
+.Pp
+.Sh DESCRIPTION
+The
+.Nm
+configuration file is read at initial startup by the
+.Xr ntpd @NTPD_MS@
+daemon in order to specify the synchronization sources,
+modes and other related information.
+Usually, it is installed in the
+.Pa /etc
+directory,
+but could be installed elsewhere
+(see the daemon's
+.Fl c
+command line option).
+.Pp
+The file format is similar to other
+.Ux
+configuration files.
+Comments begin with a
+.Ql #
+character and extend to the end of the line;
+blank lines are ignored.
+Configuration commands consist of an initial keyword
+followed by a list of arguments,
+some of which may be optional, separated by whitespace.
+Commands may not be continued over multiple lines.
+Arguments may be host names,
+host addresses written in numeric, dotted\-quad form,
+integers, floating point numbers (when specifying times in seconds)
+and text strings.
+.Pp
+The rest of this page describes the configuration and control options.
+The
+.Qq Notes on Configuring NTP and Setting up an NTP Subnet
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+contains an extended discussion of these options.
+In addition to the discussion of general
+.Sx Configuration Options ,
+there are sections describing the following supported functionality
+and the options used to control it:
+.Bl -bullet -offset indent
+.It
+.Sx Authentication Support
+.It
+.Sx Monitoring Support
+.It
+.Sx Access Control Support
+.It
+.Sx Automatic NTP Configuration Options
+.It
+.Sx Reference Clock Support
+.It
+.Sx Miscellaneous Options
+.El
+.Pp
+Following these is a section describing
+.Sx Miscellaneous Options .
+While there is a rich set of options available,
+the only required option is one or more
+.Ic pool ,
+.Ic server ,
+.Ic peer ,
+.Ic broadcast
+or
+.Ic manycastclient
+commands.
+.Sh Configuration Support
+Following is a description of the configuration commands in
+NTPv4.
+These commands have the same basic functions as in NTPv3 and
+in some cases new functions and new arguments.
+There are two
+classes of commands, configuration commands that configure a
+persistent association with a remote server or peer or reference
+clock, and auxiliary commands that specify environmental variables
+that control various related operations.
+.Ss Configuration Commands
+The various modes are determined by the command keyword and the
+type of the required IP address.
+Addresses are classed by type as
+(s) a remote server or peer (IPv4 class A, B and C), (b) the
+broadcast address of a local interface, (m) a multicast address (IPv4
+class D), or (r) a reference clock address (127.127.x.x).
+Note that
+only those options applicable to each command are listed below.
+Use
+of options not listed may not be caught as an error, but may result
+in some weird and even destructive behavior.
+.Pp
+If the Basic Socket Interface Extensions for IPv6 (RFC\-2553)
+is detected, support for the IPv6 address family is generated
+in addition to the default support of the IPv4 address family.
+In a few cases, including the reslist billboard generated
+by ntpdc, IPv6 addresses are automatically generated.
+IPv6 addresses can be identified by the presence of colons
+.Dq \&:
+in the address field.
+IPv6 addresses can be used almost everywhere where
+IPv4 addresses can be used,
+with the exception of reference clock addresses,
+which are always IPv4.
+.Pp
+Note that in contexts where a host name is expected, a
+.Fl 4
+qualifier preceding
+the host name forces DNS resolution to the IPv4 namespace,
+while a
+.Fl 6
+qualifier forces DNS resolution to the IPv6 namespace.
+See IPv6 references for the
+equivalent classes for that address family.
+.Bl -tag -width indent
+.It Xo Ic pool Ar address
+.Op Cm burst
+.Op Cm iburst
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Xc
+.It Xo Ic server Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm burst
+.Op Cm iburst
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Xc
+.It Xo Ic peer Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Xc
+.It Xo Ic broadcast Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm ttl Ar ttl
+.Xc
+.It Xo Ic manycastclient Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Op Cm ttl Ar ttl
+.Xc
+.El
+.Pp
+These five commands specify the time server name or address to
+be used and the mode in which to operate.
+The
+.Ar address
+can be
+either a DNS name or an IP address in dotted\-quad notation.
+Additional information on association behavior can be found in the
+.Qq Association Management
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.Bl -tag -width indent
+.It Ic pool
+For type s addresses, this command mobilizes a persistent
+client mode association with a number of remote servers.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+.It Ic server
+For type s and r addresses, this command mobilizes a persistent
+client mode association with the specified remote server or local
+radio clock.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+This command should
+.Em not
+be used for type
+b or m addresses.
+.It Ic peer
+For type s addresses (only), this command mobilizes a
+persistent symmetric\-active mode association with the specified
+remote peer.
+In this mode the local clock can be synchronized to
+the remote peer or the remote peer can be synchronized to the local
+clock.
+This is useful in a network of servers where, depending on
+various failure scenarios, either the local or remote peer may be
+the better source of time.
+This command should NOT be used for type
+b, m or r addresses.
+.It Ic broadcast
+For type b and m addresses (only), this
+command mobilizes a persistent broadcast mode association.
+Multiple
+commands can be used to specify multiple local broadcast interfaces
+(subnets) and/or multiple multicast groups.
+Note that local
+broadcast messages go only to the interface associated with the
+subnet specified, but multicast messages go to all interfaces.
+In broadcast mode the local server sends periodic broadcast
+messages to a client population at the
+.Ar address
+specified, which is usually the broadcast address on (one of) the
+local network(s) or a multicast address assigned to NTP.
+The IANA
+has assigned the multicast group address IPv4 224.0.1.1 and
+IPv6 ff05::101 (site local) exclusively to
+NTP, but other nonconflicting addresses can be used to contain the
+messages within administrative boundaries.
+Ordinarily, this
+specification applies only to the local server operating as a
+sender; for operation as a broadcast client, see the
+.Ic broadcastclient
+or
+.Ic multicastclient
+commands
+below.
+.It Ic manycastclient
+For type m addresses (only), this command mobilizes a
+manycast client mode association for the multicast address
+specified.
+In this case a specific address must be supplied which
+matches the address used on the
+.Ic manycastserver
+command for
+the designated manycast servers.
+The NTP multicast address
+224.0.1.1 assigned by the IANA should NOT be used, unless specific
+means are taken to avoid spraying large areas of the Internet with
+these messages and causing a possibly massive implosion of replies
+at the sender.
+The
+.Ic manycastserver
+command specifies that the local server
+is to operate in client mode with the remote servers that are
+discovered as the result of broadcast/multicast messages.
+The
+client broadcasts a request message to the group address associated
+with the specified
+.Ar address
+and specifically enabled
+servers respond to these messages.
+The client selects the servers
+providing the best time and continues as with the
+.Ic server
+command.
+The remaining servers are discarded as if never
+heard.
+.El
+.Pp
+Options:
+.Bl -tag -width indent
+.It Cm autokey
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the autokey scheme
+described in
+.Sx Authentication Options .
+.It Cm burst
+when the server is reachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first and second packets
+can be changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to improve timekeeping quality
+with the
+.Ic server
+command and s addresses.
+.It Cm iburst
+When the server is unreachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first two packets can be
+changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to speed the initial synchronization
+acquisition with the
+.Ic server
+command and s addresses and when
+.Xr ntpd @NTPD_MS@
+is started with the
+.Fl q
+option.
+.It Cm key Ar key
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the specified
+.Ar key
+identifier with values from 1 to 65534, inclusive.
+The
+default is to include no encryption field.
+.It Cm minpoll Ar minpoll
+.It Cm maxpoll Ar maxpoll
+These options specify the minimum and maximum poll intervals
+for NTP messages, as a power of 2 in seconds
+The maximum poll
+interval defaults to 10 (1,024 s), but can be increased by the
+.Cm maxpoll
+option to an upper limit of 17 (36.4 h).
+The
+minimum poll interval defaults to 6 (64 s), but can be decreased by
+the
+.Cm minpoll
+option to a lower limit of 4 (16 s).
+.It Cm noselect
+Marks the server as unused, except for display purposes.
+The server is discarded by the selection algroithm.
+.It Cm prefer
+Marks the server as preferred.
+All other things being equal,
+this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+.Qq Mitigation Rules and the prefer Keyword
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+for further information.
+.It Cm ttl Ar ttl
+This option is used only with broadcast server and manycast
+client modes.
+It specifies the time\-to\-live
+.Ar ttl
+to
+use on broadcast server and multicast server and the maximum
+.Ar ttl
+for the expanding ring search with manycast
+client packets.
+Selection of the proper value, which defaults to
+127, is something of a black art and should be coordinated with the
+network administrator.
+.It Cm version Ar version
+Specifies the version number to be used for outgoing NTP
+packets.
+Versions 1\-4 are the choices, with version 4 the
+default.
+.El
+.Ss Auxiliary Commands
+.Bl -tag -width indent
+.It Ic broadcastclient
+This command enables reception of broadcast server messages to
+any local interface (type b) address.
+Upon receiving a message for
+the first time, the broadcast client measures the nominal server
+propagation delay using a brief client/server exchange with the
+server, then enters the broadcast client mode, in which it
+synchronizes to succeeding broadcast messages.
+Note that, in order
+to avoid accidental or malicious disruption in this mode, both the
+server and client should operate using symmetric\-key or public\-key
+authentication as described in
+.Sx Authentication Options .
+.It Ic manycastserver Ar address ...
+This command enables reception of manycast client messages to
+the multicast group address(es) (type m) specified.
+At least one
+address is required, but the NTP multicast address 224.0.1.1
+assigned by the IANA should NOT be used, unless specific means are
+taken to limit the span of the reply and avoid a possibly massive
+implosion at the original sender.
+Note that, in order to avoid
+accidental or malicious disruption in this mode, both the server
+and client should operate using symmetric\-key or public\-key
+authentication as described in
+.Sx Authentication Options .
+.It Ic multicastclient Ar address ...
+This command enables reception of multicast server messages to
+the multicast group address(es) (type m) specified.
+Upon receiving
+a message for the first time, the multicast client measures the
+nominal server propagation delay using a brief client/server
+exchange with the server, then enters the broadcast client mode, in
+which it synchronizes to succeeding multicast messages.
+Note that,
+in order to avoid accidental or malicious disruption in this mode,
+both the server and client should operate using symmetric\-key or
+public\-key authentication as described in
+.Sx Authentication Options .
+.El
+.Sh Authentication Support
+Authentication support allows the NTP client to verify that the
+server is in fact known and trusted and not an intruder intending
+accidentally or on purpose to masquerade as that server.
+The NTPv3
+specification RFC\-1305 defines a scheme which provides
+cryptographic authentication of received NTP packets.
+Originally,
+this was done using the Data Encryption Standard (DES) algorithm
+operating in Cipher Block Chaining (CBC) mode, commonly called
+DES\-CBC.
+Subsequently, this was replaced by the RSA Message Digest
+5 (MD5) algorithm using a private key, commonly called keyed\-MD5.
+Either algorithm computes a message digest, or one\-way hash, which
+can be used to verify the server has the correct private key and
+key identifier.
+.Pp
+NTPv4 retains the NTPv3 scheme, properly described as symmetric key
+cryptography and, in addition, provides a new Autokey scheme
+based on public key cryptography.
+Public key cryptography is generally considered more secure
+than symmetric key cryptography, since the security is based
+on a private value which is generated by each server and
+never revealed.
+With Autokey all key distribution and
+management functions involve only public values, which
+considerably simplifies key distribution and storage.
+Public key management is based on X.509 certificates,
+which can be provided by commercial services or
+produced by utility programs in the OpenSSL software library
+or the NTPv4 distribution.
+.Pp
+While the algorithms for symmetric key cryptography are
+included in the NTPv4 distribution, public key cryptography
+requires the OpenSSL software library to be installed
+before building the NTP distribution.
+Directions for doing that
+are on the Building and Installing the Distribution page.
+.Pp
+Authentication is configured separately for each association
+using the
+.Cm key
+or
+.Cm autokey
+subcommand on the
+.Ic peer ,
+.Ic server ,
+.Ic broadcast
+and
+.Ic manycastclient
+configuration commands as described in
+.Sx Configuration Options
+page.
+The authentication
+options described below specify the locations of the key files,
+if other than default, which symmetric keys are trusted
+and the interval between various operations, if other than default.
+.Pp
+Authentication is always enabled,
+although ineffective if not configured as
+described below.
+If a NTP packet arrives
+including a message authentication
+code (MAC), it is accepted only if it
+passes all cryptographic checks.
+The
+checks require correct key ID, key value
+and message digest.
+If the packet has
+been modified in any way or replayed
+by an intruder, it will fail one or more
+of these checks and be discarded.
+Furthermore, the Autokey scheme requires a
+preliminary protocol exchange to obtain
+the server certificate, verify its
+credentials and initialize the protocol
+.Pp
+The
+.Cm auth
+flag controls whether new associations or
+remote configuration commands require cryptographic authentication.
+This flag can be set or reset by the
+.Ic enable
+and
+.Ic disable
+commands and also by remote
+configuration commands sent by a
+.Xr ntpdc @NTPDC_MS@
+program running in
+another machine.
+If this flag is enabled, which is the default
+case, new broadcast client and symmetric passive associations and
+remote configuration commands must be cryptographically
+authenticated using either symmetric key or public key cryptography.
+If this
+flag is disabled, these operations are effective
+even if not cryptographic
+authenticated.
+It should be understood
+that operating with the
+.Ic auth
+flag disabled invites a significant vulnerability
+where a rogue hacker can
+masquerade as a falseticker and seriously
+disrupt system timekeeping.
+It is
+important to note that this flag has no purpose
+other than to allow or disallow
+a new association in response to new broadcast
+and symmetric active messages
+and remote configuration commands and, in particular,
+the flag has no effect on
+the authentication process itself.
+.Pp
+An attractive alternative where multicast support is available
+is manycast mode, in which clients periodically troll
+for servers as described in the
+.Sx Automatic NTP Configuration Options
+page.
+Either symmetric key or public key
+cryptographic authentication can be used in this mode.
+The principle advantage
+of manycast mode is that potential servers need not be
+configured in advance,
+since the client finds them during regular operation,
+and the configuration
+files for all clients can be identical.
+.Pp
+The security model and protocol schemes for
+both symmetric key and public key
+cryptography are summarized below;
+further details are in the briefings, papers
+and reports at the NTP project page linked from
+.Li http://www.ntp.org/ .
+.Ss Symmetric\-Key Cryptography
+The original RFC\-1305 specification allows any one of possibly
+65,534 keys, each distinguished by a 32\-bit key identifier, to
+authenticate an association.
+The servers and clients involved must
+agree on the key and key identifier to
+authenticate NTP packets.
+Keys and
+related information are specified in a key
+file, usually called
+.Pa ntp.keys ,
+which must be distributed and stored using
+secure means beyond the scope of the NTP protocol itself.
+Besides the keys used
+for ordinary NTP associations,
+additional keys can be used as passwords for the
+.Xr ntpq @NTPQ_MS@
+and
+.Xr ntpdc @NTPDC_MS@
+utility programs.
+.Pp
+When
+.Xr ntpd @NTPD_MS@
+is first started, it reads the key file specified in the
+.Ic keys
+configuration command and installs the keys
+in the key cache.
+However,
+individual keys must be activated with the
+.Ic trusted
+command before use.
+This
+allows, for instance, the installation of possibly
+several batches of keys and
+then activating or deactivating each batch
+remotely using
+.Xr ntpdc @NTPDC_MS@ .
+This also provides a revocation capability that can be used
+if a key becomes compromised.
+The
+.Ic requestkey
+command selects the key used as the password for the
+.Xr ntpdc @NTPDC_MS@
+utility, while the
+.Ic controlkey
+command selects the key used as the password for the
+.Xr ntpq @NTPQ_MS@
+utility.
+.Ss Public Key Cryptography
+NTPv4 supports the original NTPv3 symmetric key scheme
+described in RFC\-1305 and in addition the Autokey protocol,
+which is based on public key cryptography.
+The Autokey Version 2 protocol described on the Autokey Protocol
+page verifies packet integrity using MD5 message digests
+and verifies the source with digital signatures and any of several
+digest/signature schemes.
+Optional identity schemes described on the Identity Schemes
+page and based on cryptographic challenge/response algorithms
+are also available.
+Using all of these schemes provides strong security against
+replay with or without modification, spoofing, masquerade
+and most forms of clogging attacks.
+.\" .Pp
+.\" The cryptographic means necessary for all Autokey operations
+.\" is provided by the OpenSSL software library.
+.\" This library is available from http://www.openssl.org/
+.\" and can be installed using the procedures outlined
+.\" in the Building and Installing the Distribution page.
+.\" Once installed,
+.\" the configure and build
+.\" process automatically detects the library and links
+.\" the library routines required.
+.Pp
+The Autokey protocol has several modes of operation
+corresponding to the various NTP modes supported.
+Most modes use a special cookie which can be
+computed independently by the client and server,
+but encrypted in transmission.
+All modes use in addition a variant of the S\-KEY scheme,
+in which a pseudo\-random key list is generated and used
+in reverse order.
+These schemes are described along with an executive summary,
+current status, briefing slides and reading list on the
+.Sx Autonomous Authentication
+page.
+.Pp
+The specific cryptographic environment used by Autokey servers
+and clients is determined by a set of files
+and soft links generated by the
+.Xr ntp\-keygen 1ntpkeygenmdoc
+program.
+This includes a required host key file,
+required certificate file and optional sign key file,
+leapsecond file and identity scheme files.
+The
+digest/signature scheme is specified in the X.509 certificate
+along with the matching sign key.
+There are several schemes
+available in the OpenSSL software library, each identified
+by a specific string such as
+.Cm md5WithRSAEncryption ,
+which stands for the MD5 message digest with RSA
+encryption scheme.
+The current NTP distribution supports
+all the schemes in the OpenSSL library, including
+those based on RSA and DSA digital signatures.
+.Pp
+NTP secure groups can be used to define cryptographic compartments
+and security hierarchies.
+It is important that every host
+in the group be able to construct a certificate trail to one
+or more trusted hosts in the same group.
+Each group
+host runs the Autokey protocol to obtain the certificates
+for all hosts along the trail to one or more trusted hosts.
+This requires the configuration file in all hosts to be
+engineered so that, even under anticipated failure conditions,
+the NTP subnet will form such that every group host can find
+a trail to at least one trusted host.
+.Ss Naming and Addressing
+It is important to note that Autokey does not use DNS to
+resolve addresses, since DNS can't be completely trusted
+until the name servers have synchronized clocks.
+The cryptographic name used by Autokey to bind the host identity
+credentials and cryptographic values must be independent
+of interface, network and any other naming convention.
+The name appears in the host certificate in either or both
+the subject and issuer fields, so protection against
+DNS compromise is essential.
+.Pp
+By convention, the name of an Autokey host is the name returned
+by the Unix
+.Xr gethostname 2
+system call or equivalent in other systems.
+By the system design
+model, there are no provisions to allow alternate names or aliases.
+However, this is not to say that DNS aliases, different names
+for each interface, etc., are constrained in any way.
+.Pp
+It is also important to note that Autokey verifies authenticity
+using the host name, network address and public keys,
+all of which are bound together by the protocol specifically
+to deflect masquerade attacks.
+For this reason Autokey
+includes the source and destinatino IP addresses in message digest
+computations and so the same addresses must be available
+at both the server and client.
+For this reason operation
+with network address translation schemes is not possible.
+This reflects the intended robust security model where government
+and corporate NTP servers are operated outside firewall perimeters.
+.Ss Operation
+A specific combination of authentication scheme (none,
+symmetric key, public key) and identity scheme is called
+a cryptotype, although not all combinations are compatible.
+There may be management configurations where the clients,
+servers and peers may not all support the same cryptotypes.
+A secure NTPv4 subnet can be configured in many ways while
+keeping in mind the principles explained above and
+in this section.
+Note however that some cryptotype
+combinations may successfully interoperate with each other,
+but may not represent good security practice.
+.Pp
+The cryptotype of an association is determined at the time
+of mobilization, either at configuration time or some time
+later when a message of appropriate cryptotype arrives.
+When mobilized by a
+.Ic server
+or
+.Ic peer
+configuration command and no
+.Ic key
+or
+.Ic autokey
+subcommands are present, the association is not
+authenticated; if the
+.Ic key
+subcommand is present, the association is authenticated
+using the symmetric key ID specified; if the
+.Ic autokey
+subcommand is present, the association is authenticated
+using Autokey.
+.Pp
+When multiple identity schemes are supported in the Autokey
+protocol, the first message exchange determines which one is used.
+The client request message contains bits corresponding
+to which schemes it has available.
+The server response message
+contains bits corresponding to which schemes it has available.
+Both server and client match the received bits with their own
+and select a common scheme.
+.Pp
+Following the principle that time is a public value,
+a server responds to any client packet that matches
+its cryptotype capabilities.
+Thus, a server receiving
+an unauthenticated packet will respond with an unauthenticated
+packet, while the same server receiving a packet of a cryptotype
+it supports will respond with packets of that cryptotype.
+However, unconfigured broadcast or manycast client
+associations or symmetric passive associations will not be
+mobilized unless the server supports a cryptotype compatible
+with the first packet received.
+By default, unauthenticated associations will not be mobilized
+unless overridden in a decidedly dangerous way.
+.Pp
+Some examples may help to reduce confusion.
+Client Alice has no specific cryptotype selected.
+Server Bob has both a symmetric key file and minimal Autokey files.
+Alice's unauthenticated messages arrive at Bob, who replies with
+unauthenticated messages.
+Cathy has a copy of Bob's symmetric
+key file and has selected key ID 4 in messages to Bob.
+Bob verifies the message with his key ID 4.
+If it's the
+same key and the message is verified, Bob sends Cathy a reply
+authenticated with that key.
+If verification fails,
+Bob sends Cathy a thing called a crypto\-NAK, which tells her
+something broke.
+She can see the evidence using the
+.Xr ntpq @NTPQ_MS@
+program.
+.Pp
+Denise has rolled her own host key and certificate.
+She also uses one of the identity schemes as Bob.
+She sends the first Autokey message to Bob and they
+both dance the protocol authentication and identity steps.
+If all comes out okay, Denise and Bob continue as described above.
+.Pp
+It should be clear from the above that Bob can support
+all the girls at the same time, as long as he has compatible
+authentication and identity credentials.
+Now, Bob can act just like the girls in his own choice of servers;
+he can run multiple configured associations with multiple different
+servers (or the same server, although that might not be useful).
+But, wise security policy might preclude some cryptotype
+combinations; for instance, running an identity scheme
+with one server and no authentication with another might not be wise.
+.Ss Key Management
+The cryptographic values used by the Autokey protocol are
+incorporated as a set of files generated by the
+.Xr ntp\-keygen 1ntpkeygenmdoc
+utility program, including symmetric key, host key and
+public certificate files, as well as sign key, identity parameters
+and leapseconds files.
+Alternatively, host and sign keys and
+certificate files can be generated by the OpenSSL utilities
+and certificates can be imported from public certificate
+authorities.
+Note that symmetric keys are necessary for the
+.Xr ntpq @NTPQ_MS@
+and
+.Xr ntpdc @NTPDC_MS@
+utility programs.
+The remaining files are necessary only for the
+Autokey protocol.
+.Pp
+Certificates imported from OpenSSL or public certificate
+authorities have certian limitations.
+The certificate should be in ASN.1 syntax, X.509 Version 3
+format and encoded in PEM, which is the same format
+used by OpenSSL.
+The overall length of the certificate encoded
+in ASN.1 must not exceed 1024 bytes.
+The subject distinguished
+name field (CN) is the fully qualified name of the host
+on which it is used; the remaining subject fields are ignored.
+The certificate extension fields must not contain either
+a subject key identifier or a issuer key identifier field;
+however, an extended key usage field for a trusted host must
+contain the value
+.Cm trustRoot ; .
+Other extension fields are ignored.
+.Ss Authentication Commands
+.Bl -tag -width indent
+.It Ic autokey Op Ar logsec
+Specifies the interval between regenerations of the session key
+list used with the Autokey protocol.
+Note that the size of the key
+list for each association depends on this interval and the current
+poll interval.
+The default value is 12 (4096 s or about 1.1 hours).
+For poll intervals above the specified interval, a session key list
+with a single entry will be regenerated for every message
+sent.
+.It Ic controlkey Ar key
+Specifies the key identifier to use with the
+.Xr ntpq @NTPQ_MS@
+utility, which uses the standard
+protocol defined in RFC\-1305.
+The
+.Ar key
+argument is
+the key identifier for a trusted key, where the value can be in the
+range 1 to 65,534, inclusive.
+.It Xo Ic crypto
+.Op Cm cert Ar file
+.Op Cm leap Ar file
+.Op Cm randfile Ar file
+.Op Cm host Ar file
+.Op Cm sign Ar file
+.Op Cm gq Ar file
+.Op Cm gqpar Ar file
+.Op Cm iffpar Ar file
+.Op Cm mvpar Ar file
+.Op Cm pw Ar password
+.Xc
+This command requires the OpenSSL library.
+It activates public key
+cryptography, selects the message digest and signature
+encryption scheme and loads the required private and public
+values described above.
+If one or more files are left unspecified,
+the default names are used as described above.
+Unless the complete path and name of the file are specified, the
+location of a file is relative to the keys directory specified
+in the
+.Ic keysdir
+command or default
+.Pa /usr/local/etc .
+Following are the subcommands:
+.Bl -tag -width indent
+.It Cm cert Ar file
+Specifies the location of the required host public certificate file.
+This overrides the link
+.Pa ntpkey_cert_ Ns Ar hostname
+in the keys directory.
+.It Cm gqpar Ar file
+Specifies the location of the optional GQ parameters file.
+This
+overrides the link
+.Pa ntpkey_gq_ Ns Ar hostname
+in the keys directory.
+.It Cm host Ar file
+Specifies the location of the required host key file.
+This overrides
+the link
+.Pa ntpkey_key_ Ns Ar hostname
+in the keys directory.
+.It Cm iffpar Ar file
+Specifies the location of the optional IFF parameters file.This
+overrides the link
+.Pa ntpkey_iff_ Ns Ar hostname
+in the keys directory.
+.It Cm leap Ar file
+Specifies the location of the optional leapsecond file.
+This overrides the link
+.Pa ntpkey_leap
+in the keys directory.
+.It Cm mvpar Ar file
+Specifies the location of the optional MV parameters file.
+This
+overrides the link
+.Pa ntpkey_mv_ Ns Ar hostname
+in the keys directory.
+.It Cm pw Ar password
+Specifies the password to decrypt files containing private keys and
+identity parameters.
+This is required only if these files have been
+encrypted.
+.It Cm randfile Ar file
+Specifies the location of the random seed file used by the OpenSSL
+library.
+The defaults are described in the main text above.
+.It Cm sign Ar file
+Specifies the location of the optional sign key file.
+This overrides
+the link
+.Pa ntpkey_sign_ Ns Ar hostname
+in the keys directory.
+If this file is
+not found, the host key is also the sign key.
+.El
+.It Ic keys Ar keyfile
+Specifies the complete path and location of the MD5 key file
+containing the keys and key identifiers used by
+.Xr ntpd @NTPD_MS@ ,
+.Xr ntpq @NTPQ_MS@
+and
+.Xr ntpdc @NTPDC_MS@
+when operating with symmetric key cryptography.
+This is the same operation as the
+.Fl k
+command line option.
+.It Ic keysdir Ar path
+This command specifies the default directory path for
+cryptographic keys, parameters and certificates.
+The default is
+.Pa /usr/local/etc/ .
+.It Ic requestkey Ar key
+Specifies the key identifier to use with the
+.Xr ntpdc @NTPDC_MS@
+utility program, which uses a
+proprietary protocol specific to this implementation of
+.Xr ntpd @NTPD_MS@ .
+The
+.Ar key
+argument is a key identifier
+for the trusted key, where the value can be in the range 1 to
+65,534, inclusive.
+.It Ic revoke Ar logsec
+Specifies the interval between re\-randomization of certain
+cryptographic values used by the Autokey scheme, as a power of 2 in
+seconds.
+These values need to be updated frequently in order to
+deflect brute\-force attacks on the algorithms of the scheme;
+however, updating some values is a relatively expensive operation.
+The default interval is 16 (65,536 s or about 18 hours).
+For poll
+intervals above the specified interval, the values will be updated
+for every message sent.
+.It Ic trustedkey Ar key ...
+Specifies the key identifiers which are trusted for the
+purposes of authenticating peers with symmetric key cryptography,
+as well as keys used by the
+.Xr ntpq @NTPQ_MS@
+and
+.Xr ntpdc @NTPDC_MS@
+programs.
+The authentication procedures require that both the local
+and remote servers share the same key and key identifier for this
+purpose, although different keys can be used with different
+servers.
+The
+.Ar key
+arguments are 32\-bit unsigned
+integers with values from 1 to 65,534.
+.El
+.Ss Error Codes
+The following error codes are reported via the NTP control
+and monitoring protocol trap mechanism.
+.Bl -tag -width indent
+.It 101
+.Pq bad field format or length
+The packet has invalid version, length or format.
+.It 102
+.Pq bad timestamp
+The packet timestamp is the same or older than the most recent received.
+This could be due to a replay or a server clock time step.
+.It 103
+.Pq bad filestamp
+The packet filestamp is the same or older than the most recent received.
+This could be due to a replay or a key file generation error.
+.It 104
+.Pq bad or missing public key
+The public key is missing, has incorrect format or is an unsupported type.
+.It 105
+.Pq unsupported digest type
+The server requires an unsupported digest/signature scheme.
+.It 106
+.Pq mismatched digest types
+Not used.
+.It 107
+.Pq bad signature length
+The signature length does not match the current public key.
+.It 108
+.Pq signature not verified
+The message fails the signature check.
+It could be bogus or signed by a
+different private key.
+.It 109
+.Pq certificate not verified
+The certificate is invalid or signed with the wrong key.
+.It 110
+.Pq certificate not verified
+The certificate is not yet valid or has expired or the signature could not
+be verified.
+.It 111
+.Pq bad or missing cookie
+The cookie is missing, corrupted or bogus.
+.It 112
+.Pq bad or missing leapseconds table
+The leapseconds table is missing, corrupted or bogus.
+.It 113
+.Pq bad or missing certificate
+The certificate is missing, corrupted or bogus.
+.It 114
+.Pq bad or missing identity
+The identity key is missing, corrupt or bogus.
+.El
+.Sh Monitoring Support
+.Xr ntpd @NTPD_MS@
+includes a comprehensive monitoring facility suitable
+for continuous, long term recording of server and client
+timekeeping performance.
+See the
+.Ic statistics
+command below
+for a listing and example of each type of statistics currently
+supported.
+Statistic files are managed using file generation sets
+and scripts in the
+.Pa ./scripts
+directory of this distribution.
+Using
+these facilities and
+.Ux
+.Xr cron 8
+jobs, the data can be
+automatically summarized and archived for retrospective analysis.
+.Ss Monitoring Commands
+.Bl -tag -width indent
+.It Ic statistics Ar name ...
+Enables writing of statistics records.
+Currently, eight kinds of
+.Ar name
+statistics are supported.
+.Bl -tag -width indent
+.It Cm clockstats
+Enables recording of clock driver statistics information.
+Each update
+received from a clock driver appends a line of the following form to
+the file generation set named
+.Cm clockstats :
+.Bd -literal
+49213 525.624 127.127.4.1 93 226 00:08:29.606 D
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the
+clock address in dotted\-quad notation.
+The final field shows the last
+timecode received from the clock in decoded ASCII format, where
+meaningful.
+In some clock drivers a good deal of additional information
+can be gathered and displayed as well.
+See information specific to each
+clock for further details.
+.It Cm cryptostats
+This option requires the OpenSSL cryptographic software library.
+It
+enables recording of cryptographic public key protocol information.
+Each message received by the protocol module appends a line of the
+following form to the file generation set named
+.Cm cryptostats :
+.Bd -literal
+49213 525.624 127.127.4.1 message
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the peer
+address in dotted\-quad notation, The final message field includes the
+message type and certain ancillary information.
+See the
+.Sx Authentication Options
+section for further information.
+.It Cm loopstats
+Enables recording of loop filter statistics information.
+Each
+update of the local clock outputs a line of the following form to
+the file generation set named
+.Cm loopstats :
+.Bd -literal
+50935 75440.031 0.000006019 13.778190 0.000351733 0.0133806
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next five fields
+show time offset (seconds), frequency offset (parts per million \-
+PPM), RMS jitter (seconds), Allan deviation (PPM) and clock
+discipline time constant.
+.It Cm peerstats
+Enables recording of peer statistics information.
+This includes
+statistics records of all peers of a NTP server and of special
+signals, where present and configured.
+Each valid update appends a
+line of the following form to the current element of a file
+generation set named
+.Cm peerstats :
+.Bd -literal
+48773 10847.650 127.127.4.1 9714 \-0.001605376 0.000000000 0.001424877 0.000958674
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the peer address in dotted\-quad notation and status,
+respectively.
+The status field is encoded in hex in the format
+described in Appendix A of the NTP specification RFC 1305.
+The final four fields show the offset,
+delay, dispersion and RMS jitter, all in seconds.
+.It Cm rawstats
+Enables recording of raw\-timestamp statistics information.
+This
+includes statistics records of all peers of a NTP server and of
+special signals, where present and configured.
+Each NTP message
+received from a peer or clock driver appends a line of the
+following form to the file generation set named
+.Cm rawstats :
+.Bd -literal
+50928 2132.543 128.4.1.1 128.4.1.20 3102453281.584327000 3102453281.58622800031 02453332.540806000 3102453332.541458000
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the remote peer or clock address followed by the local address
+in dotted\-quad notation.
+The final four fields show the originate,
+receive, transmit and final NTP timestamps in order.
+The timestamp
+values are as received and before processing by the various data
+smoothing and mitigation algorithms.
+.It Cm sysstats
+Enables recording of ntpd statistics counters on a periodic basis.
+Each
+hour a line of the following form is appended to the file generation
+set named
+.Cm sysstats :
+.Bd -literal
+50928 2132.543 36000 81965 0 9546 56 71793 512 540 10 147
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The remaining ten fields show
+the statistics counter values accumulated since the last generated
+line.
+.Bl -tag -width indent
+.It Time since restart Cm 36000
+Time in hours since the system was last rebooted.
+.It Packets received Cm 81965
+Total number of packets received.
+.It Packets processed Cm 0
+Number of packets received in response to previous packets sent
+.It Current version Cm 9546
+Number of packets matching the current NTP version.
+.It Previous version Cm 56
+Number of packets matching the previous NTP version.
+.It Bad version Cm 71793
+Number of packets matching neither NTP version.
+.It Access denied Cm 512
+Number of packets denied access for any reason.
+.It Bad length or format Cm 540
+Number of packets with invalid length, format or port number.
+.It Bad authentication Cm 10
+Number of packets not verified as authentic.
+.It Rate exceeded Cm 147
+Number of packets discarded due to rate limitation.
+.El
+.It Cm statsdir Ar directory_path
+Indicates the full path of a directory where statistics files
+should be created (see below).
+This keyword allows
+the (otherwise constant)
+.Cm filegen
+filename prefix to be modified for file generation sets, which
+is useful for handling statistics logs.
+.It Cm filegen Ar name Xo
+.Op Cm file Ar filename
+.Op Cm type Ar typename
+.Op Cm link | nolink
+.Op Cm enable | disable
+.Xc
+Configures setting of generation file set name.
+Generation
+file sets provide a means for handling files that are
+continuously growing during the lifetime of a server.
+Server statistics are a typical example for such files.
+Generation file sets provide access to a set of files used
+to store the actual data.
+At any time at most one element
+of the set is being written to.
+The type given specifies
+when and how data will be directed to a new element of the set.
+This way, information stored in elements of a file set
+that are currently unused are available for administrational
+operations without the risk of disturbing the operation of ntpd.
+(Most important: they can be removed to free space for new data
+produced.)
+.Pp
+Note that this command can be sent from the
+.Xr ntpdc @NTPDC_MS@
+program running at a remote location.
+.Bl -tag -width indent
+.It Cm name
+This is the type of the statistics records, as shown in the
+.Cm statistics
+command.
+.It Cm file Ar filename
+This is the file name for the statistics records.
+Filenames of set
+members are built from three concatenated elements
+.Ar Cm prefix ,
+.Ar Cm filename
+and
+.Ar Cm suffix :
+.Bl -tag -width indent
+.It Cm prefix
+This is a constant filename path.
+It is not subject to
+modifications via the
+.Ar filegen
+option.
+It is defined by the
+server, usually specified as a compile\-time constant.
+It may,
+however, be configurable for individual file generation sets
+via other commands.
+For example, the prefix used with
+.Ar loopstats
+and
+.Ar peerstats
+generation can be configured using the
+.Ar statsdir
+option explained above.
+.It Cm filename
+This string is directly concatenated to the prefix mentioned
+above (no intervening
+.Ql / ) .
+This can be modified using
+the file argument to the
+.Ar filegen
+statement.
+No
+.Pa ..
+elements are
+allowed in this component to prevent filenames referring to
+parts outside the filesystem hierarchy denoted by
+.Ar prefix .
+.It Cm suffix
+This part is reflects individual elements of a file set.
+It is
+generated according to the type of a file set.
+.El
+.It Cm type Ar typename
+A file generation set is characterized by its type.
+The following
+types are supported:
+.Bl -tag -width indent
+.It Cm none
+The file set is actually a single plain file.
+.It Cm pid
+One element of file set is used per incarnation of a ntpd
+server.
+This type does not perform any changes to file set
+members during runtime, however it provides an easy way of
+separating files belonging to different
+.Xr ntpd @NTPD_MS@
+server incarnations.
+The set member filename is built by appending a
+.Ql \&.
+to concatenated
+.Ar prefix
+and
+.Ar filename
+strings, and
+appending the decimal representation of the process ID of the
+.Xr ntpd @NTPD_MS@
+server process.
+.It Cm day
+One file generation set element is created per day.
+A day is
+defined as the period between 00:00 and 24:00 UTC.
+The file set
+member suffix consists of a
+.Ql \&.
+and a day specification in
+the form
+.Cm YYYYMMdd .
+.Cm YYYY
+is a 4\-digit year number (e.g., 1992).
+.Cm MM
+is a two digit month number.
+.Cm dd
+is a two digit day number.
+Thus, all information written at 10 December 1992 would end up
+in a file named
+.Ar prefix
+.Ar filename Ns .19921210 .
+.It Cm week
+Any file set member contains data related to a certain week of
+a year.
+The term week is defined by computing day\-of\-year
+modulo 7.
+Elements of such a file generation set are
+distinguished by appending the following suffix to the file set
+filename base: A dot, a 4\-digit year number, the letter
+.Cm W ,
+and a 2\-digit week number.
+For example, information from January,
+10th 1992 would end up in a file with suffix
+.No . Ns Ar 1992W1 .
+.It Cm month
+One generation file set element is generated per month.
+The
+file name suffix consists of a dot, a 4\-digit year number, and
+a 2\-digit month.
+.It Cm year
+One generation file element is generated per year.
+The filename
+suffix consists of a dot and a 4 digit year number.
+.It Cm age
+This type of file generation sets changes to a new element of
+the file set every 24 hours of server operation.
+The filename
+suffix consists of a dot, the letter
+.Cm a ,
+and an 8\-digit number.
+This number is taken to be the number of seconds the server is
+running at the start of the corresponding 24\-hour period.
+Information is only written to a file generation by specifying
+.Cm enable ;
+output is prevented by specifying
+.Cm disable .
+.El
+.It Cm link | nolink
+It is convenient to be able to access the current element of a file
+generation set by a fixed name.
+This feature is enabled by
+specifying
+.Cm link
+and disabled using
+.Cm nolink .
+If link is specified, a
+hard link from the current file set element to a file without
+suffix is created.
+When there is already a file with this name and
+the number of links of this file is one, it is renamed appending a
+dot, the letter
+.Cm C ,
+and the pid of the ntpd server process.
+When the
+number of links is greater than one, the file is unlinked.
+This
+allows the current file to be accessed by a constant name.
+.It Cm enable \&| Cm disable
+Enables or disables the recording function.
+.El
+.El
+.El
+.Sh Access Control Support
+The
+.Xr ntpd @NTPD_MS@
+daemon implements a general purpose address/mask based restriction
+list.
+The list contains address/match entries sorted first
+by increasing address values and and then by increasing mask values.
+A match occurs when the bitwise AND of the mask and the packet
+source address is equal to the bitwise AND of the mask and
+address in the list.
+The list is searched in order with the
+last match found defining the restriction flags associated
+with the entry.
+Additional information and examples can be found in the
+.Qq Notes on Configuring NTP and Setting up a NTP Subnet
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.Pp
+The restriction facility was implemented in conformance
+with the access policies for the original NSFnet backbone
+time servers.
+Later the facility was expanded to deflect
+cryptographic and clogging attacks.
+While this facility may
+be useful for keeping unwanted or broken or malicious clients
+from congesting innocent servers, it should not be considered
+an alternative to the NTP authentication facilities.
+Source address based restrictions are easily circumvented
+by a determined cracker.
+.Pp
+Clients can be denied service because they are explicitly
+included in the restrict list created by the restrict command
+or implicitly as the result of cryptographic or rate limit
+violations.
+Cryptographic violations include certificate
+or identity verification failure; rate limit violations generally
+result from defective NTP implementations that send packets
+at abusive rates.
+Some violations cause denied service
+only for the offending packet, others cause denied service
+for a timed period and others cause the denied service for
+an indefinate period.
+When a client or network is denied access
+for an indefinate period, the only way at present to remove
+the restrictions is by restarting the server.
+.Ss The Kiss\-of\-Death Packet
+Ordinarily, packets denied service are simply dropped with no
+further action except incrementing statistics counters.
+Sometimes a
+more proactive response is needed, such as a server message that
+explicitly requests the client to stop sending and leave a message
+for the system operator.
+A special packet format has been created
+for this purpose called the "kiss\-of\-death" (KoD) packet.
+KoD packets have the leap bits set unsynchronized and stratum set
+to zero and the reference identifier field set to a four\-byte
+ASCII code.
+If the
+.Cm noserve
+or
+.Cm notrust
+flag of the matching restrict list entry is set,
+the code is "DENY"; if the
+.Cm limited
+flag is set and the rate limit
+is exceeded, the code is "RATE".
+Finally, if a cryptographic violation occurs, the code is "CRYP".
+.Pp
+A client receiving a KoD performs a set of sanity checks to
+minimize security exposure, then updates the stratum and
+reference identifier peer variables, sets the access
+denied (TEST4) bit in the peer flash variable and sends
+a message to the log.
+As long as the TEST4 bit is set,
+the client will send no further packets to the server.
+The only way at present to recover from this condition is
+to restart the protocol at both the client and server.
+This
+happens automatically at the client when the association times out.
+It will happen at the server only if the server operator cooperates.
+.Ss Access Control Commands
+.Bl -tag -width indent
+.It Xo Ic discard
+.Op Cm average Ar avg
+.Op Cm minimum Ar min
+.Op Cm monitor Ar prob
+.Xc
+Set the parameters of the
+.Cm limited
+facility which protects the server from
+client abuse.
+The
+.Cm average
+subcommand specifies the minimum average packet
+spacing, while the
+.Cm minimum
+subcommand specifies the minimum packet spacing.
+Packets that violate these minima are discarded
+and a kiss\-o'\-death packet returned if enabled.
+The default
+minimum average and minimum are 5 and 2, respectively.
+The monitor subcommand specifies the probability of discard
+for packets that overflow the rate\-control window.
+.It Xo Ic restrict address
+.Op Cm mask Ar mask
+.Op Ar flag ...
+.Xc
+The
+.Ar address
+argument expressed in
+dotted\-quad form is the address of a host or network.
+Alternatively, the
+.Ar address
+argument can be a valid host DNS name.
+The
+.Ar mask
+argument expressed in dotted\-quad form defaults to
+.Cm 255.255.255.255 ,
+meaning that the
+.Ar address
+is treated as the address of an individual host.
+A default entry (address
+.Cm 0.0.0.0 ,
+mask
+.Cm 0.0.0.0 )
+is always included and is always the first entry in the list.
+Note that text string
+.Cm default ,
+with no mask option, may
+be used to indicate the default entry.
+In the current implementation,
+.Cm flag
+always
+restricts access, i.e., an entry with no flags indicates that free
+access to the server is to be given.
+The flags are not orthogonal,
+in that more restrictive flags will often make less restrictive
+ones redundant.
+The flags can generally be classed into two
+categories, those which restrict time service and those which
+restrict informational queries and attempts to do run\-time
+reconfiguration of the server.
+One or more of the following flags
+may be specified:
+.Bl -tag -width indent
+.It Cm ignore
+Deny packets of all kinds, including
+.Xr ntpq @NTPQ_MS@
+and
+.Xr ntpdc @NTPDC_MS@
+queries.
+.It Cm kod
+If this flag is set when an access violation occurs, a kiss\-o'\-death
+(KoD) packet is sent.
+KoD packets are rate limited to no more than one
+per second.
+If another KoD packet occurs within one second after the
+last one, the packet is dropped.
+.It Cm limited
+Deny service if the packet spacing violates the lower limits specified
+in the discard command.
+A history of clients is kept using the
+monitoring capability of
+.Xr ntpd @NTPD_MS@ .
+Thus, monitoring is always active as
+long as there is a restriction entry with the
+.Cm limited
+flag.
+.It Cm lowpriotrap
+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.
+.It Cm nomodify
+Deny
+.Xr ntpq @NTPQ_MS@
+and
+.Xr ntpdc @NTPDC_MS@
+queries which attempt to modify the state of the
+server (i.e., run time reconfiguration).
+Queries which return
+information are permitted.
+.It Cm noquery
+Deny
+.Xr ntpq @NTPQ_MS@
+and
+.Xr ntpdc @NTPDC_MS@
+queries.
+Time service is not affected.
+.It Cm nopeer
+Deny packets which would result in mobilizing a new association.
+This
+includes broadcast and symmetric active packets when a configured
+association does not exist.
+It also includes
+.Cm pool
+associations, so if you want to use servers from a
+.Cm pool
+directive and also want to use
+.Cm nopeer
+by default, you'll want a
+.Cm "restrict source ..." line as well that does
+.It not
+include the
+.Cm nopeer
+directive.
+.It Cm noserve
+Deny all packets except
+.Xr ntpq @NTPQ_MS@
+and
+.Xr ntpdc @NTPDC_MS@
+queries.
+.It Cm notrap
+Decline to provide mode 6 control message trap service to matching
+hosts.
+The trap service is a subsystem of the ntpdq control message
+protocol which is intended for use by remote event logging programs.
+.It Cm notrust
+Deny service unless the packet is cryptographically authenticated.
+.It Cm ntpport
+This is actually a match algorithm modifier, rather than a
+restriction flag.
+Its presence causes the restriction entry to be
+matched only if the source port in the packet is the standard NTP
+UDP port (123).
+Both
+.Cm ntpport
+and
+.Cm non\-ntpport
+may
+be specified.
+The
+.Cm ntpport
+is considered more specific and
+is sorted later in the list.
+.It Cm version
+Deny packets that do not match the current NTP version.
+.El
+.Pp
+Default restriction list entries with the flags ignore, interface,
+ntpport, for each of the local host's interface addresses are
+inserted into the table at startup to prevent the server
+from attempting to synchronize to its own time.
+A default entry is also always present, though if it is
+otherwise unconfigured; no flags are associated
+with the default entry (i.e., everything besides your own
+NTP server is unrestricted).
+.El
+.Sh Automatic NTP Configuration Options
+.Ss Manycasting
+Manycasting is a automatic discovery and configuration paradigm
+new to NTPv4.
+It is intended as a means for a multicast client
+to troll the nearby network neighborhood to find cooperating
+manycast servers, validate them using cryptographic means
+and evaluate their time values with respect to other servers
+that might be lurking in the vicinity.
+The intended result is that each manycast client mobilizes
+client associations with some number of the "best"
+of the nearby manycast servers, yet automatically reconfigures
+to sustain this number of servers should one or another fail.
+.Pp
+Note that the manycasting paradigm does not coincide
+with the anycast paradigm described in RFC\-1546,
+which is designed to find a single server from a clique
+of servers providing the same service.
+The manycast paradigm is designed to find a plurality
+of redundant servers satisfying defined optimality criteria.
+.Pp
+Manycasting can be used with either symmetric key
+or public key cryptography.
+The public key infrastructure (PKI)
+offers the best protection against compromised keys
+and is generally considered stronger, at least with relatively
+large key sizes.
+It is implemented using the Autokey protocol and
+the OpenSSL cryptographic library available from
+.Li http://www.openssl.org/ .
+The library can also be used with other NTPv4 modes
+as well and is highly recommended, especially for broadcast modes.
+.Pp
+A persistent manycast client association is configured
+using the manycastclient command, which is similar to the
+server command but with a multicast (IPv4 class
+.Cm D
+or IPv6 prefix
+.Cm FF )
+group address.
+The IANA has designated IPv4 address 224.1.1.1
+and IPv6 address FF05::101 (site local) for NTP.
+When more servers are needed, it broadcasts manycast
+client messages to this address at the minimum feasible rate
+and minimum feasible time\-to\-live (TTL) hops, depending
+on how many servers have already been found.
+There can be as many manycast client associations
+as different group address, each one serving as a template
+for a future ephemeral unicast client/server association.
+.Pp
+Manycast servers configured with the
+.Ic manycastserver
+command listen on the specified group address for manycast
+client messages.
+Note the distinction between manycast client,
+which actively broadcasts messages, and manycast server,
+which passively responds to them.
+If a manycast server is
+in scope of the current TTL and is itself synchronized
+to a valid source and operating at a stratum level equal
+to or lower than the manycast client, it replies to the
+manycast client message with an ordinary unicast server message.
+.Pp
+The manycast client receiving this message mobilizes
+an ephemeral client/server association according to the
+matching manycast client template, but only if cryptographically
+authenticated and the server stratum is less than or equal
+to the client stratum.
+Authentication is explicitly required
+and either symmetric key or public key (Autokey) can be used.
+Then, the client polls the server at its unicast address
+in burst mode in order to reliably set the host clock
+and validate the source.
+This normally results
+in a volley of eight client/server at 2\-s intervals
+during which both the synchronization and cryptographic
+protocols run concurrently.
+Following the volley,
+the client runs the NTP intersection and clustering
+algorithms, which act to discard all but the "best"
+associations according to stratum and synchronization
+distance.
+The surviving associations then continue
+in ordinary client/server mode.
+.Pp
+The manycast client polling strategy is designed to reduce
+as much as possible the volume of manycast client messages
+and the effects of implosion due to near\-simultaneous
+arrival of manycast server messages.
+The strategy is determined by the
+.Ic manycastclient ,
+.Ic tos
+and
+.Ic ttl
+configuration commands.
+The manycast poll interval is
+normally eight times the system poll interval,
+which starts out at the
+.Cm minpoll
+value specified in the
+.Ic manycastclient ,
+command and, under normal circumstances, increments to the
+.Cm maxpolll
+value specified in this command.
+Initially, the TTL is
+set at the minimum hops specified by the ttl command.
+At each retransmission the TTL is increased until reaching
+the maximum hops specified by this command or a sufficient
+number client associations have been found.
+Further retransmissions use the same TTL.
+.Pp
+The quality and reliability of the suite of associations
+discovered by the manycast client is determined by the NTP
+mitigation algorithms and the
+.Cm minclock
+and
+.Cm minsane
+values specified in the
+.Ic tos
+configuration command.
+At least
+.Cm minsane
+candidate servers must be available and the mitigation
+algorithms produce at least
+.Cm minclock
+survivors in order to synchronize the clock.
+Byzantine agreement principles require at least four
+candidates in order to correctly discard a single falseticker.
+For legacy purposes,
+.Cm minsane
+defaults to 1 and
+.Cm minclock
+defaults to 3.
+For manycast service
+.Cm minsane
+should be explicitly set to 4, assuming at least that
+number of servers are available.
+.Pp
+If at least
+.Cm minclock
+servers are found, the manycast poll interval is immediately
+set to eight times
+.Cm maxpoll .
+If less than
+.Cm minclock
+servers are found when the TTL has reached the maximum hops,
+the manycast poll interval is doubled.
+For each transmission
+after that, the poll interval is doubled again until
+reaching the maximum of eight times
+.Cm maxpoll .
+Further transmissions use the same poll interval and
+TTL values.
+Note that while all this is going on,
+each client/server association found is operating normally
+it the system poll interval.
+.Pp
+Administratively scoped multicast boundaries are normally
+specified by the network router configuration and,
+in the case of IPv6, the link/site scope prefix.
+By default, the increment for TTL hops is 32 starting
+from 31; however, the
+.Ic ttl
+configuration command can be
+used to modify the values to match the scope rules.
+.Pp
+It is often useful to narrow the range of acceptable
+servers which can be found by manycast client associations.
+Because manycast servers respond only when the client
+stratum is equal to or greater than the server stratum,
+primary (stratum 1) servers fill find only primary servers
+in TTL range, which is probably the most common objective.
+However, unless configured otherwise, all manycast clients
+in TTL range will eventually find all primary servers
+in TTL range, which is probably not the most common
+objective in large networks.
+The
+.Ic tos
+command can be used to modify this behavior.
+Servers with stratum below
+.Cm floor
+or above
+.Cm ceiling
+specified in the
+.Ic tos
+command are strongly discouraged during the selection
+process; however, these servers may be temporally
+accepted if the number of servers within TTL range is
+less than
+.Cm minclock .
+.Pp
+The above actions occur for each manycast client message,
+which repeats at the designated poll interval.
+However, once the ephemeral client association is mobilized,
+subsequent manycast server replies are discarded,
+since that would result in a duplicate association.
+If during a poll interval the number of client associations
+falls below
+.Cm minclock ,
+all manycast client prototype associations are reset
+to the initial poll interval and TTL hops and operation
+resumes from the beginning.
+It is important to avoid
+frequent manycast client messages, since each one requires
+all manycast servers in TTL range to respond.
+The result could well be an implosion, either minor or major,
+depending on the number of servers in range.
+The recommended value for
+.Cm maxpoll
+is 12 (4,096 s).
+.Pp
+It is possible and frequently useful to configure a host
+as both manycast client and manycast server.
+A number of hosts configured this way and sharing a common
+group address will automatically organize themselves
+in an optimum configuration based on stratum and
+synchronization distance.
+For example, consider an NTP
+subnet of two primary servers and a hundred or more
+dependent clients.
+With two exceptions, all servers
+and clients have identical configuration files including both
+.Ic multicastclient
+and
+.Ic multicastserver
+commands using, for instance, multicast group address
+239.1.1.1.
+The only exception is that each primary server
+configuration file must include commands for the primary
+reference source such as a GPS receiver.
+.Pp
+The remaining configuration files for all secondary
+servers and clients have the same contents, except for the
+.Ic tos
+command, which is specific for each stratum level.
+For stratum 1 and stratum 2 servers, that command is
+not necessary.
+For stratum 3 and above servers the
+.Cm floor
+value is set to the intended stratum number.
+Thus, all stratum 3 configuration files are identical,
+all stratum 4 files are identical and so forth.
+.Pp
+Once operations have stabilized in this scenario,
+the primary servers will find the primary reference source
+and each other, since they both operate at the same
+stratum (1), but not with any secondary server or client,
+since these operate at a higher stratum.
+The secondary
+servers will find the servers at the same stratum level.
+If one of the primary servers loses its GPS receiver,
+it will continue to operate as a client and other clients
+will time out the corresponding association and
+re\-associate accordingly.
+.Pp
+Some administrators prefer to avoid running
+.Xr ntpd @NTPD_MS@
+continuously and run either
+.Xr ntpdate 8
+or
+.Xr ntpd @NTPD_MS@
+.Fl q
+as a cron job.
+In either case the servers must be
+configured in advance and the program fails if none are
+available when the cron job runs.
+A really slick
+application of manycast is with
+.Xr ntpd @NTPD_MS@
+.Fl q .
+The program wakes up, scans the local landscape looking
+for the usual suspects, selects the best from among
+the rascals, sets the clock and then departs.
+Servers do not have to be configured in advance and
+all clients throughout the network can have the same
+configuration file.
+.Ss Manycast Interactions with Autokey
+Each time a manycast client sends a client mode packet
+to a multicast group address, all manycast servers
+in scope generate a reply including the host name
+and status word.
+The manycast clients then run
+the Autokey protocol, which collects and verifies
+all certificates involved.
+Following the burst interval
+all but three survivors are cast off,
+but the certificates remain in the local cache.
+It often happens that several complete signing trails
+from the client to the primary servers are collected in this way.
+.Pp
+About once an hour or less often if the poll interval
+exceeds this, the client regenerates the Autokey key list.
+This is in general transparent in client/server mode.
+However, about once per day the server private value
+used to generate cookies is refreshed along with all
+manycast client associations.
+In this case all
+cryptographic values including certificates is refreshed.
+If a new certificate has been generated since
+the last refresh epoch, it will automatically revoke
+all prior certificates that happen to be in the
+certificate cache.
+At the same time, the manycast
+scheme starts all over from the beginning and
+the expanding ring shrinks to the minimum and increments
+from there while collecting all servers in scope.
+.Ss Manycast Options
+.Bl -tag -width indent
+.It Xo Ic tos
+.Oo
+.Cm ceiling Ar ceiling |
+.Cm cohort { 0 | 1 } |
+.Cm floor Ar floor |
+.Cm minclock Ar minclock |
+.Cm minsane Ar minsane
+.Oc
+.Xc
+This command affects the clock selection and clustering
+algorithms.
+It can be used to select the quality and
+quantity of peers used to synchronize the system clock
+and is most useful in manycast mode.
+The variables operate
+as follows:
+.Bl -tag -width indent
+.It Cm ceiling Ar ceiling
+Peers with strata above
+.Cm ceiling
+will be discarded if there are at least
+.Cm minclock
+peers remaining.
+This value defaults to 15, but can be changed
+to any number from 1 to 15.
+.It Cm cohort Bro 0 | 1 Brc
+This is a binary flag which enables (0) or disables (1)
+manycast server replies to manycast clients with the same
+stratum level.
+This is useful to reduce implosions where
+large numbers of clients with the same stratum level
+are present.
+The default is to enable these replies.
+.It Cm floor Ar floor
+Peers with strata below
+.Cm floor
+will be discarded if there are at least
+.Cm minclock
+peers remaining.
+This value defaults to 1, but can be changed
+to any number from 1 to 15.
+.It Cm minclock Ar minclock
+The clustering algorithm repeatedly casts out outlyer
+associations until no more than
+.Cm minclock
+associations remain.
+This value defaults to 3,
+but can be changed to any number from 1 to the number of
+configured sources.
+.It Cm minsane Ar minsane
+This is the minimum number of candidates available
+to the clock selection algorithm in order to produce
+one or more truechimers for the clustering algorithm.
+If fewer than this number are available, the clock is
+undisciplined and allowed to run free.
+The default is 1
+for legacy purposes.
+However, according to principles of
+Byzantine agreement,
+.Cm minsane
+should be at least 4 in order to detect and discard
+a single falseticker.
+.El
+.It Cm ttl Ar hop ...
+This command specifies a list of TTL values in increasing
+order, up to 8 values can be specified.
+In manycast mode these values are used in turn
+in an expanding\-ring search.
+The default is eight
+multiples of 32 starting at 31.
+.El
+.Sh Reference Clock Support
+The NTP Version 4 daemon supports some three dozen different radio,
+satellite and modem reference clocks plus a special pseudo\-clock
+used for backup or when no other clock source is available.
+Detailed descriptions of individual device drivers and options can
+be found in the
+.Qq Reference Clock Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+Additional information can be found in the pages linked
+there, including the
+.Qq Debugging Hints for Reference Clock Drivers
+and
+.Qq How To Write a Reference Clock Driver
+pages
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+In addition, support for a PPS
+signal is available as described in the
+.Qq Pulse\-per\-second (PPS) Signal Interfacing
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+Many
+drivers support special line discipline/streams modules which can
+significantly improve the accuracy using the driver.
+These are
+described in the
+.Qq Line Disciplines and Streams Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.Pp
+A reference clock will generally (though not always) be a radio
+timecode receiver which is synchronized to a source of standard
+time such as the services offered by the NRC in Canada and NIST and
+USNO in the US.
+The interface between the computer and the timecode
+receiver is device dependent, but is usually a serial port.
+A
+device driver specific to each reference clock must be selected and
+compiled in the distribution; however, most common radio, satellite
+and modem clocks are included by default.
+Note that an attempt to
+configure a reference clock when the driver has not been compiled
+or the hardware port has not been appropriately configured results
+in a scalding remark to the system log file, but is otherwise non
+hazardous.
+.Pp
+For the purposes of configuration,
+.Xr ntpd @NTPD_MS@
+treats
+reference clocks in a manner analogous to normal NTP peers as much
+as possible.
+Reference clocks are identified by a syntactically
+correct but invalid IP address, in order to distinguish them from
+normal NTP peers.
+Reference clock addresses are of the form
+.Sm off
+.Li 127.127. Ar t . Ar u ,
+.Sm on
+where
+.Ar t
+is an integer
+denoting the clock type and
+.Ar u
+indicates the unit
+number in the range 0\-3.
+While it may seem overkill, it is in fact
+sometimes useful to configure multiple reference clocks of the same
+type, in which case the unit numbers must be unique.
+.Pp
+The
+.Ic server
+command is used to configure a reference
+clock, where the
+.Ar address
+argument in that command
+is the clock address.
+The
+.Cm key ,
+.Cm version
+and
+.Cm ttl
+options are not used for reference clock support.
+The
+.Cm mode
+option is added for reference clock support, as
+described below.
+The
+.Cm prefer
+option can be useful to
+persuade the server to cherish a reference clock with somewhat more
+enthusiasm than other reference clocks or peers.
+Further
+information on this option can be found in the
+.Qq Mitigation Rules and the prefer Keyword
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+page.
+The
+.Cm minpoll
+and
+.Cm maxpoll
+options have
+meaning only for selected clock drivers.
+See the individual clock
+driver document pages for additional information.
+.Pp
+The
+.Ic fudge
+command is used to provide additional
+information for individual clock drivers and normally follows
+immediately after the
+.Ic server
+command.
+The
+.Ar address
+argument specifies the clock address.
+The
+.Cm refid
+and
+.Cm stratum
+options can be used to
+override the defaults for the device.
+There are two optional
+device\-dependent time offsets and four flags that can be included
+in the
+.Ic fudge
+command as well.
+.Pp
+The stratum number of a reference clock is by default zero.
+Since the
+.Xr ntpd @NTPD_MS@
+daemon adds one to the stratum of each
+peer, a primary server ordinarily displays an external stratum of
+one.
+In order to provide engineered backups, it is often useful to
+specify the reference clock stratum as greater than zero.
+The
+.Cm stratum
+option is used for this purpose.
+Also, in cases
+involving both a reference clock and a pulse\-per\-second (PPS)
+discipline signal, it is useful to specify the reference clock
+identifier as other than the default, depending on the driver.
+The
+.Cm refid
+option is used for this purpose.
+Except where noted,
+these options apply to all clock drivers.
+.Ss Reference Clock Commands
+.Bl -tag -width indent
+.It Xo Ic server
+.Sm off
+.Li 127.127. Ar t . Ar u
+.Sm on
+.Op Cm prefer
+.Op Cm mode Ar int
+.Op Cm minpoll Ar int
+.Op Cm maxpoll Ar int
+.Xc
+This command can be used to configure reference clocks in
+special ways.
+The options are interpreted as follows:
+.Bl -tag -width indent
+.It Cm prefer
+Marks the reference clock as preferred.
+All other things being
+equal, this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+.Qq Mitigation Rules and the prefer Keyword
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+for further information.
+.It Cm mode Ar int
+Specifies a mode number which is interpreted in a
+device\-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+.It Cm minpoll Ar int
+.It Cm maxpoll Ar int
+These options specify the minimum and maximum polling interval
+for reference clock messages, as a power of 2 in seconds
+For
+most directly connected reference clocks, both
+.Cm minpoll
+and
+.Cm maxpoll
+default to 6 (64 s).
+For modem reference clocks,
+.Cm minpoll
+defaults to 10 (17.1 m) and
+.Cm maxpoll
+defaults to 14 (4.5 h).
+The allowable range is 4 (16 s) to 17 (36.4 h) inclusive.
+.El
+.It Xo Ic fudge
+.Sm off
+.Li 127.127. Ar t . Ar u
+.Sm on
+.Op Cm time1 Ar sec
+.Op Cm time2 Ar sec
+.Op Cm stratum Ar int
+.Op Cm refid Ar string
+.Op Cm mode Ar int
+.Op Cm flag1 Cm 0 \&| Cm 1
+.Op Cm flag2 Cm 0 \&| Cm 1
+.Op Cm flag3 Cm 0 \&| Cm 1
+.Op Cm flag4 Cm 0 \&| Cm 1
+.Xc
+This command can be used to configure reference clocks in
+special ways.
+It must immediately follow the
+.Ic server
+command which configures the driver.
+Note that the same capability
+is possible at run time using the
+.Xr ntpdc @NTPDC_MS@
+program.
+The options are interpreted as
+follows:
+.Bl -tag -width indent
+.It Cm time1 Ar sec
+Specifies a constant to be added to the time offset produced by
+the driver, a fixed\-point decimal number in seconds.
+This is used
+as a calibration constant to adjust the nominal time offset of a
+particular clock to agree with an external standard, such as a
+precision PPS signal.
+It also provides a way to correct a
+systematic error or bias due to serial port or operating system
+latencies, different cable lengths or receiver internal delay.
+The
+specified offset is in addition to the propagation delay provided
+by other means, such as internal DIPswitches.
+Where a calibration
+for an individual system and driver is available, an approximate
+correction is noted in the driver documentation pages.
+Note: in order to facilitate calibration when more than one
+radio clock or PPS signal is supported, a special calibration
+feature is available.
+It takes the form of an argument to the
+.Ic enable
+command described in
+.Sx Miscellaneous Options
+page and operates as described in the
+.Qq Reference Clock Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.It Cm time2 Ar secs
+Specifies a fixed\-point decimal number in seconds, which is
+interpreted in a driver\-dependent way.
+See the descriptions of
+specific drivers in the
+.Qq Reference Clock Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.It Cm stratum Ar int
+Specifies the stratum number assigned to the driver, an integer
+between 0 and 15.
+This number overrides the default stratum number
+ordinarily assigned by the driver itself, usually zero.
+.It Cm refid Ar string
+Specifies an ASCII string of from one to four characters which
+defines the reference identifier used by the driver.
+This string
+overrides the default identifier ordinarily assigned by the driver
+itself.
+.It Cm mode Ar int
+Specifies a mode number which is interpreted in a
+device\-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+.It Cm flag1 Cm 0 \&| Cm 1
+.It Cm flag2 Cm 0 \&| Cm 1
+.It Cm flag3 Cm 0 \&| Cm 1
+.It Cm flag4 Cm 0 \&| Cm 1
+These four flags are used for customizing the clock driver.
+The
+interpretation of these values, and whether they are used at all,
+is a function of the particular clock driver.
+However, by
+convention
+.Cm flag4
+is used to enable recording monitoring
+data to the
+.Cm clockstats
+file configured with the
+.Ic filegen
+command.
+Further information on the
+.Ic filegen
+command can be found in
+.Sx Monitoring Options .
+.El
+.El
+.Sh Miscellaneous Options
+.Bl -tag -width indent
+.It Ic broadcastdelay Ar seconds
+The broadcast and multicast modes require a special calibration
+to determine the network delay between the local and remote
+servers.
+Ordinarily, this is done automatically by the initial
+protocol exchanges between the client and server.
+In some cases,
+the calibration procedure may fail due to network or server access
+controls, for example.
+This command specifies the default delay to
+be used under these circumstances.
+Typically (for Ethernet), a
+number between 0.003 and 0.007 seconds is appropriate.
+The default
+when this command is not used is 0.004 seconds.
+.It Ic calldelay Ar delay
+This option controls the delay in seconds between the first and second
+packets sent in burst or iburst mode to allow additional time for a modem
+or ISDN call to complete.
+.It Ic driftfile Ar driftfile
+This command specifies the complete path and name of the file used to
+record the frequency of the local clock oscillator.
+This is the same
+operation as the
+.Fl f
+command line option.
+If the file exists, it is read at
+startup in order to set the initial frequency and then updated once per
+hour with the current frequency computed by the daemon.
+If the file name is
+specified, but the file itself does not exist, the starts with an initial
+frequency of zero and creates the file when writing it for the first time.
+If this command is not given, the daemon will always start with an initial
+frequency of zero.
+.Pp
+The file format consists of a single line containing a single
+floating point number, which records the frequency offset measured
+in parts\-per\-million (PPM).
+The file is updated by first writing
+the current drift value into a temporary file and then renaming
+this file to replace the old version.
+This implies that
+.Xr ntpd @NTPD_MS@
+must have write permission for the directory the
+drift file is located in, and that file system links, symbolic or
+otherwise, should be avoided.
+.It Xo Ic enable
+.Oo
+.Cm auth | Cm bclient |
+.Cm calibrate | Cm kernel |
+.Cm mode7 | monitor |
+.Cm ntp | Cm stats
+.Oc
+.Xc
+.It Xo Ic disable
+.Oo
+.Cm auth | Cm bclient |
+.Cm calibrate | Cm kernel |
+.Cm mode7 | monitor |
+.Cm ntp | Cm stats
+.Oc
+.Xc
+Provides a way to enable or disable various server options.
+Flags not mentioned are unaffected.
+Note that all of these flags
+can be controlled remotely using the
+.Xr ntpdc @NTPDC_MS@
+utility program.
+.Bl -tag -width indent
+.It Cm auth
+Enables the server to synchronize with unconfigured peers only if the
+peer has been correctly authenticated using either public key or
+private key cryptography.
+The default for this flag is
+.Ic enable .
+.It Cm bclient
+Enables the server to listen for a message from a broadcast or
+multicast server, as in the
+.Ic multicastclient
+command with default
+address.
+The default for this flag is
+.Ic disable .
+.It Cm calibrate
+Enables the calibrate feature for reference clocks.
+The default for
+this flag is
+.Ic disable .
+.It Cm kernel
+Enables the kernel time discipline, if available.
+The default for this
+flag is
+.Ic enable
+if support is available, otherwise
+.Ic disable .
+.It Cm mode7
+Enables processing of NTP mode 7 implementation\-specific requests
+which are used by the deprecated
+.Xr ntpdc @NTPDC_MS@
+program.
+The default for this flag is disable.
+This flag is excluded from runtime configuration using
+.Xr ntpq @NTPQ_MS@ .
+The
+.Xr ntpq @NTPQ_MS@
+program provides the same capabilities as
+.Xr ntpdc @NTPDC_MS@
+using standard mode 6 requests.
+.It Cm monitor
+Enables the monitoring facility.
+See the
+.Xr ntpdc @NTPDC_MS@
+program
+and the
+.Ic monlist
+command or further information.
+The
+default for this flag is
+.Ic enable .
+.It Cm ntp
+Enables time and frequency discipline.
+In effect, this switch opens and
+closes the feedback loop, which is useful for testing.
+The default for
+this flag is
+.Ic enable .
+.It Cm stats
+Enables the statistics facility.
+See the
+.Sx Monitoring Options
+section for further information.
+The default for this flag is
+.Ic disable .
+.El
+.It Ic includefile Ar includefile
+This command allows additional configuration commands
+to be included from a separate file.
+Include files may
+be nested to a depth of five; upon reaching the end of any
+include file, command processing resumes in the previous
+configuration file.
+This option is useful for sites that run
+.Xr ntpd @NTPD_MS@
+on multiple hosts, with (mostly) common options (e.g., a
+restriction list).
+.It Ic logconfig Ar configkeyword
+This command controls the amount and type of output written to
+the system
+.Xr syslog 3
+facility or the alternate
+.Ic logfile
+log file.
+By default, all output is turned on.
+All
+.Ar configkeyword
+keywords can be prefixed with
+.Ql = ,
+.Ql +
+and
+.Ql \- ,
+where
+.Ql =
+sets the
+.Xr syslog 3
+priority mask,
+.Ql +
+adds and
+.Ql \-
+removes
+messages.
+.Xr syslog 3
+messages can be controlled in four
+classes
+.Po
+.Cm clock ,
+.Cm peer ,
+.Cm sys
+and
+.Cm sync
+.Pc .
+Within these classes four types of messages can be
+controlled: informational messages
+.Po
+.Cm info
+.Pc ,
+event messages
+.Po
+.Cm events
+.Pc ,
+statistics messages
+.Po
+.Cm statistics
+.Pc
+and
+status messages
+.Po
+.Cm status
+.Pc .
+.Pp
+Configuration keywords are formed by concatenating the message class with
+the event class.
+The
+.Cm all
+prefix can be used instead of a message class.
+A
+message class may also be followed by the
+.Cm all
+keyword to enable/disable all
+messages of the respective message class.Thus, a minimal log configuration
+could look like this:
+.Bd -literal
+logconfig =syncstatus +sysevents
+.Ed
+.Pp
+This would just list the synchronizations state of
+.Xr ntpd @NTPD_MS@
+and the major system events.
+For a simple reference server, the
+following minimum message configuration could be useful:
+.Bd -literal
+logconfig =syncall +clockall
+.Ed
+.Pp
+This configuration will list all clock information and
+synchronization information.
+All other events and messages about
+peers, system events and so on is suppressed.
+.It Ic logfile Ar logfile
+This command specifies the location of an alternate log file to
+be used instead of the default system
+.Xr syslog 3
+facility.
+This is the same operation as the \-l command line option.
+.It Ic setvar Ar variable Op Cm default
+This command adds an additional system variable.
+These
+variables can be used to distribute additional information such as
+the access policy.
+If the variable of the form
+.Sm off
+.Va name = Ar value
+.Sm on
+is followed by the
+.Cm default
+keyword, the
+variable will be listed as part of the default system variables
+.Po
+.Xr ntpq @NTPQ_MS@
+.Ic rv
+command
+.Pc ) .
+These additional variables serve
+informational purposes only.
+They are not related to the protocol
+other that they can be listed.
+The known protocol variables will
+always override any variables defined via the
+.Ic setvar
+mechanism.
+There are three special variables that contain the names
+of all variable of the same group.
+The
+.Va sys_var_list
+holds
+the names of all system variables.
+The
+.Va peer_var_list
+holds
+the names of all peer variables and the
+.Va clock_var_list
+holds the names of the reference clock variables.
+.It Xo Ic tinker
+.Oo
+.Cm allan Ar allan |
+.Cm dispersion Ar dispersion |
+.Cm freq Ar freq |
+.Cm huffpuff Ar huffpuff |
+.Cm panic Ar panic |
+.Cm step Ar srep |
+.Cm stepout Ar stepout
+.Oc
+.Xc
+This command can be used to alter several system variables in
+very exceptional circumstances.
+It should occur in the
+configuration file before any other configuration options.
+The
+default values of these variables have been carefully optimized for
+a wide range of network speeds and reliability expectations.
+In
+general, they interact in intricate ways that are hard to predict
+and some combinations can result in some very nasty behavior.
+Very
+rarely is it necessary to change the default values; but, some
+folks cannot resist twisting the knobs anyway and this command is
+for them.
+Emphasis added: twisters are on their own and can expect
+no help from the support group.
+.Pp
+The variables operate as follows:
+.Bl -tag -width indent
+.It Cm allan Ar allan
+The argument becomes the new value for the minimum Allan
+intercept, which is a parameter of the PLL/FLL clock discipline
+algorithm.
+The value in log2 seconds defaults to 7 (1024 s), which is also the lower
+limit.
+.It Cm dispersion Ar dispersion
+The argument becomes the new value for the dispersion increase rate,
+normally .000015 s/s.
+.It Cm freq Ar freq
+The argument becomes the initial value of the frequency offset in
+parts\-per\-million.
+This overrides the value in the frequency file, if
+present, and avoids the initial training state if it is not.
+.It Cm huffpuff Ar huffpuff
+The argument becomes the new value for the experimental
+huff\-n'\-puff filter span, which determines the most recent interval
+the algorithm will search for a minimum delay.
+The lower limit is
+900 s (15 m), but a more reasonable value is 7200 (2 hours).
+There
+is no default, since the filter is not enabled unless this command
+is given.
+.It Cm panic Ar panic
+The argument is the panic threshold, normally 1000 s.
+If set to zero,
+the panic sanity check is disabled and a clock offset of any value will
+be accepted.
+.It Cm step Ar step
+The argument is the step threshold, which by default is 0.128 s.
+It can
+be set to any positive number in seconds.
+If set to zero, step
+adjustments will never occur.
+Note: The kernel time discipline is
+disabled if the step threshold is set to zero or greater than the
+default.
+.It Cm stepout Ar stepout
+The argument is the stepout timeout, which by default is 900 s.
+It can
+be set to any positive number in seconds.
+If set to zero, the stepout
+pulses will not be suppressed.
+.El
+.It Xo Ic rlimit
+.Oo
+.Cm memlock Ar Nmegabytes |
+.Cm stacksize Ar N4kPages
+.Cm filenum Ar Nfiledescriptors
+.Oc
+.Xc
+.Bl -tag -width indent
+.It Cm memlock Ar Nmegabytes
+Specify the number of megabytes of memory that can be allocated.
+Probably only available under Linux, this option is useful
+when dropping root (the
+.Fl i
+option).
+The default is 32 megabytes. Setting this to zero will prevent any attemp to lock memory.
+.It Cm stacksize Ar N4kPages
+Specifies the maximum size of the process stack on systems with the
+.It Cm filenum Ar Nfiledescriptors
+Specifies the maximum number of file descriptors ntpd may have open at once. Defaults to the system default.
+.Fn mlockall
+function.
+Defaults to 50 4k pages (200 4k pages in OpenBSD).
+.El
+.It Xo Ic trap Ar host_address
+.Op Cm port Ar port_number
+.Op Cm interface Ar interface_address
+.Xc
+This command configures a trap receiver at the given host
+address and port number for sending messages with the specified
+local interface address.
+If the port number is unspecified, a value
+of 18447 is used.
+If the interface address is not specified, the
+message is sent with a source address of the local interface the
+message is sent through.
+Note that on a multihomed host the
+interface used may vary from time to time with routing changes.
+.Pp
+The trap receiver will generally log event messages and other
+information from the server in a log file.
+While such monitor
+programs may also request their own trap dynamically, configuring a
+trap receiver will ensure that no messages are lost when the server
+is started.
+.It Cm hop Ar ...
+This command specifies a list of TTL values in increasing order, up to 8
+values can be specified.
+In manycast mode these values are used in turn in
+an expanding\-ring search.
+The default is eight multiples of 32 starting at
+31.
+.El
+.Sh "OPTIONS"
+.Bl -tag
+.It Fl \-help
+Display usage information and exit.
+.It Fl \-more\-help
+Pass the extended usage information through a pager.
+.It Fl \-version Op Brq Ar v|c|n
+Output version of program and exit. The default mode is `v', a simple
+version. The `c' mode will print copyright information and `n' will
+print the full copyright notice.
+.El
+.Sh "OPTION PRESETS"
+Any option that is not marked as \fInot presettable\fP may be preset
+by loading values from environment variables named:
+.nf
+ \fBNTP_CONF_<option\-name>\fP or \fBNTP_CONF\fP
+.fi
+.ad
+.Sh "ENVIRONMENT"
+See \fBOPTION PRESETS\fP for configuration environment variables.
+.Sh FILES
+.Bl -tag -width /etc/ntp.drift -compact
+.It Pa /etc/ntp.conf
+the default name of the configuration file
+.It Pa ntp.keys
+private MD5 keys
+.It Pa ntpkey
+RSA private key
+.It Pa ntpkey_ Ns Ar host
+RSA public key
+.It Pa ntp_dh
+Diffie\-Hellman agreement parameters
+.El
+.Sh "EXIT STATUS"
+One of the following exit values will be returned:
+.Bl -tag
+.It 0 " (EXIT_SUCCESS)"
+Successful program execution.
+.It 1 " (EXIT_FAILURE)"
+The operation failed or the command syntax was not valid.
+.It 70 " (EX_SOFTWARE)"
+libopts had an internal operational error. Please report
+it to autogen\-users@lists.sourceforge.net. Thank you.
+.El
+.Sh "SEE ALSO"
+.Xr ntpd @NTPD_MS@ ,
+.Xr ntpdc @NTPDC_MS@ ,
+.Xr ntpq @NTPQ_MS@
+.Pp
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+.Li http://www.ntp.org/ .
+A snapshot of this documentation is available in HTML format in
+.Pa /usr/share/doc/ntp .
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 4)
+.%O RFC5905
+.Re
+.Sh "AUTHORS"
+The University of Delaware
+.Sh "COPYRIGHT"
+Copyright (C) 1970\-2014 The University of Delaware all rights reserved.
+This program is released under the terms of the NTP license, <http://ntp.org/license>.
+.Sh BUGS
+The syntax checking is not picky; some combinations of
+ridiculous and even hilarious options and modes may not be
+detected.
+.Pp
+The
+.Pa ntpkey_ Ns Ar host
+files are really digital
+certificates.
+These should be obtained via secure directory
+services when they become universally available.
+.Pp
+Please send bug reports to: http://bugs.ntp.org, bugs@ntp.org
+.Sh NOTES
+This document was derived from FreeBSD.
+.Pp
+This manual page was \fIAutoGen\fP\-erated from the \fBntp.conf\fP
+option definitions.
diff --git a/ntpd/ntp.conf.texi b/ntpd/ntp.conf.texi
new file mode 100644
index 0000000..fb1a7ab
--- /dev/null
+++ b/ntpd/ntp.conf.texi
@@ -0,0 +1,49 @@
+\input texinfo @c -*-texinfo-*-
+@c %**start of header
+@setfilename ntp.conf.info
+@settitle NTP Configuration File User's Manual
+@include ../sntp/include/version.texi
+@paragraphindent 2
+@c %**end of header
+
+@ifinfo
+This file documents the use of the NTP Project's NTP configuration file,
+version @value{VERSION}, @value{UPDATED}.
+@end ifinfo
+
+@direntry
+* ntp.conf: (ntp.conf). NTP's configuration file
+@end direntry
+
+@titlepage
+@title NTP's Configuration File User's Manual
+@subtitle ntp.conf, version @value{VERSION}, @value{UPDATED}
+@c @author Max @email{foo@ntp.org}
+@end titlepage
+
+@c @page
+@c @vskip 0pt plus 1filll
+
+@node Top, ntp.conf Description, (dir), (dir)
+@top NTP's Configuration File User Manual
+
+This document describes the configuration file for the NTP Project's
+@code{ntpd} program.
+
+This document applies to version @value{VERSION} of @code{ntp.conf}.
+
+@shortcontents
+
+@menu
+* ntp.conf Description::
+* ntp.conf Notes::
+@end menu
+
+@node ntp.conf Description, , Top, Top
+@comment node-name, next, previous, up
+@section Description
+
+The behavior of @code{ntpd} can be changed by a configuration file,
+by default @code{ntp.conf}.
+
+@include invoke-ntp.conf.texi
diff --git a/ntpd/ntp.keys.5man b/ntpd/ntp.keys.5man
new file mode 100644
index 0000000..52acb7f
--- /dev/null
+++ b/ntpd/ntp.keys.5man
@@ -0,0 +1,173 @@
+.TH ntp.keys 5man "02 Dec 2014" "4.2.7p482" "File Formats"
+.\"
+.\" EDIT THIS FILE WITH CAUTION (ntp.man)
+.\"
+.\" It has been AutoGen-ed December 2, 2014 at 08:56:42 AM by AutoGen 5.18.5pre4
+.\" From the definitions ntp.keys.def
+.\" and the template file agman-file.tpl
+.Sh NAME
+.Nm ntp.keys
+.Nd NTP symmetric key file format
+
+.\"
+.SH NAME
+ntp.keys \- NTP symmetric key file format configuration file
+.de1 NOP
+. it 1 an-trap
+. if \\n[.$] \,\\$*\/
+..
+.ie t \
+.ds B-Font [CB]
+.ds I-Font [CI]
+.ds R-Font [CR]
+.el \
+.ds B-Font B
+.ds I-Font I
+.ds R-Font R
+.SH SYNOPSIS
+\f\*[B-Font]\fP
+[\f\*[B-Font]\-\-option-name\f[]]
+[\f\*[B-Font]\-\-option-name\f[] \f\*[I-Font]value\f[]]
+.sp \n(Ppu
+.ne 2
+
+All arguments must be options.
+.sp \n(Ppu
+.ne 2
+
+.SH DESCRIPTION
+This document describes the format of an NTP symmetric key file.
+For a description of the use of this type of file, see the
+"Authentication Support"
+section of the
+\fCntp.conf\fR(5)\f[]
+page.
+.sp \n(Ppu
+.ne 2
+
+\fCntpd\fR(8)\f[]
+reads its keys from a file specified using the
+\f\*[B-Font]\-k\f[]
+command line option or the
+\f\*[B-Font]keys\f[]
+statement in the configuration file.
+While key number 0 is fixed by the NTP standard
+(as 56 zero bits)
+and may not be changed,
+one or more keys numbered between 1 and 65534
+may be arbitrarily set in the keys file.
+.sp \n(Ppu
+.ne 2
+
+The key file uses the same comment conventions
+as the configuration file.
+Key entries use a fixed format of the form
+.sp \n(Ppu
+.ne 2
+
+.in +4
+\f\*[I-Font]keyno\f[] \f\*[I-Font]type\f[] \f\*[I-Font]key\f[]
+.in -4
+.sp \n(Ppu
+.ne 2
+
+where
+\f\*[I-Font]keyno\f[]
+is a positive integer (between 1 and 65534),
+\f\*[I-Font]type\f[]
+is the message digest algorithm,
+and
+\f\*[I-Font]key\f[]
+is the key itself.
+.sp \n(Ppu
+.ne 2
+
+The
+\f\*[I-Font]key\f[]
+may be given in a format
+controlled by the
+\f\*[I-Font]type\f[]
+field.
+The
+\f\*[I-Font]type\f[]
+\f[C]MD5\f[]
+is always supported.
+If
+\f[C]ntpd\f[]
+was built with the OpenSSL library
+then any digest library supported by that library may be specified.
+However, if compliance with FIPS 140-2 is required the
+\f\*[I-Font]type\f[]
+must be either
+\f[C]SHA\f[]
+or
+\f[C]SHA1\f[].
+.sp \n(Ppu
+.ne 2
+
+What follows are some key types, and corresponding formats:
+.sp \n(Ppu
+.ne 2
+
+.TP 7
+.NOP \f[C]MD5\f[]
+The key is 1 to 16 printable characters terminated by
+an EOL,
+whitespace,
+or
+a
+\f[C]#\f[]
+(which is the "start of comment" character).
+.sp \n(Ppu
+.ne 2
+
+.br
+.ns
+.TP 7
+.NOP \f[C]SHA\f[]
+.br
+.ns
+.TP 7
+.NOP \f[C]SHA1\f[]
+.br
+.ns
+.TP 7
+.NOP \f[C]RMD160\f[]
+The key is a hex-encoded ASCII string of 40 characters,
+which is truncated as necessary.
+.PP
+.sp \n(Ppu
+.ne 2
+
+Note that the keys used by the
+\fCntpq\fR(8)\f[]
+and
+\fCntpdc\fR(8)\f[]
+programs are checked against passwords
+requested by the programs and entered by hand,
+so it is generally appropriate to specify these keys in ASCII format.
+.SH FILES
+.TP 14
+.NOP \fI/etc/ntp.keys\f[]
+the default name of the configuration file
+.PP
+.SH "SEE ALSO"
+\fCntp.conf\fR(5)\f[],
+\fCntpd\fR(1ntpdmdoc)\f[],
+\fCntpdate\fR(1ntpdatemdoc)\f[],
+\fCntpdc\fR(1ntpdcmdoc)\f[],
+\fCsntp\fR(1sntpmdoc)\f[]
+.SH "AUTHORS"
+The University of Delaware
+.SH "COPYRIGHT"
+Copyright (C) 1970-2014 The University of Delaware all rights reserved.
+This program is released under the terms of the NTP license, <http://ntp.org/license>.
+.SH "BUGS"
+Please send bug reports to: http://bugs.ntp.org, bugs@ntp.org
+.SH NOTES
+This document was derived from FreeBSD.
+.sp \n(Ppu
+.ne 2
+
+This manual page was \fIAutoGen\fP-erated from the \fBntp.keys\fP
+option definitions.
diff --git a/ntpd/ntp.keys.5mdoc b/ntpd/ntp.keys.5mdoc
new file mode 100644
index 0000000..56ad9c1
--- /dev/null
+++ b/ntpd/ntp.keys.5mdoc
@@ -0,0 +1,158 @@
+.Dd December 2 2014
+.Dt NTP_KEYS 5mdoc File Formats
+.Os SunOS 5.10
+.\" EDIT THIS FILE WITH CAUTION (ntp.mdoc)
+.\"
+.\" It has been AutoGen-ed December 2, 2014 at 08:57:01 AM by AutoGen 5.18.5pre4
+.\" From the definitions ntp.keys.def
+.\" and the template file agmdoc-file.tpl
+.Sh NAME
+.Nm ntp.keys
+.Nd NTP symmetric key file format
+
+.Sh NAME
+.Nm ntp.keys
+.Nd NTP symmetric key file format
+.Sh SYNOPSIS
+.Nm
+.Op Fl \-option\-name
+.Op Fl \-option\-name Ar value
+.Pp
+All arguments must be options.
+.Pp
+.Sh DESCRIPTION
+This document describes the format of an NTP symmetric key file.
+For a description of the use of this type of file, see the
+.Qq Authentication Support
+section of the
+.Xr ntp.conf 5
+page.
+.Pp
+.Xr ntpd 8
+reads its keys from a file specified using the
+.Fl k
+command line option or the
+.Ic keys
+statement in the configuration file.
+While key number 0 is fixed by the NTP standard
+(as 56 zero bits)
+and may not be changed,
+one or more keys numbered between 1 and 65534
+may be arbitrarily set in the keys file.
+.Pp
+The key file uses the same comment conventions
+as the configuration file.
+Key entries use a fixed format of the form
+.Pp
+.D1 Ar keyno type key
+.Pp
+where
+.Ar keyno
+is a positive integer (between 1 and 65534),
+.Ar type
+is the message digest algorithm,
+and
+.Ar key
+is the key itself.
+.Pp
+The
+.Ar key
+may be given in a format
+controlled by the
+.Ar type
+field.
+The
+.Ar type
+.Li MD5
+is always supported.
+If
+.Li ntpd
+was built with the OpenSSL library
+then any digest library supported by that library may be specified.
+However, if compliance with FIPS 140\-2 is required the
+.Ar type
+must be either
+.Li SHA
+or
+.Li SHA1 .
+.Pp
+What follows are some key types, and corresponding formats:
+.Pp
+.Bl -tag -width RMD160 -compact
+.It Li MD5
+The key is 1 to 16 printable characters terminated by
+an EOL,
+whitespace,
+or
+a
+.Li #
+(which is the "start of comment" character).
+.Pp
+.It Li SHA
+.It Li SHA1
+.It Li RMD160
+The key is a hex\-encoded ASCII string of 40 characters,
+which is truncated as necessary.
+.El
+.Pp
+Note that the keys used by the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+programs are checked against passwords
+requested by the programs and entered by hand,
+so it is generally appropriate to specify these keys in ASCII format.
+.Sh "OPTIONS"
+.Bl -tag
+.It Fl \-help
+Display usage information and exit.
+.It Fl \-more\-help
+Pass the extended usage information through a pager.
+.It Fl \-version Op Brq Ar v|c|n
+Output version of program and exit. The default mode is `v', a simple
+version. The `c' mode will print copyright information and `n' will
+print the full copyright notice.
+.El
+.Sh "OPTION PRESETS"
+Any option that is not marked as \fInot presettable\fP may be preset
+by loading values from environment variables named:
+.nf
+ \fBNTP_KEYS_<option\-name>\fP or \fBNTP_KEYS\fP
+.fi
+.ad
+.Sh "ENVIRONMENT"
+See \fBOPTION PRESETS\fP for configuration environment variables.
+.Sh FILES
+.Bl -tag -width /etc/ntp.keys -compact
+.It Pa /etc/ntp.keys
+the default name of the configuration file
+.El
+.Sh "EXIT STATUS"
+One of the following exit values will be returned:
+.Bl -tag
+.It 0 " (EXIT_SUCCESS)"
+Successful program execution.
+.It 1 " (EXIT_FAILURE)"
+The operation failed or the command syntax was not valid.
+.It 70 " (EX_SOFTWARE)"
+libopts had an internal operational error. Please report
+it to autogen\-users@lists.sourceforge.net. Thank you.
+.El
+.Sh "SEE ALSO"
+.Xr ntp.conf 5 ,
+.Xr ntpd 1ntpdmdoc ,
+.Xr ntpdate 1ntpdatemdoc ,
+.Xr ntpdc 1ntpdcmdoc ,
+.Xr sntp 1sntpmdoc
+.Sh "AUTHORS"
+The University of Delaware
+.Sh "COPYRIGHT"
+Copyright (C) 1970\-2014 The University of Delaware all rights reserved.
+This program is released under the terms of the NTP license, <http://ntp.org/license>.
+.Sh "BUGS"
+Please send bug reports to: http://bugs.ntp.org, bugs@ntp.org
+.Sh NOTES
+This document was derived from FreeBSD.
+.Pp
+This manual page was \fIAutoGen\fP\-erated from the \fBntp.keys\fP
+option definitions.
diff --git a/ntpd/ntp.keys.def b/ntpd/ntp.keys.def
new file mode 100644
index 0000000..dcb3d55
--- /dev/null
+++ b/ntpd/ntp.keys.def
@@ -0,0 +1,152 @@
+/* -*- Mode: Text -*- */
+
+autogen definitions options;
+
+#include copyright.def
+#include version.def
+
+// We want the synopsis to be "/etc/ntp.keys" but we need the prog-name
+// to be ntp.keys - the latter is also how autogen produces the output
+// file name.
+prog-name = "ntp.keys";
+file-path = "/etc/ntp.keys";
+prog-title = "NTP symmetric key file format";
+
+/* explain: Additional information whenever the usage routine is invoked */
+explain = <<- _END_EXPLAIN
+ _END_EXPLAIN;
+
+doc-section = {
+ ds-type = 'DESCRIPTION';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_PROG_MDOC_DESCRIP
+This document describes the format of an NTP symmetric key file.
+For a description of the use of this type of file, see the
+.Qq Authentication Support
+section of the
+.Xr ntp.conf 5
+page.
+.Pp
+.Xr ntpd 8
+reads its keys from a file specified using the
+.Fl k
+command line option or the
+.Ic keys
+statement in the configuration file.
+While key number 0 is fixed by the NTP standard
+(as 56 zero bits)
+and may not be changed,
+one or more keys numbered between 1 and 65534
+may be arbitrarily set in the keys file.
+.Pp
+The key file uses the same comment conventions
+as the configuration file.
+Key entries use a fixed format of the form
+.Pp
+.D1 Ar keyno type key
+.Pp
+where
+.Ar keyno
+is a positive integer (between 1 and 65534),
+.Ar type
+is the message digest algorithm,
+and
+.Ar key
+is the key itself.
+.Pp
+The
+.Ar key
+may be given in a format
+controlled by the
+.Ar type
+field.
+The
+.Ar type
+.Li MD5
+is always supported.
+If
+.Li ntpd
+was built with the OpenSSL library
+then any digest library supported by that library may be specified.
+However, if compliance with FIPS 140-2 is required the
+.Ar type
+must be either
+.Li SHA
+or
+.Li SHA1 .
+.Pp
+What follows are some key types, and corresponding formats:
+.Pp
+.Bl -tag -width RMD160 -compact
+.It Li MD5
+The key is 1 to 16 printable characters terminated by
+an EOL,
+whitespace,
+or
+a
+.Li #
+(which is the "start of comment" character).
+.Pp
+.It Li SHA
+.It Li SHA1
+.It Li RMD160
+The key is a hex-encoded ASCII string of 40 characters,
+which is truncated as necessary.
+.El
+.Pp
+Note that the keys used by the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+programs are checked against passwords
+requested by the programs and entered by hand,
+so it is generally appropriate to specify these keys in ASCII format.
+ _END_PROG_MDOC_DESCRIP;
+};
+
+doc-section = {
+ ds-type = 'FILES';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_MDOC_FILES
+.Bl -tag -width /etc/ntp.keys -compact
+.It Pa /etc/ntp.keys
+the default name of the configuration file
+.El
+ _END_MDOC_FILES;
+};
+
+doc-section = {
+ ds-type = 'SEE ALSO';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_MDOC_SEE_ALSO
+.Xr ntp.conf 5 ,
+.Xr ntpd 1ntpdmdoc ,
+.Xr ntpdate 1ntpdatemdoc ,
+.Xr ntpdc 1ntpdcmdoc ,
+.Xr sntp 1sntpmdoc
+ _END_MDOC_SEE_ALSO;
+};
+
+/*
+doc-section = {
+ ds-type = 'BUGS';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_MDOC_BUGS
+.Xr ntpd 8
+has gotten rather fat.
+While not huge, it has gotten larger than might
+be desirable for an elevated-priority daemon running on a workstation,
+particularly since many of the fancy features which consume the space
+were designed more with a busy primary server, rather than a high
+stratum workstation, in mind.
+ _END_MDOC_BUGS;
+};
+*/
+
+doc-section = {
+ ds-type = 'NOTES';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_MDOC_NOTES
+This document was derived from FreeBSD.
+ _END_MDOC_NOTES;
+};
diff --git a/ntpd/ntp.keys.html b/ntpd/ntp.keys.html
new file mode 100644
index 0000000..ab4b9b7
--- /dev/null
+++ b/ntpd/ntp.keys.html
@@ -0,0 +1,200 @@
+<html lang="en">
+<head>
+<title>NTP Symmetric Key</title>
+<meta http-equiv="Content-Type" content="text/html">
+<meta name="description" content="NTP Symmetric Key">
+<meta name="generator" content="makeinfo 4.7">
+<link title="Top" rel="top" href="#Top">
+<link href="http://www.gnu.org/software/texinfo/" rel="generator-home" title="Texinfo Homepage">
+<meta http-equiv="Content-Style-Type" content="text/css">
+<style type="text/css"><!--
+ pre.display { font-family:inherit }
+ pre.format { font-family:inherit }
+ pre.smalldisplay { font-family:inherit; font-size:smaller }
+ pre.smallformat { font-family:inherit; font-size:smaller }
+ pre.smallexample { font-size:smaller }
+ pre.smalllisp { font-size:smaller }
+ span.sc { font-variant:small-caps }
+ span.roman { font-family: serif; font-weight: normal; }
+--></style>
+</head>
+<body>
+<h1 class="settitle">NTP Symmetric Key</h1>
+<div class="node">
+<p><hr>
+<a name="Top"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntp_002ekeys-Description">ntp.keys Description</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#dir">(dir)</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#dir">(dir)</a>
+<br>
+</div>
+
+<h2 class="unnumbered">NTP's Symmetric Key File User Manual</h2>
+
+<p>This document describes the symmetric key file for the NTP Project's
+<code>ntpd</code> program.
+
+ <p>This document applies to version 4.2.7p482 of <code>ntp.keys</code>.
+
+ <div class="shortcontents">
+<h2>Short Contents</h2>
+<ul>
+<a href="#Top">NTP's Symmetric Key File User Manual</a>
+</ul>
+</div>
+
+<ul class="menu">
+<li><a accesskey="1" href="#ntp_002ekeys-Description">ntp.keys Description</a>
+<li><a accesskey="2" href="#ntp_002ekeys-Notes">ntp.keys Notes</a>
+</ul>
+
+<div class="node">
+<p><hr>
+<a name="ntp_002ekeys-Description"></a>Previous:&nbsp;<a rel="previous" accesskey="p" href="#Top">Top</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#Top">Top</a>
+<br>
+</div>
+
+<!-- node-name, next, previous, up -->
+<h3 class="section">Description</h3>
+
+<p>The name and location of the symmetric key file for <code>ntpd</code> can
+be specified in a configuration file, by default <code>/etc/ntp.keys</code>.
+
+<div class="node">
+<p><hr>
+<a name="ntp_002ekeys-Notes"></a>
+<br>
+</div>
+
+<h3 class="section">Notes about ntp.keys</h3>
+
+<p><a name="index-ntp_002ekeys-1"></a><a name="index-NTP-symmetric-key-file-format-2"></a>
+
+ <p>This document describes the format of an NTP symmetric key file.
+For a description of the use of this type of file, see the
+"Authentication Support"
+section of the
+<code>ntp.conf(5)</code>
+page.
+
+ <p><code>ntpd(8)</code>
+reads its keys from a file specified using the
+<code>-k</code>
+command line option or the
+<code>keys</code>
+statement in the configuration file.
+While key number 0 is fixed by the NTP standard
+(as 56 zero bits)
+and may not be changed,
+one or more keys numbered between 1 and 65534
+may be arbitrarily set in the keys file.
+
+ <p>The key file uses the same comment conventions
+as the configuration file.
+Key entries use a fixed format of the form
+
+<pre class="example"> <kbd>keyno</kbd> <kbd>type</kbd> <kbd>key</kbd>
+</pre>
+ <p>where
+<kbd>keyno</kbd>
+is a positive integer (between 1 and 65534),
+<kbd>type</kbd>
+is the message digest algorithm,
+and
+<kbd>key</kbd>
+is the key itself.
+
+ <p>The
+<kbd>key</kbd>
+may be given in a format
+controlled by the
+<kbd>type</kbd>
+field.
+The
+<kbd>type</kbd>
+<code>MD5</code>
+is always supported.
+If
+<code>ntpd</code>
+was built with the OpenSSL library
+then any digest library supported by that library may be specified.
+However, if compliance with FIPS 140-2 is required the
+<kbd>type</kbd>
+must be either
+<code>SHA</code>
+or
+<code>SHA1</code>.
+
+ <p>What follows are some key types, and corresponding formats:
+
+ <dl>
+<dt><code>MD5</code><dd>The key is 1 to 16 printable characters terminated by
+an EOL,
+whitespace,
+or
+a
+<code>#</code>
+(which is the "start of comment" character).
+
+ <br><dt><code>SHA</code><br><dt><code>SHA1</code><br><dt><code>RMD160</code><dd>The key is a hex-encoded ASCII string of 40 characters,
+which is truncated as necessary.
+</dl>
+
+ <p>Note that the keys used by the
+<code>ntpq(8)</code>
+and
+<code>ntpdc(8)</code>
+programs are checked against passwords
+requested by the programs and entered by hand,
+so it is generally appropriate to specify these keys in ASCII format.
+
+ <p>This section was generated by <strong>AutoGen</strong>,
+using the <code>agtexi-cmd</code> template and the option descriptions for the <code>ntp.keys</code> program.
+This software is released under the NTP license, &lt;http://ntp.org/license&gt;.
+
+<ul class="menu">
+<li><a accesskey="1" href="#ntp_002ekeys-Files">ntp.keys Files</a>: Files
+<li><a accesskey="2" href="#ntp_002ekeys-See-Also">ntp.keys See Also</a>: See Also
+<li><a accesskey="3" href="#ntp_002ekeys-Notes">ntp.keys Notes</a>: Notes
+</ul>
+
+<div class="node">
+<p><hr>
+<a name="ntp_002ekeys-Files"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntp_002ekeys-See-Also">ntp.keys See Also</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntp_002ekeys-Notes">ntp.keys Notes</a>
+<br>
+</div>
+
+<h4 class="subsection">ntp.keys Files</h4>
+
+ <dl>
+<dt><span class="file">/etc/ntp.keys</span><dd>the default name of the configuration file
+</dl>
+<div class="node">
+<p><hr>
+<a name="ntp_002ekeys-See-Also"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntp_002ekeys-Notes">ntp.keys Notes</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntp_002ekeys-Files">ntp.keys Files</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntp_002ekeys-Notes">ntp.keys Notes</a>
+<br>
+</div>
+
+<h4 class="subsection">ntp.keys See Also</h4>
+
+<p><code>ntp.conf(5)</code>,
+<code>ntpd(1ntpdmdoc)</code>,
+<code>ntpdate(1ntpdatemdoc)</code>,
+<code>ntpdc(1ntpdcmdoc)</code>,
+<code>sntp(1sntpmdoc)</code>
+<div class="node">
+<p><hr>
+<a name="ntp_002ekeys-Notes"></a>Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntp_002ekeys-See-Also">ntp.keys See Also</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntp_002ekeys-Notes">ntp.keys Notes</a>
+<br>
+</div>
+
+<h4 class="subsection">ntp.keys Notes</h4>
+
+<p>This document was derived from FreeBSD.
+
+</body></html>
+
diff --git a/ntpd/ntp.keys.man.in b/ntpd/ntp.keys.man.in
new file mode 100644
index 0000000..943b27e
--- /dev/null
+++ b/ntpd/ntp.keys.man.in
@@ -0,0 +1,173 @@
+.TH ntp.keys 5 "02 Dec 2014" "4.2.7p482" "File Formats"
+.\"
+.\" EDIT THIS FILE WITH CAUTION (ntp.man)
+.\"
+.\" It has been AutoGen-ed December 2, 2014 at 08:56:42 AM by AutoGen 5.18.5pre4
+.\" From the definitions ntp.keys.def
+.\" and the template file agman-file.tpl
+.Sh NAME
+.Nm ntp.keys
+.Nd NTP symmetric key file format
+
+.\"
+.SH NAME
+ntp.keys \- NTP symmetric key file format configuration file
+.de1 NOP
+. it 1 an-trap
+. if \\n[.$] \,\\$*\/
+..
+.ie t \
+.ds B-Font [CB]
+.ds I-Font [CI]
+.ds R-Font [CR]
+.el \
+.ds B-Font B
+.ds I-Font I
+.ds R-Font R
+.SH SYNOPSIS
+\f\*[B-Font]\fP
+[\f\*[B-Font]\-\-option-name\f[]]
+[\f\*[B-Font]\-\-option-name\f[] \f\*[I-Font]value\f[]]
+.sp \n(Ppu
+.ne 2
+
+All arguments must be options.
+.sp \n(Ppu
+.ne 2
+
+.SH DESCRIPTION
+This document describes the format of an NTP symmetric key file.
+For a description of the use of this type of file, see the
+"Authentication Support"
+section of the
+\fCntp.conf\fR(5)\f[]
+page.
+.sp \n(Ppu
+.ne 2
+
+\fCntpd\fR(8)\f[]
+reads its keys from a file specified using the
+\f\*[B-Font]\-k\f[]
+command line option or the
+\f\*[B-Font]keys\f[]
+statement in the configuration file.
+While key number 0 is fixed by the NTP standard
+(as 56 zero bits)
+and may not be changed,
+one or more keys numbered between 1 and 65534
+may be arbitrarily set in the keys file.
+.sp \n(Ppu
+.ne 2
+
+The key file uses the same comment conventions
+as the configuration file.
+Key entries use a fixed format of the form
+.sp \n(Ppu
+.ne 2
+
+.in +4
+\f\*[I-Font]keyno\f[] \f\*[I-Font]type\f[] \f\*[I-Font]key\f[]
+.in -4
+.sp \n(Ppu
+.ne 2
+
+where
+\f\*[I-Font]keyno\f[]
+is a positive integer (between 1 and 65534),
+\f\*[I-Font]type\f[]
+is the message digest algorithm,
+and
+\f\*[I-Font]key\f[]
+is the key itself.
+.sp \n(Ppu
+.ne 2
+
+The
+\f\*[I-Font]key\f[]
+may be given in a format
+controlled by the
+\f\*[I-Font]type\f[]
+field.
+The
+\f\*[I-Font]type\f[]
+\f[C]MD5\f[]
+is always supported.
+If
+\f[C]ntpd\f[]
+was built with the OpenSSL library
+then any digest library supported by that library may be specified.
+However, if compliance with FIPS 140-2 is required the
+\f\*[I-Font]type\f[]
+must be either
+\f[C]SHA\f[]
+or
+\f[C]SHA1\f[].
+.sp \n(Ppu
+.ne 2
+
+What follows are some key types, and corresponding formats:
+.sp \n(Ppu
+.ne 2
+
+.TP 7
+.NOP \f[C]MD5\f[]
+The key is 1 to 16 printable characters terminated by
+an EOL,
+whitespace,
+or
+a
+\f[C]#\f[]
+(which is the "start of comment" character).
+.sp \n(Ppu
+.ne 2
+
+.br
+.ns
+.TP 7
+.NOP \f[C]SHA\f[]
+.br
+.ns
+.TP 7
+.NOP \f[C]SHA1\f[]
+.br
+.ns
+.TP 7
+.NOP \f[C]RMD160\f[]
+The key is a hex-encoded ASCII string of 40 characters,
+which is truncated as necessary.
+.PP
+.sp \n(Ppu
+.ne 2
+
+Note that the keys used by the
+\fCntpq\fR(8)\f[]
+and
+\fCntpdc\fR(8)\f[]
+programs are checked against passwords
+requested by the programs and entered by hand,
+so it is generally appropriate to specify these keys in ASCII format.
+.SH FILES
+.TP 14
+.NOP \fI/etc/ntp.keys\f[]
+the default name of the configuration file
+.PP
+.SH "SEE ALSO"
+\fCntp.conf\fR(5)\f[],
+\fCntpd\fR(@NTPD_MS@)\f[],
+\fCntpdate\fR(@NTPDATE_MS@)\f[],
+\fCntpdc\fR(@NTPDC_MS@)\f[],
+\fCsntp\fR(@SNTP_MS@)\f[]
+.SH "AUTHORS"
+The University of Delaware
+.SH "COPYRIGHT"
+Copyright (C) 1970-2014 The University of Delaware all rights reserved.
+This program is released under the terms of the NTP license, <http://ntp.org/license>.
+.SH "BUGS"
+Please send bug reports to: http://bugs.ntp.org, bugs@ntp.org
+.SH NOTES
+This document was derived from FreeBSD.
+.sp \n(Ppu
+.ne 2
+
+This manual page was \fIAutoGen\fP-erated from the \fBntp.keys\fP
+option definitions.
diff --git a/ntpd/ntp.keys.mdoc.in b/ntpd/ntp.keys.mdoc.in
new file mode 100644
index 0000000..b2a24e9
--- /dev/null
+++ b/ntpd/ntp.keys.mdoc.in
@@ -0,0 +1,158 @@
+.Dd December 2 2014
+.Dt NTP_KEYS 5 File Formats
+.Os SunOS 5.10
+.\" EDIT THIS FILE WITH CAUTION (ntp.mdoc)
+.\"
+.\" It has been AutoGen-ed December 2, 2014 at 08:57:01 AM by AutoGen 5.18.5pre4
+.\" From the definitions ntp.keys.def
+.\" and the template file agmdoc-file.tpl
+.Sh NAME
+.Nm ntp.keys
+.Nd NTP symmetric key file format
+
+.Sh NAME
+.Nm ntp.keys
+.Nd NTP symmetric key file format
+.Sh SYNOPSIS
+.Nm
+.Op Fl \-option\-name
+.Op Fl \-option\-name Ar value
+.Pp
+All arguments must be options.
+.Pp
+.Sh DESCRIPTION
+This document describes the format of an NTP symmetric key file.
+For a description of the use of this type of file, see the
+.Qq Authentication Support
+section of the
+.Xr ntp.conf 5
+page.
+.Pp
+.Xr ntpd 8
+reads its keys from a file specified using the
+.Fl k
+command line option or the
+.Ic keys
+statement in the configuration file.
+While key number 0 is fixed by the NTP standard
+(as 56 zero bits)
+and may not be changed,
+one or more keys numbered between 1 and 65534
+may be arbitrarily set in the keys file.
+.Pp
+The key file uses the same comment conventions
+as the configuration file.
+Key entries use a fixed format of the form
+.Pp
+.D1 Ar keyno type key
+.Pp
+where
+.Ar keyno
+is a positive integer (between 1 and 65534),
+.Ar type
+is the message digest algorithm,
+and
+.Ar key
+is the key itself.
+.Pp
+The
+.Ar key
+may be given in a format
+controlled by the
+.Ar type
+field.
+The
+.Ar type
+.Li MD5
+is always supported.
+If
+.Li ntpd
+was built with the OpenSSL library
+then any digest library supported by that library may be specified.
+However, if compliance with FIPS 140\-2 is required the
+.Ar type
+must be either
+.Li SHA
+or
+.Li SHA1 .
+.Pp
+What follows are some key types, and corresponding formats:
+.Pp
+.Bl -tag -width RMD160 -compact
+.It Li MD5
+The key is 1 to 16 printable characters terminated by
+an EOL,
+whitespace,
+or
+a
+.Li #
+(which is the "start of comment" character).
+.Pp
+.It Li SHA
+.It Li SHA1
+.It Li RMD160
+The key is a hex\-encoded ASCII string of 40 characters,
+which is truncated as necessary.
+.El
+.Pp
+Note that the keys used by the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+programs are checked against passwords
+requested by the programs and entered by hand,
+so it is generally appropriate to specify these keys in ASCII format.
+.Sh "OPTIONS"
+.Bl -tag
+.It Fl \-help
+Display usage information and exit.
+.It Fl \-more\-help
+Pass the extended usage information through a pager.
+.It Fl \-version Op Brq Ar v|c|n
+Output version of program and exit. The default mode is `v', a simple
+version. The `c' mode will print copyright information and `n' will
+print the full copyright notice.
+.El
+.Sh "OPTION PRESETS"
+Any option that is not marked as \fInot presettable\fP may be preset
+by loading values from environment variables named:
+.nf
+ \fBNTP_KEYS_<option\-name>\fP or \fBNTP_KEYS\fP
+.fi
+.ad
+.Sh "ENVIRONMENT"
+See \fBOPTION PRESETS\fP for configuration environment variables.
+.Sh FILES
+.Bl -tag -width /etc/ntp.keys -compact
+.It Pa /etc/ntp.keys
+the default name of the configuration file
+.El
+.Sh "EXIT STATUS"
+One of the following exit values will be returned:
+.Bl -tag
+.It 0 " (EXIT_SUCCESS)"
+Successful program execution.
+.It 1 " (EXIT_FAILURE)"
+The operation failed or the command syntax was not valid.
+.It 70 " (EX_SOFTWARE)"
+libopts had an internal operational error. Please report
+it to autogen\-users@lists.sourceforge.net. Thank you.
+.El
+.Sh "SEE ALSO"
+.Xr ntp.conf 5 ,
+.Xr ntpd @NTPD_MS@ ,
+.Xr ntpdate @NTPDATE_MS@ ,
+.Xr ntpdc @NTPDC_MS@ ,
+.Xr sntp @SNTP_MS@
+.Sh "AUTHORS"
+The University of Delaware
+.Sh "COPYRIGHT"
+Copyright (C) 1970\-2014 The University of Delaware all rights reserved.
+This program is released under the terms of the NTP license, <http://ntp.org/license>.
+.Sh "BUGS"
+Please send bug reports to: http://bugs.ntp.org, bugs@ntp.org
+.Sh NOTES
+This document was derived from FreeBSD.
+.Pp
+This manual page was \fIAutoGen\fP\-erated from the \fBntp.keys\fP
+option definitions.
diff --git a/ntpd/ntp.keys.texi b/ntpd/ntp.keys.texi
new file mode 100644
index 0000000..1fc7b5c
--- /dev/null
+++ b/ntpd/ntp.keys.texi
@@ -0,0 +1,49 @@
+\input texinfo @c -*-texinfo-*-
+@c %**start of header
+@setfilename ntp.keys.info
+@settitle NTP Symmetric Key
+@include ../sntp/include/version.texi
+@paragraphindent 2
+@c %**end of header
+
+@ifinfo
+This file documents the NTP Project's NTP Symmetric Key file,
+version @value{VERSION}, @value{UPDATED}.
+@end ifinfo
+
+@direntry
+* ntp.keys: (ntp.keys). NTP's Symmetric Key file
+@end direntry
+
+@titlepage
+@title NTP's Symmetric Key File User's Manual
+@subtitle ntp.keys, version @value{VERSION}, @value{UPDATED}
+@c @author Max @email{foo@ntp.org}
+@end titlepage
+
+@c @page
+@c @vskip 0pt plus 1filll
+
+@node Top, ntp.keys Description, (dir), (dir)
+@top NTP's Symmetric Key File User Manual
+
+This document describes the symmetric key file for the NTP Project's
+@code{ntpd} program.
+
+This document applies to version @value{VERSION} of @code{ntp.keys}.
+
+@shortcontents
+
+@menu
+* ntp.keys Description::
+* ntp.keys Notes::
+@end menu
+
+@node ntp.keys Description, , Top, Top
+@comment node-name, next, previous, up
+@section Description
+
+The name and location of the symmetric key file for @code{ntpd} can
+be specified in a configuration file, by default @code{/etc/ntp.keys}.
+
+@include invoke-ntp.keys.texi
diff --git a/ntpd/ntp_config.c b/ntpd/ntp_config.c
new file mode 100644
index 0000000..0f48983
--- /dev/null
+++ b/ntpd/ntp_config.c
@@ -0,0 +1,4957 @@
+/* ntp_config.c
+ *
+ * This file contains the ntpd configuration code.
+ *
+ * Written By: Sachin Kamboj
+ * University of Delaware
+ * Newark, DE 19711
+ * Some parts borrowed from the older ntp_config.c
+ * Copyright (c) 2006
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_NETINFO
+# include <netinfo/ni.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+#include <signal.h>
+#ifndef SIGCHLD
+# define SIGCHLD SIGCLD
+#endif
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#include <isc/net.h>
+#include <isc/result.h>
+
+#include "ntp.h"
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_filegen.h"
+#include "ntp_stdlib.h"
+#include "lib_strbuf.h"
+#include "ntp_assert.h"
+#include "ntp_random.h"
+/*
+ * [Bug 467]: Some linux headers collide with CONFIG_PHONE and CONFIG_KEYS
+ * so #include these later.
+ */
+#include "ntp_config.h"
+#include "ntp_cmdargs.h"
+#include "ntp_scanner.h"
+#include "ntp_parser.h"
+#include "ntpd-opts.h"
+
+
+/* Bison still(!) does not emit usable prototypes for the calling code */
+int yyparse (struct FILE_INFO *ip_file);
+
+/* list of servers from command line for config_peers() */
+int cmdline_server_count;
+char ** cmdline_servers;
+
+/* set to zero if admin doesn't want memory locked */
+int do_memlock = 1;
+
+/*
+ * "logconfig" building blocks
+ */
+struct masks {
+ const char * const name;
+ const u_int32 mask;
+};
+
+static struct masks logcfg_class[] = {
+ { "clock", NLOG_OCLOCK },
+ { "peer", NLOG_OPEER },
+ { "sync", NLOG_OSYNC },
+ { "sys", NLOG_OSYS },
+ { NULL, 0 }
+};
+
+/* logcfg_noclass_items[] masks are complete and must not be shifted */
+static struct masks logcfg_noclass_items[] = {
+ { "allall", NLOG_SYSMASK | NLOG_PEERMASK | NLOG_CLOCKMASK | NLOG_SYNCMASK },
+ { "allinfo", NLOG_SYSINFO | NLOG_PEERINFO | NLOG_CLOCKINFO | NLOG_SYNCINFO },
+ { "allevents", NLOG_SYSEVENT | NLOG_PEEREVENT | NLOG_CLOCKEVENT | NLOG_SYNCEVENT },
+ { "allstatus", NLOG_SYSSTATUS | NLOG_PEERSTATUS | NLOG_CLOCKSTATUS | NLOG_SYNCSTATUS },
+ { "allstatistics", NLOG_SYSSTATIST | NLOG_PEERSTATIST | NLOG_CLOCKSTATIST | NLOG_SYNCSTATIST },
+ /* the remainder are misspellings of clockall, peerall, sysall, and syncall. */
+ { "allclock", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OCLOCK },
+ { "allpeer", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OPEER },
+ { "allsys", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OSYS },
+ { "allsync", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OSYNC },
+ { NULL, 0 }
+};
+
+/* logcfg_class_items[] masks are shiftable by NLOG_O* counts */
+static struct masks logcfg_class_items[] = {
+ { "all", NLOG_INFO | NLOG_EVENT | NLOG_STATUS | NLOG_STATIST },
+ { "info", NLOG_INFO },
+ { "events", NLOG_EVENT },
+ { "status", NLOG_STATUS },
+ { "statistics", NLOG_STATIST },
+ { NULL, 0 }
+};
+
+typedef struct peer_resolved_ctx_tag {
+ int flags;
+ int host_mode; /* T_* token identifier */
+ u_short family;
+ keyid_t keyid;
+ u_char hmode; /* MODE_* */
+ u_char version;
+ u_char minpoll;
+ u_char maxpoll;
+ u_int32 ttl;
+ const char * group;
+} peer_resolved_ctx;
+
+/* Limits */
+#define MAXPHONE 10 /* maximum number of phone strings */
+#define MAXPPS 20 /* maximum length of PPS device string */
+
+/*
+ * Miscellaneous macros
+ */
+#define ISEOL(c) ((c) == '#' || (c) == '\n' || (c) == '\0')
+#define ISSPACE(c) ((c) == ' ' || (c) == '\t')
+
+/*
+ * Definitions of things either imported from or exported to outside
+ */
+extern int yydebug; /* ntp_parser.c (.y) */
+int curr_include_level; /* The current include level */
+struct FILE_INFO *fp[MAXINCLUDELEVEL+1];
+config_tree cfgt; /* Parser output stored here */
+struct config_tree_tag *cfg_tree_history; /* History of configs */
+char *sys_phone[MAXPHONE] = {NULL}; /* ACTS phone numbers */
+char default_keysdir[] = NTP_KEYSDIR;
+char *keysdir = default_keysdir; /* crypto keys directory */
+char * saveconfigdir;
+#if defined(HAVE_SCHED_SETSCHEDULER)
+int config_priority_override = 0;
+int config_priority;
+#endif
+
+const char *config_file;
+static char default_ntp_signd_socket[] =
+#ifdef NTP_SIGND_PATH
+ NTP_SIGND_PATH;
+#else
+ "";
+#endif
+char *ntp_signd_socket = default_ntp_signd_socket;
+#ifdef HAVE_NETINFO
+struct netinfo_config_state *config_netinfo = NULL;
+int check_netinfo = 1;
+#endif /* HAVE_NETINFO */
+#ifdef SYS_WINNT
+char *alt_config_file;
+LPTSTR temp;
+char config_file_storage[MAX_PATH];
+char alt_config_file_storage[MAX_PATH];
+#endif /* SYS_WINNT */
+
+#ifdef HAVE_NETINFO
+/*
+ * NetInfo configuration state
+ */
+struct netinfo_config_state {
+ void *domain; /* domain with config */
+ ni_id config_dir; /* ID config dir */
+ int prop_index; /* current property */
+ int val_index; /* current value */
+ char **val_list; /* value list */
+};
+#endif
+
+struct REMOTE_CONFIG_INFO remote_config; /* Remote configuration buffer and
+ pointer info */
+int input_from_file = 1; /* A boolean flag, which when set, indicates that
+ the input is to be taken from the configuration
+ file, instead of the remote-configuration buffer
+ */
+
+int old_config_style = 1; /* A boolean flag, which when set,
+ * indicates that the old configuration
+ * format with a newline at the end of
+ * every command is being used
+ */
+int cryptosw; /* crypto command called */
+
+extern char *stats_drift_file; /* name of the driftfile */
+
+#ifdef BC_LIST_FRAMEWORK_NOT_YET_USED
+/*
+ * backwards compatibility flags
+ */
+bc_entry bc_list[] = {
+ { T_Bc_bugXXXX, 1 } /* default enabled */
+};
+
+/*
+ * declare an int pointer for each flag for quick testing without
+ * walking bc_list. If the pointer is consumed by libntp rather
+ * than ntpd, declare it in a libntp source file pointing to storage
+ * initialized with the appropriate value for other libntp clients, and
+ * redirect it to point into bc_list during ntpd startup.
+ */
+int *p_bcXXXX_enabled = &bc_list[0].enabled;
+#endif
+
+/* FUNCTION PROTOTYPES */
+
+static void init_syntax_tree(config_tree *);
+static void apply_enable_disable(attr_val_fifo *q, int enable);
+
+#ifdef FREE_CFG_T
+static void free_auth_node(config_tree *);
+static void free_all_config_trees(void);
+
+static void free_config_access(config_tree *);
+static void free_config_auth(config_tree *);
+static void free_config_fudge(config_tree *);
+static void free_config_logconfig(config_tree *);
+static void free_config_monitor(config_tree *);
+static void free_config_nic_rules(config_tree *);
+static void free_config_other_modes(config_tree *);
+static void free_config_peers(config_tree *);
+static void free_config_phone(config_tree *);
+static void free_config_reset_counters(config_tree *);
+static void free_config_rlimit(config_tree *);
+static void free_config_setvar(config_tree *);
+static void free_config_system_opts(config_tree *);
+static void free_config_tinker(config_tree *);
+static void free_config_tos(config_tree *);
+static void free_config_trap(config_tree *);
+static void free_config_ttl(config_tree *);
+static void free_config_unpeers(config_tree *);
+static void free_config_vars(config_tree *);
+
+#ifdef SIM
+static void free_config_sim(config_tree *);
+#endif
+static void destroy_address_fifo(address_fifo *);
+#define FREE_ADDRESS_FIFO(pf) \
+ do { \
+ destroy_address_fifo(pf); \
+ (pf) = NULL; \
+ } while (0)
+ void free_all_config_trees(void); /* atexit() */
+static void free_config_tree(config_tree *ptree);
+#endif /* FREE_CFG_T */
+
+static void destroy_restrict_node(restrict_node *my_node);
+static int is_sane_resolved_address(sockaddr_u *peeraddr, int hmode);
+static void save_and_apply_config_tree(void);
+static void destroy_int_fifo(int_fifo *);
+#define FREE_INT_FIFO(pf) \
+ do { \
+ destroy_int_fifo(pf); \
+ (pf) = NULL; \
+ } while (0)
+static void destroy_string_fifo(string_fifo *);
+#define FREE_STRING_FIFO(pf) \
+ do { \
+ destroy_string_fifo(pf); \
+ (pf) = NULL; \
+ } while (0)
+static void destroy_attr_val_fifo(attr_val_fifo *);
+#define FREE_ATTR_VAL_FIFO(pf) \
+ do { \
+ destroy_attr_val_fifo(pf); \
+ (pf) = NULL; \
+ } while (0)
+static void destroy_filegen_fifo(filegen_fifo *);
+#define FREE_FILEGEN_FIFO(pf) \
+ do { \
+ destroy_filegen_fifo(pf); \
+ (pf) = NULL; \
+ } while (0)
+static void destroy_restrict_fifo(restrict_fifo *);
+#define FREE_RESTRICT_FIFO(pf) \
+ do { \
+ destroy_restrict_fifo(pf); \
+ (pf) = NULL; \
+ } while (0)
+static void destroy_setvar_fifo(setvar_fifo *);
+#define FREE_SETVAR_FIFO(pf) \
+ do { \
+ destroy_setvar_fifo(pf); \
+ (pf) = NULL; \
+ } while (0)
+static void destroy_addr_opts_fifo(addr_opts_fifo *);
+#define FREE_ADDR_OPTS_FIFO(pf) \
+ do { \
+ destroy_addr_opts_fifo(pf); \
+ (pf) = NULL; \
+ } while (0)
+
+static void config_logconfig(config_tree *);
+static void config_monitor(config_tree *);
+static void config_rlimit(config_tree *);
+static void config_system_opts(config_tree *);
+static void config_tinker(config_tree *);
+static void config_tos(config_tree *);
+static void config_vars(config_tree *);
+
+#ifdef SIM
+static sockaddr_u *get_next_address(address_node *addr);
+static void config_sim(config_tree *);
+static void config_ntpdsim(config_tree *);
+#else /* !SIM follows */
+static void config_ntpd(config_tree *);
+static void config_other_modes(config_tree *);
+static void config_auth(config_tree *);
+static void config_access(config_tree *);
+static void config_phone(config_tree *);
+static void config_setvar(config_tree *);
+static void config_ttl(config_tree *);
+static void config_trap(config_tree *);
+static void config_fudge(config_tree *);
+static void config_peers(config_tree *);
+static void config_unpeers(config_tree *);
+static void config_nic_rules(config_tree *);
+static void config_reset_counters(config_tree *);
+static u_char get_correct_host_mode(int token);
+static int peerflag_bits(peer_node *);
+#endif /* !SIM */
+
+#ifdef WORKER
+static void peer_name_resolved(int, int, void *, const char *, const char *,
+ const struct addrinfo *,
+ const struct addrinfo *);
+static void unpeer_name_resolved(int, int, void *, const char *, const char *,
+ const struct addrinfo *,
+ const struct addrinfo *);
+static void trap_name_resolved(int, int, void *, const char *, const char *,
+ const struct addrinfo *,
+ const struct addrinfo *);
+#endif
+
+enum gnn_type {
+ t_UNK, /* Unknown */
+ t_REF, /* Refclock */
+ t_MSK /* Network Mask */
+};
+
+static void ntpd_set_tod_using(const char *);
+static char * normal_dtoa(double);
+static u_int32 get_pfxmatch(const char **, struct masks *);
+static u_int32 get_match(const char *, struct masks *);
+static u_int32 get_logmask(const char *);
+#ifndef SIM
+static int getnetnum(const char *num, sockaddr_u *addr, int complain,
+ enum gnn_type a_type);
+#endif
+
+
+/* FUNCTIONS FOR INITIALIZATION
+ * ----------------------------
+ */
+
+#ifdef FREE_CFG_T
+static void
+free_auth_node(
+ config_tree *ptree
+ )
+{
+ if (ptree->auth.keys) {
+ free(ptree->auth.keys);
+ ptree->auth.keys = NULL;
+ }
+
+ if (ptree->auth.keysdir) {
+ free(ptree->auth.keysdir);
+ ptree->auth.keysdir = NULL;
+ }
+
+ if (ptree->auth.ntp_signd_socket) {
+ free(ptree->auth.ntp_signd_socket);
+ ptree->auth.ntp_signd_socket = NULL;
+ }
+}
+#endif /* DEBUG */
+
+
+static void
+init_syntax_tree(
+ config_tree *ptree
+ )
+{
+ ZERO(*ptree);
+}
+
+
+#ifdef FREE_CFG_T
+static void
+free_all_config_trees(void)
+{
+ config_tree *ptree;
+ config_tree *pnext;
+
+ ptree = cfg_tree_history;
+
+ while (ptree != NULL) {
+ pnext = ptree->link;
+ free_config_tree(ptree);
+ ptree = pnext;
+ }
+}
+
+
+static void
+free_config_tree(
+ config_tree *ptree
+ )
+{
+#if defined(_MSC_VER) && defined (_DEBUG)
+ _CrtCheckMemory();
+#endif
+
+ if (ptree->source.value.s != NULL)
+ free(ptree->source.value.s);
+
+ free_config_other_modes(ptree);
+ free_config_auth(ptree);
+ free_config_tos(ptree);
+ free_config_monitor(ptree);
+ free_config_access(ptree);
+ free_config_tinker(ptree);
+ free_config_rlimit(ptree);
+ free_config_system_opts(ptree);
+ free_config_logconfig(ptree);
+ free_config_phone(ptree);
+ free_config_setvar(ptree);
+ free_config_ttl(ptree);
+ free_config_trap(ptree);
+ free_config_fudge(ptree);
+ free_config_vars(ptree);
+ free_config_peers(ptree);
+ free_config_unpeers(ptree);
+ free_config_nic_rules(ptree);
+ free_config_reset_counters(ptree);
+#ifdef SIM
+ free_config_sim(ptree);
+#endif
+ free_auth_node(ptree);
+
+ free(ptree);
+
+#if defined(_MSC_VER) && defined (_DEBUG)
+ _CrtCheckMemory();
+#endif
+}
+#endif /* FREE_CFG_T */
+
+
+#ifdef SAVECONFIG
+/* Dump all trees */
+int
+dump_all_config_trees(
+ FILE *df,
+ int comment
+ )
+{
+ config_tree * cfg_ptr;
+ int return_value;
+
+ return_value = 0;
+ for (cfg_ptr = cfg_tree_history;
+ cfg_ptr != NULL;
+ cfg_ptr = cfg_ptr->link)
+ return_value |= dump_config_tree(cfg_ptr, df, comment);
+
+ return return_value;
+}
+
+
+/* The config dumper */
+int
+dump_config_tree(
+ config_tree *ptree,
+ FILE *df,
+ int comment
+ )
+{
+ peer_node *peern;
+ unpeer_node *unpeern;
+ attr_val *atrv;
+ address_node *addr;
+ address_node *peer_addr;
+ address_node *fudge_addr;
+ filegen_node *fgen_node;
+ restrict_node *rest_node;
+ addr_opts_node *addr_opts;
+ setvar_node *setv_node;
+ nic_rule_node *rule_node;
+ int_node *i_n;
+ int_node *flags;
+ int_node *counter_set;
+ string_node *str_node;
+
+ const char *s;
+ char *s1;
+ char *s2;
+ char timestamp[80];
+ int enable;
+
+ DPRINTF(1, ("dump_config_tree(%p)\n", ptree));
+
+ if (comment) {
+ if (!strftime(timestamp, sizeof(timestamp),
+ "%Y-%m-%d %H:%M:%S",
+ localtime(&ptree->timestamp)))
+ timestamp[0] = '\0';
+
+ fprintf(df, "# %s %s %s\n",
+ timestamp,
+ (CONF_SOURCE_NTPQ == ptree->source.attr)
+ ? "ntpq remote config from"
+ : "startup configuration file",
+ ptree->source.value.s);
+ }
+
+ /* For options I didn't find documentation I'll just output its name and the cor. value */
+ atrv = HEAD_PFIFO(ptree->vars);
+ for ( ; atrv != NULL; atrv = atrv->link) {
+ switch (atrv->type) {
+#ifdef DEBUG
+ default:
+ fprintf(df, "\n# dump error:\n"
+ "# unknown vars type %d (%s) for %s\n",
+ atrv->type, token_name(atrv->type),
+ token_name(atrv->attr));
+ break;
+#endif
+ case T_Double:
+ fprintf(df, "%s %s\n", keyword(atrv->attr),
+ normal_dtoa(atrv->value.d));
+ break;
+
+ case T_Integer:
+ fprintf(df, "%s %d\n", keyword(atrv->attr),
+ atrv->value.i);
+ break;
+
+ case T_String:
+ fprintf(df, "%s \"%s\"", keyword(atrv->attr),
+ atrv->value.s);
+ if (T_Driftfile == atrv->attr &&
+ atrv->link != NULL &&
+ T_WanderThreshold == atrv->link->attr) {
+ atrv = atrv->link;
+ fprintf(df, " %s\n",
+ normal_dtoa(atrv->value.d));
+ } else {
+ fprintf(df, "\n");
+ }
+ break;
+ }
+ }
+
+ atrv = HEAD_PFIFO(ptree->logconfig);
+ if (atrv != NULL) {
+ fprintf(df, "logconfig");
+ for ( ; atrv != NULL; atrv = atrv->link)
+ fprintf(df, " %c%s", atrv->attr, atrv->value.s);
+ fprintf(df, "\n");
+ }
+
+ if (ptree->stats_dir)
+ fprintf(df, "statsdir \"%s\"\n", ptree->stats_dir);
+
+ i_n = HEAD_PFIFO(ptree->stats_list);
+ if (i_n != NULL) {
+ fprintf(df, "statistics");
+ for ( ; i_n != NULL; i_n = i_n->link)
+ fprintf(df, " %s", keyword(i_n->i));
+ fprintf(df, "\n");
+ }
+
+ fgen_node = HEAD_PFIFO(ptree->filegen_opts);
+ for ( ; fgen_node != NULL; fgen_node = fgen_node->link) {
+ atrv = HEAD_PFIFO(fgen_node->options);
+ if (atrv != NULL) {
+ fprintf(df, "filegen %s",
+ keyword(fgen_node->filegen_token));
+ for ( ; atrv != NULL; atrv = atrv->link) {
+ switch (atrv->attr) {
+#ifdef DEBUG
+ default:
+ fprintf(df, "\n# dump error:\n"
+ "# unknown filegen option token %s\n"
+ "filegen %s",
+ token_name(atrv->attr),
+ keyword(fgen_node->filegen_token));
+ break;
+#endif
+ case T_File:
+ fprintf(df, " file %s",
+ atrv->value.s);
+ break;
+
+ case T_Type:
+ fprintf(df, " type %s",
+ keyword(atrv->value.i));
+ break;
+
+ case T_Flag:
+ fprintf(df, " %s",
+ keyword(atrv->value.i));
+ break;
+ }
+ }
+ fprintf(df, "\n");
+ }
+ }
+
+ atrv = HEAD_PFIFO(ptree->auth.crypto_cmd_list);
+ if (atrv != NULL) {
+ fprintf(df, "crypto");
+ for ( ; atrv != NULL; atrv = atrv->link) {
+ fprintf(df, " %s %s", keyword(atrv->attr),
+ atrv->value.s);
+ }
+ fprintf(df, "\n");
+ }
+
+ if (ptree->auth.revoke != 0)
+ fprintf(df, "revoke %d\n", ptree->auth.revoke);
+
+ if (ptree->auth.keysdir != NULL)
+ fprintf(df, "keysdir \"%s\"\n", ptree->auth.keysdir);
+
+ if (ptree->auth.keys != NULL)
+ fprintf(df, "keys \"%s\"\n", ptree->auth.keys);
+
+ atrv = HEAD_PFIFO(ptree->auth.trusted_key_list);
+ if (atrv != NULL) {
+ fprintf(df, "trustedkey");
+ for ( ; atrv != NULL; atrv = atrv->link) {
+ if (T_Integer == atrv->type)
+ fprintf(df, " %d", atrv->value.i);
+ else if (T_Intrange == atrv->type)
+ fprintf(df, " (%d ... %d)",
+ atrv->value.r.first,
+ atrv->value.r.last);
+#ifdef DEBUG
+ else
+ fprintf(df, "\n# dump error:\n"
+ "# unknown trustedkey attr type %d\n"
+ "trustedkey", atrv->type);
+#endif
+ }
+ fprintf(df, "\n");
+ }
+
+ if (ptree->auth.control_key)
+ fprintf(df, "controlkey %d\n", ptree->auth.control_key);
+
+ if (ptree->auth.request_key)
+ fprintf(df, "requestkey %d\n", ptree->auth.request_key);
+
+ /* dump enable list, then disable list */
+ for (enable = 1; enable >= 0; enable--) {
+ atrv = (enable)
+ ? HEAD_PFIFO(ptree->enable_opts)
+ : HEAD_PFIFO(ptree->disable_opts);
+ if (atrv != NULL) {
+ fprintf(df, "%s", (enable)
+ ? "enable"
+ : "disable");
+ for ( ; atrv != NULL; atrv = atrv->link)
+ fprintf(df, " %s",
+ keyword(atrv->value.i));
+ fprintf(df, "\n");
+ }
+ }
+
+ atrv = HEAD_PFIFO(ptree->orphan_cmds);
+ if (atrv != NULL) {
+ fprintf(df, "tos");
+ for ( ; atrv != NULL; atrv = atrv->link) {
+ switch (atrv->type) {
+#ifdef DEBUG
+ default:
+ fprintf(df, "\n# dump error:\n"
+ "# unknown tos attr type %d %s\n"
+ "tos", atrv->type,
+ token_name(atrv->type));
+ break;
+#endif
+ case T_Double:
+ fprintf(df, " %s %s",
+ keyword(atrv->attr),
+ normal_dtoa(atrv->value.d));
+ break;
+ }
+ }
+ fprintf(df, "\n");
+ }
+
+ atrv = HEAD_PFIFO(ptree->rlimit);
+ if (atrv != NULL) {
+ fprintf(df, "rlimit");
+ for ( ; atrv != NULL; atrv = atrv->link) {
+ INSIST(T_Integer == atrv->type);
+ fprintf(df, " %s %d", keyword(atrv->attr),
+ atrv->value.i);
+ }
+ fprintf(df, "\n");
+ }
+
+ atrv = HEAD_PFIFO(ptree->tinker);
+ if (atrv != NULL) {
+ fprintf(df, "tinker");
+ for ( ; atrv != NULL; atrv = atrv->link) {
+ INSIST(T_Double == atrv->type);
+ fprintf(df, " %s %s", keyword(atrv->attr),
+ normal_dtoa(atrv->value.d));
+ }
+ fprintf(df, "\n");
+ }
+
+ if (ptree->broadcastclient)
+ fprintf(df, "broadcastclient\n");
+
+ peern = HEAD_PFIFO(ptree->peers);
+ for ( ; peern != NULL; peern = peern->link) {
+ addr = peern->addr;
+ fprintf(df, "%s", keyword(peern->host_mode));
+ switch (addr->type) {
+#ifdef DEBUG
+ default:
+ fprintf(df, "# dump error:\n"
+ "# unknown peer family %d for:\n"
+ "%s", addr->type,
+ keyword(peern->host_mode));
+ break;
+#endif
+ case AF_UNSPEC:
+ break;
+
+ case AF_INET:
+ fprintf(df, " -4");
+ break;
+
+ case AF_INET6:
+ fprintf(df, " -6");
+ break;
+ }
+ fprintf(df, " %s", addr->address);
+
+ if (peern->minpoll != 0)
+ fprintf(df, " minpoll %u", peern->minpoll);
+
+ if (peern->maxpoll != 0)
+ fprintf(df, " maxpoll %u", peern->maxpoll);
+
+ if (peern->ttl != 0) {
+ if (strlen(addr->address) > 8
+ && !memcmp(addr->address, "127.127.", 8))
+ fprintf(df, " mode %u", peern->ttl);
+ else
+ fprintf(df, " ttl %u", peern->ttl);
+ }
+
+ if (peern->peerversion != NTP_VERSION)
+ fprintf(df, " version %u", peern->peerversion);
+
+ if (peern->peerkey != 0)
+ fprintf(df, " key %u", peern->peerkey);
+
+ if (peern->group != NULL)
+ fprintf(df, " ident \"%s\"", peern->group);
+
+ atrv = HEAD_PFIFO(peern->peerflags);
+ for ( ; atrv != NULL; atrv = atrv->link) {
+ INSIST(T_Flag == atrv->attr);
+ INSIST(T_Integer == atrv->type);
+ fprintf(df, " %s", keyword(atrv->value.i));
+ }
+
+ fprintf(df, "\n");
+
+ addr_opts = HEAD_PFIFO(ptree->fudge);
+ for ( ; addr_opts != NULL; addr_opts = addr_opts->link) {
+ peer_addr = peern->addr;
+ fudge_addr = addr_opts->addr;
+
+ s1 = peer_addr->address;
+ s2 = fudge_addr->address;
+
+ if (strcmp(s1, s2))
+ continue;
+
+ fprintf(df, "fudge %s", s1);
+
+ for (atrv = HEAD_PFIFO(addr_opts->options);
+ atrv != NULL;
+ atrv = atrv->link) {
+
+ switch (atrv->type) {
+#ifdef DEBUG
+ default:
+ fprintf(df, "\n# dump error:\n"
+ "# unknown fudge atrv->type %d\n"
+ "fudge %s", atrv->type,
+ s1);
+ break;
+#endif
+ case T_Double:
+ fprintf(df, " %s %s",
+ keyword(atrv->attr),
+ normal_dtoa(atrv->value.d));
+ break;
+
+ case T_Integer:
+ fprintf(df, " %s %d",
+ keyword(atrv->attr),
+ atrv->value.i);
+ break;
+
+ case T_String:
+ fprintf(df, " %s %s",
+ keyword(atrv->attr),
+ atrv->value.s);
+ break;
+ }
+ }
+ fprintf(df, "\n");
+ }
+ }
+
+ addr = HEAD_PFIFO(ptree->manycastserver);
+ if (addr != NULL) {
+ fprintf(df, "manycastserver");
+ for ( ; addr != NULL; addr = addr->link)
+ fprintf(df, " %s", addr->address);
+ fprintf(df, "\n");
+ }
+
+ addr = HEAD_PFIFO(ptree->multicastclient);
+ if (addr != NULL) {
+ fprintf(df, "multicastclient");
+ for ( ; addr != NULL; addr = addr->link)
+ fprintf(df, " %s", addr->address);
+ fprintf(df, "\n");
+ }
+
+
+ for (unpeern = HEAD_PFIFO(ptree->unpeers);
+ unpeern != NULL;
+ unpeern = unpeern->link)
+ fprintf(df, "unpeer %s\n", unpeern->addr->address);
+
+ atrv = HEAD_PFIFO(ptree->mru_opts);
+ if (atrv != NULL) {
+ fprintf(df, "mru");
+ for ( ; atrv != NULL; atrv = atrv->link)
+ fprintf(df, " %s %d", keyword(atrv->attr),
+ atrv->value.i);
+ fprintf(df, "\n");
+ }
+
+ atrv = HEAD_PFIFO(ptree->discard_opts);
+ if (atrv != NULL) {
+ fprintf(df, "discard");
+ for ( ; atrv != NULL; atrv = atrv->link)
+ fprintf(df, " %s %d", keyword(atrv->attr),
+ atrv->value.i);
+ fprintf(df, "\n");
+ }
+
+
+ for (rest_node = HEAD_PFIFO(ptree->restrict_opts);
+ rest_node != NULL;
+ rest_node = rest_node->link) {
+
+ if (NULL == rest_node->addr) {
+ s = "default";
+ flags = HEAD_PFIFO(rest_node->flags);
+ for ( ; flags != NULL; flags = flags->link)
+ if (T_Source == flags->i) {
+ s = "source";
+ break;
+ }
+ } else {
+ s = rest_node->addr->address;
+ }
+ fprintf(df, "restrict %s", s);
+ if (rest_node->mask != NULL)
+ fprintf(df, " mask %s",
+ rest_node->mask->address);
+ flags = HEAD_PFIFO(rest_node->flags);
+ for ( ; flags != NULL; flags = flags->link)
+ if (T_Source != flags->i)
+ fprintf(df, " %s", keyword(flags->i));
+ fprintf(df, "\n");
+ }
+
+ rule_node = HEAD_PFIFO(ptree->nic_rules);
+ for ( ; rule_node != NULL; rule_node = rule_node->link) {
+ fprintf(df, "interface %s %s\n",
+ keyword(rule_node->action),
+ (rule_node->match_class)
+ ? keyword(rule_node->match_class)
+ : rule_node->if_name);
+ }
+
+ str_node = HEAD_PFIFO(ptree->phone);
+ if (str_node != NULL) {
+ fprintf(df, "phone");
+ for ( ; str_node != NULL; str_node = str_node->link)
+ fprintf(df, " \"%s\"", str_node->s);
+ fprintf(df, "\n");
+ }
+
+ setv_node = HEAD_PFIFO(ptree->setvar);
+ for ( ; setv_node != NULL; setv_node = setv_node->link) {
+ s1 = quote_if_needed(setv_node->var);
+ s2 = quote_if_needed(setv_node->val);
+ fprintf(df, "setvar %s = %s", s1, s2);
+ free(s1);
+ free(s2);
+ if (setv_node->isdefault)
+ fprintf(df, " default");
+ fprintf(df, "\n");
+ }
+
+ i_n = HEAD_PFIFO(ptree->ttl);
+ if (i_n != NULL) {
+ fprintf(df, "ttl");
+ for( ; i_n != NULL; i_n = i_n->link)
+ fprintf(df, " %d", i_n->i);
+ fprintf(df, "\n");
+ }
+
+ addr_opts = HEAD_PFIFO(ptree->trap);
+ for ( ; addr_opts != NULL; addr_opts = addr_opts->link) {
+ addr = addr_opts->addr;
+ fprintf(df, "trap %s", addr->address);
+ atrv = HEAD_PFIFO(addr_opts->options);
+ for ( ; atrv != NULL; atrv = atrv->link) {
+ switch (atrv->attr) {
+#ifdef DEBUG
+ default:
+ fprintf(df, "\n# dump error:\n"
+ "# unknown trap token %d\n"
+ "trap %s", atrv->attr,
+ addr->address);
+ break;
+#endif
+ case T_Port:
+ fprintf(df, " port %d", atrv->value.i);
+ break;
+
+ case T_Interface:
+ fprintf(df, " interface %s",
+ atrv->value.s);
+ break;
+ }
+ }
+ fprintf(df, "\n");
+ }
+
+ counter_set = HEAD_PFIFO(ptree->reset_counters);
+ if (counter_set != NULL) {
+ fprintf(df, "reset");
+ for ( ; counter_set != NULL;
+ counter_set = counter_set->link)
+ fprintf(df, " %s", keyword(counter_set->i));
+ fprintf(df, "\n");
+ }
+
+ return 0;
+}
+#endif /* SAVECONFIG */
+
+
+
+/* generic fifo routines for structs linked by 1st member */
+void *
+append_gen_fifo(
+ void *fifo,
+ void *entry
+ )
+{
+ gen_fifo *pf;
+ gen_node *pe;
+
+ pf = fifo;
+ pe = entry;
+ if (NULL == pf)
+ pf = emalloc_zero(sizeof(*pf));
+ else
+ CHECK_FIFO_CONSISTENCY(*pf);
+ if (pe != NULL)
+ LINK_FIFO(*pf, pe, link);
+ CHECK_FIFO_CONSISTENCY(*pf);
+
+ return pf;
+}
+
+
+void *
+concat_gen_fifos(
+ void *first,
+ void *second
+ )
+{
+ gen_fifo *pf1;
+ gen_fifo *pf2;
+
+ pf1 = first;
+ pf2 = second;
+ if (NULL == pf1)
+ return pf2;
+ if (NULL == pf2)
+ return pf1;
+
+ CONCAT_FIFO(*pf1, *pf2, link);
+ free(pf2);
+
+ return pf1;
+}
+
+
+/* FUNCTIONS FOR CREATING NODES ON THE SYNTAX TREE
+ * -----------------------------------------------
+ */
+
+attr_val *
+create_attr_dval(
+ int attr,
+ double value
+ )
+{
+ attr_val *my_val;
+
+ my_val = emalloc_zero(sizeof(*my_val));
+ my_val->attr = attr;
+ my_val->value.d = value;
+ my_val->type = T_Double;
+
+ return my_val;
+}
+
+
+attr_val *
+create_attr_ival(
+ int attr,
+ int value
+ )
+{
+ attr_val *my_val;
+
+ my_val = emalloc_zero(sizeof(*my_val));
+ my_val->attr = attr;
+ my_val->value.i = value;
+ my_val->type = T_Integer;
+
+ return my_val;
+}
+
+
+attr_val *
+create_attr_uval(
+ int attr,
+ u_int value
+ )
+{
+ attr_val *my_val;
+
+ my_val = emalloc_zero(sizeof(*my_val));
+ my_val->attr = attr;
+ my_val->value.u = value;
+ my_val->type = T_U_int;
+
+ return my_val;
+}
+
+
+attr_val *
+create_attr_rangeval(
+ int attr,
+ int first,
+ int last
+ )
+{
+ attr_val *my_val;
+
+ my_val = emalloc_zero(sizeof(*my_val));
+ my_val->attr = attr;
+ my_val->value.r.first = first;
+ my_val->value.r.last = last;
+ my_val->type = T_Intrange;
+
+ return my_val;
+}
+
+
+attr_val *
+create_attr_sval(
+ int attr,
+ char *s
+ )
+{
+ attr_val *my_val;
+
+ my_val = emalloc_zero(sizeof(*my_val));
+ my_val->attr = attr;
+ if (NULL == s) /* free() hates NULL */
+ s = estrdup("");
+ my_val->value.s = s;
+ my_val->type = T_String;
+
+ return my_val;
+}
+
+
+int_node *
+create_int_node(
+ int val
+ )
+{
+ int_node *i_n;
+
+ i_n = emalloc_zero(sizeof(*i_n));
+ i_n->i = val;
+
+ return i_n;
+}
+
+
+string_node *
+create_string_node(
+ char *str
+ )
+{
+ string_node *sn;
+
+ sn = emalloc_zero(sizeof(*sn));
+ sn->s = str;
+
+ return sn;
+}
+
+
+address_node *
+create_address_node(
+ char * addr,
+ int type
+ )
+{
+ address_node *my_node;
+
+ NTP_REQUIRE(NULL != addr);
+ NTP_REQUIRE(AF_INET == type ||
+ AF_INET6 == type || AF_UNSPEC == type);
+ my_node = emalloc_zero(sizeof(*my_node));
+ my_node->address = addr;
+ my_node->type = (u_short)type;
+
+ return my_node;
+}
+
+
+void
+destroy_address_node(
+ address_node *my_node
+ )
+{
+ if (NULL == my_node)
+ return;
+ NTP_REQUIRE(NULL != my_node->address);
+
+ free(my_node->address);
+ free(my_node);
+}
+
+
+peer_node *
+create_peer_node(
+ int hmode,
+ address_node * addr,
+ attr_val_fifo * options
+ )
+{
+ peer_node *my_node;
+ attr_val *option;
+ int freenode;
+ int errflag = 0;
+
+ my_node = emalloc_zero(sizeof(*my_node));
+
+ /* Initialize node values to default */
+ my_node->peerversion = NTP_VERSION;
+
+ /* Now set the node to the read values */
+ my_node->host_mode = hmode;
+ my_node->addr = addr;
+
+ /*
+ * the options FIFO mixes items that will be saved in the
+ * peer_node as explicit members, such as minpoll, and
+ * those that are moved intact to the peer_node's peerflags
+ * FIFO. The options FIFO is consumed and reclaimed here.
+ */
+
+ if (options != NULL)
+ CHECK_FIFO_CONSISTENCY(*options);
+ while (options != NULL) {
+ UNLINK_FIFO(option, *options, link);
+ if (NULL == option) {
+ free(options);
+ break;
+ }
+
+ freenode = 1;
+ /* Check the kind of option being set */
+ switch (option->attr) {
+
+ case T_Flag:
+ APPEND_G_FIFO(my_node->peerflags, option);
+ freenode = 0;
+ break;
+
+ case T_Minpoll:
+ if (option->value.i < NTP_MINPOLL ||
+ option->value.i > UCHAR_MAX) {
+ msyslog(LOG_INFO,
+ "minpoll: provided value (%d) is out of range [%d-%d])",
+ option->value.i, NTP_MINPOLL,
+ UCHAR_MAX);
+ my_node->minpoll = NTP_MINPOLL;
+ } else {
+ my_node->minpoll =
+ (u_char)option->value.u;
+ }
+ break;
+
+ case T_Maxpoll:
+ if (option->value.i < 0 ||
+ option->value.i > NTP_MAXPOLL) {
+ msyslog(LOG_INFO,
+ "maxpoll: provided value (%d) is out of range [0-%d])",
+ option->value.i, NTP_MAXPOLL);
+ my_node->maxpoll = NTP_MAXPOLL;
+ } else {
+ my_node->maxpoll =
+ (u_char)option->value.u;
+ }
+ break;
+
+ case T_Ttl:
+ if (option->value.u >= MAX_TTL) {
+ msyslog(LOG_ERR, "ttl: invalid argument");
+ errflag = 1;
+ } else {
+ my_node->ttl = (u_char)option->value.u;
+ }
+ break;
+
+ case T_Mode:
+ my_node->ttl = option->value.u;
+ break;
+
+ case T_Key:
+ if (option->value.u >= KEYID_T_MAX) {
+ msyslog(LOG_ERR, "key: invalid argument");
+ errflag = 1;
+ } else {
+ my_node->peerkey =
+ (keyid_t)option->value.u;
+ }
+ break;
+
+ case T_Version:
+ if (option->value.u >= UCHAR_MAX) {
+ msyslog(LOG_ERR, "version: invalid argument");
+ errflag = 1;
+ } else {
+ my_node->peerversion =
+ (u_char)option->value.u;
+ }
+ break;
+
+ case T_Ident:
+ my_node->group = option->value.s;
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "Unknown peer/server option token %s",
+ token_name(option->attr));
+ errflag = 1;
+ }
+ if (freenode)
+ free(option);
+ }
+
+ /* Check if errors were reported. If yes, ignore the node */
+ if (errflag) {
+ free(my_node);
+ my_node = NULL;
+ }
+
+ return my_node;
+}
+
+
+unpeer_node *
+create_unpeer_node(
+ address_node *addr
+ )
+{
+ unpeer_node * my_node;
+ u_int u;
+ char * pch;
+
+ my_node = emalloc_zero(sizeof(*my_node));
+
+ /*
+ * From the parser's perspective an association ID fits into
+ * its generic T_String definition of a name/address "address".
+ * We treat all valid 16-bit numbers as association IDs.
+ */
+ pch = addr->address;
+ while (*pch && isdigit(*pch))
+ pch++;
+
+ if (!*pch
+ && 1 == sscanf(addr->address, "%u", &u)
+ && u <= ASSOCID_MAX) {
+ my_node->assocID = (associd_t)u;
+ destroy_address_node(addr);
+ my_node->addr = NULL;
+ } else {
+ my_node->assocID = 0;
+ my_node->addr = addr;
+ }
+
+ return my_node;
+}
+
+filegen_node *
+create_filegen_node(
+ int filegen_token,
+ attr_val_fifo * options
+ )
+{
+ filegen_node *my_node;
+
+ my_node = emalloc_zero(sizeof(*my_node));
+ my_node->filegen_token = filegen_token;
+ my_node->options = options;
+
+ return my_node;
+}
+
+
+restrict_node *
+create_restrict_node(
+ address_node * addr,
+ address_node * mask,
+ int_fifo * flags,
+ int line_no
+ )
+{
+ restrict_node *my_node;
+
+ my_node = emalloc_zero(sizeof(*my_node));
+ my_node->addr = addr;
+ my_node->mask = mask;
+ my_node->flags = flags;
+ my_node->line_no = line_no;
+
+ return my_node;
+}
+
+
+static void
+destroy_restrict_node(
+ restrict_node *my_node
+ )
+{
+ /* With great care, free all the memory occupied by
+ * the restrict node
+ */
+ destroy_address_node(my_node->addr);
+ destroy_address_node(my_node->mask);
+ destroy_int_fifo(my_node->flags);
+ free(my_node);
+}
+
+
+static void
+destroy_int_fifo(
+ int_fifo * fifo
+ )
+{
+ int_node * i_n;
+
+ if (fifo != NULL) {
+ for (;;) {
+ UNLINK_FIFO(i_n, *fifo, link);
+ if (i_n == NULL)
+ break;
+ free(i_n);
+ }
+ free(fifo);
+ }
+}
+
+
+static void
+destroy_string_fifo(
+ string_fifo * fifo
+ )
+{
+ string_node * sn;
+
+ if (fifo != NULL) {
+ for (;;) {
+ UNLINK_FIFO(sn, *fifo, link);
+ if (sn == NULL)
+ break;
+ free(sn->s);
+ free(sn);
+ }
+ free(fifo);
+ }
+}
+
+
+static void
+destroy_attr_val_fifo(
+ attr_val_fifo * av_fifo
+ )
+{
+ attr_val * av;
+
+ if (av_fifo != NULL) {
+ for (;;) {
+ UNLINK_FIFO(av, *av_fifo, link);
+ if (av == NULL)
+ break;
+ if (T_String == av->type)
+ free(av->value.s);
+ free(av);
+ }
+ free(av_fifo);
+ }
+}
+
+
+static void
+destroy_filegen_fifo(
+ filegen_fifo * fifo
+ )
+{
+ filegen_node * fg;
+
+ if (fifo != NULL) {
+ for (;;) {
+ UNLINK_FIFO(fg, *fifo, link);
+ if (fg == NULL)
+ break;
+ destroy_attr_val_fifo(fg->options);
+ free(fg);
+ }
+ free(fifo);
+ }
+}
+
+
+static void
+destroy_restrict_fifo(
+ restrict_fifo * fifo
+ )
+{
+ restrict_node * rn;
+
+ if (fifo != NULL) {
+ for (;;) {
+ UNLINK_FIFO(rn, *fifo, link);
+ if (rn == NULL)
+ break;
+ destroy_restrict_node(rn);
+ }
+ free(fifo);
+ }
+}
+
+
+static void
+destroy_setvar_fifo(
+ setvar_fifo * fifo
+ )
+{
+ setvar_node * sv;
+
+ if (fifo != NULL) {
+ for (;;) {
+ UNLINK_FIFO(sv, *fifo, link);
+ if (sv == NULL)
+ break;
+ free(sv->var);
+ free(sv->val);
+ free(sv);
+ }
+ free(fifo);
+ }
+}
+
+
+static void
+destroy_addr_opts_fifo(
+ addr_opts_fifo * fifo
+ )
+{
+ addr_opts_node * aon;
+
+ if (fifo != NULL) {
+ for (;;) {
+ UNLINK_FIFO(aon, *fifo, link);
+ if (aon == NULL)
+ break;
+ destroy_address_node(aon->addr);
+ destroy_attr_val_fifo(aon->options);
+ free(aon);
+ }
+ free(fifo);
+ }
+}
+
+
+setvar_node *
+create_setvar_node(
+ char * var,
+ char * val,
+ int isdefault
+ )
+{
+ setvar_node * my_node;
+ char * pch;
+
+ /* do not allow = in the variable name */
+ pch = strchr(var, '=');
+ if (NULL != pch)
+ *pch = '\0';
+
+ /* Now store the string into a setvar_node */
+ my_node = emalloc_zero(sizeof(*my_node));
+ my_node->var = var;
+ my_node->val = val;
+ my_node->isdefault = isdefault;
+
+ return my_node;
+}
+
+
+nic_rule_node *
+create_nic_rule_node(
+ int match_class,
+ char *if_name, /* interface name or numeric address */
+ int action
+ )
+{
+ nic_rule_node *my_node;
+
+ NTP_REQUIRE(match_class != 0 || if_name != NULL);
+
+ my_node = emalloc_zero(sizeof(*my_node));
+ my_node->match_class = match_class;
+ my_node->if_name = if_name;
+ my_node->action = action;
+
+ return my_node;
+}
+
+
+addr_opts_node *
+create_addr_opts_node(
+ address_node * addr,
+ attr_val_fifo * options
+ )
+{
+ addr_opts_node *my_node;
+
+ my_node = emalloc_zero(sizeof(*my_node));
+ my_node->addr = addr;
+ my_node->options = options;
+
+ return my_node;
+}
+
+
+#ifdef SIM
+script_info *
+create_sim_script_info(
+ double duration,
+ attr_val_fifo * script_queue
+ )
+{
+ script_info *my_info;
+ attr_val *my_attr_val;
+
+ my_info = emalloc_zero(sizeof(*my_info));
+
+ /* Initialize Script Info with default values*/
+ my_info->duration = duration;
+ my_info->prop_delay = NET_DLY;
+ my_info->proc_delay = PROC_DLY;
+
+ /* Traverse the script_queue and fill out non-default values */
+
+ for (my_attr_val = HEAD_PFIFO(script_queue);
+ my_attr_val != NULL;
+ my_attr_val = my_attr_val->link) {
+
+ /* Set the desired value */
+ switch (my_attr_val->attr) {
+
+ case T_Freq_Offset:
+ my_info->freq_offset = my_attr_val->value.d;
+ break;
+
+ case T_Wander:
+ my_info->wander = my_attr_val->value.d;
+ break;
+
+ case T_Jitter:
+ my_info->jitter = my_attr_val->value.d;
+ break;
+
+ case T_Prop_Delay:
+ my_info->prop_delay = my_attr_val->value.d;
+ break;
+
+ case T_Proc_Delay:
+ my_info->proc_delay = my_attr_val->value.d;
+ break;
+
+ default:
+ msyslog(LOG_ERR, "Unknown script token %d",
+ my_attr_val->attr);
+ }
+ }
+
+ return my_info;
+}
+#endif /* SIM */
+
+
+#ifdef SIM
+static sockaddr_u *
+get_next_address(
+ address_node *addr
+ )
+{
+ const char addr_prefix[] = "192.168.0.";
+ static int curr_addr_num = 1;
+#define ADDR_LENGTH 16 + 1 /* room for 192.168.1.255 */
+ char addr_string[ADDR_LENGTH];
+ sockaddr_u *final_addr;
+ struct addrinfo *ptr;
+ int gai_err;
+
+ final_addr = emalloc(sizeof(*final_addr));
+
+ if (addr->type == T_String) {
+ snprintf(addr_string, sizeof(addr_string), "%s%d",
+ addr_prefix, curr_addr_num++);
+ printf("Selecting ip address %s for hostname %s\n",
+ addr_string, addr->address);
+ gai_err = getaddrinfo(addr_string, "ntp", NULL, &ptr);
+ } else {
+ gai_err = getaddrinfo(addr->address, "ntp", NULL, &ptr);
+ }
+
+ if (gai_err) {
+ fprintf(stderr, "ERROR!! Could not get a new address\n");
+ exit(1);
+ }
+ memcpy(final_addr, ptr->ai_addr, ptr->ai_addrlen);
+ fprintf(stderr, "Successful in setting ip address of simulated server to: %s\n",
+ stoa(final_addr));
+ freeaddrinfo(ptr);
+
+ return final_addr;
+}
+#endif /* SIM */
+
+
+#ifdef SIM
+server_info *
+create_sim_server(
+ address_node * addr,
+ double server_offset,
+ script_info_fifo * script
+ )
+{
+ server_info *my_info;
+
+ my_info = emalloc_zero(sizeof(*my_info));
+ my_info->server_time = server_offset;
+ my_info->addr = get_next_address(addr);
+ my_info->script = script;
+ UNLINK_FIFO(my_info->curr_script, *my_info->script, link);
+
+ return my_info;
+}
+#endif /* SIM */
+
+sim_node *
+create_sim_node(
+ attr_val_fifo * init_opts,
+ server_info_fifo * servers
+ )
+{
+ sim_node *my_node;
+
+ my_node = emalloc(sizeof(*my_node));
+ my_node->init_opts = init_opts;
+ my_node->servers = servers;
+
+ return my_node;
+}
+
+
+
+
+/* FUNCTIONS FOR PERFORMING THE CONFIGURATION
+ * ------------------------------------------
+ */
+
+#ifndef SIM
+static void
+config_other_modes(
+ config_tree * ptree
+ )
+{
+ sockaddr_u addr_sock;
+ address_node * addr_node;
+
+ if (ptree->broadcastclient)
+ proto_config(PROTO_BROADCLIENT, ptree->broadcastclient,
+ 0., NULL);
+
+ addr_node = HEAD_PFIFO(ptree->manycastserver);
+ while (addr_node != NULL) {
+ ZERO_SOCK(&addr_sock);
+ AF(&addr_sock) = addr_node->type;
+ if (1 == getnetnum(addr_node->address, &addr_sock, 1,
+ t_UNK)) {
+ proto_config(PROTO_MULTICAST_ADD,
+ 0, 0., &addr_sock);
+ sys_manycastserver = 1;
+ }
+ addr_node = addr_node->link;
+ }
+
+ /* Configure the multicast clients */
+ addr_node = HEAD_PFIFO(ptree->multicastclient);
+ if (addr_node != NULL) {
+ do {
+ ZERO_SOCK(&addr_sock);
+ AF(&addr_sock) = addr_node->type;
+ if (1 == getnetnum(addr_node->address,
+ &addr_sock, 1, t_UNK)) {
+ proto_config(PROTO_MULTICAST_ADD, 0, 0.,
+ &addr_sock);
+ }
+ addr_node = addr_node->link;
+ } while (addr_node != NULL);
+ proto_config(PROTO_MULTICAST_ADD, 1, 0., NULL);
+ }
+}
+#endif /* !SIM */
+
+
+#ifdef FREE_CFG_T
+static void
+destroy_address_fifo(
+ address_fifo * pfifo
+ )
+{
+ address_node * addr_node;
+
+ if (pfifo != NULL) {
+ for (;;) {
+ UNLINK_FIFO(addr_node, *pfifo, link);
+ if (addr_node == NULL)
+ break;
+ destroy_address_node(addr_node);
+ }
+ free(pfifo);
+ }
+}
+
+
+static void
+free_config_other_modes(
+ config_tree *ptree
+ )
+{
+ FREE_ADDRESS_FIFO(ptree->manycastserver);
+ FREE_ADDRESS_FIFO(ptree->multicastclient);
+}
+#endif /* FREE_CFG_T */
+
+
+#ifndef SIM
+static void
+config_auth(
+ config_tree *ptree
+ )
+{
+ attr_val * my_val;
+ int first;
+ int last;
+ int i;
+ int count;
+#ifdef AUTOKEY
+ int item;
+#endif
+
+ /* Crypto Command */
+#ifdef AUTOKEY
+ item = -1; /* quiet warning */
+ my_val = HEAD_PFIFO(ptree->auth.crypto_cmd_list);
+ for (; my_val != NULL; my_val = my_val->link) {
+ switch (my_val->attr) {
+
+ default:
+ INSIST(0);
+ break;
+
+ case T_Host:
+ item = CRYPTO_CONF_PRIV;
+ break;
+
+ case T_Ident:
+ item = CRYPTO_CONF_IDENT;
+ break;
+
+ case T_Pw:
+ item = CRYPTO_CONF_PW;
+ break;
+
+ case T_Randfile:
+ item = CRYPTO_CONF_RAND;
+ break;
+
+ case T_Digest:
+ item = CRYPTO_CONF_NID;
+ break;
+ }
+ crypto_config(item, my_val->value.s);
+ }
+#endif /* AUTOKEY */
+
+ /* Keysdir Command */
+ if (ptree->auth.keysdir) {
+ if (keysdir != default_keysdir)
+ free(keysdir);
+ keysdir = estrdup(ptree->auth.keysdir);
+ }
+
+
+ /* ntp_signd_socket Command */
+ if (ptree->auth.ntp_signd_socket) {
+ if (ntp_signd_socket != default_ntp_signd_socket)
+ free(ntp_signd_socket);
+ ntp_signd_socket = estrdup(ptree->auth.ntp_signd_socket);
+ }
+
+#ifdef AUTOKEY
+ if (ptree->auth.cryptosw && !cryptosw) {
+ crypto_setup();
+ cryptosw = 1;
+ }
+#endif /* AUTOKEY */
+
+ /*
+ * Count the number of trusted keys to preallocate storage and
+ * size the hash table.
+ */
+ count = 0;
+ my_val = HEAD_PFIFO(ptree->auth.trusted_key_list);
+ for (; my_val != NULL; my_val = my_val->link) {
+ if (T_Integer == my_val->type) {
+ first = my_val->value.i;
+ if (first > 1 && first <= NTP_MAXKEY)
+ count++;
+ } else {
+ REQUIRE(T_Intrange == my_val->type);
+ first = my_val->value.r.first;
+ last = my_val->value.r.last;
+ if (!(first > last || first < 1 ||
+ last > NTP_MAXKEY)) {
+ count += 1 + last - first;
+ }
+ }
+ }
+ auth_prealloc_symkeys(count);
+
+ /* Keys Command */
+ if (ptree->auth.keys)
+ getauthkeys(ptree->auth.keys);
+
+ /* Control Key Command */
+ if (ptree->auth.control_key)
+ ctl_auth_keyid = (keyid_t)ptree->auth.control_key;
+
+ /* Requested Key Command */
+ if (ptree->auth.request_key) {
+ DPRINTF(4, ("set info_auth_keyid to %08lx\n",
+ (u_long) ptree->auth.request_key));
+ info_auth_keyid = (keyid_t)ptree->auth.request_key;
+ }
+
+ /* Trusted Key Command */
+ my_val = HEAD_PFIFO(ptree->auth.trusted_key_list);
+ for (; my_val != NULL; my_val = my_val->link) {
+ if (T_Integer == my_val->type) {
+ first = my_val->value.i;
+ if (first >= 1 && first <= NTP_MAXKEY) {
+ authtrust(first, TRUE);
+ } else {
+ msyslog(LOG_NOTICE,
+ "Ignoring invalid trustedkey %d, min 1 max %d.",
+ first, NTP_MAXKEY);
+ }
+ } else {
+ first = my_val->value.r.first;
+ last = my_val->value.r.last;
+ if (first > last || first < 1 ||
+ last > NTP_MAXKEY) {
+ msyslog(LOG_NOTICE,
+ "Ignoring invalid trustedkey range %d ... %d, min 1 max %d.",
+ first, last, NTP_MAXKEY);
+ } else {
+ for (i = first; i <= last; i++) {
+ authtrust(i, TRUE);
+ }
+ }
+ }
+ }
+
+#ifdef AUTOKEY
+ /* crypto revoke command */
+ if (ptree->auth.revoke)
+ sys_revoke = 1UL << ptree->auth.revoke;
+#endif /* AUTOKEY */
+}
+#endif /* !SIM */
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_auth(
+ config_tree *ptree
+ )
+{
+ destroy_attr_val_fifo(ptree->auth.crypto_cmd_list);
+ ptree->auth.crypto_cmd_list = NULL;
+ destroy_attr_val_fifo(ptree->auth.trusted_key_list);
+ ptree->auth.trusted_key_list = NULL;
+}
+#endif /* FREE_CFG_T */
+
+
+static void
+config_tos(
+ config_tree *ptree
+ )
+{
+ attr_val * tos;
+ int item;
+ double val;
+
+ item = -1; /* quiet warning */
+ tos = HEAD_PFIFO(ptree->orphan_cmds);
+ for (; tos != NULL; tos = tos->link) {
+ val = tos->value.d;
+ switch(tos->attr) {
+
+ default:
+ INSIST(0);
+ break;
+
+ case T_Ceiling:
+ if (val > STRATUM_UNSPEC - 1) {
+ msyslog(LOG_WARNING,
+ "Using maximum tos ceiling %d, %g requested",
+ STRATUM_UNSPEC - 1, val);
+ val = STRATUM_UNSPEC - 1;
+ }
+ item = PROTO_CEILING;
+ break;
+
+ case T_Floor:
+ item = PROTO_FLOOR;
+ break;
+
+ case T_Cohort:
+ item = PROTO_COHORT;
+ break;
+
+ case T_Orphan:
+ item = PROTO_ORPHAN;
+ break;
+
+ case T_Orphanwait:
+ item = PROTO_ORPHWAIT;
+ break;
+
+ case T_Mindist:
+ item = PROTO_MINDISP;
+ break;
+
+ case T_Maxdist:
+ item = PROTO_MAXDIST;
+ break;
+
+ case T_Minclock:
+ item = PROTO_MINCLOCK;
+ break;
+
+ case T_Maxclock:
+ item = PROTO_MAXCLOCK;
+ break;
+
+ case T_Minsane:
+ item = PROTO_MINSANE;
+ break;
+
+ case T_Beacon:
+ item = PROTO_BEACON;
+ break;
+ }
+ proto_config(item, 0, val, NULL);
+ }
+}
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_tos(
+ config_tree *ptree
+ )
+{
+ FREE_ATTR_VAL_FIFO(ptree->orphan_cmds);
+}
+#endif /* FREE_CFG_T */
+
+
+static void
+config_monitor(
+ config_tree *ptree
+ )
+{
+ int_node *pfilegen_token;
+ const char *filegen_string;
+ const char *filegen_file;
+ FILEGEN *filegen;
+ filegen_node *my_node;
+ attr_val *my_opts;
+ int filegen_type;
+ int filegen_flag;
+
+ /* Set the statistics directory */
+ if (ptree->stats_dir)
+ stats_config(STATS_STATSDIR, ptree->stats_dir);
+
+ /* NOTE:
+ * Calling filegen_get is brain dead. Doing a string
+ * comparison to find the relavant filegen structure is
+ * expensive.
+ *
+ * Through the parser, we already know which filegen is
+ * being specified. Hence, we should either store a
+ * pointer to the specified structure in the syntax tree
+ * or an index into a filegen array.
+ *
+ * Need to change the filegen code to reflect the above.
+ */
+
+ /* Turn on the specified statistics */
+ pfilegen_token = HEAD_PFIFO(ptree->stats_list);
+ for (; pfilegen_token != NULL; pfilegen_token = pfilegen_token->link) {
+ filegen_string = keyword(pfilegen_token->i);
+ filegen = filegen_get(filegen_string);
+ if (NULL == filegen) {
+ msyslog(LOG_ERR,
+ "stats %s unrecognized",
+ filegen_string);
+ continue;
+ }
+ DPRINTF(4, ("enabling filegen for %s statistics '%s%s'\n",
+ filegen_string, filegen->dir,
+ filegen->fname));
+ filegen_flag = filegen->flag;
+ filegen_flag |= FGEN_FLAG_ENABLED;
+ filegen_config(filegen, statsdir, filegen_string,
+ filegen->type, filegen_flag);
+ }
+
+ /* Configure the statistics with the options */
+ my_node = HEAD_PFIFO(ptree->filegen_opts);
+ for (; my_node != NULL; my_node = my_node->link) {
+ filegen_string = keyword(my_node->filegen_token);
+ filegen = filegen_get(filegen_string);
+ if (NULL == filegen) {
+ msyslog(LOG_ERR,
+ "filegen category '%s' unrecognized",
+ filegen_string);
+ continue;
+ }
+ filegen_file = filegen_string;
+
+ /* Initialize the filegen variables to their pre-configuration states */
+ filegen_flag = filegen->flag;
+ filegen_type = filegen->type;
+
+ /* "filegen ... enabled" is the default (when filegen is used) */
+ filegen_flag |= FGEN_FLAG_ENABLED;
+
+ my_opts = HEAD_PFIFO(my_node->options);
+ for (; my_opts != NULL; my_opts = my_opts->link) {
+ switch (my_opts->attr) {
+
+ case T_File:
+ filegen_file = my_opts->value.s;
+ break;
+
+ case T_Type:
+ switch (my_opts->value.i) {
+
+ default:
+ INSIST(0);
+ break;
+
+ case T_None:
+ filegen_type = FILEGEN_NONE;
+ break;
+
+ case T_Pid:
+ filegen_type = FILEGEN_PID;
+ break;
+
+ case T_Day:
+ filegen_type = FILEGEN_DAY;
+ break;
+
+ case T_Week:
+ filegen_type = FILEGEN_WEEK;
+ break;
+
+ case T_Month:
+ filegen_type = FILEGEN_MONTH;
+ break;
+
+ case T_Year:
+ filegen_type = FILEGEN_YEAR;
+ break;
+
+ case T_Age:
+ filegen_type = FILEGEN_AGE;
+ break;
+ }
+ break;
+
+ case T_Flag:
+ switch (my_opts->value.i) {
+
+ case T_Link:
+ filegen_flag |= FGEN_FLAG_LINK;
+ break;
+
+ case T_Nolink:
+ filegen_flag &= ~FGEN_FLAG_LINK;
+ break;
+
+ case T_Enable:
+ filegen_flag |= FGEN_FLAG_ENABLED;
+ break;
+
+ case T_Disable:
+ filegen_flag &= ~FGEN_FLAG_ENABLED;
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "Unknown filegen flag token %d",
+ my_opts->value.i);
+ exit(1);
+ }
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "Unknown filegen option token %d",
+ my_opts->attr);
+ exit(1);
+ }
+ }
+ filegen_config(filegen, statsdir, filegen_file,
+ filegen_type, filegen_flag);
+ }
+}
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_monitor(
+ config_tree *ptree
+ )
+{
+ if (ptree->stats_dir) {
+ free(ptree->stats_dir);
+ ptree->stats_dir = NULL;
+ }
+
+ FREE_INT_FIFO(ptree->stats_list);
+ FREE_FILEGEN_FIFO(ptree->filegen_opts);
+}
+#endif /* FREE_CFG_T */
+
+
+#ifndef SIM
+static void
+config_access(
+ config_tree *ptree
+ )
+{
+ static int warned_signd;
+ attr_val * my_opt;
+ restrict_node * my_node;
+ int_node * curr_flag;
+ sockaddr_u addr;
+ sockaddr_u mask;
+ struct addrinfo hints;
+ struct addrinfo * ai_list;
+ struct addrinfo * pai;
+ int rc;
+ int restrict_default;
+ u_short flags;
+ u_short mflags;
+ int range_err;
+ const char * signd_warning =
+#ifdef HAVE_NTP_SIGND
+ "MS-SNTP signd operations currently block ntpd degrading service to all clients.";
+#else
+ "mssntp restrict bit ignored, this ntpd was configured without --enable-ntp-signd.";
+#endif
+
+ /* Configure the mru options */
+ my_opt = HEAD_PFIFO(ptree->mru_opts);
+ for (; my_opt != NULL; my_opt = my_opt->link) {
+
+ range_err = FALSE;
+
+ switch (my_opt->attr) {
+
+ case T_Incalloc:
+ if (0 <= my_opt->value.i)
+ mru_incalloc = my_opt->value.u;
+ else
+ range_err = TRUE;
+ break;
+
+ case T_Incmem:
+ if (0 <= my_opt->value.i)
+ mru_incalloc = (my_opt->value.u * 1024U)
+ / sizeof(mon_entry);
+ else
+ range_err = TRUE;
+ break;
+
+ case T_Initalloc:
+ if (0 <= my_opt->value.i)
+ mru_initalloc = my_opt->value.u;
+ else
+ range_err = TRUE;
+ break;
+
+ case T_Initmem:
+ if (0 <= my_opt->value.i)
+ mru_initalloc = (my_opt->value.u * 1024U)
+ / sizeof(mon_entry);
+ else
+ range_err = TRUE;
+ break;
+
+ case T_Mindepth:
+ if (0 <= my_opt->value.i)
+ mru_mindepth = my_opt->value.u;
+ else
+ range_err = TRUE;
+ break;
+
+ case T_Maxage:
+ mru_maxage = my_opt->value.i;
+ break;
+
+ case T_Maxdepth:
+ if (0 <= my_opt->value.i)
+ mru_maxdepth = my_opt->value.u;
+ else
+ mru_maxdepth = UINT_MAX;
+ break;
+
+ case T_Maxmem:
+ if (0 <= my_opt->value.i)
+ mru_maxdepth = (my_opt->value.u * 1024U) /
+ sizeof(mon_entry);
+ else
+ mru_maxdepth = UINT_MAX;
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "Unknown mru option %s (%d)",
+ keyword(my_opt->attr), my_opt->attr);
+ exit(1);
+ }
+ if (range_err)
+ msyslog(LOG_ERR,
+ "mru %s %d out of range, ignored.",
+ keyword(my_opt->attr), my_opt->value.i);
+ }
+
+ /* Configure the discard options */
+ my_opt = HEAD_PFIFO(ptree->discard_opts);
+ for (; my_opt != NULL; my_opt = my_opt->link) {
+
+ switch (my_opt->attr) {
+
+ case T_Average:
+ if (0 <= my_opt->value.i &&
+ my_opt->value.i <= UCHAR_MAX)
+ ntp_minpoll = (u_char)my_opt->value.u;
+ else
+ msyslog(LOG_ERR,
+ "discard average %d out of range, ignored.",
+ my_opt->value.i);
+ break;
+
+ case T_Minimum:
+ ntp_minpkt = my_opt->value.i;
+ break;
+
+ case T_Monitor:
+ mon_age = my_opt->value.i;
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "Unknown discard option %s (%d)",
+ keyword(my_opt->attr), my_opt->attr);
+ exit(1);
+ }
+ }
+
+ /* Configure the restrict options */
+ my_node = HEAD_PFIFO(ptree->restrict_opts);
+ for (; my_node != NULL; my_node = my_node->link) {
+ /* Parse the flags */
+ flags = 0;
+ mflags = 0;
+
+ curr_flag = HEAD_PFIFO(my_node->flags);
+ for (; curr_flag != NULL; curr_flag = curr_flag->link) {
+ switch (curr_flag->i) {
+
+ default:
+ INSIST(0);
+ break;
+
+ case T_Ntpport:
+ mflags |= RESM_NTPONLY;
+ break;
+
+ case T_Source:
+ mflags |= RESM_SOURCE;
+ break;
+
+ case T_Flake:
+ flags |= RES_FLAKE;
+ break;
+
+ case T_Ignore:
+ flags |= RES_IGNORE;
+ break;
+
+ case T_Kod:
+ flags |= RES_KOD;
+ break;
+
+ case T_Mssntp:
+ flags |= RES_MSSNTP;
+ break;
+
+ case T_Limited:
+ flags |= RES_LIMITED;
+ break;
+
+ case T_Lowpriotrap:
+ flags |= RES_LPTRAP;
+ break;
+
+ case T_Nomodify:
+ flags |= RES_NOMODIFY;
+ break;
+
+ case T_Nomrulist:
+ flags |= RES_NOMRULIST;
+ break;
+
+ case T_Nopeer:
+ flags |= RES_NOPEER;
+ break;
+
+ case T_Noquery:
+ flags |= RES_NOQUERY;
+ break;
+
+ case T_Noserve:
+ flags |= RES_DONTSERVE;
+ break;
+
+ case T_Notrap:
+ flags |= RES_NOTRAP;
+ break;
+
+ case T_Notrust:
+ flags |= RES_DONTTRUST;
+ break;
+
+ case T_Version:
+ flags |= RES_VERSION;
+ break;
+ }
+ }
+
+ if ((RES_MSSNTP & flags) && !warned_signd) {
+ warned_signd = 1;
+ fprintf(stderr, "%s\n", signd_warning);
+ msyslog(LOG_WARNING, "%s", signd_warning);
+ }
+
+ /* It would be swell if we could identify the line number */
+ if ((RES_KOD & flags) && !(RES_LIMITED & flags)) {
+ char *kod_where = (my_node->addr)
+ ? my_node->addr->address
+ : (mflags & RESM_SOURCE)
+ ? "source"
+ : "default";
+ char *kod_warn = "KOD does nothing without LIMITED.";
+
+ fprintf(stderr, "restrict %s: %s\n", kod_where, kod_warn);
+ msyslog(LOG_WARNING, "restrict %s: %s", kod_where, kod_warn);
+ }
+
+ ZERO_SOCK(&addr);
+ ai_list = NULL;
+ pai = NULL;
+ restrict_default = 0;
+
+ if (NULL == my_node->addr) {
+ ZERO_SOCK(&mask);
+ if (!(RESM_SOURCE & mflags)) {
+ /*
+ * The user specified a default rule
+ * without a -4 / -6 qualifier, add to
+ * both lists
+ */
+ restrict_default = 1;
+ } else {
+ /* apply "restrict source ..." */
+ DPRINTF(1, ("restrict source template mflags %x flags %x\n",
+ mflags, flags));
+ hack_restrict(RESTRICT_FLAGS, NULL,
+ NULL, mflags, flags, 0);
+ continue;
+ }
+ } else {
+ /* Resolve the specified address */
+ AF(&addr) = (u_short)my_node->addr->type;
+
+ if (getnetnum(my_node->addr->address,
+ &addr, 1, t_UNK) != 1) {
+ /*
+ * Attempt a blocking lookup. This
+ * is in violation of the nonblocking
+ * design of ntpd's mainline code. The
+ * alternative of running without the
+ * restriction until the name resolved
+ * seems worse.
+ * Ideally some scheme could be used for
+ * restrict directives in the startup
+ * ntp.conf to delay starting up the
+ * protocol machinery until after all
+ * restrict hosts have been resolved.
+ */
+ ai_list = NULL;
+ ZERO(hints);
+ hints.ai_protocol = IPPROTO_UDP;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_family = my_node->addr->type;
+ rc = getaddrinfo(my_node->addr->address,
+ "ntp", &hints,
+ &ai_list);
+ if (rc) {
+ msyslog(LOG_ERR,
+ "restrict: ignoring line %d, address/host '%s' unusable.",
+ my_node->line_no,
+ my_node->addr->address);
+ continue;
+ }
+ INSIST(ai_list != NULL);
+ pai = ai_list;
+ INSIST(pai->ai_addr != NULL);
+ INSIST(sizeof(addr) >=
+ pai->ai_addrlen);
+ memcpy(&addr, pai->ai_addr,
+ pai->ai_addrlen);
+ INSIST(AF_INET == AF(&addr) ||
+ AF_INET6 == AF(&addr));
+ }
+
+ SET_HOSTMASK(&mask, AF(&addr));
+
+ /* Resolve the mask */
+ if (my_node->mask) {
+ ZERO_SOCK(&mask);
+ AF(&mask) = my_node->mask->type;
+ if (getnetnum(my_node->mask->address,
+ &mask, 1, t_MSK) != 1) {
+ msyslog(LOG_ERR,
+ "restrict: ignoring line %d, mask '%s' unusable.",
+ my_node->line_no,
+ my_node->mask->address);
+ continue;
+ }
+ }
+ }
+
+ /* Set the flags */
+ if (restrict_default) {
+ AF(&addr) = AF_INET;
+ AF(&mask) = AF_INET;
+ hack_restrict(RESTRICT_FLAGS, &addr,
+ &mask, mflags, flags, 0);
+ AF(&addr) = AF_INET6;
+ AF(&mask) = AF_INET6;
+ }
+
+ do {
+ hack_restrict(RESTRICT_FLAGS, &addr,
+ &mask, mflags, flags, 0);
+ if (pai != NULL &&
+ NULL != (pai = pai->ai_next)) {
+ INSIST(pai->ai_addr != NULL);
+ INSIST(sizeof(addr) >=
+ pai->ai_addrlen);
+ ZERO_SOCK(&addr);
+ memcpy(&addr, pai->ai_addr,
+ pai->ai_addrlen);
+ INSIST(AF_INET == AF(&addr) ||
+ AF_INET6 == AF(&addr));
+ SET_HOSTMASK(&mask, AF(&addr));
+ }
+ } while (pai != NULL);
+
+ if (ai_list != NULL)
+ freeaddrinfo(ai_list);
+ }
+}
+#endif /* !SIM */
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_access(
+ config_tree *ptree
+ )
+{
+ FREE_ATTR_VAL_FIFO(ptree->mru_opts);
+ FREE_ATTR_VAL_FIFO(ptree->discard_opts);
+ FREE_RESTRICT_FIFO(ptree->restrict_opts);
+}
+#endif /* FREE_CFG_T */
+
+
+static void
+config_rlimit(
+ config_tree *ptree
+ )
+{
+ attr_val * rlimit_av;
+
+ rlimit_av = HEAD_PFIFO(ptree->rlimit);
+ for (; rlimit_av != NULL; rlimit_av = rlimit_av->link) {
+ switch (rlimit_av->attr) {
+
+ default:
+ INSIST(0);
+ break;
+
+ case T_Memlock:
+ if (rlimit_av->value.i != 0) {
+#if defined(RLIMIT_MEMLOCK)
+ ntp_rlimit(RLIMIT_MEMLOCK,
+ (rlim_t)(rlimit_av->value.i * 1024 * 1024),
+ 1024 * 1024,
+ "MB");
+#else
+ /* STDERR as well would be fine... */
+ msyslog(LOG_WARNING, "'rlimit memlock' specified but is not available on this system.");
+#endif /* RLIMIT_MEMLOCK */
+ } else {
+ do_memlock = 0;
+ }
+ break;
+
+ case T_Stacksize:
+#if defined(RLIMIT_STACK)
+ ntp_rlimit(RLIMIT_STACK,
+ (rlim_t)(rlimit_av->value.i * 4096),
+ 4096,
+ "4k");
+#else
+ /* STDERR as well would be fine... */
+ msyslog(LOG_WARNING, "'rlimit stacksize' specified but is not available on this system.");
+#endif /* RLIMIT_STACK */
+ break;
+
+ case T_Filenum:
+#if defined(RLIMIT_NOFILE)
+ ntp_rlimit(RLIMIT_NOFILE,
+ (rlim_t)(rlimit_av->value.i),
+ 1,
+ "");
+#else
+ /* STDERR as well would be fine... */
+ msyslog(LOG_WARNING, "'rlimit filenum' specified but is not available on this system.");
+#endif /* RLIMIT_NOFILE */
+ break;
+
+ }
+ }
+}
+
+
+static void
+config_tinker(
+ config_tree *ptree
+ )
+{
+ attr_val * tinker;
+ int item;
+
+ item = -1; /* quiet warning */
+ tinker = HEAD_PFIFO(ptree->tinker);
+ for (; tinker != NULL; tinker = tinker->link) {
+ switch (tinker->attr) {
+
+ default:
+ INSIST(0);
+ break;
+
+ case T_Allan:
+ item = LOOP_ALLAN;
+ break;
+
+ case T_Dispersion:
+ item = LOOP_PHI;
+ break;
+
+ case T_Freq:
+ item = LOOP_FREQ;
+ break;
+
+ case T_Huffpuff:
+ item = LOOP_HUFFPUFF;
+ break;
+
+ case T_Panic:
+ item = LOOP_PANIC;
+ break;
+
+ case T_Step:
+ item = LOOP_MAX;
+ break;
+
+ case T_Stepout:
+ item = LOOP_MINSTEP;
+ break;
+
+ case T_Tick:
+ item = LOOP_TICK;
+ break;
+ }
+ loop_config(item, tinker->value.d);
+ }
+}
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_rlimit(
+ config_tree *ptree
+ )
+{
+ FREE_ATTR_VAL_FIFO(ptree->rlimit);
+}
+
+static void
+free_config_tinker(
+ config_tree *ptree
+ )
+{
+ FREE_ATTR_VAL_FIFO(ptree->tinker);
+}
+#endif /* FREE_CFG_T */
+
+
+/*
+ * config_nic_rules - apply interface listen/ignore/drop items
+ */
+#ifndef SIM
+static void
+config_nic_rules(
+ config_tree *ptree
+ )
+{
+ nic_rule_node * curr_node;
+ sockaddr_u addr;
+ nic_rule_match match_type;
+ nic_rule_action action;
+ char * if_name;
+ char * pchSlash;
+ int prefixlen;
+ int addrbits;
+
+ curr_node = HEAD_PFIFO(ptree->nic_rules);
+
+ if (curr_node != NULL
+ && (HAVE_OPT( NOVIRTUALIPS ) || HAVE_OPT( INTERFACE ))) {
+ msyslog(LOG_ERR,
+ "interface/nic rules are not allowed with --interface (-I) or --novirtualips (-L)%s",
+ (input_from_file) ? ", exiting" : "");
+ if (input_from_file)
+ exit(1);
+ else
+ return;
+ }
+
+ for (; curr_node != NULL; curr_node = curr_node->link) {
+ prefixlen = -1;
+ if_name = curr_node->if_name;
+ if (if_name != NULL)
+ if_name = estrdup(if_name);
+
+ switch (curr_node->match_class) {
+
+ default:
+ /*
+ * this assignment quiets a gcc "may be used
+ * uninitialized" warning and is here for no
+ * other reason.
+ */
+ match_type = MATCH_ALL;
+ INSIST(FALSE);
+ break;
+
+ case 0:
+ /*
+ * 0 is out of range for valid token T_...
+ * and in a nic_rules_node indicates the
+ * interface descriptor is either a name or
+ * address, stored in if_name in either case.
+ */
+ INSIST(if_name != NULL);
+ pchSlash = strchr(if_name, '/');
+ if (pchSlash != NULL)
+ *pchSlash = '\0';
+ if (is_ip_address(if_name, AF_UNSPEC, &addr)) {
+ match_type = MATCH_IFADDR;
+ if (pchSlash != NULL
+ && 1 == sscanf(pchSlash + 1, "%d",
+ &prefixlen)) {
+ addrbits = 8 *
+ SIZEOF_INADDR(AF(&addr));
+ prefixlen = max(-1, prefixlen);
+ prefixlen = min(prefixlen,
+ addrbits);
+ }
+ } else {
+ match_type = MATCH_IFNAME;
+ if (pchSlash != NULL)
+ *pchSlash = '/';
+ }
+ break;
+
+ case T_All:
+ match_type = MATCH_ALL;
+ break;
+
+ case T_Ipv4:
+ match_type = MATCH_IPV4;
+ break;
+
+ case T_Ipv6:
+ match_type = MATCH_IPV6;
+ break;
+
+ case T_Wildcard:
+ match_type = MATCH_WILDCARD;
+ break;
+ }
+
+ switch (curr_node->action) {
+
+ default:
+ /*
+ * this assignment quiets a gcc "may be used
+ * uninitialized" warning and is here for no
+ * other reason.
+ */
+ action = ACTION_LISTEN;
+ INSIST(FALSE);
+ break;
+
+ case T_Listen:
+ action = ACTION_LISTEN;
+ break;
+
+ case T_Ignore:
+ action = ACTION_IGNORE;
+ break;
+
+ case T_Drop:
+ action = ACTION_DROP;
+ break;
+ }
+
+ add_nic_rule(match_type, if_name, prefixlen,
+ action);
+ timer_interfacetimeout(current_time + 2);
+ if (if_name != NULL)
+ free(if_name);
+ }
+}
+#endif /* !SIM */
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_nic_rules(
+ config_tree *ptree
+ )
+{
+ nic_rule_node *curr_node;
+
+ if (ptree->nic_rules != NULL) {
+ for (;;) {
+ UNLINK_FIFO(curr_node, *ptree->nic_rules, link);
+ if (NULL == curr_node)
+ break;
+ free(curr_node->if_name);
+ free(curr_node);
+ }
+ free(ptree->nic_rules);
+ ptree->nic_rules = NULL;
+ }
+}
+#endif /* FREE_CFG_T */
+
+
+static void
+apply_enable_disable(
+ attr_val_fifo * fifo,
+ int enable
+ )
+{
+ attr_val *curr_flag;
+ int option;
+#ifdef BC_LIST_FRAMEWORK_NOT_YET_USED
+ bc_entry *pentry;
+#endif
+
+ for (curr_flag = HEAD_PFIFO(fifo);
+ curr_flag != NULL;
+ curr_flag = curr_flag->link) {
+
+ option = curr_flag->value.i;
+ switch (option) {
+
+ default:
+ msyslog(LOG_ERR,
+ "can not apply enable/disable token %d, unknown",
+ option);
+ break;
+
+ case T_Auth:
+ proto_config(PROTO_AUTHENTICATE, enable, 0., NULL);
+ break;
+
+ case T_Bclient:
+ proto_config(PROTO_BROADCLIENT, enable, 0., NULL);
+ break;
+
+ case T_Calibrate:
+ proto_config(PROTO_CAL, enable, 0., NULL);
+ break;
+
+ case T_Kernel:
+ proto_config(PROTO_KERNEL, enable, 0., NULL);
+ break;
+
+ case T_Monitor:
+ proto_config(PROTO_MONITOR, enable, 0., NULL);
+ break;
+
+ case T_Ntp:
+ proto_config(PROTO_NTP, enable, 0., NULL);
+ break;
+
+ case T_Mode7:
+ proto_config(PROTO_MODE7, enable, 0., NULL);
+ break;
+
+ case T_Stats:
+ proto_config(PROTO_FILEGEN, enable, 0., NULL);
+ break;
+
+#ifdef BC_LIST_FRAMEWORK_NOT_YET_USED
+ case T_Bc_bugXXXX:
+ pentry = bc_list;
+ while (pentry->token) {
+ if (pentry->token == option)
+ break;
+ pentry++;
+ }
+ if (!pentry->token) {
+ msyslog(LOG_ERR,
+ "compat token %d not in bc_list[]",
+ option);
+ continue;
+ }
+ pentry->enabled = enable;
+ break;
+#endif
+ }
+ }
+}
+
+
+static void
+config_system_opts(
+ config_tree *ptree
+ )
+{
+ apply_enable_disable(ptree->enable_opts, 1);
+ apply_enable_disable(ptree->disable_opts, 0);
+}
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_system_opts(
+ config_tree *ptree
+ )
+{
+ FREE_ATTR_VAL_FIFO(ptree->enable_opts);
+ FREE_ATTR_VAL_FIFO(ptree->disable_opts);
+}
+#endif /* FREE_CFG_T */
+
+
+static void
+config_logconfig(
+ config_tree *ptree
+ )
+{
+ attr_val * my_lc;
+
+ my_lc = HEAD_PFIFO(ptree->logconfig);
+ for (; my_lc != NULL; my_lc = my_lc->link) {
+ switch (my_lc->attr) {
+
+ case '+':
+ ntp_syslogmask |= get_logmask(my_lc->value.s);
+ break;
+
+ case '-':
+ ntp_syslogmask &= ~get_logmask(my_lc->value.s);
+ break;
+
+ case '=':
+ ntp_syslogmask = get_logmask(my_lc->value.s);
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+ }
+}
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_logconfig(
+ config_tree *ptree
+ )
+{
+ FREE_ATTR_VAL_FIFO(ptree->logconfig);
+}
+#endif /* FREE_CFG_T */
+
+
+#ifndef SIM
+static void
+config_phone(
+ config_tree *ptree
+ )
+{
+ int i;
+ string_node * sn;
+
+ i = 0;
+ sn = HEAD_PFIFO(ptree->phone);
+ for (; sn != NULL; sn = sn->link) {
+ /* need to leave array entry for NULL terminator */
+ if (i < COUNTOF(sys_phone) - 1) {
+ sys_phone[i++] = estrdup(sn->s);
+ sys_phone[i] = NULL;
+ } else {
+ msyslog(LOG_INFO,
+ "phone: Number of phone entries exceeds %lu. Ignoring phone %s...",
+ (u_long)(COUNTOF(sys_phone) - 1), sn->s);
+ }
+ }
+}
+#endif /* !SIM */
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_phone(
+ config_tree *ptree
+ )
+{
+ FREE_STRING_FIFO(ptree->phone);
+}
+#endif /* FREE_CFG_T */
+
+
+#ifndef SIM
+static void
+config_setvar(
+ config_tree *ptree
+ )
+{
+ setvar_node *my_node;
+ size_t varlen, vallen, octets;
+ char * str;
+
+ str = NULL;
+ my_node = HEAD_PFIFO(ptree->setvar);
+ for (; my_node != NULL; my_node = my_node->link) {
+ varlen = strlen(my_node->var);
+ vallen = strlen(my_node->val);
+ octets = varlen + vallen + 1 + 1;
+ str = erealloc(str, octets);
+ snprintf(str, octets, "%s=%s", my_node->var,
+ my_node->val);
+ set_sys_var(str, octets, (my_node->isdefault)
+ ? DEF
+ : 0);
+ }
+ if (str != NULL)
+ free(str);
+}
+#endif /* !SIM */
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_setvar(
+ config_tree *ptree
+ )
+{
+ FREE_SETVAR_FIFO(ptree->setvar);
+}
+#endif /* FREE_CFG_T */
+
+
+#ifndef SIM
+static void
+config_ttl(
+ config_tree *ptree
+ )
+{
+ int i = 0;
+ int_node *curr_ttl;
+
+ curr_ttl = HEAD_PFIFO(ptree->ttl);
+ for (; curr_ttl != NULL; curr_ttl = curr_ttl->link) {
+ if (i < COUNTOF(sys_ttl))
+ sys_ttl[i++] = (u_char)curr_ttl->i;
+ else
+ msyslog(LOG_INFO,
+ "ttl: Number of TTL entries exceeds %lu. Ignoring TTL %d...",
+ (u_long)COUNTOF(sys_ttl), curr_ttl->i);
+ }
+ sys_ttlmax = i - 1;
+}
+#endif /* !SIM */
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_ttl(
+ config_tree *ptree
+ )
+{
+ FREE_INT_FIFO(ptree->ttl);
+}
+#endif /* FREE_CFG_T */
+
+
+#ifndef SIM
+static void
+config_trap(
+ config_tree *ptree
+ )
+{
+ addr_opts_node *curr_trap;
+ attr_val *curr_opt;
+ sockaddr_u addr_sock;
+ sockaddr_u peeraddr;
+ struct interface *localaddr;
+ struct addrinfo hints;
+ char port_text[8];
+ settrap_parms *pstp;
+ u_short port;
+ int err_flag;
+ int rc;
+
+ /* silence warning about addr_sock potentially uninitialized */
+ AF(&addr_sock) = AF_UNSPEC;
+
+ curr_trap = HEAD_PFIFO(ptree->trap);
+ for (; curr_trap != NULL; curr_trap = curr_trap->link) {
+ err_flag = 0;
+ port = 0;
+ localaddr = NULL;
+
+ curr_opt = HEAD_PFIFO(curr_trap->options);
+ for (; curr_opt != NULL; curr_opt = curr_opt->link) {
+ if (T_Port == curr_opt->attr) {
+ if (curr_opt->value.i < 1
+ || curr_opt->value.i > USHRT_MAX) {
+ msyslog(LOG_ERR,
+ "invalid port number "
+ "%d, trap ignored",
+ curr_opt->value.i);
+ err_flag = 1;
+ }
+ port = (u_short)curr_opt->value.i;
+ }
+ else if (T_Interface == curr_opt->attr) {
+ /* Resolve the interface address */
+ ZERO_SOCK(&addr_sock);
+ if (getnetnum(curr_opt->value.s,
+ &addr_sock, 1, t_UNK) != 1) {
+ err_flag = 1;
+ break;
+ }
+
+ localaddr = findinterface(&addr_sock);
+
+ if (NULL == localaddr) {
+ msyslog(LOG_ERR,
+ "can't find interface with address %s",
+ stoa(&addr_sock));
+ err_flag = 1;
+ }
+ }
+ }
+
+ /* Now process the trap for the specified interface
+ * and port number
+ */
+ if (!err_flag) {
+ if (!port)
+ port = TRAPPORT;
+ ZERO_SOCK(&peeraddr);
+ rc = getnetnum(curr_trap->addr->address,
+ &peeraddr, 1, t_UNK);
+ if (1 != rc) {
+#ifndef WORKER
+ msyslog(LOG_ERR,
+ "trap: unable to use IP address %s.",
+ curr_trap->addr->address);
+#else /* WORKER follows */
+ /*
+ * save context and hand it off
+ * for name resolution.
+ */
+ ZERO(hints);
+ hints.ai_protocol = IPPROTO_UDP;
+ hints.ai_socktype = SOCK_DGRAM;
+ snprintf(port_text, sizeof(port_text),
+ "%u", port);
+ hints.ai_flags = Z_AI_NUMERICSERV;
+ pstp = emalloc_zero(sizeof(*pstp));
+ if (localaddr != NULL) {
+ hints.ai_family = localaddr->family;
+ pstp->ifaddr_nonnull = 1;
+ memcpy(&pstp->ifaddr,
+ &localaddr->sin,
+ sizeof(pstp->ifaddr));
+ }
+ rc = getaddrinfo_sometime(
+ curr_trap->addr->address,
+ port_text, &hints,
+ INITIAL_DNS_RETRY,
+ &trap_name_resolved,
+ pstp);
+ if (!rc)
+ msyslog(LOG_ERR,
+ "config_trap: getaddrinfo_sometime(%s,%s): %m",
+ curr_trap->addr->address,
+ port_text);
+#endif /* WORKER */
+ continue;
+ }
+ /* port is at same location for v4 and v6 */
+ SET_PORT(&peeraddr, port);
+
+ if (NULL == localaddr)
+ localaddr = ANY_INTERFACE_CHOOSE(&peeraddr);
+ else
+ AF(&peeraddr) = AF(&addr_sock);
+
+ if (!ctlsettrap(&peeraddr, localaddr, 0,
+ NTP_VERSION))
+ msyslog(LOG_ERR,
+ "set trap %s -> %s failed.",
+ latoa(localaddr),
+ stoa(&peeraddr));
+ }
+ }
+}
+
+
+/*
+ * trap_name_resolved()
+ *
+ * Callback invoked when config_trap()'s DNS lookup completes.
+ */
+# ifdef WORKER
+static void
+trap_name_resolved(
+ int rescode,
+ int gai_errno,
+ void * context,
+ const char * name,
+ const char * service,
+ const struct addrinfo * hints,
+ const struct addrinfo * res
+ )
+{
+ settrap_parms *pstp;
+ struct interface *localaddr;
+ sockaddr_u peeraddr;
+
+ (void)gai_errno;
+ (void)service;
+ (void)hints;
+ pstp = context;
+ if (rescode) {
+ msyslog(LOG_ERR,
+ "giving up resolving trap host %s: %s (%d)",
+ name, gai_strerror(rescode), rescode);
+ free(pstp);
+ return;
+ }
+ INSIST(sizeof(peeraddr) >= res->ai_addrlen);
+ ZERO(peeraddr);
+ memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
+ localaddr = NULL;
+ if (pstp->ifaddr_nonnull)
+ localaddr = findinterface(&pstp->ifaddr);
+ if (NULL == localaddr)
+ localaddr = ANY_INTERFACE_CHOOSE(&peeraddr);
+ if (!ctlsettrap(&peeraddr, localaddr, 0, NTP_VERSION))
+ msyslog(LOG_ERR, "set trap %s -> %s failed.",
+ latoa(localaddr), stoa(&peeraddr));
+ free(pstp);
+}
+# endif /* WORKER */
+#endif /* !SIM */
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_trap(
+ config_tree *ptree
+ )
+{
+ FREE_ADDR_OPTS_FIFO(ptree->trap);
+}
+#endif /* FREE_CFG_T */
+
+
+#ifndef SIM
+static void
+config_fudge(
+ config_tree *ptree
+ )
+{
+ addr_opts_node *curr_fudge;
+ attr_val *curr_opt;
+ sockaddr_u addr_sock;
+ address_node *addr_node;
+ struct refclockstat clock_stat;
+ int err_flag;
+
+ curr_fudge = HEAD_PFIFO(ptree->fudge);
+ for (; curr_fudge != NULL; curr_fudge = curr_fudge->link) {
+ err_flag = 0;
+
+ /* Get the reference clock address and
+ * ensure that it is sane
+ */
+ addr_node = curr_fudge->addr;
+ ZERO_SOCK(&addr_sock);
+ if (getnetnum(addr_node->address, &addr_sock, 1, t_REF)
+ != 1) {
+ err_flag = 1;
+ msyslog(LOG_ERR,
+ "unrecognized fudge reference clock address %s, line ignored",
+ stoa(&addr_sock));
+ }
+
+ if (!ISREFCLOCKADR(&addr_sock)) {
+ err_flag = 1;
+ msyslog(LOG_ERR,
+ "inappropriate address %s for the fudge command, line ignored",
+ stoa(&addr_sock));
+ }
+
+ /* Parse all the options to the fudge command */
+ ZERO(clock_stat);
+ curr_opt = HEAD_PFIFO(curr_fudge->options);
+ for (; curr_opt != NULL; curr_opt = curr_opt->link) {
+ switch (curr_opt->attr) {
+
+ case T_Time1:
+ clock_stat.haveflags |= CLK_HAVETIME1;
+ clock_stat.fudgetime1 = curr_opt->value.d;
+ break;
+
+ case T_Time2:
+ clock_stat.haveflags |= CLK_HAVETIME2;
+ clock_stat.fudgetime2 = curr_opt->value.d;
+ break;
+
+ case T_Stratum:
+ clock_stat.haveflags |= CLK_HAVEVAL1;
+ clock_stat.fudgeval1 = curr_opt->value.i;
+ break;
+
+ case T_Refid:
+ clock_stat.haveflags |= CLK_HAVEVAL2;
+ clock_stat.fudgeval2 = 0;
+ memcpy(&clock_stat.fudgeval2,
+ curr_opt->value.s,
+ min(strlen(curr_opt->value.s), 4));
+ break;
+
+ case T_Flag1:
+ clock_stat.haveflags |= CLK_HAVEFLAG1;
+ if (curr_opt->value.i)
+ clock_stat.flags |= CLK_FLAG1;
+ else
+ clock_stat.flags &= ~CLK_FLAG1;
+ break;
+
+ case T_Flag2:
+ clock_stat.haveflags |= CLK_HAVEFLAG2;
+ if (curr_opt->value.i)
+ clock_stat.flags |= CLK_FLAG2;
+ else
+ clock_stat.flags &= ~CLK_FLAG2;
+ break;
+
+ case T_Flag3:
+ clock_stat.haveflags |= CLK_HAVEFLAG3;
+ if (curr_opt->value.i)
+ clock_stat.flags |= CLK_FLAG3;
+ else
+ clock_stat.flags &= ~CLK_FLAG3;
+ break;
+
+ case T_Flag4:
+ clock_stat.haveflags |= CLK_HAVEFLAG4;
+ if (curr_opt->value.i)
+ clock_stat.flags |= CLK_FLAG4;
+ else
+ clock_stat.flags &= ~CLK_FLAG4;
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "Unexpected fudge flag %s (%d) for %s",
+ token_name(curr_opt->attr),
+ curr_opt->attr, stoa(&addr_sock));
+ exit(curr_opt->attr ? curr_opt->attr : 1);
+ }
+ }
+# ifdef REFCLOCK
+ if (!err_flag)
+ refclock_control(&addr_sock, &clock_stat, NULL);
+# endif
+ }
+}
+#endif /* !SIM */
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_fudge(
+ config_tree *ptree
+ )
+{
+ FREE_ADDR_OPTS_FIFO(ptree->fudge);
+}
+#endif /* FREE_CFG_T */
+
+
+static void
+config_vars(
+ config_tree *ptree
+ )
+{
+ attr_val *curr_var;
+ int len;
+
+ curr_var = HEAD_PFIFO(ptree->vars);
+ for (; curr_var != NULL; curr_var = curr_var->link) {
+ /* Determine which variable to set and set it */
+ switch (curr_var->attr) {
+
+ case T_Broadcastdelay:
+ proto_config(PROTO_BROADDELAY, 0, curr_var->value.d, NULL);
+ break;
+
+ case T_Tick:
+ loop_config(LOOP_TICK, curr_var->value.d);
+ break;
+
+ case T_Driftfile:
+ if ('\0' == curr_var->value.s[0]) {
+ stats_drift_file = 0;
+ msyslog(LOG_INFO, "config: driftfile disabled");
+ } else
+ stats_config(STATS_FREQ_FILE, curr_var->value.s);
+ break;
+
+ case T_Ident:
+ sys_ident = curr_var->value.s;
+ break;
+
+ case T_WanderThreshold: /* FALLTHROUGH */
+ case T_Nonvolatile:
+ wander_threshold = curr_var->value.d;
+ break;
+
+ case T_Leapfile:
+ stats_config(STATS_LEAP_FILE, curr_var->value.s);
+ break;
+
+ case T_Pidfile:
+ stats_config(STATS_PID_FILE, curr_var->value.s);
+ break;
+
+ case T_Logfile:
+ if (-1 == change_logfile(curr_var->value.s, TRUE))
+ msyslog(LOG_ERR,
+ "Cannot open logfile %s: %m",
+ curr_var->value.s);
+ break;
+
+ case T_Saveconfigdir:
+ if (saveconfigdir != NULL)
+ free(saveconfigdir);
+ len = strlen(curr_var->value.s);
+ if (0 == len) {
+ saveconfigdir = NULL;
+ } else if (DIR_SEP != curr_var->value.s[len - 1]
+#ifdef SYS_WINNT /* slash is also a dir. sep. on Windows */
+ && '/' != curr_var->value.s[len - 1]
+#endif
+ ) {
+ len++;
+ saveconfigdir = emalloc(len + 1);
+ snprintf(saveconfigdir, len + 1,
+ "%s%c",
+ curr_var->value.s,
+ DIR_SEP);
+ } else {
+ saveconfigdir = estrdup(
+ curr_var->value.s);
+ }
+ break;
+
+ case T_Automax:
+#ifdef AUTOKEY
+ sys_automax = curr_var->value.i;
+#endif
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "config_vars(): unexpected token %d",
+ curr_var->attr);
+ }
+ }
+}
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_vars(
+ config_tree *ptree
+ )
+{
+ FREE_ATTR_VAL_FIFO(ptree->vars);
+}
+#endif /* FREE_CFG_T */
+
+
+/* Define a function to check if a resolved address is sane.
+ * If yes, return 1, else return 0;
+ */
+static int
+is_sane_resolved_address(
+ sockaddr_u * peeraddr,
+ int hmode
+ )
+{
+ if (!ISREFCLOCKADR(peeraddr) && ISBADADR(peeraddr)) {
+ msyslog(LOG_ERR,
+ "attempt to configure invalid address %s",
+ stoa(peeraddr));
+ return 0;
+ }
+ /*
+ * Shouldn't be able to specify multicast
+ * address for server/peer!
+ * and unicast address for manycastclient!
+ */
+ if ((T_Server == hmode || T_Peer == hmode || T_Pool == hmode)
+ && IS_MCAST(peeraddr)) {
+ msyslog(LOG_ERR,
+ "attempt to configure invalid address %s",
+ stoa(peeraddr));
+ return 0;
+ }
+ if (T_Manycastclient == hmode && !IS_MCAST(peeraddr)) {
+ msyslog(LOG_ERR,
+ "attempt to configure invalid address %s",
+ stoa(peeraddr));
+ return 0;
+ }
+
+ if (IS_IPV6(peeraddr) && !ipv6_works)
+ return 0;
+
+ /* Ok, all tests succeeded, now we can return 1 */
+ return 1;
+}
+
+
+#ifndef SIM
+static u_char
+get_correct_host_mode(
+ int token
+ )
+{
+ switch (token) {
+
+ case T_Server:
+ case T_Pool:
+ case T_Manycastclient:
+ return MODE_CLIENT;
+
+ case T_Peer:
+ return MODE_ACTIVE;
+
+ case T_Broadcast:
+ return MODE_BROADCAST;
+
+ default:
+ return 0;
+ }
+}
+
+
+/*
+ * peerflag_bits() get config_peers() peerflags value from a
+ * peer_node's queue of flag attr_val entries.
+ */
+static int
+peerflag_bits(
+ peer_node *pn
+ )
+{
+ int peerflags;
+ attr_val *option;
+
+ /* translate peerflags options to bits */
+ peerflags = 0;
+ option = HEAD_PFIFO(pn->peerflags);
+ for (; option != NULL; option = option->link) {
+ switch (option->value.i) {
+
+ default:
+ INSIST(0);
+ break;
+
+ case T_Autokey:
+ peerflags |= FLAG_SKEY;
+ break;
+
+ case T_Burst:
+ peerflags |= FLAG_BURST;
+ break;
+
+ case T_Iburst:
+ peerflags |= FLAG_IBURST;
+ break;
+
+ case T_Noselect:
+ peerflags |= FLAG_NOSELECT;
+ break;
+
+ case T_Preempt:
+ peerflags |= FLAG_PREEMPT;
+ break;
+
+ case T_Prefer:
+ peerflags |= FLAG_PREFER;
+ break;
+
+ case T_True:
+ peerflags |= FLAG_TRUE;
+ break;
+
+ case T_Xleave:
+ peerflags |= FLAG_XLEAVE;
+ break;
+ }
+ }
+
+ return peerflags;
+}
+
+
+static void
+config_peers(
+ config_tree *ptree
+ )
+{
+ sockaddr_u peeraddr;
+ struct addrinfo hints;
+ peer_node * curr_peer;
+ peer_resolved_ctx * ctx;
+ u_char hmode;
+
+ /* add servers named on the command line with iburst implied */
+ for (;
+ cmdline_server_count > 0;
+ cmdline_server_count--, cmdline_servers++) {
+
+ ZERO_SOCK(&peeraddr);
+ /*
+ * If we have a numeric address, we can safely
+ * proceed in the mainline with it. Otherwise, hand
+ * the hostname off to the blocking child.
+ */
+ if (is_ip_address(*cmdline_servers, AF_UNSPEC,
+ &peeraddr)) {
+
+ SET_PORT(&peeraddr, NTP_PORT);
+ if (is_sane_resolved_address(&peeraddr,
+ T_Server))
+ peer_config(
+ &peeraddr,
+ NULL,
+ NULL,
+ MODE_CLIENT,
+ NTP_VERSION,
+ 0,
+ 0,
+ FLAG_IBURST,
+ 0,
+ 0,
+ NULL);
+ } else {
+ /* we have a hostname to resolve */
+# ifdef WORKER
+ ctx = emalloc_zero(sizeof(*ctx));
+ ctx->family = AF_UNSPEC;
+ ctx->host_mode = T_Server;
+ ctx->hmode = MODE_CLIENT;
+ ctx->version = NTP_VERSION;
+ ctx->flags = FLAG_IBURST;
+
+ ZERO(hints);
+ hints.ai_family = (u_short)ctx->family;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+
+ getaddrinfo_sometime(*cmdline_servers,
+ "ntp", &hints,
+ INITIAL_DNS_RETRY,
+ &peer_name_resolved,
+ (void *)ctx);
+# else /* !WORKER follows */
+ msyslog(LOG_ERR,
+ "hostname %s can not be used, please use IP address instead.",
+ curr_peer->addr->address);
+# endif
+ }
+ }
+
+ /* add associations from the configuration file */
+ curr_peer = HEAD_PFIFO(ptree->peers);
+ for (; curr_peer != NULL; curr_peer = curr_peer->link) {
+ ZERO_SOCK(&peeraddr);
+ /* Find the correct host-mode */
+ hmode = get_correct_host_mode(curr_peer->host_mode);
+ INSIST(hmode != 0);
+
+ if (T_Pool == curr_peer->host_mode) {
+ AF(&peeraddr) = curr_peer->addr->type;
+ peer_config(
+ &peeraddr,
+ curr_peer->addr->address,
+ NULL,
+ hmode,
+ curr_peer->peerversion,
+ curr_peer->minpoll,
+ curr_peer->maxpoll,
+ peerflag_bits(curr_peer),
+ curr_peer->ttl,
+ curr_peer->peerkey,
+ curr_peer->group);
+ /*
+ * If we have a numeric address, we can safely
+ * proceed in the mainline with it. Otherwise, hand
+ * the hostname off to the blocking child.
+ */
+ } else if (is_ip_address(curr_peer->addr->address,
+ curr_peer->addr->type, &peeraddr)) {
+
+ SET_PORT(&peeraddr, NTP_PORT);
+ if (is_sane_resolved_address(&peeraddr,
+ curr_peer->host_mode))
+ peer_config(
+ &peeraddr,
+ NULL,
+ NULL,
+ hmode,
+ curr_peer->peerversion,
+ curr_peer->minpoll,
+ curr_peer->maxpoll,
+ peerflag_bits(curr_peer),
+ curr_peer->ttl,
+ curr_peer->peerkey,
+ curr_peer->group);
+ } else {
+ /* we have a hostname to resolve */
+# ifdef WORKER
+ ctx = emalloc_zero(sizeof(*ctx));
+ ctx->family = curr_peer->addr->type;
+ ctx->host_mode = curr_peer->host_mode;
+ ctx->hmode = hmode;
+ ctx->version = curr_peer->peerversion;
+ ctx->minpoll = curr_peer->minpoll;
+ ctx->maxpoll = curr_peer->maxpoll;
+ ctx->flags = peerflag_bits(curr_peer);
+ ctx->ttl = curr_peer->ttl;
+ ctx->keyid = curr_peer->peerkey;
+ ctx->group = curr_peer->group;
+
+ ZERO(hints);
+ hints.ai_family = ctx->family;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+
+ getaddrinfo_sometime(curr_peer->addr->address,
+ "ntp", &hints,
+ INITIAL_DNS_RETRY,
+ &peer_name_resolved, ctx);
+# else /* !WORKER follows */
+ msyslog(LOG_ERR,
+ "hostname %s can not be used, please use IP address instead.",
+ curr_peer->addr->address);
+# endif
+ }
+ }
+}
+#endif /* !SIM */
+
+/*
+ * peer_name_resolved()
+ *
+ * Callback invoked when config_peers()'s DNS lookup completes.
+ */
+#ifdef WORKER
+static void
+peer_name_resolved(
+ int rescode,
+ int gai_errno,
+ void * context,
+ const char * name,
+ const char * service,
+ const struct addrinfo * hints,
+ const struct addrinfo * res
+ )
+{
+ sockaddr_u peeraddr;
+ peer_resolved_ctx * ctx;
+ u_short af;
+ const char * fam_spec;
+
+ (void)gai_errno;
+ (void)service;
+ (void)hints;
+ ctx = context;
+
+ DPRINTF(1, ("peer_name_resolved(%s) rescode %d\n", name, rescode));
+
+ if (rescode) {
+#ifndef IGNORE_DNS_ERRORS
+ free(ctx);
+ msyslog(LOG_ERR,
+ "giving up resolving host %s: %s (%d)",
+ name, gai_strerror(rescode), rescode);
+#else /* IGNORE_DNS_ERRORS follows */
+ getaddrinfo_sometime(name, service, hints,
+ INITIAL_DNS_RETRY,
+ &peer_name_resolved, context);
+#endif
+ return;
+ }
+
+ /* Loop to configure a single association */
+ for (; res != NULL; res = res->ai_next) {
+ memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
+ if (is_sane_resolved_address(&peeraddr,
+ ctx->host_mode)) {
+ NLOG(NLOG_SYSINFO) {
+ af = ctx->family;
+ fam_spec = (AF_INET6 == af)
+ ? "(AAAA) "
+ : (AF_INET == af)
+ ? "(A) "
+ : "";
+ msyslog(LOG_INFO, "DNS %s %s-> %s",
+ name, fam_spec,
+ stoa(&peeraddr));
+ }
+ peer_config(
+ &peeraddr,
+ NULL,
+ NULL,
+ ctx->hmode,
+ ctx->version,
+ ctx->minpoll,
+ ctx->maxpoll,
+ ctx->flags,
+ ctx->ttl,
+ ctx->keyid,
+ ctx->group);
+ break;
+ }
+ }
+ free(ctx);
+}
+#endif /* WORKER */
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_peers(
+ config_tree *ptree
+ )
+{
+ peer_node *curr_peer;
+
+ if (ptree->peers != NULL) {
+ for (;;) {
+ UNLINK_FIFO(curr_peer, *ptree->peers, link);
+ if (NULL == curr_peer)
+ break;
+ destroy_address_node(curr_peer->addr);
+ destroy_attr_val_fifo(curr_peer->peerflags);
+ free(curr_peer);
+ }
+ free(ptree->peers);
+ ptree->peers = NULL;
+ }
+}
+#endif /* FREE_CFG_T */
+
+
+#ifndef SIM
+static void
+config_unpeers(
+ config_tree *ptree
+ )
+{
+ sockaddr_u peeraddr;
+ struct addrinfo hints;
+ unpeer_node * curr_unpeer;
+ struct peer * p;
+ const char * name;
+ int rc;
+
+ curr_unpeer = HEAD_PFIFO(ptree->unpeers);
+ for (; curr_unpeer != NULL; curr_unpeer = curr_unpeer->link) {
+ /*
+ * Either AssocID will be zero, and we unpeer by name/
+ * address addr, or it is nonzero and addr NULL.
+ */
+ if (curr_unpeer->assocID) {
+ p = findpeerbyassoc(curr_unpeer->assocID);
+ if (p != NULL) {
+ msyslog(LOG_NOTICE, "unpeered %s",
+ stoa(&p->srcadr));
+ peer_clear(p, "GONE");
+ unpeer(p);
+ }
+
+ continue;
+ }
+
+ ZERO(peeraddr);
+ AF(&peeraddr) = curr_unpeer->addr->type;
+ name = curr_unpeer->addr->address;
+ rc = getnetnum(name, &peeraddr, 0, t_UNK);
+ /* Do we have a numeric address? */
+ if (rc > 0) {
+ DPRINTF(1, ("unpeer: searching for %s\n",
+ stoa(&peeraddr)));
+ p = findexistingpeer(&peeraddr, NULL, NULL, -1, 0);
+ if (p != NULL) {
+ msyslog(LOG_NOTICE, "unpeered %s",
+ stoa(&peeraddr));
+ peer_clear(p, "GONE");
+ unpeer(p);
+ }
+
+ continue;
+ }
+ /*
+ * It's not a numeric IP address, it's a hostname.
+ * Check for associations with a matching hostname.
+ */
+ for (p = peer_list; p != NULL; p = p->p_link)
+ if (p->hostname != NULL)
+ if (!strcasecmp(p->hostname, name))
+ break;
+ if (p != NULL) {
+ msyslog(LOG_NOTICE, "unpeered %s", name);
+ peer_clear(p, "GONE");
+ unpeer(p);
+ }
+ /* Resolve the hostname to address(es). */
+# ifdef WORKER
+ ZERO(hints);
+ hints.ai_family = curr_unpeer->addr->type;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ getaddrinfo_sometime(name, "ntp", &hints,
+ INITIAL_DNS_RETRY,
+ &unpeer_name_resolved, NULL);
+# else /* !WORKER follows */
+ msyslog(LOG_ERR,
+ "hostname %s can not be used, please use IP address instead.",
+ name);
+# endif
+ }
+}
+#endif /* !SIM */
+
+
+/*
+ * unpeer_name_resolved()
+ *
+ * Callback invoked when config_unpeers()'s DNS lookup completes.
+ */
+#ifdef WORKER
+static void
+unpeer_name_resolved(
+ int rescode,
+ int gai_errno,
+ void * context,
+ const char * name,
+ const char * service,
+ const struct addrinfo * hints,
+ const struct addrinfo * res
+ )
+{
+ sockaddr_u peeraddr;
+ struct peer * peer;
+ u_short af;
+ const char * fam_spec;
+
+ (void)context;
+ (void)hints;
+ DPRINTF(1, ("unpeer_name_resolved(%s) rescode %d\n", name, rescode));
+
+ if (rescode) {
+ msyslog(LOG_ERR, "giving up resolving unpeer %s: %s (%d)",
+ name, gai_strerror(rescode), rescode);
+ return;
+ }
+ /*
+ * Loop through the addresses found
+ */
+ for (; res != NULL; res = res->ai_next) {
+ INSIST(res->ai_addrlen <= sizeof(peeraddr));
+ memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
+ DPRINTF(1, ("unpeer: searching for peer %s\n",
+ stoa(&peeraddr)));
+ peer = findexistingpeer(&peeraddr, NULL, NULL, -1, 0);
+ if (peer != NULL) {
+ af = AF(&peeraddr);
+ fam_spec = (AF_INET6 == af)
+ ? "(AAAA) "
+ : (AF_INET == af)
+ ? "(A) "
+ : "";
+ msyslog(LOG_NOTICE, "unpeered %s %s-> %s", name,
+ fam_spec, stoa(&peeraddr));
+ peer_clear(peer, "GONE");
+ unpeer(peer);
+ }
+ }
+}
+#endif /* WORKER */
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_unpeers(
+ config_tree *ptree
+ )
+{
+ unpeer_node *curr_unpeer;
+
+ if (ptree->unpeers != NULL) {
+ for (;;) {
+ UNLINK_FIFO(curr_unpeer, *ptree->unpeers, link);
+ if (NULL == curr_unpeer)
+ break;
+ destroy_address_node(curr_unpeer->addr);
+ free(curr_unpeer);
+ }
+ free(ptree->unpeers);
+ }
+}
+#endif /* FREE_CFG_T */
+
+
+#ifndef SIM
+static void
+config_reset_counters(
+ config_tree *ptree
+ )
+{
+ int_node *counter_set;
+
+ for (counter_set = HEAD_PFIFO(ptree->reset_counters);
+ counter_set != NULL;
+ counter_set = counter_set->link) {
+ switch (counter_set->i) {
+ default:
+ DPRINTF(1, ("config_reset_counters %s (%d) invalid\n",
+ keyword(counter_set->i), counter_set->i));
+ break;
+
+ case T_Allpeers:
+ peer_all_reset();
+ break;
+
+ case T_Auth:
+ reset_auth_stats();
+ break;
+
+ case T_Ctl:
+ ctl_clr_stats();
+ break;
+
+ case T_Io:
+ io_clr_stats();
+ break;
+
+ case T_Mem:
+ peer_clr_stats();
+ break;
+
+ case T_Sys:
+ proto_clr_stats();
+ break;
+
+ case T_Timer:
+ timer_clr_stats();
+ break;
+ }
+ }
+}
+#endif /* !SIM */
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_reset_counters(
+ config_tree *ptree
+ )
+{
+ FREE_INT_FIFO(ptree->reset_counters);
+}
+#endif /* FREE_CFG_T */
+
+
+#ifdef SIM
+static void
+config_sim(
+ config_tree *ptree
+ )
+{
+ int i;
+ server_info *serv_info;
+ attr_val *init_stmt;
+ sim_node *sim_n;
+
+ /* Check if a simulate block was found in the configuration code.
+ * If not, return an error and exit
+ */
+ sim_n = HEAD_PFIFO(ptree->sim_details);
+ if (NULL == sim_n) {
+ fprintf(stderr, "ERROR!! I couldn't find a \"simulate\" block for configuring the simulator.\n");
+ fprintf(stderr, "\tCheck your configuration file.\n");
+ exit(1);
+ }
+
+ /* Process the initialization statements
+ * -------------------------------------
+ */
+ init_stmt = HEAD_PFIFO(sim_n->init_opts);
+ for (; init_stmt != NULL; init_stmt = init_stmt->link) {
+ switch(init_stmt->attr) {
+
+ case T_Beep_Delay:
+ simulation.beep_delay = init_stmt->value.d;
+ break;
+
+ case T_Sim_Duration:
+ simulation.end_time = init_stmt->value.d;
+ break;
+
+ default:
+ fprintf(stderr,
+ "Unknown simulator init token %d\n",
+ init_stmt->attr);
+ exit(1);
+ }
+ }
+
+ /* Process the server list
+ * -----------------------
+ */
+ simulation.num_of_servers = 0;
+ serv_info = HEAD_PFIFO(sim_n->servers);
+ for (; serv_info != NULL; serv_info = serv_info->link)
+ simulation.num_of_servers++;
+ simulation.servers = emalloc(simulation.num_of_servers *
+ sizeof(simulation.servers[0]));
+
+ i = 0;
+ serv_info = HEAD_PFIFO(sim_n->servers);
+ for (; serv_info != NULL; serv_info = serv_info->link) {
+ if (NULL == serv_info) {
+ fprintf(stderr, "Simulator server list is corrupt\n");
+ exit(1);
+ } else {
+ simulation.servers[i] = *serv_info;
+ simulation.servers[i].link = NULL;
+ i++;
+ }
+ }
+
+ printf("Creating server associations\n");
+ create_server_associations();
+ fprintf(stderr,"\tServer associations successfully created!!\n");
+}
+
+
+#ifdef FREE_CFG_T
+static void
+free_config_sim(
+ config_tree *ptree
+ )
+{
+ sim_node *sim_n;
+ server_info *serv_n;
+ script_info *script_n;
+
+ if (NULL == ptree->sim_details)
+ return;
+ sim_n = HEAD_PFIFO(ptree->sim_details);
+ free(ptree->sim_details);
+ ptree->sim_details = NULL;
+ if (NULL == sim_n)
+ return;
+
+ FREE_ATTR_VAL_FIFO(sim_n->init_opts);
+ for (;;) {
+ UNLINK_FIFO(serv_n, *sim_n->servers, link);
+ if (NULL == serv_n)
+ break;
+ free(serv_n->curr_script);
+ if (serv_n->script != NULL) {
+ for (;;) {
+ UNLINK_FIFO(script_n, *serv_n->script,
+ link);
+ if (script_n == NULL)
+ break;
+ free(script_n);
+ }
+ free(serv_n->script);
+ }
+ free(serv_n);
+ }
+ free(sim_n);
+}
+#endif /* FREE_CFG_T */
+#endif /* SIM */
+
+
+/* Define two different config functions. One for the daemon and the other for
+ * the simulator. The simulator ignores a lot of the standard ntpd configuration
+ * options
+ */
+#ifndef SIM
+static void
+config_ntpd(
+ config_tree *ptree
+ )
+{
+ config_nic_rules(ptree);
+ io_open_sockets();
+ config_monitor(ptree);
+ config_auth(ptree);
+ config_tos(ptree);
+ config_access(ptree);
+ config_tinker(ptree);
+ config_rlimit(ptree);
+ config_system_opts(ptree);
+ config_logconfig(ptree);
+ config_phone(ptree);
+ config_setvar(ptree);
+ config_ttl(ptree);
+ config_trap(ptree);
+ config_vars(ptree);
+ config_other_modes(ptree);
+ config_peers(ptree);
+ config_unpeers(ptree);
+ config_fudge(ptree);
+ config_reset_counters(ptree);
+
+#ifdef TEST_BLOCKING_WORKER
+ {
+ struct addrinfo hints;
+
+ ZERO(hints);
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ getaddrinfo_sometime("www.cnn.com", "ntp", &hints,
+ INITIAL_DNS_RETRY,
+ gai_test_callback, (void *)1);
+ hints.ai_family = AF_INET6;
+ getaddrinfo_sometime("ipv6.google.com", "ntp", &hints,
+ INITIAL_DNS_RETRY,
+ gai_test_callback, (void *)0x600);
+ }
+#endif
+}
+#endif /* !SIM */
+
+
+#ifdef SIM
+static void
+config_ntpdsim(
+ config_tree *ptree
+ )
+{
+ printf("Configuring Simulator...\n");
+ printf("Some ntpd-specific commands in the configuration file will be ignored.\n");
+
+ config_tos(ptree);
+ config_monitor(ptree);
+ config_tinker(ptree);
+ if (0)
+ config_rlimit(ptree); /* not needed for the simulator */
+ config_system_opts(ptree);
+ config_logconfig(ptree);
+ config_vars(ptree);
+ config_sim(ptree);
+}
+#endif /* SIM */
+
+
+/*
+ * config_remotely() - implements ntpd side of ntpq :config
+ */
+void
+config_remotely(
+ sockaddr_u * remote_addr
+ )
+{
+ struct FILE_INFO remote_cuckoo;
+ char origin[128];
+
+ snprintf(origin, sizeof(origin), "remote config from %s",
+ stoa(remote_addr));
+ ZERO(remote_cuckoo);
+ remote_cuckoo.fname = origin;
+ remote_cuckoo.line_no = 1;
+ remote_cuckoo.col_no = 1;
+ input_from_file = 0;
+
+ init_syntax_tree(&cfgt);
+ yyparse(&remote_cuckoo);
+ cfgt.source.attr = CONF_SOURCE_NTPQ;
+ cfgt.timestamp = time(NULL);
+ cfgt.source.value.s = estrdup(stoa(remote_addr));
+
+ DPRINTF(1, ("Finished Parsing!!\n"));
+
+ save_and_apply_config_tree();
+
+ input_from_file = 1;
+}
+
+
+/*
+ * getconfig() - process startup configuration file e.g /etc/ntp.conf
+ */
+void
+getconfig(
+ int argc,
+ char ** argv
+ )
+{
+ char line[256];
+
+#ifdef DEBUG
+ atexit(free_all_config_trees);
+#endif
+#ifndef SYS_WINNT
+ config_file = CONFIG_FILE;
+#else
+ temp = CONFIG_FILE;
+ if (!ExpandEnvironmentStringsA(temp, config_file_storage,
+ sizeof(config_file_storage))) {
+ msyslog(LOG_ERR, "ExpandEnvironmentStrings CONFIG_FILE failed: %m");
+ exit(1);
+ }
+ config_file = config_file_storage;
+
+ temp = ALT_CONFIG_FILE;
+ if (!ExpandEnvironmentStringsA(temp, alt_config_file_storage,
+ sizeof(alt_config_file_storage))) {
+ msyslog(LOG_ERR, "ExpandEnvironmentStrings ALT_CONFIG_FILE failed: %m");
+ exit(1);
+ }
+ alt_config_file = alt_config_file_storage;
+#endif /* SYS_WINNT */
+
+ /*
+ * install a non default variable with this daemon version
+ */
+ snprintf(line, sizeof(line), "daemon_version=\"%s\"", Version);
+ set_sys_var(line, strlen(line) + 1, RO);
+
+ /*
+ * Set up for the first time step to install a variable showing
+ * which syscall is being used to step.
+ */
+ set_tod_using = &ntpd_set_tod_using;
+
+ getCmdOpts(argc, argv);
+ init_syntax_tree(&cfgt);
+ curr_include_level = 0;
+ if (
+ (fp[curr_include_level] = F_OPEN(FindConfig(config_file), "r")) == NULL
+#ifdef HAVE_NETINFO
+ /* If there is no config_file, try NetInfo. */
+ && check_netinfo && !(config_netinfo = get_netinfo_config())
+#endif /* HAVE_NETINFO */
+ ) {
+ msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(config_file));
+#ifndef SYS_WINNT
+ io_open_sockets();
+
+ return;
+#else
+ /* Under WinNT try alternate_config_file name, first NTP.CONF, then NTP.INI */
+
+ if ((fp[curr_include_level] = F_OPEN(FindConfig(alt_config_file), "r")) == NULL) {
+
+ /*
+ * Broadcast clients can sometimes run without
+ * a configuration file.
+ */
+ msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(alt_config_file));
+ io_open_sockets();
+
+ return;
+ }
+ cfgt.source.value.s = estrdup(alt_config_file);
+#endif /* SYS_WINNT */
+ } else
+ cfgt.source.value.s = estrdup(config_file);
+
+
+ /*** BULK OF THE PARSER ***/
+#ifdef DEBUG
+ yydebug = !!(debug >= 5);
+#endif
+ yyparse(fp[curr_include_level]);
+
+ DPRINTF(1, ("Finished Parsing!!\n"));
+
+ cfgt.source.attr = CONF_SOURCE_FILE;
+ cfgt.timestamp = time(NULL);
+
+ save_and_apply_config_tree();
+
+ while (curr_include_level != -1)
+ FCLOSE(fp[curr_include_level--]);
+
+#ifdef HAVE_NETINFO
+ if (config_netinfo)
+ free_netinfo_config(config_netinfo);
+#endif /* HAVE_NETINFO */
+}
+
+
+void
+save_and_apply_config_tree(void)
+{
+ config_tree *ptree;
+#ifndef SAVECONFIG
+ config_tree *punlinked;
+#endif
+
+ /*
+ * Keep all the configuration trees applied since startup in
+ * a list that can be used to dump the configuration back to
+ * a text file.
+ */
+ ptree = emalloc(sizeof(*ptree));
+ memcpy(ptree, &cfgt, sizeof(*ptree));
+ ZERO(cfgt);
+
+ LINK_TAIL_SLIST(cfg_tree_history, ptree, link, config_tree);
+
+#ifdef SAVECONFIG
+ if (HAVE_OPT( SAVECONFIGQUIT )) {
+ FILE *dumpfile;
+ int err;
+ int dumpfailed;
+
+ dumpfile = fopen(OPT_ARG( SAVECONFIGQUIT ), "w");
+ if (NULL == dumpfile) {
+ err = errno;
+ mfprintf(stderr,
+ "can not create save file %s, error %d %m\n",
+ OPT_ARG(SAVECONFIGQUIT), err);
+ exit(err);
+ }
+
+ dumpfailed = dump_all_config_trees(dumpfile, 0);
+ if (dumpfailed)
+ fprintf(stderr,
+ "--saveconfigquit %s error %d\n",
+ OPT_ARG( SAVECONFIGQUIT ),
+ dumpfailed);
+ else
+ fprintf(stderr,
+ "configuration saved to %s\n",
+ OPT_ARG( SAVECONFIGQUIT ));
+
+ exit(dumpfailed);
+ }
+#endif /* SAVECONFIG */
+
+ /* The actual configuration done depends on whether we are configuring the
+ * simulator or the daemon. Perform a check and call the appropriate
+ * function as needed.
+ */
+
+#ifndef SIM
+ config_ntpd(ptree);
+#else
+ config_ntpdsim(ptree);
+#endif
+
+ /*
+ * With configure --disable-saveconfig, there's no use keeping
+ * the config tree around after application, so free it.
+ */
+#ifndef SAVECONFIG
+ UNLINK_SLIST(punlinked, cfg_tree_history, ptree, link,
+ config_tree);
+ INSIST(punlinked == ptree);
+ free_config_tree(ptree);
+#endif
+}
+
+
+static void
+ntpd_set_tod_using(
+ const char *which
+ )
+{
+ char line[128];
+
+ snprintf(line, sizeof(line), "settimeofday=\"%s\"", which);
+ set_sys_var(line, strlen(line) + 1, RO);
+}
+
+
+static char *
+normal_dtoa(
+ double d
+ )
+{
+ char * buf;
+ char * pch_e;
+ char * pch_nz;
+
+ LIB_GETBUF(buf);
+ snprintf(buf, LIB_BUFLENGTH, "%g", d);
+
+ /* use lowercase 'e', strip any leading zeroes in exponent */
+ pch_e = strchr(buf, 'e');
+ if (NULL == pch_e) {
+ pch_e = strchr(buf, 'E');
+ if (NULL == pch_e)
+ return buf;
+ *pch_e = 'e';
+ }
+ pch_e++;
+ if ('-' == *pch_e)
+ pch_e++;
+ pch_nz = pch_e;
+ while ('0' == *pch_nz)
+ pch_nz++;
+ if (pch_nz == pch_e)
+ return buf;
+ strlcpy(pch_e, pch_nz, LIB_BUFLENGTH - (pch_e - buf));
+
+ return buf;
+}
+
+
+/* FUNCTIONS COPIED FROM THE OLDER ntp_config.c
+ * --------------------------------------------
+ */
+
+
+/*
+ * get_pfxmatch - find value for prefixmatch
+ * and update char * accordingly
+ */
+static u_int32
+get_pfxmatch(
+ const char ** pstr,
+ struct masks * m
+ )
+{
+ while (m->name != NULL) {
+ if (strncmp(*pstr, m->name, strlen(m->name)) == 0) {
+ *pstr += strlen(m->name);
+ return m->mask;
+ } else {
+ m++;
+ }
+ }
+ return 0;
+}
+
+/*
+ * get_match - find logmask value
+ */
+static u_int32
+get_match(
+ const char * str,
+ struct masks * m
+ )
+{
+ while (m->name != NULL) {
+ if (strcmp(str, m->name) == 0)
+ return m->mask;
+ else
+ m++;
+ }
+ return 0;
+}
+
+/*
+ * get_logmask - build bitmask for ntp_syslogmask
+ */
+static u_int32
+get_logmask(
+ const char * str
+ )
+{
+ const char * t;
+ u_int32 offset;
+ u_int32 mask;
+
+ mask = get_match(str, logcfg_noclass_items);
+ if (mask != 0)
+ return mask;
+
+ t = str;
+ offset = get_pfxmatch(&t, logcfg_class);
+ mask = get_match(t, logcfg_class_items);
+
+ if (mask)
+ return mask << offset;
+ else
+ msyslog(LOG_ERR, "logconfig: '%s' not recognized - ignored",
+ str);
+
+ return 0;
+}
+
+
+#ifdef HAVE_NETINFO
+
+/*
+ * get_netinfo_config - find the nearest NetInfo domain with an ntp
+ * configuration and initialize the configuration state.
+ */
+static struct netinfo_config_state *
+get_netinfo_config(void)
+{
+ ni_status status;
+ void *domain;
+ ni_id config_dir;
+ struct netinfo_config_state *config;
+
+ if (ni_open(NULL, ".", &domain) != NI_OK) return NULL;
+
+ while ((status = ni_pathsearch(domain, &config_dir, NETINFO_CONFIG_DIR)) == NI_NODIR) {
+ void *next_domain;
+ if (ni_open(domain, "..", &next_domain) != NI_OK) {
+ ni_free(next_domain);
+ break;
+ }
+ ni_free(domain);
+ domain = next_domain;
+ }
+ if (status != NI_OK) {
+ ni_free(domain);
+ return NULL;
+ }
+
+ config = emalloc(sizeof(*config));
+ config->domain = domain;
+ config->config_dir = config_dir;
+ config->prop_index = 0;
+ config->val_index = 0;
+ config->val_list = NULL;
+
+ return config;
+}
+
+
+/*
+ * free_netinfo_config - release NetInfo configuration state
+ */
+static void
+free_netinfo_config(
+ struct netinfo_config_state *config
+ )
+{
+ ni_free(config->domain);
+ free(config);
+}
+
+
+/*
+ * gettokens_netinfo - return tokens from NetInfo
+ */
+static int
+gettokens_netinfo (
+ struct netinfo_config_state *config,
+ char **tokenlist,
+ int *ntokens
+ )
+{
+ int prop_index = config->prop_index;
+ int val_index = config->val_index;
+ char **val_list = config->val_list;
+
+ /*
+ * Iterate through each keyword and look for a property that matches it.
+ */
+ again:
+ if (!val_list) {
+ for (; prop_index < COUNTOF(keywords); prop_index++)
+ {
+ ni_namelist namelist;
+ struct keyword current_prop = keywords[prop_index];
+ ni_index index;
+
+ /*
+ * For each value associated in the property, we're going to return
+ * a separate line. We squirrel away the values in the config state
+ * so the next time through, we don't need to do this lookup.
+ */
+ NI_INIT(&namelist);
+ if (NI_OK == ni_lookupprop(config->domain,
+ &config->config_dir, current_prop.text,
+ &namelist)) {
+
+ /* Found the property, but it has no values */
+ if (namelist.ni_namelist_len == 0) continue;
+
+ config->val_list =
+ emalloc(sizeof(char*) *
+ (namelist.ni_namelist_len + 1));
+ val_list = config->val_list;
+
+ for (index = 0;
+ index < namelist.ni_namelist_len;
+ index++) {
+ char *value;
+
+ value = namelist.ni_namelist_val[index];
+ val_list[index] = estrdup(value);
+ }
+ val_list[index] = NULL;
+
+ break;
+ }
+ ni_namelist_free(&namelist);
+ }
+ config->prop_index = prop_index;
+ }
+
+ /* No list; we're done here. */
+ if (!val_list)
+ return CONFIG_UNKNOWN;
+
+ /*
+ * We have a list of values for the current property.
+ * Iterate through them and return each in order.
+ */
+ if (val_list[val_index]) {
+ int ntok = 1;
+ int quoted = 0;
+ char *tokens = val_list[val_index];
+
+ msyslog(LOG_INFO, "%s %s", keywords[prop_index].text, val_list[val_index]);
+
+ (const char*)tokenlist[0] = keywords[prop_index].text;
+ for (ntok = 1; ntok < MAXTOKENS; ntok++) {
+ tokenlist[ntok] = tokens;
+ while (!ISEOL(*tokens) && (!ISSPACE(*tokens) || quoted))
+ quoted ^= (*tokens++ == '"');
+
+ if (ISEOL(*tokens)) {
+ *tokens = '\0';
+ break;
+ } else { /* must be space */
+ *tokens++ = '\0';
+ while (ISSPACE(*tokens))
+ tokens++;
+ if (ISEOL(*tokens))
+ break;
+ }
+ }
+
+ if (ntok == MAXTOKENS) {
+ /* HMS: chomp it to lose the EOL? */
+ msyslog(LOG_ERR,
+ "gettokens_netinfo: too many tokens. Ignoring: %s",
+ tokens);
+ } else {
+ *ntokens = ntok + 1;
+ }
+
+ config->val_index++; /* HMS: Should this be in the 'else'? */
+
+ return keywords[prop_index].keytype;
+ }
+
+ /* We're done with the current property. */
+ prop_index = ++config->prop_index;
+
+ /* Free val_list and reset counters. */
+ for (val_index = 0; val_list[val_index]; val_index++)
+ free(val_list[val_index]);
+ free(val_list);
+ val_list = config->val_list = NULL;
+ val_index = config->val_index = 0;
+
+ goto again;
+}
+#endif /* HAVE_NETINFO */
+
+
+/*
+ * getnetnum - return a net number (this is crude, but careful)
+ *
+ * returns 1 for success, and mysteriously, 0 for most failures, and
+ * -1 if the address found is IPv6 and we believe IPv6 isn't working.
+ */
+#ifndef SIM
+static int
+getnetnum(
+ const char *num,
+ sockaddr_u *addr,
+ int complain,
+ enum gnn_type a_type /* ignored */
+ )
+{
+ NTP_REQUIRE(AF_UNSPEC == AF(addr) ||
+ AF_INET == AF(addr) ||
+ AF_INET6 == AF(addr));
+
+ if (!is_ip_address(num, AF(addr), addr))
+ return 0;
+
+ if (IS_IPV6(addr) && !ipv6_works)
+ return -1;
+
+# ifdef ISC_PLATFORM_HAVESALEN
+ addr->sa.sa_len = SIZEOF_SOCKADDR(AF(addr));
+# endif
+ SET_PORT(addr, NTP_PORT);
+
+ DPRINTF(2, ("getnetnum given %s, got %s\n", num, stoa(addr)));
+
+ return 1;
+}
+#endif /* !SIM */
+
+#if defined(HAVE_SETRLIMIT)
+void
+ntp_rlimit(
+ int rl_what,
+ rlim_t rl_value,
+ int rl_scale,
+ char * rl_sstr
+ )
+{
+ struct rlimit rl;
+
+ switch (rl_what) {
+# ifdef RLIMIT_MEMLOCK
+ case RLIMIT_MEMLOCK:
+ /*
+ * The default RLIMIT_MEMLOCK is very low on Linux systems.
+ * Unless we increase this limit malloc calls are likely to
+ * fail if we drop root privilege. To be useful the value
+ * has to be larger than the largest ntpd resident set size.
+ */
+ DPRINTF(2, ("ntp_rlimit: MEMLOCK: %d %s\n",
+ (int)(rl_value / rl_scale), rl_sstr));
+ rl.rlim_cur = rl.rlim_max = rl_value;
+ if (setrlimit(RLIMIT_MEMLOCK, &rl) == -1)
+ msyslog(LOG_ERR, "Cannot set RLIMIT_MEMLOCK: %m");
+ break;
+# endif /* RLIMIT_MEMLOCK */
+
+# ifdef RLIMIT_NOFILE
+ case RLIMIT_NOFILE:
+ /*
+ * For large systems the default file descriptor limit may
+ * not be enough.
+ */
+ DPRINTF(2, ("ntp_rlimit: NOFILE: %d %s\n",
+ (int)(rl_value / rl_scale), rl_sstr));
+ rl.rlim_cur = rl.rlim_max = rl_value;
+ if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
+ msyslog(LOG_ERR, "Cannot set RLIMIT_NOFILE: %m");
+ break;
+# endif /* RLIMIT_NOFILE */
+
+# ifdef RLIMIT_STACK
+ case RLIMIT_STACK:
+ /*
+ * Provide a way to set the stack limit to something
+ * smaller, so that we don't lock a lot of unused
+ * stack memory.
+ */
+ DPRINTF(2, ("ntp_rlimit: STACK: %d %s pages\n",
+ (int)(rl_value / rl_scale), rl_sstr));
+ if (-1 == getrlimit(RLIMIT_STACK, &rl)) {
+ msyslog(LOG_ERR, "getrlimit() failed: %m");
+ } else {
+ if (rl_value > rl.rlim_max) {
+ msyslog(LOG_WARNING,
+ "ntp_rlimit: using maximum allowed stack limit %lu instead of %lu.",
+ (u_long)rl.rlim_max,
+ (u_long)rl_value);
+ rl_value = rl.rlim_max;
+ }
+ if (-1 == setrlimit(RLIMIT_STACK, &rl)) {
+ msyslog(LOG_ERR,
+ "ntp_rlimit: Cannot adjust stack limit: %m");
+ }
+ }
+ break;
+# endif /* RLIMIT_STACK */
+
+ default:
+ INSIST(!"Unexpected setrlimit() case!");
+ break;
+ }
+}
+#endif /* HAVE_SETRLIMIT */
diff --git a/ntpd/ntp_control.c b/ntpd/ntp_control.c
new file mode 100644
index 0000000..266978e
--- /dev/null
+++ b/ntpd/ntp_control.c
@@ -0,0 +1,4942 @@
+/*
+ * ntp_control.c - respond to mode 6 control messages and send async
+ * traps. Provides service to ntpq and others.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/stat.h>
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#include <arpa/inet.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_control.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+#include "ntp_config.h"
+#include "ntp_crypto.h"
+#include "ntp_assert.h"
+#include "ntp_leapsec.h"
+#include "ntp_md5.h" /* provides OpenSSL digest API */
+#include "lib_strbuf.h"
+#ifdef KERNEL_PLL
+# include "ntp_syscall.h"
+#endif
+
+
+/*
+ * Structure to hold request procedure information
+ */
+
+struct ctl_proc {
+ short control_code; /* defined request code */
+#define NO_REQUEST (-1)
+ u_short flags; /* flags word */
+ /* Only one flag. Authentication required or not. */
+#define NOAUTH 0
+#define AUTH 1
+ void (*handler) (struct recvbuf *, int); /* handle request */
+};
+
+
+/*
+ * Request processing routines
+ */
+static void ctl_error (u_char);
+#ifdef REFCLOCK
+static u_short ctlclkstatus (struct refclockstat *);
+#endif
+static void ctl_flushpkt (u_char);
+static void ctl_putdata (const char *, unsigned int, int);
+static void ctl_putstr (const char *, const char *, size_t);
+static void ctl_putdblf (const char *, const char *, double);
+const char ctl_def_dbl_fmt[] = "%.3f";
+#define ctl_putdbl(tag, d) ctl_putdblf(tag, ctl_def_dbl_fmt, d)
+const char ctl_def_dbl6_fmt[] = "%.6f";
+#define ctl_putdbl6(tag, d) ctl_putdblf(tag, ctl_def_dbl6_fmt, d)
+const char ctl_def_sfp_fmt[] = "%g";
+#define ctl_putsfp(tag, sfp) ctl_putdblf(tag, ctl_def_sfp_fmt, \
+ FPTOD(sfp))
+static void ctl_putuint (const char *, u_long);
+static void ctl_puthex (const char *, u_long);
+static void ctl_putint (const char *, long);
+static void ctl_putts (const char *, l_fp *);
+static void ctl_putadr (const char *, u_int32,
+ sockaddr_u *);
+static void ctl_putrefid (const char *, u_int32);
+static void ctl_putarray (const char *, double *, int);
+static void ctl_putsys (int);
+static void ctl_putpeer (int, struct peer *);
+static void ctl_putfs (const char *, tstamp_t);
+#ifdef REFCLOCK
+static void ctl_putclock (int, struct refclockstat *, int);
+#endif /* REFCLOCK */
+static const struct ctl_var *ctl_getitem(const struct ctl_var *,
+ char **);
+static u_short count_var (const struct ctl_var *);
+static void control_unspec (struct recvbuf *, int);
+static void read_status (struct recvbuf *, int);
+static void read_sysvars (void);
+static void read_peervars (void);
+static void read_variables (struct recvbuf *, int);
+static void write_variables (struct recvbuf *, int);
+static void read_clockstatus(struct recvbuf *, int);
+static void write_clockstatus(struct recvbuf *, int);
+static void set_trap (struct recvbuf *, int);
+static void save_config (struct recvbuf *, int);
+static void configure (struct recvbuf *, int);
+static void send_mru_entry (mon_entry *, int);
+static void send_random_tag_value(int);
+static void read_mru_list (struct recvbuf *, int);
+static void send_ifstats_entry(endpt *, u_int);
+static void read_ifstats (struct recvbuf *);
+static void sockaddrs_from_restrict_u(sockaddr_u *, sockaddr_u *,
+ restrict_u *, int);
+static void send_restrict_entry(restrict_u *, int, u_int);
+static void send_restrict_list(restrict_u *, int, u_int *);
+static void read_addr_restrictions(struct recvbuf *);
+static void read_ordlist (struct recvbuf *, int);
+static u_int32 derive_nonce (sockaddr_u *, u_int32, u_int32);
+static void generate_nonce (struct recvbuf *, char *, size_t);
+static int validate_nonce (const char *, struct recvbuf *);
+static void req_nonce (struct recvbuf *, int);
+static void unset_trap (struct recvbuf *, int);
+static struct ctl_trap *ctlfindtrap(sockaddr_u *,
+ struct interface *);
+
+static const struct ctl_proc control_codes[] = {
+ { CTL_OP_UNSPEC, NOAUTH, control_unspec },
+ { CTL_OP_READSTAT, NOAUTH, read_status },
+ { CTL_OP_READVAR, NOAUTH, read_variables },
+ { CTL_OP_WRITEVAR, AUTH, write_variables },
+ { CTL_OP_READCLOCK, NOAUTH, read_clockstatus },
+ { CTL_OP_WRITECLOCK, NOAUTH, write_clockstatus },
+ { CTL_OP_SETTRAP, NOAUTH, set_trap },
+ { CTL_OP_CONFIGURE, AUTH, configure },
+ { CTL_OP_SAVECONFIG, AUTH, save_config },
+ { CTL_OP_READ_MRU, NOAUTH, read_mru_list },
+ { CTL_OP_READ_ORDLIST_A, AUTH, read_ordlist },
+ { CTL_OP_REQ_NONCE, NOAUTH, req_nonce },
+ { CTL_OP_UNSETTRAP, NOAUTH, unset_trap },
+ { NO_REQUEST, 0, NULL }
+};
+
+/*
+ * System variables we understand
+ */
+#define CS_LEAP 1
+#define CS_STRATUM 2
+#define CS_PRECISION 3
+#define CS_ROOTDELAY 4
+#define CS_ROOTDISPERSION 5
+#define CS_REFID 6
+#define CS_REFTIME 7
+#define CS_POLL 8
+#define CS_PEERID 9
+#define CS_OFFSET 10
+#define CS_DRIFT 11
+#define CS_JITTER 12
+#define CS_ERROR 13
+#define CS_CLOCK 14
+#define CS_PROCESSOR 15
+#define CS_SYSTEM 16
+#define CS_VERSION 17
+#define CS_STABIL 18
+#define CS_VARLIST 19
+#define CS_TAI 20
+#define CS_LEAPTAB 21
+#define CS_LEAPEND 22
+#define CS_RATE 23
+#define CS_MRU_ENABLED 24
+#define CS_MRU_DEPTH 25
+#define CS_MRU_DEEPEST 26
+#define CS_MRU_MINDEPTH 27
+#define CS_MRU_MAXAGE 28
+#define CS_MRU_MAXDEPTH 29
+#define CS_MRU_MEM 30
+#define CS_MRU_MAXMEM 31
+#define CS_SS_UPTIME 32
+#define CS_SS_RESET 33
+#define CS_SS_RECEIVED 34
+#define CS_SS_THISVER 35
+#define CS_SS_OLDVER 36
+#define CS_SS_BADFORMAT 37
+#define CS_SS_BADAUTH 38
+#define CS_SS_DECLINED 39
+#define CS_SS_RESTRICTED 40
+#define CS_SS_LIMITED 41
+#define CS_SS_KODSENT 42
+#define CS_SS_PROCESSED 43
+#define CS_PEERADR 44
+#define CS_PEERMODE 45
+#define CS_BCASTDELAY 46
+#define CS_AUTHDELAY 47
+#define CS_AUTHKEYS 48
+#define CS_AUTHFREEK 49
+#define CS_AUTHKLOOKUPS 50
+#define CS_AUTHKNOTFOUND 51
+#define CS_AUTHKUNCACHED 52
+#define CS_AUTHKEXPIRED 53
+#define CS_AUTHENCRYPTS 54
+#define CS_AUTHDECRYPTS 55
+#define CS_AUTHRESET 56
+#define CS_K_OFFSET 57
+#define CS_K_FREQ 58
+#define CS_K_MAXERR 59
+#define CS_K_ESTERR 60
+#define CS_K_STFLAGS 61
+#define CS_K_TIMECONST 62
+#define CS_K_PRECISION 63
+#define CS_K_FREQTOL 64
+#define CS_K_PPS_FREQ 65
+#define CS_K_PPS_STABIL 66
+#define CS_K_PPS_JITTER 67
+#define CS_K_PPS_CALIBDUR 68
+#define CS_K_PPS_CALIBS 69
+#define CS_K_PPS_CALIBERRS 70
+#define CS_K_PPS_JITEXC 71
+#define CS_K_PPS_STBEXC 72
+#define CS_KERN_FIRST CS_K_OFFSET
+#define CS_KERN_LAST CS_K_PPS_STBEXC
+#define CS_IOSTATS_RESET 73
+#define CS_TOTAL_RBUF 74
+#define CS_FREE_RBUF 75
+#define CS_USED_RBUF 76
+#define CS_RBUF_LOWATER 77
+#define CS_IO_DROPPED 78
+#define CS_IO_IGNORED 79
+#define CS_IO_RECEIVED 80
+#define CS_IO_SENT 81
+#define CS_IO_SENDFAILED 82
+#define CS_IO_WAKEUPS 83
+#define CS_IO_GOODWAKEUPS 84
+#define CS_TIMERSTATS_RESET 85
+#define CS_TIMER_OVERRUNS 86
+#define CS_TIMER_XMTS 87
+#define CS_FUZZ 88
+#define CS_MAX_NOAUTOKEY CS_FUZZ
+#ifdef AUTOKEY
+#define CS_FLAGS (1 + CS_MAX_NOAUTOKEY)
+#define CS_HOST (2 + CS_MAX_NOAUTOKEY)
+#define CS_PUBLIC (3 + CS_MAX_NOAUTOKEY)
+#define CS_CERTIF (4 + CS_MAX_NOAUTOKEY)
+#define CS_SIGNATURE (5 + CS_MAX_NOAUTOKEY)
+#define CS_REVTIME (6 + CS_MAX_NOAUTOKEY)
+#define CS_IDENT (7 + CS_MAX_NOAUTOKEY)
+#define CS_DIGEST (8 + CS_MAX_NOAUTOKEY)
+#define CS_MAXCODE CS_DIGEST
+#else /* !AUTOKEY follows */
+#define CS_MAXCODE CS_MAX_NOAUTOKEY
+#endif /* !AUTOKEY */
+
+/*
+ * Peer variables we understand
+ */
+#define CP_CONFIG 1
+#define CP_AUTHENABLE 2
+#define CP_AUTHENTIC 3
+#define CP_SRCADR 4
+#define CP_SRCPORT 5
+#define CP_DSTADR 6
+#define CP_DSTPORT 7
+#define CP_LEAP 8
+#define CP_HMODE 9
+#define CP_STRATUM 10
+#define CP_PPOLL 11
+#define CP_HPOLL 12
+#define CP_PRECISION 13
+#define CP_ROOTDELAY 14
+#define CP_ROOTDISPERSION 15
+#define CP_REFID 16
+#define CP_REFTIME 17
+#define CP_ORG 18
+#define CP_REC 19
+#define CP_XMT 20
+#define CP_REACH 21
+#define CP_UNREACH 22
+#define CP_TIMER 23
+#define CP_DELAY 24
+#define CP_OFFSET 25
+#define CP_JITTER 26
+#define CP_DISPERSION 27
+#define CP_KEYID 28
+#define CP_FILTDELAY 29
+#define CP_FILTOFFSET 30
+#define CP_PMODE 31
+#define CP_RECEIVED 32
+#define CP_SENT 33
+#define CP_FILTERROR 34
+#define CP_FLASH 35
+#define CP_TTL 36
+#define CP_VARLIST 37
+#define CP_IN 38
+#define CP_OUT 39
+#define CP_RATE 40
+#define CP_BIAS 41
+#define CP_SRCHOST 42
+#define CP_TIMEREC 43
+#define CP_TIMEREACH 44
+#define CP_BADAUTH 45
+#define CP_BOGUSORG 46
+#define CP_OLDPKT 47
+#define CP_SELDISP 48
+#define CP_SELBROKEN 49
+#define CP_CANDIDATE 50
+#define CP_MAX_NOAUTOKEY CP_CANDIDATE
+#ifdef AUTOKEY
+#define CP_FLAGS (1 + CP_MAX_NOAUTOKEY)
+#define CP_HOST (2 + CP_MAX_NOAUTOKEY)
+#define CP_VALID (3 + CP_MAX_NOAUTOKEY)
+#define CP_INITSEQ (4 + CP_MAX_NOAUTOKEY)
+#define CP_INITKEY (5 + CP_MAX_NOAUTOKEY)
+#define CP_INITTSP (6 + CP_MAX_NOAUTOKEY)
+#define CP_SIGNATURE (7 + CP_MAX_NOAUTOKEY)
+#define CP_IDENT (8 + CP_MAX_NOAUTOKEY)
+#define CP_MAXCODE CP_IDENT
+#else /* !AUTOKEY follows */
+#define CP_MAXCODE CP_MAX_NOAUTOKEY
+#endif /* !AUTOKEY */
+
+/*
+ * Clock variables we understand
+ */
+#define CC_TYPE 1
+#define CC_TIMECODE 2
+#define CC_POLL 3
+#define CC_NOREPLY 4
+#define CC_BADFORMAT 5
+#define CC_BADDATA 6
+#define CC_FUDGETIME1 7
+#define CC_FUDGETIME2 8
+#define CC_FUDGEVAL1 9
+#define CC_FUDGEVAL2 10
+#define CC_FLAGS 11
+#define CC_DEVICE 12
+#define CC_VARLIST 13
+#define CC_MAXCODE CC_VARLIST
+
+/*
+ * System variable values. The array can be indexed by the variable
+ * index to find the textual name.
+ */
+static const struct ctl_var sys_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CS_LEAP, RW, "leap" }, /* 1 */
+ { CS_STRATUM, RO, "stratum" }, /* 2 */
+ { CS_PRECISION, RO, "precision" }, /* 3 */
+ { CS_ROOTDELAY, RO, "rootdelay" }, /* 4 */
+ { CS_ROOTDISPERSION, RO, "rootdisp" }, /* 5 */
+ { CS_REFID, RO, "refid" }, /* 6 */
+ { CS_REFTIME, RO, "reftime" }, /* 7 */
+ { CS_POLL, RO, "tc" }, /* 8 */
+ { CS_PEERID, RO, "peer" }, /* 9 */
+ { CS_OFFSET, RO, "offset" }, /* 10 */
+ { CS_DRIFT, RO, "frequency" }, /* 11 */
+ { CS_JITTER, RO, "sys_jitter" }, /* 12 */
+ { CS_ERROR, RO, "clk_jitter" }, /* 13 */
+ { CS_CLOCK, RO, "clock" }, /* 14 */
+ { CS_PROCESSOR, RO, "processor" }, /* 15 */
+ { CS_SYSTEM, RO, "system" }, /* 16 */
+ { CS_VERSION, RO, "version" }, /* 17 */
+ { CS_STABIL, RO, "clk_wander" }, /* 18 */
+ { CS_VARLIST, RO, "sys_var_list" }, /* 19 */
+ { CS_TAI, RO, "tai" }, /* 20 */
+ { CS_LEAPTAB, RO, "leapsec" }, /* 21 */
+ { CS_LEAPEND, RO, "expire" }, /* 22 */
+ { CS_RATE, RO, "mintc" }, /* 23 */
+ { CS_MRU_ENABLED, RO, "mru_enabled" }, /* 24 */
+ { CS_MRU_DEPTH, RO, "mru_depth" }, /* 25 */
+ { CS_MRU_DEEPEST, RO, "mru_deepest" }, /* 26 */
+ { CS_MRU_MINDEPTH, RO, "mru_mindepth" }, /* 27 */
+ { CS_MRU_MAXAGE, RO, "mru_maxage" }, /* 28 */
+ { CS_MRU_MAXDEPTH, RO, "mru_maxdepth" }, /* 29 */
+ { CS_MRU_MEM, RO, "mru_mem" }, /* 30 */
+ { CS_MRU_MAXMEM, RO, "mru_maxmem" }, /* 31 */
+ { CS_SS_UPTIME, RO, "ss_uptime" }, /* 32 */
+ { CS_SS_RESET, RO, "ss_reset" }, /* 33 */
+ { CS_SS_RECEIVED, RO, "ss_received" }, /* 34 */
+ { CS_SS_THISVER, RO, "ss_thisver" }, /* 35 */
+ { CS_SS_OLDVER, RO, "ss_oldver" }, /* 36 */
+ { CS_SS_BADFORMAT, RO, "ss_badformat" }, /* 37 */
+ { CS_SS_BADAUTH, RO, "ss_badauth" }, /* 38 */
+ { CS_SS_DECLINED, RO, "ss_declined" }, /* 39 */
+ { CS_SS_RESTRICTED, RO, "ss_restricted" }, /* 40 */
+ { CS_SS_LIMITED, RO, "ss_limited" }, /* 41 */
+ { CS_SS_KODSENT, RO, "ss_kodsent" }, /* 42 */
+ { CS_SS_PROCESSED, RO, "ss_processed" }, /* 43 */
+ { CS_PEERADR, RO, "peeradr" }, /* 44 */
+ { CS_PEERMODE, RO, "peermode" }, /* 45 */
+ { CS_BCASTDELAY, RO, "bcastdelay" }, /* 46 */
+ { CS_AUTHDELAY, RO, "authdelay" }, /* 47 */
+ { CS_AUTHKEYS, RO, "authkeys" }, /* 48 */
+ { CS_AUTHFREEK, RO, "authfreek" }, /* 49 */
+ { CS_AUTHKLOOKUPS, RO, "authklookups" }, /* 50 */
+ { CS_AUTHKNOTFOUND, RO, "authknotfound" }, /* 51 */
+ { CS_AUTHKUNCACHED, RO, "authkuncached" }, /* 52 */
+ { CS_AUTHKEXPIRED, RO, "authkexpired" }, /* 53 */
+ { CS_AUTHENCRYPTS, RO, "authencrypts" }, /* 54 */
+ { CS_AUTHDECRYPTS, RO, "authdecrypts" }, /* 55 */
+ { CS_AUTHRESET, RO, "authreset" }, /* 56 */
+ { CS_K_OFFSET, RO, "koffset" }, /* 57 */
+ { CS_K_FREQ, RO, "kfreq" }, /* 58 */
+ { CS_K_MAXERR, RO, "kmaxerr" }, /* 59 */
+ { CS_K_ESTERR, RO, "kesterr" }, /* 60 */
+ { CS_K_STFLAGS, RO, "kstflags" }, /* 61 */
+ { CS_K_TIMECONST, RO, "ktimeconst" }, /* 62 */
+ { CS_K_PRECISION, RO, "kprecis" }, /* 63 */
+ { CS_K_FREQTOL, RO, "kfreqtol" }, /* 64 */
+ { CS_K_PPS_FREQ, RO, "kppsfreq" }, /* 65 */
+ { CS_K_PPS_STABIL, RO, "kppsstab" }, /* 66 */
+ { CS_K_PPS_JITTER, RO, "kppsjitter" }, /* 67 */
+ { CS_K_PPS_CALIBDUR, RO, "kppscalibdur" }, /* 68 */
+ { CS_K_PPS_CALIBS, RO, "kppscalibs" }, /* 69 */
+ { CS_K_PPS_CALIBERRS, RO, "kppscaliberrs" }, /* 70 */
+ { CS_K_PPS_JITEXC, RO, "kppsjitexc" }, /* 71 */
+ { CS_K_PPS_STBEXC, RO, "kppsstbexc" }, /* 72 */
+ { CS_IOSTATS_RESET, RO, "iostats_reset" }, /* 73 */
+ { CS_TOTAL_RBUF, RO, "total_rbuf" }, /* 74 */
+ { CS_FREE_RBUF, RO, "free_rbuf" }, /* 75 */
+ { CS_USED_RBUF, RO, "used_rbuf" }, /* 76 */
+ { CS_RBUF_LOWATER, RO, "rbuf_lowater" }, /* 77 */
+ { CS_IO_DROPPED, RO, "io_dropped" }, /* 78 */
+ { CS_IO_IGNORED, RO, "io_ignored" }, /* 79 */
+ { CS_IO_RECEIVED, RO, "io_received" }, /* 80 */
+ { CS_IO_SENT, RO, "io_sent" }, /* 81 */
+ { CS_IO_SENDFAILED, RO, "io_sendfailed" }, /* 82 */
+ { CS_IO_WAKEUPS, RO, "io_wakeups" }, /* 83 */
+ { CS_IO_GOODWAKEUPS, RO, "io_goodwakeups" }, /* 84 */
+ { CS_TIMERSTATS_RESET, RO, "timerstats_reset" },/* 85 */
+ { CS_TIMER_OVERRUNS, RO, "timer_overruns" }, /* 86 */
+ { CS_TIMER_XMTS, RO, "timer_xmts" }, /* 87 */
+ { CS_FUZZ, RO, "fuzz" }, /* 88 */
+#ifdef AUTOKEY
+ { CS_FLAGS, RO, "flags" }, /* 1 + CS_MAX_NOAUTOKEY */
+ { CS_HOST, RO, "host" }, /* 2 + CS_MAX_NOAUTOKEY */
+ { CS_PUBLIC, RO, "update" }, /* 3 + CS_MAX_NOAUTOKEY */
+ { CS_CERTIF, RO, "cert" }, /* 4 + CS_MAX_NOAUTOKEY */
+ { CS_SIGNATURE, RO, "signature" }, /* 5 + CS_MAX_NOAUTOKEY */
+ { CS_REVTIME, RO, "until" }, /* 6 + CS_MAX_NOAUTOKEY */
+ { CS_IDENT, RO, "ident" }, /* 7 + CS_MAX_NOAUTOKEY */
+ { CS_DIGEST, RO, "digest" }, /* 8 + CS_MAX_NOAUTOKEY */
+#endif /* AUTOKEY */
+ { 0, EOV, "" } /* 87/95 */
+};
+
+static struct ctl_var *ext_sys_var = NULL;
+
+/*
+ * System variables we print by default (in fuzzball order,
+ * more-or-less)
+ */
+static const u_char def_sys_var[] = {
+ CS_VERSION,
+ CS_PROCESSOR,
+ CS_SYSTEM,
+ CS_LEAP,
+ CS_STRATUM,
+ CS_PRECISION,
+ CS_ROOTDELAY,
+ CS_ROOTDISPERSION,
+ CS_REFID,
+ CS_REFTIME,
+ CS_CLOCK,
+ CS_PEERID,
+ CS_POLL,
+ CS_RATE,
+ CS_OFFSET,
+ CS_DRIFT,
+ CS_JITTER,
+ CS_ERROR,
+ CS_STABIL,
+ CS_TAI,
+ CS_LEAPTAB,
+ CS_LEAPEND,
+#ifdef AUTOKEY
+ CS_HOST,
+ CS_IDENT,
+ CS_FLAGS,
+ CS_DIGEST,
+ CS_SIGNATURE,
+ CS_PUBLIC,
+ CS_CERTIF,
+#endif /* AUTOKEY */
+ 0
+};
+
+
+/*
+ * Peer variable list
+ */
+static const struct ctl_var peer_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CP_CONFIG, RO, "config" }, /* 1 */
+ { CP_AUTHENABLE, RO, "authenable" }, /* 2 */
+ { CP_AUTHENTIC, RO, "authentic" }, /* 3 */
+ { CP_SRCADR, RO, "srcadr" }, /* 4 */
+ { CP_SRCPORT, RO, "srcport" }, /* 5 */
+ { CP_DSTADR, RO, "dstadr" }, /* 6 */
+ { CP_DSTPORT, RO, "dstport" }, /* 7 */
+ { CP_LEAP, RO, "leap" }, /* 8 */
+ { CP_HMODE, RO, "hmode" }, /* 9 */
+ { CP_STRATUM, RO, "stratum" }, /* 10 */
+ { CP_PPOLL, RO, "ppoll" }, /* 11 */
+ { CP_HPOLL, RO, "hpoll" }, /* 12 */
+ { CP_PRECISION, RO, "precision" }, /* 13 */
+ { CP_ROOTDELAY, RO, "rootdelay" }, /* 14 */
+ { CP_ROOTDISPERSION, RO, "rootdisp" }, /* 15 */
+ { CP_REFID, RO, "refid" }, /* 16 */
+ { CP_REFTIME, RO, "reftime" }, /* 17 */
+ { CP_ORG, RO, "org" }, /* 18 */
+ { CP_REC, RO, "rec" }, /* 19 */
+ { CP_XMT, RO, "xleave" }, /* 20 */
+ { CP_REACH, RO, "reach" }, /* 21 */
+ { CP_UNREACH, RO, "unreach" }, /* 22 */
+ { CP_TIMER, RO, "timer" }, /* 23 */
+ { CP_DELAY, RO, "delay" }, /* 24 */
+ { CP_OFFSET, RO, "offset" }, /* 25 */
+ { CP_JITTER, RO, "jitter" }, /* 26 */
+ { CP_DISPERSION, RO, "dispersion" }, /* 27 */
+ { CP_KEYID, RO, "keyid" }, /* 28 */
+ { CP_FILTDELAY, RO, "filtdelay" }, /* 29 */
+ { CP_FILTOFFSET, RO, "filtoffset" }, /* 30 */
+ { CP_PMODE, RO, "pmode" }, /* 31 */
+ { CP_RECEIVED, RO, "received"}, /* 32 */
+ { CP_SENT, RO, "sent" }, /* 33 */
+ { CP_FILTERROR, RO, "filtdisp" }, /* 34 */
+ { CP_FLASH, RO, "flash" }, /* 35 */
+ { CP_TTL, RO, "ttl" }, /* 36 */
+ { CP_VARLIST, RO, "peer_var_list" }, /* 37 */
+ { CP_IN, RO, "in" }, /* 38 */
+ { CP_OUT, RO, "out" }, /* 39 */
+ { CP_RATE, RO, "headway" }, /* 40 */
+ { CP_BIAS, RO, "bias" }, /* 41 */
+ { CP_SRCHOST, RO, "srchost" }, /* 42 */
+ { CP_TIMEREC, RO, "timerec" }, /* 43 */
+ { CP_TIMEREACH, RO, "timereach" }, /* 44 */
+ { CP_BADAUTH, RO, "badauth" }, /* 45 */
+ { CP_BOGUSORG, RO, "bogusorg" }, /* 46 */
+ { CP_OLDPKT, RO, "oldpkt" }, /* 47 */
+ { CP_SELDISP, RO, "seldisp" }, /* 48 */
+ { CP_SELBROKEN, RO, "selbroken" }, /* 49 */
+ { CP_CANDIDATE, RO, "candidate" }, /* 50 */
+#ifdef AUTOKEY
+ { CP_FLAGS, RO, "flags" }, /* 1 + CP_MAX_NOAUTOKEY */
+ { CP_HOST, RO, "host" }, /* 2 + CP_MAX_NOAUTOKEY */
+ { CP_VALID, RO, "valid" }, /* 3 + CP_MAX_NOAUTOKEY */
+ { CP_INITSEQ, RO, "initsequence" }, /* 4 + CP_MAX_NOAUTOKEY */
+ { CP_INITKEY, RO, "initkey" }, /* 5 + CP_MAX_NOAUTOKEY */
+ { CP_INITTSP, RO, "timestamp" }, /* 6 + CP_MAX_NOAUTOKEY */
+ { CP_SIGNATURE, RO, "signature" }, /* 7 + CP_MAX_NOAUTOKEY */
+ { CP_IDENT, RO, "ident" }, /* 8 + CP_MAX_NOAUTOKEY */
+#endif /* AUTOKEY */
+ { 0, EOV, "" } /* 50/58 */
+};
+
+
+/*
+ * Peer variables we print by default
+ */
+static const u_char def_peer_var[] = {
+ CP_SRCADR,
+ CP_SRCPORT,
+ CP_SRCHOST,
+ CP_DSTADR,
+ CP_DSTPORT,
+ CP_OUT,
+ CP_IN,
+ CP_LEAP,
+ CP_STRATUM,
+ CP_PRECISION,
+ CP_ROOTDELAY,
+ CP_ROOTDISPERSION,
+ CP_REFID,
+ CP_REFTIME,
+ CP_REC,
+ CP_REACH,
+ CP_UNREACH,
+ CP_HMODE,
+ CP_PMODE,
+ CP_HPOLL,
+ CP_PPOLL,
+ CP_RATE,
+ CP_FLASH,
+ CP_KEYID,
+ CP_TTL,
+ CP_OFFSET,
+ CP_DELAY,
+ CP_DISPERSION,
+ CP_JITTER,
+ CP_XMT,
+ CP_BIAS,
+ CP_FILTDELAY,
+ CP_FILTOFFSET,
+ CP_FILTERROR,
+#ifdef AUTOKEY
+ CP_HOST,
+ CP_FLAGS,
+ CP_SIGNATURE,
+ CP_VALID,
+ CP_INITSEQ,
+ CP_IDENT,
+#endif /* AUTOKEY */
+ 0
+};
+
+
+#ifdef REFCLOCK
+/*
+ * Clock variable list
+ */
+static const struct ctl_var clock_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CC_TYPE, RO, "type" }, /* 1 */
+ { CC_TIMECODE, RO, "timecode" }, /* 2 */
+ { CC_POLL, RO, "poll" }, /* 3 */
+ { CC_NOREPLY, RO, "noreply" }, /* 4 */
+ { CC_BADFORMAT, RO, "badformat" }, /* 5 */
+ { CC_BADDATA, RO, "baddata" }, /* 6 */
+ { CC_FUDGETIME1, RO, "fudgetime1" }, /* 7 */
+ { CC_FUDGETIME2, RO, "fudgetime2" }, /* 8 */
+ { CC_FUDGEVAL1, RO, "stratum" }, /* 9 */
+ { CC_FUDGEVAL2, RO, "refid" }, /* 10 */
+ { CC_FLAGS, RO, "flags" }, /* 11 */
+ { CC_DEVICE, RO, "device" }, /* 12 */
+ { CC_VARLIST, RO, "clock_var_list" }, /* 13 */
+ { 0, EOV, "" } /* 14 */
+};
+
+
+/*
+ * Clock variables printed by default
+ */
+static const u_char def_clock_var[] = {
+ CC_DEVICE,
+ CC_TYPE, /* won't be output if device = known */
+ CC_TIMECODE,
+ CC_POLL,
+ CC_NOREPLY,
+ CC_BADFORMAT,
+ CC_BADDATA,
+ CC_FUDGETIME1,
+ CC_FUDGETIME2,
+ CC_FUDGEVAL1,
+ CC_FUDGEVAL2,
+ CC_FLAGS,
+ 0
+};
+#endif
+
+/*
+ * MRU string constants shared by send_mru_entry() and read_mru_list().
+ */
+static const char addr_fmt[] = "addr.%d";
+static const char last_fmt[] = "last.%d";
+
+/*
+ * System and processor definitions.
+ */
+#ifndef HAVE_UNAME
+# ifndef STR_SYSTEM
+# define STR_SYSTEM "UNIX"
+# endif
+# ifndef STR_PROCESSOR
+# define STR_PROCESSOR "unknown"
+# endif
+
+static const char str_system[] = STR_SYSTEM;
+static const char str_processor[] = STR_PROCESSOR;
+#else
+# include <sys/utsname.h>
+static struct utsname utsnamebuf;
+#endif /* HAVE_UNAME */
+
+/*
+ * Trap structures. We only allow a few of these, and send a copy of
+ * each async message to each live one. Traps time out after an hour, it
+ * is up to the trap receipient to keep resetting it to avoid being
+ * timed out.
+ */
+/* ntp_request.c */
+struct ctl_trap ctl_traps[CTL_MAXTRAPS];
+int num_ctl_traps;
+
+/*
+ * Type bits, for ctlsettrap() call.
+ */
+#define TRAP_TYPE_CONFIG 0 /* used by configuration code */
+#define TRAP_TYPE_PRIO 1 /* priority trap */
+#define TRAP_TYPE_NONPRIO 2 /* nonpriority trap */
+
+
+/*
+ * List relating reference clock types to control message time sources.
+ * Index by the reference clock type. This list will only be used iff
+ * the reference clock driver doesn't set peer->sstclktype to something
+ * different than CTL_SST_TS_UNSPEC.
+ */
+#ifdef REFCLOCK
+static const u_char clocktypes[] = {
+ CTL_SST_TS_NTP, /* REFCLK_NONE (0) */
+ CTL_SST_TS_LOCAL, /* REFCLK_LOCALCLOCK (1) */
+ CTL_SST_TS_UHF, /* deprecated REFCLK_GPS_TRAK (2) */
+ CTL_SST_TS_HF, /* REFCLK_WWV_PST (3) */
+ CTL_SST_TS_LF, /* REFCLK_WWVB_SPECTRACOM (4) */
+ CTL_SST_TS_UHF, /* REFCLK_TRUETIME (5) */
+ CTL_SST_TS_UHF, /* REFCLK_IRIG_AUDIO (6) */
+ CTL_SST_TS_HF, /* REFCLK_CHU (7) */
+ CTL_SST_TS_LF, /* REFCLOCK_PARSE (default) (8) */
+ CTL_SST_TS_LF, /* REFCLK_GPS_MX4200 (9) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_AS2201 (10) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_ARBITER (11) */
+ CTL_SST_TS_UHF, /* REFCLK_IRIG_TPRO (12) */
+ CTL_SST_TS_ATOM, /* REFCLK_ATOM_LEITCH (13) */
+ CTL_SST_TS_LF, /* deprecated REFCLK_MSF_EES (14) */
+ CTL_SST_TS_NTP, /* not used (15) */
+ CTL_SST_TS_UHF, /* REFCLK_IRIG_BANCOMM (16) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_DATU (17) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_NIST_ACTS (18) */
+ CTL_SST_TS_HF, /* REFCLK_WWV_HEATH (19) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_NMEA (20) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_VME (21) */
+ CTL_SST_TS_ATOM, /* REFCLK_ATOM_PPS (22) */
+ CTL_SST_TS_NTP, /* not used (23) */
+ CTL_SST_TS_NTP, /* not used (24) */
+ CTL_SST_TS_NTP, /* not used (25) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_HP (26) */
+ CTL_SST_TS_LF, /* REFCLK_ARCRON_MSF (27) */
+ CTL_SST_TS_UHF, /* REFCLK_SHM (28) */
+ CTL_SST_TS_UHF, /* REFCLK_PALISADE (29) */
+ CTL_SST_TS_UHF, /* REFCLK_ONCORE (30) */
+ CTL_SST_TS_UHF, /* REFCLK_JUPITER (31) */
+ CTL_SST_TS_LF, /* REFCLK_CHRONOLOG (32) */
+ CTL_SST_TS_LF, /* REFCLK_DUMBCLOCK (33) */
+ CTL_SST_TS_LF, /* REFCLK_ULINK (34) */
+ CTL_SST_TS_LF, /* REFCLK_PCF (35) */
+ CTL_SST_TS_HF, /* REFCLK_WWV (36) */
+ CTL_SST_TS_LF, /* REFCLK_FG (37) */
+ CTL_SST_TS_UHF, /* REFCLK_HOPF_SERIAL (38) */
+ CTL_SST_TS_UHF, /* REFCLK_HOPF_PCI (39) */
+ CTL_SST_TS_LF, /* REFCLK_JJY (40) */
+ CTL_SST_TS_UHF, /* REFCLK_TT560 (41) */
+ CTL_SST_TS_UHF, /* REFCLK_ZYFER (42) */
+ CTL_SST_TS_UHF, /* REFCLK_RIPENCC (43) */
+ CTL_SST_TS_UHF, /* REFCLK_NEOCLOCK4X (44) */
+ CTL_SST_TS_UHF, /* REFCLK_TSYNCPCI (45) */
+ CTL_SST_TS_UHF /* REFCLK_GPSDJSON (46) */
+};
+#endif /* REFCLOCK */
+
+
+/*
+ * Keyid used for authenticating write requests.
+ */
+keyid_t ctl_auth_keyid;
+
+/*
+ * We keep track of the last error reported by the system internally
+ */
+static u_char ctl_sys_last_event;
+static u_char ctl_sys_num_events;
+
+
+/*
+ * Statistic counters to keep track of requests and responses.
+ */
+u_long ctltimereset; /* time stats reset */
+u_long numctlreq; /* number of requests we've received */
+u_long numctlbadpkts; /* number of bad control packets */
+u_long numctlresponses; /* number of resp packets sent with data */
+u_long numctlfrags; /* number of fragments sent */
+u_long numctlerrors; /* number of error responses sent */
+u_long numctltooshort; /* number of too short input packets */
+u_long numctlinputresp; /* number of responses on input */
+u_long numctlinputfrag; /* number of fragments on input */
+u_long numctlinputerr; /* number of input pkts with err bit set */
+u_long numctlbadoffset; /* number of input pkts with nonzero offset */
+u_long numctlbadversion; /* number of input pkts with unknown version */
+u_long numctldatatooshort; /* data too short for count */
+u_long numctlbadop; /* bad op code found in packet */
+u_long numasyncmsgs; /* number of async messages we've sent */
+
+/*
+ * Response packet used by these routines. Also some state information
+ * so that we can handle packet formatting within a common set of
+ * subroutines. Note we try to enter data in place whenever possible,
+ * but the need to set the more bit correctly means we occasionally
+ * use the extra buffer and copy.
+ */
+static struct ntp_control rpkt;
+static u_char res_version;
+static u_char res_opcode;
+static associd_t res_associd;
+static u_short res_frags; /* datagrams in this response */
+static int res_offset; /* offset of payload in response */
+static u_char * datapt;
+static u_char * dataend;
+static int datalinelen;
+static int datanotbinflag;
+static sockaddr_u *rmt_addr;
+static struct interface *lcl_inter;
+
+static u_char res_authenticate;
+static u_char res_authokay;
+static keyid_t res_keyid;
+
+#define MAXDATALINELEN (72)
+
+static u_char res_async; /* sending async trap response? */
+
+/*
+ * Pointers for saving state when decoding request packets
+ */
+static char *reqpt;
+static char *reqend;
+
+/*
+ * init_control - initialize request data
+ */
+void
+init_control(void)
+{
+ int i;
+
+#ifdef HAVE_UNAME
+ uname(&utsnamebuf);
+#endif /* HAVE_UNAME */
+
+ ctl_clr_stats();
+
+ ctl_auth_keyid = 0;
+ ctl_sys_last_event = EVNT_UNSPEC;
+ ctl_sys_num_events = 0;
+
+ num_ctl_traps = 0;
+ for (i = 0; i < COUNTOF(ctl_traps); i++)
+ ctl_traps[i].tr_flags = 0;
+}
+
+
+/*
+ * ctl_error - send an error response for the current request
+ */
+static void
+ctl_error(
+ u_char errcode
+ )
+{
+ int maclen;
+
+ numctlerrors++;
+ DPRINTF(3, ("sending control error %u\n", errcode));
+
+ /*
+ * Fill in the fields. We assume rpkt.sequence and rpkt.associd
+ * have already been filled in.
+ */
+ rpkt.r_m_e_op = CTL_RESPONSE | CTL_ERROR |
+ (res_opcode & CTL_OP_MASK);
+ rpkt.status = htons((errcode << 8) & 0xff00);
+ rpkt.count = 0;
+
+ /*
+ * send packet and bump counters
+ */
+ if (res_authenticate && sys_authenticate) {
+ maclen = authencrypt(res_keyid, (u_int32 *)&rpkt,
+ CTL_HEADER_LEN);
+ sendpkt(rmt_addr, lcl_inter, -2, (void *)&rpkt,
+ CTL_HEADER_LEN + maclen);
+ } else
+ sendpkt(rmt_addr, lcl_inter, -3, (void *)&rpkt,
+ CTL_HEADER_LEN);
+}
+
+/*
+ * save_config - Implements ntpq -c "saveconfig <filename>"
+ * Writes current configuration including any runtime
+ * changes by ntpq's :config or config-from-file
+ */
+void
+save_config(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ char reply[128];
+#ifdef SAVECONFIG
+ char filespec[128];
+ char filename[128];
+ char fullpath[512];
+ const char savedconfig_eq[] = "savedconfig=";
+ char savedconfig[sizeof(savedconfig_eq) + sizeof(filename)];
+ time_t now;
+ int fd;
+ FILE *fptr;
+#endif
+
+ if (RES_NOMODIFY & restrict_mask) {
+ snprintf(reply, sizeof(reply),
+ "saveconfig prohibited by restrict ... nomodify");
+ ctl_putdata(reply, strlen(reply), 0);
+ ctl_flushpkt(0);
+ NLOG(NLOG_SYSINFO)
+ msyslog(LOG_NOTICE,
+ "saveconfig from %s rejected due to nomodify restriction",
+ stoa(&rbufp->recv_srcadr));
+ sys_restricted++;
+ return;
+ }
+
+#ifdef SAVECONFIG
+ if (NULL == saveconfigdir) {
+ snprintf(reply, sizeof(reply),
+ "saveconfig prohibited, no saveconfigdir configured");
+ ctl_putdata(reply, strlen(reply), 0);
+ ctl_flushpkt(0);
+ NLOG(NLOG_SYSINFO)
+ msyslog(LOG_NOTICE,
+ "saveconfig from %s rejected, no saveconfigdir",
+ stoa(&rbufp->recv_srcadr));
+ return;
+ }
+
+ if (0 == reqend - reqpt)
+ return;
+
+ strlcpy(filespec, reqpt, sizeof(filespec));
+ time(&now);
+
+ /*
+ * allow timestamping of the saved config filename with
+ * strftime() format such as:
+ * ntpq -c "saveconfig ntp-%Y%m%d-%H%M%S.conf"
+ */
+ if (0 == strftime(filename, sizeof(filename), filespec,
+ localtime(&now)))
+ strlcpy(filename, filespec, sizeof(filename));
+
+ /*
+ * Conceptually we should be searching for DIRSEP in filename,
+ * however Windows actually recognizes both forward and
+ * backslashes as equivalent directory separators at the API
+ * level. On POSIX systems we could allow '\\' but such
+ * filenames are tricky to manipulate from a shell, so just
+ * reject both types of slashes on all platforms.
+ */
+ if (strchr(filename, '\\') || strchr(filename, '/')) {
+ snprintf(reply, sizeof(reply),
+ "saveconfig does not allow directory in filename");
+ ctl_putdata(reply, strlen(reply), 0);
+ ctl_flushpkt(0);
+ msyslog(LOG_NOTICE,
+ "saveconfig with path from %s rejected",
+ stoa(&rbufp->recv_srcadr));
+ return;
+ }
+
+ snprintf(fullpath, sizeof(fullpath), "%s%s",
+ saveconfigdir, filename);
+
+ fd = open(fullpath, O_CREAT | O_TRUNC | O_WRONLY,
+ S_IRUSR | S_IWUSR);
+ if (-1 == fd)
+ fptr = NULL;
+ else
+ fptr = fdopen(fd, "w");
+
+ if (NULL == fptr || -1 == dump_all_config_trees(fptr, 1)) {
+ snprintf(reply, sizeof(reply),
+ "Unable to save configuration to file %s",
+ filename);
+ msyslog(LOG_ERR,
+ "saveconfig %s from %s failed", filename,
+ stoa(&rbufp->recv_srcadr));
+ } else {
+ snprintf(reply, sizeof(reply),
+ "Configuration saved to %s", filename);
+ msyslog(LOG_NOTICE,
+ "Configuration saved to %s (requested by %s)",
+ fullpath, stoa(&rbufp->recv_srcadr));
+ /*
+ * save the output filename in system variable
+ * savedconfig, retrieved with:
+ * ntpq -c "rv 0 savedconfig"
+ */
+ snprintf(savedconfig, sizeof(savedconfig), "%s%s",
+ savedconfig_eq, filename);
+ set_sys_var(savedconfig, strlen(savedconfig) + 1, RO);
+ }
+
+ if (NULL != fptr)
+ fclose(fptr);
+#else /* !SAVECONFIG follows */
+ snprintf(reply, sizeof(reply),
+ "saveconfig unavailable, configured with --disable-saveconfig");
+#endif
+
+ ctl_putdata(reply, strlen(reply), 0);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * process_control - process an incoming control message
+ */
+void
+process_control(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ struct ntp_control *pkt;
+ int req_count;
+ int req_data;
+ const struct ctl_proc *cc;
+ keyid_t *pkid;
+ int properlen;
+ int maclen;
+
+ DPRINTF(3, ("in process_control()\n"));
+
+ /*
+ * Save the addresses for error responses
+ */
+ numctlreq++;
+ rmt_addr = &rbufp->recv_srcadr;
+ lcl_inter = rbufp->dstadr;
+ pkt = (struct ntp_control *)&rbufp->recv_pkt;
+
+ /*
+ * If the length is less than required for the header, or
+ * it is a response or a fragment, ignore this.
+ */
+ if (rbufp->recv_length < CTL_HEADER_LEN
+ || (CTL_RESPONSE | CTL_MORE | CTL_ERROR) & pkt->r_m_e_op
+ || pkt->offset != 0) {
+ DPRINTF(1, ("invalid format in control packet\n"));
+ if (rbufp->recv_length < CTL_HEADER_LEN)
+ numctltooshort++;
+ if (CTL_RESPONSE & pkt->r_m_e_op)
+ numctlinputresp++;
+ if (CTL_MORE & pkt->r_m_e_op)
+ numctlinputfrag++;
+ if (CTL_ERROR & pkt->r_m_e_op)
+ numctlinputerr++;
+ if (pkt->offset != 0)
+ numctlbadoffset++;
+ return;
+ }
+ res_version = PKT_VERSION(pkt->li_vn_mode);
+ if (res_version > NTP_VERSION || res_version < NTP_OLDVERSION) {
+ DPRINTF(1, ("unknown version %d in control packet\n",
+ res_version));
+ numctlbadversion++;
+ return;
+ }
+
+ /*
+ * Pull enough data from the packet to make intelligent
+ * responses
+ */
+ rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, res_version,
+ MODE_CONTROL);
+ res_opcode = pkt->r_m_e_op;
+ rpkt.sequence = pkt->sequence;
+ rpkt.associd = pkt->associd;
+ rpkt.status = 0;
+ res_frags = 1;
+ res_offset = 0;
+ res_associd = htons(pkt->associd);
+ res_async = FALSE;
+ res_authenticate = FALSE;
+ res_keyid = 0;
+ res_authokay = FALSE;
+ req_count = (int)ntohs(pkt->count);
+ datanotbinflag = FALSE;
+ datalinelen = 0;
+ datapt = rpkt.u.data;
+ dataend = &rpkt.u.data[CTL_MAX_DATA_LEN];
+
+ if ((rbufp->recv_length & 0x3) != 0)
+ DPRINTF(3, ("Control packet length %d unrounded\n",
+ rbufp->recv_length));
+
+ /*
+ * We're set up now. Make sure we've got at least enough
+ * incoming data space to match the count.
+ */
+ req_data = rbufp->recv_length - CTL_HEADER_LEN;
+ if (req_data < req_count || rbufp->recv_length & 0x3) {
+ ctl_error(CERR_BADFMT);
+ numctldatatooshort++;
+ return;
+ }
+
+ properlen = req_count + CTL_HEADER_LEN;
+ /* round up proper len to a 8 octet boundary */
+
+ properlen = (properlen + 7) & ~7;
+ maclen = rbufp->recv_length - properlen;
+ if ((rbufp->recv_length & 3) == 0 &&
+ maclen >= MIN_MAC_LEN && maclen <= MAX_MAC_LEN &&
+ sys_authenticate) {
+ res_authenticate = TRUE;
+ pkid = (void *)((char *)pkt + properlen);
+ res_keyid = ntohl(*pkid);
+ DPRINTF(3, ("recv_len %d, properlen %d, wants auth with keyid %08x, MAC length=%d\n",
+ rbufp->recv_length, properlen, res_keyid,
+ maclen));
+
+ if (!authistrusted(res_keyid))
+ DPRINTF(3, ("invalid keyid %08x\n", res_keyid));
+ else if (authdecrypt(res_keyid, (u_int32 *)pkt,
+ rbufp->recv_length - maclen,
+ maclen)) {
+ res_authokay = TRUE;
+ DPRINTF(3, ("authenticated okay\n"));
+ } else {
+ res_keyid = 0;
+ DPRINTF(3, ("authentication failed\n"));
+ }
+ }
+
+ /*
+ * Set up translate pointers
+ */
+ reqpt = (char *)pkt->u.data;
+ reqend = reqpt + req_count;
+
+ /*
+ * Look for the opcode processor
+ */
+ for (cc = control_codes; cc->control_code != NO_REQUEST; cc++) {
+ if (cc->control_code == res_opcode) {
+ DPRINTF(3, ("opcode %d, found command handler\n",
+ res_opcode));
+ if (cc->flags == AUTH
+ && (!res_authokay
+ || res_keyid != ctl_auth_keyid)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ (cc->handler)(rbufp, restrict_mask);
+ return;
+ }
+ }
+
+ /*
+ * Can't find this one, return an error.
+ */
+ numctlbadop++;
+ ctl_error(CERR_BADOP);
+ return;
+}
+
+
+/*
+ * ctlpeerstatus - return a status word for this peer
+ */
+u_short
+ctlpeerstatus(
+ register struct peer *p
+ )
+{
+ u_short status;
+
+ status = p->status;
+ if (FLAG_CONFIG & p->flags)
+ status |= CTL_PST_CONFIG;
+ if (p->keyid)
+ status |= CTL_PST_AUTHENABLE;
+ if (FLAG_AUTHENTIC & p->flags)
+ status |= CTL_PST_AUTHENTIC;
+ if (p->reach)
+ status |= CTL_PST_REACH;
+ if (MDF_TXONLY_MASK & p->cast_flags)
+ status |= CTL_PST_BCAST;
+
+ return CTL_PEER_STATUS(status, p->num_events, p->last_event);
+}
+
+
+/*
+ * ctlclkstatus - return a status word for this clock
+ */
+#ifdef REFCLOCK
+static u_short
+ctlclkstatus(
+ struct refclockstat *pcs
+ )
+{
+ return CTL_PEER_STATUS(0, pcs->lastevent, pcs->currentstatus);
+}
+#endif
+
+
+/*
+ * ctlsysstatus - return the system status word
+ */
+u_short
+ctlsysstatus(void)
+{
+ register u_char this_clock;
+
+ this_clock = CTL_SST_TS_UNSPEC;
+#ifdef REFCLOCK
+ if (sys_peer != NULL) {
+ if (CTL_SST_TS_UNSPEC != sys_peer->sstclktype)
+ this_clock = sys_peer->sstclktype;
+ else if (sys_peer->refclktype < COUNTOF(clocktypes))
+ this_clock = clocktypes[sys_peer->refclktype];
+ }
+#else /* REFCLOCK */
+ if (sys_peer != 0)
+ this_clock = CTL_SST_TS_NTP;
+#endif /* REFCLOCK */
+ return CTL_SYS_STATUS(sys_leap, this_clock, ctl_sys_num_events,
+ ctl_sys_last_event);
+}
+
+
+/*
+ * ctl_flushpkt - write out the current packet and prepare
+ * another if necessary.
+ */
+static void
+ctl_flushpkt(
+ u_char more
+ )
+{
+ int i;
+ int dlen;
+ int sendlen;
+ int maclen;
+ int totlen;
+ keyid_t keyid;
+
+ dlen = datapt - rpkt.u.data;
+ if (!more && datanotbinflag && dlen + 2 < CTL_MAX_DATA_LEN) {
+ /*
+ * Big hack, output a trailing \r\n
+ */
+ *datapt++ = '\r';
+ *datapt++ = '\n';
+ dlen += 2;
+ }
+ sendlen = dlen + CTL_HEADER_LEN;
+
+ /*
+ * Pad to a multiple of 32 bits
+ */
+ while (sendlen & 0x3) {
+ *datapt++ = '\0';
+ sendlen++;
+ }
+
+ /*
+ * Fill in the packet with the current info
+ */
+ rpkt.r_m_e_op = CTL_RESPONSE | more |
+ (res_opcode & CTL_OP_MASK);
+ rpkt.count = htons((u_short)dlen);
+ rpkt.offset = htons((u_short)res_offset);
+ if (res_async) {
+ for (i = 0; i < COUNTOF(ctl_traps); i++) {
+ if (TRAP_INUSE & ctl_traps[i].tr_flags) {
+ rpkt.li_vn_mode =
+ PKT_LI_VN_MODE(
+ sys_leap,
+ ctl_traps[i].tr_version,
+ MODE_CONTROL);
+ rpkt.sequence =
+ htons(ctl_traps[i].tr_sequence);
+ sendpkt(&ctl_traps[i].tr_addr,
+ ctl_traps[i].tr_localaddr, -4,
+ (struct pkt *)&rpkt, sendlen);
+ if (!more)
+ ctl_traps[i].tr_sequence++;
+ numasyncmsgs++;
+ }
+ }
+ } else {
+ if (res_authenticate && sys_authenticate) {
+ totlen = sendlen;
+ /*
+ * If we are going to authenticate, then there
+ * is an additional requirement that the MAC
+ * begin on a 64 bit boundary.
+ */
+ while (totlen & 7) {
+ *datapt++ = '\0';
+ totlen++;
+ }
+ keyid = htonl(res_keyid);
+ memcpy(datapt, &keyid, sizeof(keyid));
+ maclen = authencrypt(res_keyid,
+ (u_int32 *)&rpkt, totlen);
+ sendpkt(rmt_addr, lcl_inter, -5,
+ (struct pkt *)&rpkt, totlen + maclen);
+ } else {
+ sendpkt(rmt_addr, lcl_inter, -6,
+ (struct pkt *)&rpkt, sendlen);
+ }
+ if (more)
+ numctlfrags++;
+ else
+ numctlresponses++;
+ }
+
+ /*
+ * Set us up for another go around.
+ */
+ res_frags++;
+ res_offset += dlen;
+ datapt = rpkt.u.data;
+}
+
+
+/*
+ * ctl_putdata - write data into the packet, fragmenting and starting
+ * another if this one is full.
+ */
+static void
+ctl_putdata(
+ const char *dp,
+ unsigned int dlen,
+ int bin /* set to 1 when data is binary */
+ )
+{
+ int overhead;
+
+ overhead = 0;
+ if (!bin) {
+ datanotbinflag = TRUE;
+ overhead = 3;
+ if (datapt != rpkt.u.data) {
+ *datapt++ = ',';
+ datalinelen++;
+ if ((dlen + datalinelen + 1) >= MAXDATALINELEN) {
+ *datapt++ = '\r';
+ *datapt++ = '\n';
+ datalinelen = 0;
+ } else {
+ *datapt++ = ' ';
+ datalinelen++;
+ }
+ }
+ }
+
+ /*
+ * Save room for trailing junk
+ */
+ if (dlen + overhead + datapt > dataend) {
+ /*
+ * Not enough room in this one, flush it out.
+ */
+ ctl_flushpkt(CTL_MORE);
+ }
+ memcpy(datapt, dp, dlen);
+ datapt += dlen;
+ datalinelen += dlen;
+}
+
+
+/*
+ * ctl_putstr - write a tagged string into the response packet
+ * in the form:
+ *
+ * tag="data"
+ *
+ * len is the data length excluding the NUL terminator,
+ * as in ctl_putstr("var", "value", strlen("value"));
+ */
+static void
+ctl_putstr(
+ const char * tag,
+ const char * data,
+ size_t len
+ )
+{
+ char buffer[512];
+ char *cp;
+ size_t tl;
+
+ tl = strlen(tag);
+ memcpy(buffer, tag, tl);
+ cp = buffer + tl;
+ if (len > 0) {
+ NTP_INSIST(tl + 3 + len <= sizeof(buffer));
+ *cp++ = '=';
+ *cp++ = '"';
+ memcpy(cp, data, len);
+ cp += len;
+ *cp++ = '"';
+ }
+ ctl_putdata(buffer, (u_int)(cp - buffer), 0);
+}
+
+
+/*
+ * ctl_putunqstr - write a tagged string into the response packet
+ * in the form:
+ *
+ * tag=data
+ *
+ * len is the data length excluding the NUL terminator.
+ * data must not contain a comma or whitespace.
+ */
+static void
+ctl_putunqstr(
+ const char * tag,
+ const char * data,
+ size_t len
+ )
+{
+ char buffer[512];
+ char *cp;
+ size_t tl;
+
+ tl = strlen(tag);
+ memcpy(buffer, tag, tl);
+ cp = buffer + tl;
+ if (len > 0) {
+ NTP_INSIST(tl + 1 + len <= sizeof(buffer));
+ *cp++ = '=';
+ memcpy(cp, data, len);
+ cp += len;
+ }
+ ctl_putdata(buffer, (u_int)(cp - buffer), 0);
+}
+
+
+/*
+ * ctl_putdblf - write a tagged, signed double into the response packet
+ */
+static void
+ctl_putdblf(
+ const char * tag,
+ const char * fmt,
+ double d
+ )
+{
+ char *cp;
+ const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+ *cp++ = '=';
+ NTP_INSIST((cp - buffer) < sizeof(buffer));
+ snprintf(cp, sizeof(buffer) - (cp - buffer), fmt, d);
+ cp += strlen(cp);
+ ctl_putdata(buffer, (unsigned)(cp - buffer), 0);
+}
+
+/*
+ * ctl_putuint - write a tagged unsigned integer into the response
+ */
+static void
+ctl_putuint(
+ const char *tag,
+ u_long uval
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ NTP_INSIST((cp - buffer) < sizeof(buffer));
+ snprintf(cp, sizeof(buffer) - (cp - buffer), "%lu", uval);
+ cp += strlen(cp);
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+/*
+ * ctl_putfs - write a decoded filestamp into the response
+ */
+static void
+ctl_putfs(
+ const char *tag,
+ tstamp_t uval
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+ struct tm *tm = NULL;
+ time_t fstamp;
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ fstamp = uval - JAN_1970;
+ tm = gmtime(&fstamp);
+ if (NULL == tm)
+ return;
+ NTP_INSIST((cp - buffer) < sizeof(buffer));
+ snprintf(cp, sizeof(buffer) - (cp - buffer),
+ "%04d%02d%02d%02d%02d", tm->tm_year + 1900,
+ tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min);
+ cp += strlen(cp);
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_puthex - write a tagged unsigned integer, in hex, into the
+ * response
+ */
+static void
+ctl_puthex(
+ const char *tag,
+ u_long uval
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ NTP_INSIST((cp - buffer) < sizeof(buffer));
+ snprintf(cp, sizeof(buffer) - (cp - buffer), "0x%lx", uval);
+ cp += strlen(cp);
+ ctl_putdata(buffer,(unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putint - write a tagged signed integer into the response
+ */
+static void
+ctl_putint(
+ const char *tag,
+ long ival
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ NTP_INSIST((cp - buffer) < sizeof(buffer));
+ snprintf(cp, sizeof(buffer) - (cp - buffer), "%ld", ival);
+ cp += strlen(cp);
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putts - write a tagged timestamp, in hex, into the response
+ */
+static void
+ctl_putts(
+ const char *tag,
+ l_fp *ts
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ NTP_INSIST((cp - buffer) < sizeof(buffer));
+ snprintf(cp, sizeof(buffer) - (cp - buffer), "0x%08x.%08x",
+ (u_int)ts->l_ui, (u_int)ts->l_uf);
+ cp += strlen(cp);
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putadr - write an IP address into the response
+ */
+static void
+ctl_putadr(
+ const char *tag,
+ u_int32 addr32,
+ sockaddr_u *addr
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ if (NULL == addr)
+ cq = numtoa(addr32);
+ else
+ cq = stoa(addr);
+ NTP_INSIST((cp - buffer) < sizeof(buffer));
+ snprintf(cp, sizeof(buffer) - (cp - buffer), "%s", cq);
+ cp += strlen(cp);
+ ctl_putdata(buffer, (unsigned)(cp - buffer), 0);
+}
+
+
+/*
+ * ctl_putrefid - send a u_int32 refid as printable text
+ */
+static void
+ctl_putrefid(
+ const char * tag,
+ u_int32 refid
+ )
+{
+ char output[16];
+ char * optr;
+ char * oplim;
+ char * iptr;
+ char * iplim;
+ char * past_eq;
+
+ optr = output;
+ oplim = output + sizeof(output);
+ while (optr < oplim && '\0' != *tag)
+ *optr++ = *tag++;
+ if (optr < oplim) {
+ *optr++ = '=';
+ past_eq = optr;
+ }
+ if (!(optr < oplim))
+ return;
+ iptr = (char *)&refid;
+ iplim = iptr + sizeof(refid);
+ for ( ; optr < oplim && iptr < iplim && '\0' != *iptr;
+ iptr++, optr++)
+ if (isprint(*iptr))
+ *optr = *iptr;
+ else
+ *optr = '.';
+ if (!(optr <= oplim))
+ optr = past_eq;
+ ctl_putdata(output, (u_int)(optr - output), FALSE);
+}
+
+
+/*
+ * ctl_putarray - write a tagged eight element double array into the response
+ */
+static void
+ctl_putarray(
+ const char *tag,
+ double *arr,
+ int start
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+ int i;
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+ *cp++ = '=';
+ i = start;
+ do {
+ if (i == 0)
+ i = NTP_SHIFT;
+ i--;
+ NTP_INSIST((cp - buffer) < sizeof(buffer));
+ snprintf(cp, sizeof(buffer) - (cp - buffer),
+ " %.2f", arr[i] * 1e3);
+ cp += strlen(cp);
+ } while (i != start);
+ ctl_putdata(buffer, (unsigned)(cp - buffer), 0);
+}
+
+
+/*
+ * ctl_putsys - output a system variable
+ */
+static void
+ctl_putsys(
+ int varid
+ )
+{
+ l_fp tmp;
+ char str[256];
+ u_int u;
+ double kb;
+ double dtemp;
+ const char *ss;
+ size_t len;
+ int firstvarname;
+ const struct ctl_var *k;
+#ifdef AUTOKEY
+ struct cert_info *cp;
+#endif /* AUTOKEY */
+#ifdef KERNEL_PLL
+ static struct timex ntx;
+ static u_long ntp_adjtime_time;
+
+ static const double to_ms =
+# ifdef STA_NANO
+ 1.0e-6; /* nsec to msec */
+# else
+ 1.0e-3; /* usec to msec */
+# endif
+
+ /*
+ * CS_K_* variables depend on up-to-date output of ntp_adjtime()
+ */
+ if (CS_KERN_FIRST <= varid && varid <= CS_KERN_LAST &&
+ current_time != ntp_adjtime_time) {
+ ZERO(ntx);
+ if (ntp_adjtime(&ntx) < 0)
+ msyslog(LOG_ERR, "ntp_adjtime() for mode 6 query failed: %m");
+ else
+ ntp_adjtime_time = current_time;
+ }
+#endif /* KERNEL_PLL */
+
+ switch (varid) {
+
+ case CS_LEAP:
+ ctl_putuint(sys_var[CS_LEAP].text, sys_leap);
+ break;
+
+ case CS_STRATUM:
+ ctl_putuint(sys_var[CS_STRATUM].text, sys_stratum);
+ break;
+
+ case CS_PRECISION:
+ ctl_putint(sys_var[CS_PRECISION].text, sys_precision);
+ break;
+
+ case CS_ROOTDELAY:
+ ctl_putdbl(sys_var[CS_ROOTDELAY].text, sys_rootdelay *
+ 1e3);
+ break;
+
+ case CS_ROOTDISPERSION:
+ ctl_putdbl(sys_var[CS_ROOTDISPERSION].text,
+ sys_rootdisp * 1e3);
+ break;
+
+ case CS_REFID:
+ if (sys_stratum > 1 && sys_stratum < STRATUM_UNSPEC)
+ ctl_putadr(sys_var[varid].text, sys_refid, NULL);
+ else
+ ctl_putrefid(sys_var[varid].text, sys_refid);
+ break;
+
+ case CS_REFTIME:
+ ctl_putts(sys_var[CS_REFTIME].text, &sys_reftime);
+ break;
+
+ case CS_POLL:
+ ctl_putuint(sys_var[CS_POLL].text, sys_poll);
+ break;
+
+ case CS_PEERID:
+ if (sys_peer == NULL)
+ ctl_putuint(sys_var[CS_PEERID].text, 0);
+ else
+ ctl_putuint(sys_var[CS_PEERID].text,
+ sys_peer->associd);
+ break;
+
+ case CS_PEERADR:
+ if (sys_peer != NULL && sys_peer->dstadr != NULL)
+ ss = sptoa(&sys_peer->srcadr);
+ else
+ ss = "0.0.0.0:0";
+ ctl_putunqstr(sys_var[CS_PEERADR].text, ss, strlen(ss));
+ break;
+
+ case CS_PEERMODE:
+ u = (sys_peer != NULL)
+ ? sys_peer->hmode
+ : MODE_UNSPEC;
+ ctl_putuint(sys_var[CS_PEERMODE].text, u);
+ break;
+
+ case CS_OFFSET:
+ ctl_putdbl6(sys_var[CS_OFFSET].text, last_offset * 1e3);
+ break;
+
+ case CS_DRIFT:
+ ctl_putdbl(sys_var[CS_DRIFT].text, drift_comp * 1e6);
+ break;
+
+ case CS_JITTER:
+ ctl_putdbl6(sys_var[CS_JITTER].text, sys_jitter * 1e3);
+ break;
+
+ case CS_ERROR:
+ ctl_putdbl(sys_var[CS_ERROR].text, clock_jitter * 1e3);
+ break;
+
+ case CS_CLOCK:
+ get_systime(&tmp);
+ ctl_putts(sys_var[CS_CLOCK].text, &tmp);
+ break;
+
+ case CS_PROCESSOR:
+#ifndef HAVE_UNAME
+ ctl_putstr(sys_var[CS_PROCESSOR].text, str_processor,
+ sizeof(str_processor) - 1);
+#else
+ ctl_putstr(sys_var[CS_PROCESSOR].text,
+ utsnamebuf.machine, strlen(utsnamebuf.machine));
+#endif /* HAVE_UNAME */
+ break;
+
+ case CS_SYSTEM:
+#ifndef HAVE_UNAME
+ ctl_putstr(sys_var[CS_SYSTEM].text, str_system,
+ sizeof(str_system) - 1);
+#else
+ snprintf(str, sizeof(str), "%s/%s", utsnamebuf.sysname,
+ utsnamebuf.release);
+ ctl_putstr(sys_var[CS_SYSTEM].text, str, strlen(str));
+#endif /* HAVE_UNAME */
+ break;
+
+ case CS_VERSION:
+ ctl_putstr(sys_var[CS_VERSION].text, Version,
+ strlen(Version));
+ break;
+
+ case CS_STABIL:
+ ctl_putdbl(sys_var[CS_STABIL].text, clock_stability *
+ 1e6);
+ break;
+
+ case CS_VARLIST:
+ {
+ char buf[CTL_MAX_DATA_LEN];
+ //buffPointer, firstElementPointer, buffEndPointer
+ register char *buffp, *buffend;
+ register int firstVarName;
+ register const char *ss;
+ register int len;
+ register struct ctl_var *k;
+
+ buffp = buf;
+ buffend = buf + sizeof(buf);
+ if (buffp + strlen(sys_var[CS_VARLIST].text) + 4 > buffend)
+ break; /* really long var name */
+
+ snprintf(buffp, sizeof(buf), "%s=\"",sys_var[CS_VARLIST].text);
+ buffp += strlen(buffp);
+ firstVarName = TRUE;
+ for (k = sys_var; !(k->flags & EOV); k++) {
+ if (k->flags & PADDING)
+ continue;
+ len = strlen(k->text);
+ if (buffp + len + 1 >= buffend)
+ break;
+ if (!firstVarName)
+ *buffp++ = ',';
+ else
+ firstVarName = FALSE;
+ memcpy(buffp, k->text, len);
+ buffp += len;
+ }
+
+ for (k = ext_sys_var; k && !(k->flags & EOV); k++) {
+ if (k->flags & PADDING)
+ continue;
+ if (NULL == k->text)
+ continue;
+ ss = strchr(k->text, '=');
+ if (NULL == ss)
+ len = strlen(k->text);
+ else
+ len = ss - k->text;
+ if (buffp + len + 1 >= buffend)
+ break;
+ if (firstVarName) {
+ *buffp++ = ',';
+ firstVarName = FALSE;
+ }
+ memcpy(buffp, k->text,(unsigned)len);
+ buffp += len;
+ }
+ if (buffp + 2 >= buffend)
+ break;
+
+ *buffp++ = '"';
+ *buffp = '\0';
+
+ ctl_putdata(buf, (unsigned)( buffp - buf ), 0);
+ break;
+ }
+
+ case CS_TAI:
+ if (sys_tai > 0)
+ ctl_putuint(sys_var[CS_TAI].text, sys_tai);
+ break;
+
+ case CS_LEAPTAB:
+ {
+ leap_signature_t lsig;
+ leapsec_getsig(&lsig);
+ if (lsig.ttime > 0)
+ ctl_putfs(sys_var[CS_LEAPTAB].text, lsig.ttime);
+ break;
+ }
+
+ case CS_LEAPEND:
+ {
+ leap_signature_t lsig;
+ leapsec_getsig(&lsig);
+ if (lsig.etime > 0)
+ ctl_putfs(sys_var[CS_LEAPEND].text, lsig.etime);
+ break;
+ }
+
+ case CS_RATE:
+ ctl_putuint(sys_var[CS_RATE].text, ntp_minpoll);
+ break;
+
+ case CS_MRU_ENABLED:
+ ctl_puthex(sys_var[varid].text, mon_enabled);
+ break;
+
+ case CS_MRU_DEPTH:
+ ctl_putuint(sys_var[varid].text, mru_entries);
+ break;
+
+ case CS_MRU_MEM:
+ kb = mru_entries * (sizeof(mon_entry) / 1024.);
+ u = (u_int)kb;
+ if (kb - u >= 0.5)
+ u++;
+ ctl_putuint(sys_var[varid].text, u);
+ break;
+
+ case CS_MRU_DEEPEST:
+ ctl_putuint(sys_var[varid].text, mru_peakentries);
+ break;
+
+ case CS_MRU_MINDEPTH:
+ ctl_putuint(sys_var[varid].text, mru_mindepth);
+ break;
+
+ case CS_MRU_MAXAGE:
+ ctl_putint(sys_var[varid].text, mru_maxage);
+ break;
+
+ case CS_MRU_MAXDEPTH:
+ ctl_putuint(sys_var[varid].text, mru_maxdepth);
+ break;
+
+ case CS_MRU_MAXMEM:
+ kb = mru_maxdepth * (sizeof(mon_entry) / 1024.);
+ u = (u_int)kb;
+ if (kb - u >= 0.5)
+ u++;
+ ctl_putuint(sys_var[varid].text, u);
+ break;
+
+ case CS_SS_UPTIME:
+ ctl_putuint(sys_var[varid].text, current_time);
+ break;
+
+ case CS_SS_RESET:
+ ctl_putuint(sys_var[varid].text,
+ current_time - sys_stattime);
+ break;
+
+ case CS_SS_RECEIVED:
+ ctl_putuint(sys_var[varid].text, sys_received);
+ break;
+
+ case CS_SS_THISVER:
+ ctl_putuint(sys_var[varid].text, sys_newversion);
+ break;
+
+ case CS_SS_OLDVER:
+ ctl_putuint(sys_var[varid].text, sys_oldversion);
+ break;
+
+ case CS_SS_BADFORMAT:
+ ctl_putuint(sys_var[varid].text, sys_badlength);
+ break;
+
+ case CS_SS_BADAUTH:
+ ctl_putuint(sys_var[varid].text, sys_badauth);
+ break;
+
+ case CS_SS_DECLINED:
+ ctl_putuint(sys_var[varid].text, sys_declined);
+ break;
+
+ case CS_SS_RESTRICTED:
+ ctl_putuint(sys_var[varid].text, sys_restricted);
+ break;
+
+ case CS_SS_LIMITED:
+ ctl_putuint(sys_var[varid].text, sys_limitrejected);
+ break;
+
+ case CS_SS_KODSENT:
+ ctl_putuint(sys_var[varid].text, sys_kodsent);
+ break;
+
+ case CS_SS_PROCESSED:
+ ctl_putuint(sys_var[varid].text, sys_processed);
+ break;
+
+ case CS_BCASTDELAY:
+ ctl_putdbl(sys_var[varid].text, sys_bdelay * 1e3);
+ break;
+
+ case CS_AUTHDELAY:
+ LFPTOD(&sys_authdelay, dtemp);
+ ctl_putdbl(sys_var[varid].text, dtemp * 1e3);
+ break;
+
+ case CS_AUTHKEYS:
+ ctl_putuint(sys_var[varid].text, authnumkeys);
+ break;
+
+ case CS_AUTHFREEK:
+ ctl_putuint(sys_var[varid].text, authnumfreekeys);
+ break;
+
+ case CS_AUTHKLOOKUPS:
+ ctl_putuint(sys_var[varid].text, authkeylookups);
+ break;
+
+ case CS_AUTHKNOTFOUND:
+ ctl_putuint(sys_var[varid].text, authkeynotfound);
+ break;
+
+ case CS_AUTHKUNCACHED:
+ ctl_putuint(sys_var[varid].text, authkeyuncached);
+ break;
+
+ case CS_AUTHKEXPIRED:
+ ctl_putuint(sys_var[varid].text, authkeyexpired);
+ break;
+
+ case CS_AUTHENCRYPTS:
+ ctl_putuint(sys_var[varid].text, authencryptions);
+ break;
+
+ case CS_AUTHDECRYPTS:
+ ctl_putuint(sys_var[varid].text, authdecryptions);
+ break;
+
+ case CS_AUTHRESET:
+ ctl_putuint(sys_var[varid].text,
+ current_time - auth_timereset);
+ break;
+
+ /*
+ * CTL_IF_KERNLOOP() puts a zero if the kernel loop is
+ * unavailable, otherwise calls putfunc with args.
+ */
+#ifndef KERNEL_PLL
+# define CTL_IF_KERNLOOP(putfunc, args) \
+ ctl_putint(sys_var[varid].text, 0)
+#else
+# define CTL_IF_KERNLOOP(putfunc, args) \
+ putfunc args
+#endif
+
+ /*
+ * CTL_IF_KERNPPS() puts a zero if either the kernel
+ * loop is unavailable, or kernel hard PPS is not
+ * active, otherwise calls putfunc with args.
+ */
+#ifndef KERNEL_PLL
+# define CTL_IF_KERNPPS(putfunc, args) \
+ ctl_putint(sys_var[varid].text, 0)
+#else
+# define CTL_IF_KERNPPS(putfunc, args) \
+ if (0 == ntx.shift) \
+ ctl_putint(sys_var[varid].text, 0); \
+ else \
+ putfunc args /* no trailing ; */
+#endif
+
+ case CS_K_OFFSET:
+ CTL_IF_KERNLOOP(
+ ctl_putdblf,
+ (sys_var[varid].text, "%g", to_ms * ntx.offset)
+ );
+ break;
+
+ case CS_K_FREQ:
+ CTL_IF_KERNLOOP(
+ ctl_putsfp,
+ (sys_var[varid].text, ntx.freq)
+ );
+ break;
+
+ case CS_K_MAXERR:
+ CTL_IF_KERNLOOP(
+ ctl_putdblf,
+ (sys_var[varid].text, "%.6g",
+ to_ms * ntx.maxerror)
+ );
+ break;
+
+ case CS_K_ESTERR:
+ CTL_IF_KERNLOOP(
+ ctl_putdblf,
+ (sys_var[varid].text, "%.6g",
+ to_ms * ntx.esterror)
+ );
+ break;
+
+ case CS_K_STFLAGS:
+#ifndef KERNEL_PLL
+ ss = "";
+#else
+ ss = k_st_flags(ntx.status);
+#endif
+ ctl_putstr(sys_var[varid].text, ss, strlen(ss));
+ break;
+
+ case CS_K_TIMECONST:
+ CTL_IF_KERNLOOP(
+ ctl_putint,
+ (sys_var[varid].text, ntx.constant)
+ );
+ break;
+
+ case CS_K_PRECISION:
+ CTL_IF_KERNLOOP(
+ ctl_putdblf,
+ (sys_var[varid].text, "%.6g",
+ to_ms * ntx.precision)
+ );
+ break;
+
+ case CS_K_FREQTOL:
+ CTL_IF_KERNLOOP(
+ ctl_putsfp,
+ (sys_var[varid].text, ntx.tolerance)
+ );
+ break;
+
+ case CS_K_PPS_FREQ:
+ CTL_IF_KERNPPS(
+ ctl_putsfp,
+ (sys_var[varid].text, ntx.ppsfreq)
+ );
+ break;
+
+ case CS_K_PPS_STABIL:
+ CTL_IF_KERNPPS(
+ ctl_putsfp,
+ (sys_var[varid].text, ntx.stabil)
+ );
+ break;
+
+ case CS_K_PPS_JITTER:
+ CTL_IF_KERNPPS(
+ ctl_putdbl,
+ (sys_var[varid].text, to_ms * ntx.jitter)
+ );
+ break;
+
+ case CS_K_PPS_CALIBDUR:
+ CTL_IF_KERNPPS(
+ ctl_putint,
+ (sys_var[varid].text, 1 << ntx.shift)
+ );
+ break;
+
+ case CS_K_PPS_CALIBS:
+ CTL_IF_KERNPPS(
+ ctl_putint,
+ (sys_var[varid].text, ntx.calcnt)
+ );
+ break;
+
+ case CS_K_PPS_CALIBERRS:
+ CTL_IF_KERNPPS(
+ ctl_putint,
+ (sys_var[varid].text, ntx.errcnt)
+ );
+ break;
+
+ case CS_K_PPS_JITEXC:
+ CTL_IF_KERNPPS(
+ ctl_putint,
+ (sys_var[varid].text, ntx.jitcnt)
+ );
+ break;
+
+ case CS_K_PPS_STBEXC:
+ CTL_IF_KERNPPS(
+ ctl_putint,
+ (sys_var[varid].text, ntx.stbcnt)
+ );
+ break;
+
+ case CS_IOSTATS_RESET:
+ ctl_putuint(sys_var[varid].text,
+ current_time - io_timereset);
+ break;
+
+ case CS_TOTAL_RBUF:
+ ctl_putuint(sys_var[varid].text, total_recvbuffs());
+ break;
+
+ case CS_FREE_RBUF:
+ ctl_putuint(sys_var[varid].text, free_recvbuffs());
+ break;
+
+ case CS_USED_RBUF:
+ ctl_putuint(sys_var[varid].text, full_recvbuffs());
+ break;
+
+ case CS_RBUF_LOWATER:
+ ctl_putuint(sys_var[varid].text, lowater_additions());
+ break;
+
+ case CS_IO_DROPPED:
+ ctl_putuint(sys_var[varid].text, packets_dropped);
+ break;
+
+ case CS_IO_IGNORED:
+ ctl_putuint(sys_var[varid].text, packets_ignored);
+ break;
+
+ case CS_IO_RECEIVED:
+ ctl_putuint(sys_var[varid].text, packets_received);
+ break;
+
+ case CS_IO_SENT:
+ ctl_putuint(sys_var[varid].text, packets_sent);
+ break;
+
+ case CS_IO_SENDFAILED:
+ ctl_putuint(sys_var[varid].text, packets_notsent);
+ break;
+
+ case CS_IO_WAKEUPS:
+ ctl_putuint(sys_var[varid].text, handler_calls);
+ break;
+
+ case CS_IO_GOODWAKEUPS:
+ ctl_putuint(sys_var[varid].text, handler_pkts);
+ break;
+
+ case CS_TIMERSTATS_RESET:
+ ctl_putuint(sys_var[varid].text,
+ current_time - timer_timereset);
+ break;
+
+ case CS_TIMER_OVERRUNS:
+ ctl_putuint(sys_var[varid].text, alarm_overflow);
+ break;
+
+ case CS_TIMER_XMTS:
+ ctl_putuint(sys_var[varid].text, timer_xmtcalls);
+ break;
+
+ case CS_FUZZ:
+ ctl_putdbl(sys_var[varid].text, sys_fuzz * 1e3);
+ break;
+#ifdef AUTOKEY
+ case CS_FLAGS:
+ if (crypto_flags)
+ ctl_puthex(sys_var[CS_FLAGS].text,
+ crypto_flags);
+ break;
+
+ case CS_DIGEST:
+ if (crypto_flags) {
+ strlcpy(str, OBJ_nid2ln(crypto_nid),
+ COUNTOF(str));
+ ctl_putstr(sys_var[CS_DIGEST].text, str,
+ strlen(str));
+ }
+ break;
+
+ case CS_SIGNATURE:
+ if (crypto_flags) {
+ const EVP_MD *dp;
+
+ dp = EVP_get_digestbynid(crypto_flags >> 16);
+ strlcpy(str, OBJ_nid2ln(EVP_MD_pkey_type(dp)),
+ COUNTOF(str));
+ ctl_putstr(sys_var[CS_SIGNATURE].text, str,
+ strlen(str));
+ }
+ break;
+
+ case CS_HOST:
+ if (hostval.ptr != NULL)
+ ctl_putstr(sys_var[CS_HOST].text, hostval.ptr,
+ strlen(hostval.ptr));
+ break;
+
+ case CS_IDENT:
+ if (sys_ident != NULL)
+ ctl_putstr(sys_var[CS_IDENT].text, sys_ident,
+ strlen(sys_ident));
+ break;
+
+ case CS_CERTIF:
+ for (cp = cinfo; cp != NULL; cp = cp->link) {
+ tstamp_t tstamp;
+
+ snprintf(str, sizeof(str), "%s %s 0x%x",
+ cp->subject, cp->issuer, cp->flags);
+ ctl_putstr(sys_var[CS_CERTIF].text, str,
+ strlen(str));
+ tstamp = caltontp(&(cp->last)); /* XXX too small to hold some values, but that's what ctl_putfs requires */
+ ctl_putfs(sys_var[CS_REVTIME].text, tstamp);
+ }
+ break;
+
+ case CS_PUBLIC:
+ if (hostval.tstamp != 0)
+ ctl_putfs(sys_var[CS_PUBLIC].text,
+ ntohl(hostval.tstamp));
+ break;
+#endif /* AUTOKEY */
+ }
+}
+
+
+/*
+ * ctl_putpeer - output a peer variable
+ */
+static void
+ctl_putpeer(
+ int id,
+ struct peer *p
+ )
+{
+ char buf[CTL_MAX_DATA_LEN];
+ char *s;
+ char *t;
+ char *be;
+ int i;
+ const struct ctl_var *k;
+#ifdef AUTOKEY
+ struct autokey *ap;
+ const EVP_MD *dp;
+ const char *str;
+#endif /* AUTOKEY */
+
+ switch (id) {
+
+ case CP_CONFIG:
+ ctl_putuint(peer_var[id].text,
+ !(FLAG_PREEMPT & p->flags));
+ break;
+
+ case CP_AUTHENABLE:
+ ctl_putuint(peer_var[id].text, !(p->keyid));
+ break;
+
+ case CP_AUTHENTIC:
+ ctl_putuint(peer_var[id].text,
+ !!(FLAG_AUTHENTIC & p->flags));
+ break;
+
+ case CP_SRCADR:
+ ctl_putadr(peer_var[id].text, 0, &p->srcadr);
+ break;
+
+ case CP_SRCPORT:
+ ctl_putuint(peer_var[id].text, SRCPORT(&p->srcadr));
+ break;
+
+ case CP_SRCHOST:
+ if (p->hostname != NULL)
+ ctl_putstr(peer_var[id].text, p->hostname,
+ strlen(p->hostname));
+ break;
+
+ case CP_DSTADR:
+ ctl_putadr(peer_var[id].text, 0,
+ (p->dstadr != NULL)
+ ? &p->dstadr->sin
+ : NULL);
+ break;
+
+ case CP_DSTPORT:
+ ctl_putuint(peer_var[id].text,
+ (p->dstadr != NULL)
+ ? SRCPORT(&p->dstadr->sin)
+ : 0);
+ break;
+
+ case CP_IN:
+ if (p->r21 > 0.)
+ ctl_putdbl(peer_var[id].text, p->r21 / 1e3);
+ break;
+
+ case CP_OUT:
+ if (p->r34 > 0.)
+ ctl_putdbl(peer_var[id].text, p->r34 / 1e3);
+ break;
+
+ case CP_RATE:
+ ctl_putuint(peer_var[id].text, p->throttle);
+ break;
+
+ case CP_LEAP:
+ ctl_putuint(peer_var[id].text, p->leap);
+ break;
+
+ case CP_HMODE:
+ ctl_putuint(peer_var[id].text, p->hmode);
+ break;
+
+ case CP_STRATUM:
+ ctl_putuint(peer_var[id].text, p->stratum);
+ break;
+
+ case CP_PPOLL:
+ ctl_putuint(peer_var[id].text, p->ppoll);
+ break;
+
+ case CP_HPOLL:
+ ctl_putuint(peer_var[id].text, p->hpoll);
+ break;
+
+ case CP_PRECISION:
+ ctl_putint(peer_var[id].text, p->precision);
+ break;
+
+ case CP_ROOTDELAY:
+ ctl_putdbl(peer_var[id].text, p->rootdelay * 1e3);
+ break;
+
+ case CP_ROOTDISPERSION:
+ ctl_putdbl(peer_var[id].text, p->rootdisp * 1e3);
+ break;
+
+ case CP_REFID:
+#ifdef REFCLOCK
+ if (p->flags & FLAG_REFCLOCK) {
+ ctl_putrefid(peer_var[id].text, p->refid);
+ break;
+ }
+#endif
+ if (p->stratum > 1 && p->stratum < STRATUM_UNSPEC)
+ ctl_putadr(peer_var[id].text, p->refid,
+ NULL);
+ else
+ ctl_putrefid(peer_var[id].text, p->refid);
+ break;
+
+ case CP_REFTIME:
+ ctl_putts(peer_var[id].text, &p->reftime);
+ break;
+
+ case CP_ORG:
+ ctl_putts(peer_var[id].text, &p->aorg);
+ break;
+
+ case CP_REC:
+ ctl_putts(peer_var[id].text, &p->dst);
+ break;
+
+ case CP_XMT:
+ if (p->xleave)
+ ctl_putdbl(peer_var[id].text, p->xleave * 1e3);
+ break;
+
+ case CP_BIAS:
+ if (p->bias != 0.)
+ ctl_putdbl(peer_var[id].text, p->bias * 1e3);
+ break;
+
+ case CP_REACH:
+ ctl_puthex(peer_var[id].text, p->reach);
+ break;
+
+ case CP_FLASH:
+ ctl_puthex(peer_var[id].text, p->flash);
+ break;
+
+ case CP_TTL:
+#ifdef REFCLOCK
+ if (p->flags & FLAG_REFCLOCK) {
+ ctl_putuint(peer_var[id].text, p->ttl);
+ break;
+ }
+#endif
+ if (p->ttl > 0 && p->ttl < COUNTOF(sys_ttl))
+ ctl_putint(peer_var[id].text,
+ sys_ttl[p->ttl]);
+ break;
+
+ case CP_UNREACH:
+ ctl_putuint(peer_var[id].text, p->unreach);
+ break;
+
+ case CP_TIMER:
+ ctl_putuint(peer_var[id].text,
+ p->nextdate - current_time);
+ break;
+
+ case CP_DELAY:
+ ctl_putdbl(peer_var[id].text, p->delay * 1e3);
+ break;
+
+ case CP_OFFSET:
+ ctl_putdbl(peer_var[id].text, p->offset * 1e3);
+ break;
+
+ case CP_JITTER:
+ ctl_putdbl(peer_var[id].text, p->jitter * 1e3);
+ break;
+
+ case CP_DISPERSION:
+ ctl_putdbl(peer_var[id].text, p->disp * 1e3);
+ break;
+
+ case CP_KEYID:
+ if (p->keyid > NTP_MAXKEY)
+ ctl_puthex(peer_var[id].text, p->keyid);
+ else
+ ctl_putuint(peer_var[id].text, p->keyid);
+ break;
+
+ case CP_FILTDELAY:
+ ctl_putarray(peer_var[id].text, p->filter_delay,
+ p->filter_nextpt);
+ break;
+
+ case CP_FILTOFFSET:
+ ctl_putarray(peer_var[id].text, p->filter_offset,
+ p->filter_nextpt);
+ break;
+
+ case CP_FILTERROR:
+ ctl_putarray(peer_var[id].text, p->filter_disp,
+ p->filter_nextpt);
+ break;
+
+ case CP_PMODE:
+ ctl_putuint(peer_var[id].text, p->pmode);
+ break;
+
+ case CP_RECEIVED:
+ ctl_putuint(peer_var[id].text, p->received);
+ break;
+
+ case CP_SENT:
+ ctl_putuint(peer_var[id].text, p->sent);
+ break;
+
+ case CP_VARLIST:
+ s = buf;
+ be = buf + sizeof(buf);
+ if (strlen(peer_var[id].text) + 4 > sizeof(buf))
+ break; /* really long var name */
+
+ snprintf(s, sizeof(buf), "%s=\"", peer_var[id].text);
+ s += strlen(s);
+ t = s;
+ for (k = peer_var; !(EOV & k->flags); k++) {
+ if (PADDING & k->flags)
+ continue;
+ i = strlen(k->text);
+ if (s + i + 1 >= be)
+ break;
+ if (s != t)
+ *s++ = ',';
+ memcpy(s, k->text, i);
+ s += i;
+ }
+ if (s + 2 < be) {
+ *s++ = '"';
+ *s = '\0';
+ ctl_putdata(buf, (u_int)(s - buf), 0);
+ }
+ break;
+
+ case CP_TIMEREC:
+ ctl_putuint(peer_var[id].text,
+ current_time - p->timereceived);
+ break;
+
+ case CP_TIMEREACH:
+ ctl_putuint(peer_var[id].text,
+ current_time - p->timereachable);
+ break;
+
+ case CP_BADAUTH:
+ ctl_putuint(peer_var[id].text, p->badauth);
+ break;
+
+ case CP_BOGUSORG:
+ ctl_putuint(peer_var[id].text, p->bogusorg);
+ break;
+
+ case CP_OLDPKT:
+ ctl_putuint(peer_var[id].text, p->oldpkt);
+ break;
+
+ case CP_SELDISP:
+ ctl_putuint(peer_var[id].text, p->seldisptoolarge);
+ break;
+
+ case CP_SELBROKEN:
+ ctl_putuint(peer_var[id].text, p->selbroken);
+ break;
+
+ case CP_CANDIDATE:
+ ctl_putuint(peer_var[id].text, p->status);
+ break;
+#ifdef AUTOKEY
+ case CP_FLAGS:
+ if (p->crypto)
+ ctl_puthex(peer_var[id].text, p->crypto);
+ break;
+
+ case CP_SIGNATURE:
+ if (p->crypto) {
+ dp = EVP_get_digestbynid(p->crypto >> 16);
+ str = OBJ_nid2ln(EVP_MD_pkey_type(dp));
+ ctl_putstr(peer_var[id].text, str, strlen(str));
+ }
+ break;
+
+ case CP_HOST:
+ if (p->subject != NULL)
+ ctl_putstr(peer_var[id].text, p->subject,
+ strlen(p->subject));
+ break;
+
+ case CP_VALID: /* not used */
+ break;
+
+ case CP_INITSEQ:
+ if (NULL == (ap = p->recval.ptr))
+ break;
+
+ ctl_putint(peer_var[CP_INITSEQ].text, ap->seq);
+ ctl_puthex(peer_var[CP_INITKEY].text, ap->key);
+ ctl_putfs(peer_var[CP_INITTSP].text,
+ ntohl(p->recval.tstamp));
+ break;
+
+ case CP_IDENT:
+ if (p->ident != NULL)
+ ctl_putstr(peer_var[id].text, p->ident,
+ strlen(p->ident));
+ break;
+
+
+#endif /* AUTOKEY */
+ }
+}
+
+
+#ifdef REFCLOCK
+/*
+ * ctl_putclock - output clock variables
+ */
+static void
+ctl_putclock(
+ int id,
+ struct refclockstat *pcs,
+ int mustput
+ )
+{
+ char buf[CTL_MAX_DATA_LEN];
+ char *s, *t, *be;
+ const char *ss;
+ int i;
+ const struct ctl_var *k;
+
+ switch (id) {
+
+ case CC_TYPE:
+ if (mustput || pcs->clockdesc == NULL
+ || *(pcs->clockdesc) == '\0') {
+ ctl_putuint(clock_var[id].text, pcs->type);
+ }
+ break;
+ case CC_TIMECODE:
+ ctl_putstr(clock_var[id].text,
+ pcs->p_lastcode,
+ (unsigned)pcs->lencode);
+ break;
+
+ case CC_POLL:
+ ctl_putuint(clock_var[id].text, pcs->polls);
+ break;
+
+ case CC_NOREPLY:
+ ctl_putuint(clock_var[id].text,
+ pcs->noresponse);
+ break;
+
+ case CC_BADFORMAT:
+ ctl_putuint(clock_var[id].text,
+ pcs->badformat);
+ break;
+
+ case CC_BADDATA:
+ ctl_putuint(clock_var[id].text,
+ pcs->baddata);
+ break;
+
+ case CC_FUDGETIME1:
+ if (mustput || (pcs->haveflags & CLK_HAVETIME1))
+ ctl_putdbl(clock_var[id].text,
+ pcs->fudgetime1 * 1e3);
+ break;
+
+ case CC_FUDGETIME2:
+ if (mustput || (pcs->haveflags & CLK_HAVETIME2))
+ ctl_putdbl(clock_var[id].text,
+ pcs->fudgetime2 * 1e3);
+ break;
+
+ case CC_FUDGEVAL1:
+ if (mustput || (pcs->haveflags & CLK_HAVEVAL1))
+ ctl_putint(clock_var[id].text,
+ pcs->fudgeval1);
+ break;
+
+ case CC_FUDGEVAL2:
+ if (mustput || (pcs->haveflags & CLK_HAVEVAL2)) {
+ if (pcs->fudgeval1 > 1)
+ ctl_putadr(clock_var[id].text,
+ pcs->fudgeval2, NULL);
+ else
+ ctl_putrefid(clock_var[id].text,
+ pcs->fudgeval2);
+ }
+ break;
+
+ case CC_FLAGS:
+ ctl_putuint(clock_var[id].text, pcs->flags);
+ break;
+
+ case CC_DEVICE:
+ if (pcs->clockdesc == NULL ||
+ *(pcs->clockdesc) == '\0') {
+ if (mustput)
+ ctl_putstr(clock_var[id].text,
+ "", 0);
+ } else {
+ ctl_putstr(clock_var[id].text,
+ pcs->clockdesc,
+ strlen(pcs->clockdesc));
+ }
+ break;
+
+ case CC_VARLIST:
+ s = buf;
+ be = buf + sizeof(buf);
+ if (strlen(clock_var[CC_VARLIST].text) + 4 >
+ sizeof(buf))
+ break; /* really long var name */
+
+ snprintf(s, sizeof(buf), "%s=\"",
+ clock_var[CC_VARLIST].text);
+ s += strlen(s);
+ t = s;
+
+ for (k = clock_var; !(EOV & k->flags); k++) {
+ if (PADDING & k->flags)
+ continue;
+
+ i = strlen(k->text);
+ if (s + i + 1 >= be)
+ break;
+
+ if (s != t)
+ *s++ = ',';
+ memcpy(s, k->text, i);
+ s += i;
+ }
+
+ for (k = pcs->kv_list; k && !(EOV & k->flags); k++) {
+ if (PADDING & k->flags)
+ continue;
+
+ ss = k->text;
+ if (NULL == ss)
+ continue;
+
+ while (*ss && *ss != '=')
+ ss++;
+ i = ss - k->text;
+ if (s + i + 1 >= be)
+ break;
+
+ if (s != t)
+ *s++ = ',';
+ memcpy(s, k->text, (unsigned)i);
+ s += i;
+ *s = '\0';
+ }
+ if (s + 2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+ ctl_putdata(buf, (unsigned)(s - buf), 0);
+ break;
+ }
+}
+#endif
+
+
+
+/*
+ * ctl_getitem - get the next data item from the incoming packet
+ */
+static const struct ctl_var *
+ctl_getitem(
+ const struct ctl_var *var_list,
+ char **data
+ )
+{
+ static const struct ctl_var eol = { 0, EOV, NULL };
+ static char buf[128];
+ static u_long quiet_until;
+ const struct ctl_var *v;
+ const char *pch;
+ char *cp;
+ char *tp;
+
+ /*
+ * Delete leading commas and white space
+ */
+ while (reqpt < reqend && (*reqpt == ',' ||
+ isspace((unsigned char)*reqpt)))
+ reqpt++;
+ if (reqpt >= reqend)
+ return NULL;
+
+ if (NULL == var_list)
+ return &eol;
+
+ /*
+ * Look for a first character match on the tag. If we find
+ * one, see if it is a full match.
+ */
+ v = var_list;
+ cp = reqpt;
+ for (v = var_list; !(EOV & v->flags); v++) {
+ if (!(PADDING & v->flags) && *cp == *(v->text)) {
+ pch = v->text;
+ while ('\0' != *pch && '=' != *pch && cp < reqend
+ && *cp == *pch) {
+ cp++;
+ pch++;
+ }
+ if ('\0' == *pch || '=' == *pch) {
+ while (cp < reqend && isspace((u_char)*cp))
+ cp++;
+ if (cp == reqend || ',' == *cp) {
+ buf[0] = '\0';
+ *data = buf;
+ if (cp < reqend)
+ cp++;
+ reqpt = cp;
+ return v;
+ }
+ if ('=' == *cp) {
+ cp++;
+ tp = buf;
+ while (cp < reqend && isspace((u_char)*cp))
+ cp++;
+ while (cp < reqend && *cp != ',') {
+ *tp++ = *cp++;
+ if (tp - buf >= sizeof(buf)) {
+ ctl_error(CERR_BADFMT);
+ numctlbadpkts++;
+ NLOG(NLOG_SYSEVENT)
+ if (quiet_until <= current_time) {
+ quiet_until = current_time + 300;
+ msyslog(LOG_WARNING,
+"Possible 'ntpdx' exploit from %s#%u (possibly spoofed)", stoa(rmt_addr), SRCPORT(rmt_addr));
+ }
+ return NULL;
+ }
+ }
+ if (cp < reqend)
+ cp++;
+ *tp-- = '\0';
+ while (tp >= buf && isspace((u_char)*tp))
+ *tp-- = '\0';
+ reqpt = cp;
+ *data = buf;
+ return v;
+ }
+ }
+ cp = reqpt;
+ }
+ }
+ return v;
+}
+
+
+/*
+ * control_unspec - response to an unspecified op-code
+ */
+/*ARGSUSED*/
+static void
+control_unspec(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ struct peer *peer;
+
+ /*
+ * What is an appropriate response to an unspecified op-code?
+ * I return no errors and no data, unless a specified assocation
+ * doesn't exist.
+ */
+ if (res_associd) {
+ peer = findpeerbyassoc(res_associd);
+ if (NULL == peer) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ rpkt.status = htons(ctlpeerstatus(peer));
+ } else
+ rpkt.status = htons(ctlsysstatus());
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_status - return either a list of associd's, or a particular
+ * peer's status.
+ */
+/*ARGSUSED*/
+static void
+read_status(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ struct peer *peer;
+ const u_char *cp;
+ int n;
+ /* a_st holds association ID, status pairs alternating */
+ u_short a_st[CTL_MAX_DATA_LEN / sizeof(u_short)];
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("read_status: ID %d\n", res_associd);
+#endif
+ /*
+ * Two choices here. If the specified association ID is
+ * zero we return all known assocation ID's. Otherwise
+ * we return a bunch of stuff about the particular peer.
+ */
+ if (res_associd) {
+ peer = findpeerbyassoc(res_associd);
+ if (NULL == peer) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ rpkt.status = htons(ctlpeerstatus(peer));
+ if (res_authokay)
+ peer->num_events = 0;
+ /*
+ * For now, output everything we know about the
+ * peer. May be more selective later.
+ */
+ for (cp = def_peer_var; *cp != 0; cp++)
+ ctl_putpeer((int)*cp, peer);
+ ctl_flushpkt(0);
+ return;
+ }
+ n = 0;
+ rpkt.status = htons(ctlsysstatus());
+ for (peer = peer_list; peer != NULL; peer = peer->p_link) {
+ a_st[n++] = htons(peer->associd);
+ a_st[n++] = htons(ctlpeerstatus(peer));
+ /* two entries each loop iteration, so n + 1 */
+ if (n + 1 >= COUNTOF(a_st)) {
+ ctl_putdata((void *)a_st, n * sizeof(a_st[0]),
+ 1);
+ n = 0;
+ }
+ }
+ if (n)
+ ctl_putdata((void *)a_st, n * sizeof(a_st[0]), 1);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_peervars - half of read_variables() implementation
+ */
+static void
+read_peervars(void)
+{
+ const struct ctl_var *v;
+ struct peer *peer;
+ const u_char *cp;
+ int i;
+ char * valuep;
+ u_char wants[CP_MAXCODE + 1];
+ u_int gotvar;
+
+ /*
+ * Wants info for a particular peer. See if we know
+ * the guy.
+ */
+ peer = findpeerbyassoc(res_associd);
+ if (NULL == peer) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ rpkt.status = htons(ctlpeerstatus(peer));
+ if (res_authokay)
+ peer->num_events = 0;
+ ZERO(wants);
+ gotvar = 0;
+ while (NULL != (v = ctl_getitem(peer_var, &valuep))) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ return;
+ }
+ NTP_INSIST(v->code < COUNTOF(wants));
+ wants[v->code] = 1;
+ gotvar = 1;
+ }
+ if (gotvar) {
+ for (i = 1; i < COUNTOF(wants); i++)
+ if (wants[i])
+ ctl_putpeer(i, peer);
+ } else
+ for (cp = def_peer_var; *cp != 0; cp++)
+ ctl_putpeer((int)*cp, peer);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_sysvars - half of read_variables() implementation
+ */
+static void
+read_sysvars(void)
+{
+ const struct ctl_var *v;
+ struct ctl_var *kv;
+ u_int n;
+ u_int gotvar;
+ const u_char *cs;
+ char * valuep;
+ const char * pch;
+ u_char *wants;
+ size_t wants_count;
+
+ /*
+ * Wants system variables. Figure out which he wants
+ * and give them to him.
+ */
+ rpkt.status = htons(ctlsysstatus());
+ if (res_authokay)
+ ctl_sys_num_events = 0;
+ wants_count = CS_MAXCODE + 1 + count_var(ext_sys_var);
+ wants = emalloc_zero(wants_count);
+ gotvar = 0;
+ while (NULL != (v = ctl_getitem(sys_var, &valuep))) {
+ if (!(EOV & v->flags)) {
+ NTP_INSIST(v->code < wants_count);
+ wants[v->code] = 1;
+ gotvar = 1;
+ } else {
+ v = ctl_getitem(ext_sys_var, &valuep);
+ NTP_INSIST(v != NULL);
+ if (EOV & v->flags) {
+ ctl_error(CERR_UNKNOWNVAR);
+ free(wants);
+ return;
+ }
+ n = v->code + CS_MAXCODE + 1;
+ NTP_INSIST(n < wants_count);
+ wants[n] = 1;
+ gotvar = 1;
+ }
+ }
+ if (gotvar) {
+ for (n = 1; n <= CS_MAXCODE; n++)
+ if (wants[n])
+ ctl_putsys(n);
+ for (n = 0; n + CS_MAXCODE + 1 < wants_count; n++)
+ if (wants[n + CS_MAXCODE + 1]) {
+ pch = ext_sys_var[n].text;
+ ctl_putdata(pch, strlen(pch), 0);
+ }
+ } else {
+ for (cs = def_sys_var; *cs != 0; cs++)
+ ctl_putsys((int)*cs);
+ for (kv = ext_sys_var; kv && !(EOV & kv->flags); kv++)
+ if (DEF & kv->flags)
+ ctl_putdata(kv->text, strlen(kv->text),
+ 0);
+ }
+ free(wants);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_variables - return the variables the caller asks for
+ */
+/*ARGSUSED*/
+static void
+read_variables(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ if (res_associd)
+ read_peervars();
+ else
+ read_sysvars();
+}
+
+
+/*
+ * write_variables - write into variables. We only allow leap bit
+ * writing this way.
+ */
+/*ARGSUSED*/
+static void
+write_variables(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ const struct ctl_var *v;
+ int ext_var;
+ char *valuep;
+ long val;
+ size_t octets;
+ char *vareqv;
+ const char *t;
+ char *tt;
+
+ val = 0;
+ /*
+ * If he's trying to write into a peer tell him no way
+ */
+ if (res_associd != 0) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+
+ /*
+ * Set status
+ */
+ rpkt.status = htons(ctlsysstatus());
+
+ /*
+ * Look through the variables. Dump out at the first sign of
+ * trouble.
+ */
+ while ((v = ctl_getitem(sys_var, &valuep)) != 0) {
+ ext_var = 0;
+ if (v->flags & EOV) {
+ if ((v = ctl_getitem(ext_sys_var, &valuep)) !=
+ 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ return;
+ }
+ ext_var = 1;
+ } else {
+ break;
+ }
+ }
+ if (!(v->flags & CAN_WRITE)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ if (!ext_var && (*valuep == '\0' || !atoint(valuep,
+ &val))) {
+ ctl_error(CERR_BADFMT);
+ return;
+ }
+ if (!ext_var && (val & ~LEAP_NOTINSYNC) != 0) {
+ ctl_error(CERR_BADVALUE);
+ return;
+ }
+
+ if (ext_var) {
+ octets = strlen(v->text) + strlen(valuep) + 2;
+ vareqv = emalloc(octets);
+ tt = vareqv;
+ t = v->text;
+ while (*t && *t != '=')
+ *tt++ = *t++;
+ *tt++ = '=';
+ memcpy(tt, valuep, 1 + strlen(valuep));
+ set_sys_var(vareqv, 1 + strlen(vareqv), v->flags);
+ free(vareqv);
+ } else {
+ ctl_error(CERR_UNSPEC); /* really */
+ return;
+ }
+ }
+
+ /*
+ * If we got anything, do it. xxx nothing to do ***
+ */
+ /*
+ if (leapind != ~0 || leapwarn != ~0) {
+ if (!leap_setleap((int)leapind, (int)leapwarn)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ }
+ */
+ ctl_flushpkt(0);
+}
+
+/*
+ * configure() processes ntpq :config/config-from-file, allowing
+ * generic runtime reconfiguration.
+ */
+static void configure(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ size_t data_count;
+ int retval;
+ int replace_nl;
+
+ /* I haven't yet implemented changes to an existing association.
+ * Hence check if the association id is 0
+ */
+ if (res_associd != 0) {
+ ctl_error(CERR_BADVALUE);
+ return;
+ }
+
+ if (RES_NOMODIFY & restrict_mask) {
+ snprintf(remote_config.err_msg,
+ sizeof(remote_config.err_msg),
+ "runtime configuration prohibited by restrict ... nomodify");
+ ctl_putdata(remote_config.err_msg,
+ strlen(remote_config.err_msg), 0);
+ ctl_flushpkt(0);
+ NLOG(NLOG_SYSINFO)
+ msyslog(LOG_NOTICE,
+ "runtime config from %s rejected due to nomodify restriction",
+ stoa(&rbufp->recv_srcadr));
+ sys_restricted++;
+ return;
+ }
+
+ /* Initialize the remote config buffer */
+ data_count = reqend - reqpt;
+ memcpy(remote_config.buffer, reqpt, data_count);
+ if (data_count > 0
+ && '\n' != remote_config.buffer[data_count - 1])
+ remote_config.buffer[data_count++] = '\n';
+ remote_config.buffer[data_count] = '\0';
+ remote_config.pos = 0;
+ remote_config.err_pos = 0;
+ remote_config.no_errors = 0;
+
+ /* do not include terminating newline in log */
+ if (data_count > 0
+ && '\n' == remote_config.buffer[data_count - 1]) {
+ remote_config.buffer[data_count - 1] = '\0';
+ replace_nl = TRUE;
+ } else {
+ replace_nl = FALSE;
+ }
+
+ DPRINTF(1, ("Got Remote Configuration Command: %s\n",
+ remote_config.buffer));
+ msyslog(LOG_NOTICE, "%s config: %s",
+ stoa(&rbufp->recv_srcadr),
+ remote_config.buffer);
+
+ if (replace_nl)
+ remote_config.buffer[data_count - 1] = '\n';
+
+ config_remotely(&rbufp->recv_srcadr);
+
+ /*
+ * Check if errors were reported. If not, output 'Config
+ * Succeeded'. Else output the error count. It would be nice
+ * to output any parser error messages.
+ */
+ if (0 == remote_config.no_errors) {
+ retval = snprintf(remote_config.err_msg,
+ sizeof(remote_config.err_msg),
+ "Config Succeeded");
+ if (retval > 0)
+ remote_config.err_pos += retval;
+ }
+
+ ctl_putdata(remote_config.err_msg, remote_config.err_pos, 0);
+ ctl_flushpkt(0);
+
+ DPRINTF(1, ("Reply: %s\n", remote_config.err_msg));
+
+ if (remote_config.no_errors > 0)
+ msyslog(LOG_NOTICE, "%d error in %s config",
+ remote_config.no_errors,
+ stoa(&rbufp->recv_srcadr));
+}
+
+
+/*
+ * derive_nonce - generate client-address-specific nonce value
+ * associated with a given timestamp.
+ */
+static u_int32 derive_nonce(
+ sockaddr_u * addr,
+ u_int32 ts_i,
+ u_int32 ts_f
+ )
+{
+ static u_int32 salt[4];
+ static u_long last_salt_update;
+ union d_tag {
+ u_char digest[EVP_MAX_MD_SIZE];
+ u_int32 extract;
+ } d;
+ EVP_MD_CTX ctx;
+ u_int len;
+
+ while (!salt[0] || current_time - last_salt_update >= 3600) {
+ salt[0] = ntp_random();
+ salt[1] = ntp_random();
+ salt[2] = ntp_random();
+ salt[3] = ntp_random();
+ last_salt_update = current_time;
+ }
+
+ EVP_DigestInit(&ctx, EVP_get_digestbynid(NID_md5));
+ EVP_DigestUpdate(&ctx, salt, sizeof(salt));
+ EVP_DigestUpdate(&ctx, &ts_i, sizeof(ts_i));
+ EVP_DigestUpdate(&ctx, &ts_f, sizeof(ts_f));
+ if (IS_IPV4(addr))
+ EVP_DigestUpdate(&ctx, &SOCK_ADDR4(addr),
+ sizeof(SOCK_ADDR4(addr)));
+ else
+ EVP_DigestUpdate(&ctx, &SOCK_ADDR6(addr),
+ sizeof(SOCK_ADDR6(addr)));
+ EVP_DigestUpdate(&ctx, &NSRCPORT(addr), sizeof(NSRCPORT(addr)));
+ EVP_DigestUpdate(&ctx, salt, sizeof(salt));
+ EVP_DigestFinal(&ctx, d.digest, &len);
+
+ return d.extract;
+}
+
+
+/*
+ * generate_nonce - generate client-address-specific nonce string.
+ */
+static void generate_nonce(
+ struct recvbuf * rbufp,
+ char * nonce,
+ size_t nonce_octets
+ )
+{
+ u_int32 derived;
+
+ derived = derive_nonce(&rbufp->recv_srcadr,
+ rbufp->recv_time.l_ui,
+ rbufp->recv_time.l_uf);
+ snprintf(nonce, nonce_octets, "%08x%08x%08x",
+ rbufp->recv_time.l_ui, rbufp->recv_time.l_uf, derived);
+}
+
+
+/*
+ * validate_nonce - validate client-address-specific nonce string.
+ *
+ * Returns TRUE if the local calculation of the nonce matches the
+ * client-provided value and the timestamp is recent enough.
+ */
+static int validate_nonce(
+ const char * pnonce,
+ struct recvbuf * rbufp
+ )
+{
+ u_int ts_i;
+ u_int ts_f;
+ l_fp ts;
+ l_fp now_delta;
+ u_int supposed;
+ u_int derived;
+
+ if (3 != sscanf(pnonce, "%08x%08x%08x", &ts_i, &ts_f, &supposed))
+ return FALSE;
+
+ ts.l_ui = (u_int32)ts_i;
+ ts.l_uf = (u_int32)ts_f;
+ derived = derive_nonce(&rbufp->recv_srcadr, ts.l_ui, ts.l_uf);
+ get_systime(&now_delta);
+ L_SUB(&now_delta, &ts);
+
+ return (supposed == derived && now_delta.l_ui < 16);
+}
+
+
+/*
+ * send_random_tag_value - send a randomly-generated three character
+ * tag prefix, a '.', an index, a '=' and a
+ * random integer value.
+ *
+ * To try to force clients to ignore unrecognized tags in mrulist,
+ * reslist, and ifstats responses, the first and last rows are spiced
+ * with randomly-generated tag names with correct .# index. Make it
+ * three characters knowing that none of the currently-used subscripted
+ * tags have that length, avoiding the need to test for
+ * tag collision.
+ */
+static void
+send_random_tag_value(
+ int indx
+ )
+{
+ int noise;
+ char buf[32];
+
+ noise = rand() ^ (rand() << 16);
+ buf[0] = 'a' + noise % 26;
+ noise >>= 5;
+ buf[1] = 'a' + noise % 26;
+ noise >>= 5;
+ buf[2] = 'a' + noise % 26;
+ noise >>= 5;
+ buf[3] = '.';
+ snprintf(&buf[4], sizeof(buf) - 4, "%d", indx);
+ ctl_putuint(buf, noise);
+}
+
+
+/*
+ * Send a MRU list entry in response to a "ntpq -c mrulist" operation.
+ *
+ * To keep clients honest about not depending on the order of values,
+ * and thereby avoid being locked into ugly workarounds to maintain
+ * backward compatibility later as new fields are added to the response,
+ * the order is random.
+ */
+static void
+send_mru_entry(
+ mon_entry * mon,
+ int count
+ )
+{
+ const char first_fmt[] = "first.%d";
+ const char ct_fmt[] = "ct.%d";
+ const char mv_fmt[] = "mv.%d";
+ const char rs_fmt[] = "rs.%d";
+ char tag[32];
+ u_char sent[6]; /* 6 tag=value pairs */
+ u_int32 noise;
+ u_int which;
+ u_int remaining;
+ const char * pch;
+
+ remaining = COUNTOF(sent);
+ ZERO(sent);
+ noise = (u_int32)(rand() ^ (rand() << 16));
+ while (remaining > 0) {
+ which = (noise & 7) % COUNTOF(sent);
+ noise >>= 3;
+ while (sent[which])
+ which = (which + 1) % COUNTOF(sent);
+
+ switch (which) {
+
+ case 0:
+ snprintf(tag, sizeof(tag), addr_fmt, count);
+ pch = sptoa(&mon->rmtadr);
+ ctl_putunqstr(tag, pch, strlen(pch));
+ break;
+
+ case 1:
+ snprintf(tag, sizeof(tag), last_fmt, count);
+ ctl_putts(tag, &mon->last);
+ break;
+
+ case 2:
+ snprintf(tag, sizeof(tag), first_fmt, count);
+ ctl_putts(tag, &mon->first);
+ break;
+
+ case 3:
+ snprintf(tag, sizeof(tag), ct_fmt, count);
+ ctl_putint(tag, mon->count);
+ break;
+
+ case 4:
+ snprintf(tag, sizeof(tag), mv_fmt, count);
+ ctl_putuint(tag, mon->vn_mode);
+ break;
+
+ case 5:
+ snprintf(tag, sizeof(tag), rs_fmt, count);
+ ctl_puthex(tag, mon->flags);
+ break;
+ }
+ sent[which] = TRUE;
+ remaining--;
+ }
+}
+
+
+/*
+ * read_mru_list - supports ntpq's mrulist command.
+ *
+ * The challenge here is to match ntpdc's monlist functionality without
+ * being limited to hundreds of entries returned total, and without
+ * requiring state on the server. If state were required, ntpq's
+ * mrulist command would require authentication.
+ *
+ * The approach was suggested by Ry Jones. A finite and variable number
+ * of entries are retrieved per request, to avoid having responses with
+ * such large numbers of packets that socket buffers are overflowed and
+ * packets lost. The entries are retrieved oldest-first, taking into
+ * account that the MRU list will be changing between each request. We
+ * can expect to see duplicate entries for addresses updated in the MRU
+ * list during the fetch operation. In the end, the client can assemble
+ * a close approximation of the MRU list at the point in time the last
+ * response was sent by ntpd. The only difference is it may be longer,
+ * containing some number of oldest entries which have since been
+ * reclaimed. If necessary, the protocol could be extended to zap those
+ * from the client snapshot at the end, but so far that doesn't seem
+ * useful.
+ *
+ * To accomodate the changing MRU list, the starting point for requests
+ * after the first request is supplied as a series of last seen
+ * timestamps and associated addresses, the newest ones the client has
+ * received. As long as at least one of those entries hasn't been
+ * bumped to the head of the MRU list, ntpd can pick up at that point.
+ * Otherwise, the request is failed and it is up to ntpq to back up and
+ * provide the next newest entry's timestamps and addresses, conceivably
+ * backing up all the way to the starting point.
+ *
+ * input parameters:
+ * nonce= Regurgitated nonce retrieved by the client
+ * previously using CTL_OP_REQ_NONCE, demonstrating
+ * ability to receive traffic sent to its address.
+ * frags= Limit on datagrams (fragments) in response. Used
+ * by newer ntpq versions instead of limit= when
+ * retrieving multiple entries.
+ * limit= Limit on MRU entries returned. One of frags= or
+ * limit= must be provided.
+ * limit=1 is a special case: Instead of fetching
+ * beginning with the supplied starting point's
+ * newer neighbor, fetch the supplied entry, and
+ * in that case the #.last timestamp can be zero.
+ * This enables fetching a single entry by IP
+ * address. When limit is not one and frags= is
+ * provided, the fragment limit controls.
+ * mincount= (decimal) Return entries with count >= mincount.
+ * laddr= Return entries associated with the server's IP
+ * address given. No port specification is needed,
+ * and any supplied is ignored.
+ * resall= 0x-prefixed hex restrict bits which must all be
+ * lit for an MRU entry to be included.
+ * Has precedence over any resany=.
+ * resany= 0x-prefixed hex restrict bits, at least one of
+ * which must be list for an MRU entry to be
+ * included.
+ * last.0= 0x-prefixed hex l_fp timestamp of newest entry
+ * which client previously received.
+ * addr.0= text of newest entry's IP address and port,
+ * IPv6 addresses in bracketed form: [::]:123
+ * last.1= timestamp of 2nd newest entry client has.
+ * addr.1= address of 2nd newest entry.
+ * [...]
+ *
+ * ntpq provides as many last/addr pairs as will fit in a single request
+ * packet, except for the first request in a MRU fetch operation.
+ *
+ * The response begins with a new nonce value to be used for any
+ * followup request. Following the nonce is the next newer entry than
+ * referred to by last.0 and addr.0, if the "0" entry has not been
+ * bumped to the front. If it has, the first entry returned will be the
+ * next entry newer than referred to by last.1 and addr.1, and so on.
+ * If none of the referenced entries remain unchanged, the request fails
+ * and ntpq backs up to the next earlier set of entries to resync.
+ *
+ * Except for the first response, the response begins with confirmation
+ * of the entry that precedes the first additional entry provided:
+ *
+ * last.older= hex l_fp timestamp matching one of the input
+ * .last timestamps, which entry now precedes the
+ * response 0. entry in the MRU list.
+ * addr.older= text of address corresponding to older.last.
+ *
+ * And in any case, a successful response contains sets of values
+ * comprising entries, with the oldest numbered 0 and incrementing from
+ * there:
+ *
+ * addr.# text of IPv4 or IPv6 address and port
+ * last.# hex l_fp timestamp of last receipt
+ * first.# hex l_fp timestamp of first receipt
+ * ct.# count of packets received
+ * mv.# mode and version
+ * rs.# restriction mask (RES_* bits)
+ *
+ * Note the code currently assumes there are no valid three letter
+ * tags sent with each row, and needs to be adjusted if that changes.
+ *
+ * The client should accept the values in any order, and ignore .#
+ * values which it does not understand, to allow a smooth path to
+ * future changes without requiring a new opcode. Clients can rely
+ * on all *.0 values preceding any *.1 values, that is all values for
+ * a given index number are together in the response.
+ *
+ * The end of the response list is noted with one or two tag=value
+ * pairs. Unconditionally:
+ *
+ * now= 0x-prefixed l_fp timestamp at the server marking
+ * the end of the operation.
+ *
+ * If any entries were returned, now= is followed by:
+ *
+ * last.newest= hex l_fp identical to last.# of the prior
+ * entry.
+ */
+static void read_mru_list(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ const char nonce_text[] = "nonce";
+ const char frags_text[] = "frags";
+ const char limit_text[] = "limit";
+ const char mincount_text[] = "mincount";
+ const char resall_text[] = "resall";
+ const char resany_text[] = "resany";
+ const char maxlstint_text[] = "maxlstint";
+ const char laddr_text[] = "laddr";
+ const char resaxx_fmt[] = "0x%hx";
+ u_int limit;
+ u_short frags;
+ u_short resall;
+ u_short resany;
+ int mincount;
+ u_int maxlstint;
+ sockaddr_u laddr;
+ struct interface * lcladr;
+ u_int count;
+ u_int ui;
+ u_int uf;
+ l_fp last[16];
+ sockaddr_u addr[COUNTOF(last)];
+ char buf[128];
+ struct ctl_var * in_parms;
+ const struct ctl_var * v;
+ char * val;
+ const char * pch;
+ char * pnonce;
+ int nonce_valid;
+ int i;
+ int priors;
+ u_short hash;
+ mon_entry * mon;
+ mon_entry * prior_mon;
+ l_fp now;
+
+ if (RES_NOMRULIST & restrict_mask) {
+ ctl_error(CERR_PERMISSION);
+ NLOG(NLOG_SYSINFO)
+ msyslog(LOG_NOTICE,
+ "mrulist from %s rejected due to nomrulist restriction",
+ stoa(&rbufp->recv_srcadr));
+ sys_restricted++;
+ return;
+ }
+ /*
+ * fill in_parms var list with all possible input parameters.
+ */
+ in_parms = NULL;
+ set_var(&in_parms, nonce_text, sizeof(nonce_text), 0);
+ set_var(&in_parms, frags_text, sizeof(frags_text), 0);
+ set_var(&in_parms, limit_text, sizeof(limit_text), 0);
+ set_var(&in_parms, mincount_text, sizeof(mincount_text), 0);
+ set_var(&in_parms, resall_text, sizeof(resall_text), 0);
+ set_var(&in_parms, resany_text, sizeof(resany_text), 0);
+ set_var(&in_parms, maxlstint_text, sizeof(maxlstint_text), 0);
+ set_var(&in_parms, laddr_text, sizeof(laddr_text), 0);
+ for (i = 0; i < COUNTOF(last); i++) {
+ snprintf(buf, sizeof(buf), last_fmt, i);
+ set_var(&in_parms, buf, strlen(buf) + 1, 0);
+ snprintf(buf, sizeof(buf), addr_fmt, i);
+ set_var(&in_parms, buf, strlen(buf) + 1, 0);
+ }
+
+ /* decode input parms */
+ pnonce = NULL;
+ frags = 0;
+ limit = 0;
+ mincount = 0;
+ resall = 0;
+ resany = 0;
+ maxlstint = 0;
+ lcladr = NULL;
+ priors = 0;
+ ZERO(last);
+ ZERO(addr);
+
+ while (NULL != (v = ctl_getitem(in_parms, &val)) &&
+ !(EOV & v->flags)) {
+
+ if (!strcmp(nonce_text, v->text)) {
+ if (NULL != pnonce)
+ free(pnonce);
+ pnonce = estrdup(val);
+ } else if (!strcmp(frags_text, v->text)) {
+ sscanf(val, "%hu", &frags);
+ } else if (!strcmp(limit_text, v->text)) {
+ sscanf(val, "%u", &limit);
+ } else if (!strcmp(mincount_text, v->text)) {
+ if (1 != sscanf(val, "%d", &mincount) ||
+ mincount < 0)
+ mincount = 0;
+ } else if (!strcmp(resall_text, v->text)) {
+ sscanf(val, resaxx_fmt, &resall);
+ } else if (!strcmp(resany_text, v->text)) {
+ sscanf(val, resaxx_fmt, &resany);
+ } else if (!strcmp(maxlstint_text, v->text)) {
+ sscanf(val, "%u", &maxlstint);
+ } else if (!strcmp(laddr_text, v->text)) {
+ if (decodenetnum(val, &laddr))
+ lcladr = getinterface(&laddr, 0);
+ } else if (1 == sscanf(v->text, last_fmt, &i) &&
+ i < COUNTOF(last)) {
+ if (2 == sscanf(val, "0x%08x.%08x", &ui, &uf)) {
+ last[i].l_ui = ui;
+ last[i].l_uf = uf;
+ if (!SOCK_UNSPEC(&addr[i]) &&
+ i == priors)
+ priors++;
+ }
+ } else if (1 == sscanf(v->text, addr_fmt, &i) &&
+ i < COUNTOF(addr)) {
+ if (decodenetnum(val, &addr[i])
+ && last[i].l_ui && last[i].l_uf &&
+ i == priors)
+ priors++;
+ }
+ }
+ free_varlist(in_parms);
+ in_parms = NULL;
+
+ /* return no responses until the nonce is validated */
+ if (NULL == pnonce)
+ return;
+
+ nonce_valid = validate_nonce(pnonce, rbufp);
+ free(pnonce);
+ if (!nonce_valid)
+ return;
+
+ if ((0 == frags && !(0 < limit && limit <= MRU_ROW_LIMIT)) ||
+ frags > MRU_FRAGS_LIMIT) {
+ ctl_error(CERR_BADVALUE);
+ return;
+ }
+
+ /*
+ * If either frags or limit is not given, use the max.
+ */
+ if (0 != frags && 0 == limit)
+ limit = UINT_MAX;
+ else if (0 != limit && 0 == frags)
+ frags = MRU_FRAGS_LIMIT;
+
+ /*
+ * Find the starting point if one was provided.
+ */
+ mon = NULL;
+ for (i = 0; i < priors; i++) {
+ hash = MON_HASH(&addr[i]);
+ for (mon = mon_hash[hash];
+ mon != NULL;
+ mon = mon->hash_next)
+ if (ADDR_PORT_EQ(&mon->rmtadr, &addr[i]))
+ break;
+ if (mon != NULL) {
+ if (L_ISEQU(&mon->last, &last[i]))
+ break;
+ mon = NULL;
+ }
+ }
+
+ /* If a starting point was provided... */
+ if (priors) {
+ /* and none could be found unmodified... */
+ if (NULL == mon) {
+ /* tell ntpq to try again with older entries */
+ ctl_error(CERR_UNKNOWNVAR);
+ return;
+ }
+ /* confirm the prior entry used as starting point */
+ ctl_putts("last.older", &mon->last);
+ pch = sptoa(&mon->rmtadr);
+ ctl_putunqstr("addr.older", pch, strlen(pch));
+
+ /*
+ * Move on to the first entry the client doesn't have,
+ * except in the special case of a limit of one. In
+ * that case return the starting point entry.
+ */
+ if (limit > 1)
+ mon = PREV_DLIST(mon_mru_list, mon, mru);
+ } else { /* start with the oldest */
+ mon = TAIL_DLIST(mon_mru_list, mru);
+ }
+
+ /*
+ * send up to limit= entries in up to frags= datagrams
+ */
+ get_systime(&now);
+ generate_nonce(rbufp, buf, sizeof(buf));
+ ctl_putunqstr("nonce", buf, strlen(buf));
+ prior_mon = NULL;
+ for (count = 0;
+ mon != NULL && res_frags < frags && count < limit;
+ mon = PREV_DLIST(mon_mru_list, mon, mru)) {
+
+ if (mon->count < mincount)
+ continue;
+ if (resall && resall != (resall & mon->flags))
+ continue;
+ if (resany && !(resany & mon->flags))
+ continue;
+ if (maxlstint > 0 && now.l_ui - mon->last.l_ui >
+ maxlstint)
+ continue;
+ if (lcladr != NULL && mon->lcladr != lcladr)
+ continue;
+
+ send_mru_entry(mon, count);
+ if (!count)
+ send_random_tag_value(0);
+ count++;
+ prior_mon = mon;
+ }
+
+ /*
+ * If this batch completes the MRU list, say so explicitly with
+ * a now= l_fp timestamp.
+ */
+ if (NULL == mon) {
+ if (count > 1)
+ send_random_tag_value(count - 1);
+ ctl_putts("now", &now);
+ /* if any entries were returned confirm the last */
+ if (prior_mon != NULL)
+ ctl_putts("last.newest", &prior_mon->last);
+ }
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * Send a ifstats entry in response to a "ntpq -c ifstats" request.
+ *
+ * To keep clients honest about not depending on the order of values,
+ * and thereby avoid being locked into ugly workarounds to maintain
+ * backward compatibility later as new fields are added to the response,
+ * the order is random.
+ */
+static void
+send_ifstats_entry(
+ endpt * la,
+ u_int ifnum
+ )
+{
+ const char addr_fmtu[] = "addr.%u";
+ const char bcast_fmt[] = "bcast.%u";
+ const char en_fmt[] = "en.%u"; /* enabled */
+ const char name_fmt[] = "name.%u";
+ const char flags_fmt[] = "flags.%u";
+ const char tl_fmt[] = "tl.%u"; /* ttl */
+ const char mc_fmt[] = "mc.%u"; /* mcast count */
+ const char rx_fmt[] = "rx.%u";
+ const char tx_fmt[] = "tx.%u";
+ const char txerr_fmt[] = "txerr.%u";
+ const char pc_fmt[] = "pc.%u"; /* peer count */
+ const char up_fmt[] = "up.%u"; /* uptime */
+ char tag[32];
+ u_char sent[IFSTATS_FIELDS]; /* 12 tag=value pairs */
+ int noisebits;
+ u_int32 noise;
+ u_int which;
+ u_int remaining;
+ const char *pch;
+
+ remaining = COUNTOF(sent);
+ ZERO(sent);
+ noise = 0;
+ noisebits = 0;
+ while (remaining > 0) {
+ if (noisebits < 4) {
+ noise = rand() ^ (rand() << 16);
+ noisebits = 31;
+ }
+ which = (noise & 0xf) % COUNTOF(sent);
+ noise >>= 4;
+ noisebits -= 4;
+
+ while (sent[which])
+ which = (which + 1) % COUNTOF(sent);
+
+ switch (which) {
+
+ case 0:
+ snprintf(tag, sizeof(tag), addr_fmtu, ifnum);
+ pch = sptoa(&la->sin);
+ ctl_putunqstr(tag, pch, strlen(pch));
+ break;
+
+ case 1:
+ snprintf(tag, sizeof(tag), bcast_fmt, ifnum);
+ if (INT_BCASTOPEN & la->flags)
+ pch = sptoa(&la->bcast);
+ else
+ pch = "";
+ ctl_putunqstr(tag, pch, strlen(pch));
+ break;
+
+ case 2:
+ snprintf(tag, sizeof(tag), en_fmt, ifnum);
+ ctl_putint(tag, !la->ignore_packets);
+ break;
+
+ case 3:
+ snprintf(tag, sizeof(tag), name_fmt, ifnum);
+ ctl_putstr(tag, la->name, strlen(la->name));
+ break;
+
+ case 4:
+ snprintf(tag, sizeof(tag), flags_fmt, ifnum);
+ ctl_puthex(tag, (u_int)la->flags);
+ break;
+
+ case 5:
+ snprintf(tag, sizeof(tag), tl_fmt, ifnum);
+ ctl_putint(tag, la->last_ttl);
+ break;
+
+ case 6:
+ snprintf(tag, sizeof(tag), mc_fmt, ifnum);
+ ctl_putint(tag, la->num_mcast);
+ break;
+
+ case 7:
+ snprintf(tag, sizeof(tag), rx_fmt, ifnum);
+ ctl_putint(tag, la->received);
+ break;
+
+ case 8:
+ snprintf(tag, sizeof(tag), tx_fmt, ifnum);
+ ctl_putint(tag, la->sent);
+ break;
+
+ case 9:
+ snprintf(tag, sizeof(tag), txerr_fmt, ifnum);
+ ctl_putint(tag, la->notsent);
+ break;
+
+ case 10:
+ snprintf(tag, sizeof(tag), pc_fmt, ifnum);
+ ctl_putuint(tag, la->peercnt);
+ break;
+
+ case 11:
+ snprintf(tag, sizeof(tag), up_fmt, ifnum);
+ ctl_putuint(tag, current_time - la->starttime);
+ break;
+ }
+ sent[which] = TRUE;
+ remaining--;
+ }
+ send_random_tag_value((int)ifnum);
+}
+
+
+/*
+ * read_ifstats - send statistics for each local address, exposed by
+ * ntpq -c ifstats
+ */
+static void
+read_ifstats(
+ struct recvbuf * rbufp
+ )
+{
+ u_int ifidx;
+ endpt * la;
+
+ /*
+ * loop over [0..sys_ifnum] searching ep_list for each
+ * ifnum in turn.
+ */
+ for (ifidx = 0; ifidx < sys_ifnum; ifidx++) {
+ for (la = ep_list; la != NULL; la = la->elink)
+ if (ifidx == la->ifnum)
+ break;
+ if (NULL == la)
+ continue;
+ /* return stats for one local address */
+ send_ifstats_entry(la, ifidx);
+ }
+ ctl_flushpkt(0);
+}
+
+static void
+sockaddrs_from_restrict_u(
+ sockaddr_u * psaA,
+ sockaddr_u * psaM,
+ restrict_u * pres,
+ int ipv6
+ )
+{
+ ZERO(*psaA);
+ ZERO(*psaM);
+ if (!ipv6) {
+ psaA->sa.sa_family = AF_INET;
+ psaA->sa4.sin_addr.s_addr = htonl(pres->u.v4.addr);
+ psaM->sa.sa_family = AF_INET;
+ psaM->sa4.sin_addr.s_addr = htonl(pres->u.v4.mask);
+ } else {
+ psaA->sa.sa_family = AF_INET6;
+ memcpy(&psaA->sa6.sin6_addr, &pres->u.v6.addr,
+ sizeof(psaA->sa6.sin6_addr));
+ psaM->sa.sa_family = AF_INET6;
+ memcpy(&psaM->sa6.sin6_addr, &pres->u.v6.mask,
+ sizeof(psaA->sa6.sin6_addr));
+ }
+}
+
+
+/*
+ * Send a restrict entry in response to a "ntpq -c reslist" request.
+ *
+ * To keep clients honest about not depending on the order of values,
+ * and thereby avoid being locked into ugly workarounds to maintain
+ * backward compatibility later as new fields are added to the response,
+ * the order is random.
+ */
+static void
+send_restrict_entry(
+ restrict_u * pres,
+ int ipv6,
+ u_int idx
+ )
+{
+ const char addr_fmtu[] = "addr.%u";
+ const char mask_fmtu[] = "mask.%u";
+ const char hits_fmt[] = "hits.%u";
+ const char flags_fmt[] = "flags.%u";
+ char tag[32];
+ u_char sent[RESLIST_FIELDS]; /* 4 tag=value pairs */
+ int noisebits;
+ u_int32 noise;
+ u_int which;
+ u_int remaining;
+ sockaddr_u addr;
+ sockaddr_u mask;
+ const char * pch;
+ char * buf;
+ const char * match_str;
+ const char * access_str;
+
+ sockaddrs_from_restrict_u(&addr, &mask, pres, ipv6);
+ remaining = COUNTOF(sent);
+ ZERO(sent);
+ noise = 0;
+ noisebits = 0;
+ while (remaining > 0) {
+ if (noisebits < 2) {
+ noise = rand() ^ (rand() << 16);
+ noisebits = 31;
+ }
+ which = (noise & 0x3) % COUNTOF(sent);
+ noise >>= 2;
+ noisebits -= 2;
+
+ while (sent[which])
+ which = (which + 1) % COUNTOF(sent);
+
+ switch (which) {
+
+ case 0:
+ snprintf(tag, sizeof(tag), addr_fmtu, idx);
+ pch = stoa(&addr);
+ ctl_putunqstr(tag, pch, strlen(pch));
+ break;
+
+ case 1:
+ snprintf(tag, sizeof(tag), mask_fmtu, idx);
+ pch = stoa(&mask);
+ ctl_putunqstr(tag, pch, strlen(pch));
+ break;
+
+ case 2:
+ snprintf(tag, sizeof(tag), hits_fmt, idx);
+ ctl_putuint(tag, pres->count);
+ break;
+
+ case 3:
+ snprintf(tag, sizeof(tag), flags_fmt, idx);
+ match_str = res_match_flags(pres->mflags);
+ access_str = res_access_flags(pres->flags);
+ if ('\0' == match_str[0]) {
+ pch = access_str;
+ } else {
+ LIB_GETBUF(buf);
+ snprintf(buf, LIB_BUFLENGTH, "%s %s",
+ match_str, access_str);
+ pch = buf;
+ }
+ ctl_putunqstr(tag, pch, strlen(pch));
+ break;
+ }
+ sent[which] = TRUE;
+ remaining--;
+ }
+ send_random_tag_value((int)idx);
+}
+
+
+static void
+send_restrict_list(
+ restrict_u * pres,
+ int ipv6,
+ u_int * pidx
+ )
+{
+ for ( ; pres != NULL; pres = pres->link) {
+ send_restrict_entry(pres, ipv6, *pidx);
+ (*pidx)++;
+ }
+}
+
+
+/*
+ * read_addr_restrictions - returns IPv4 and IPv6 access control lists
+ */
+static void
+read_addr_restrictions(
+ struct recvbuf * rbufp
+)
+{
+ u_int idx;
+
+ idx = 0;
+ send_restrict_list(restrictlist4, FALSE, &idx);
+ send_restrict_list(restrictlist6, TRUE, &idx);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_ordlist - CTL_OP_READ_ORDLIST_A for ntpq -c ifstats & reslist
+ */
+static void
+read_ordlist(
+ struct recvbuf * rbufp,
+ int restrict_mask
+ )
+{
+ const char ifstats_s[] = "ifstats";
+ const size_t ifstats_chars = COUNTOF(ifstats_s) - 1;
+ const char addr_rst_s[] = "addr_restrictions";
+ const size_t a_r_chars = COUNTOF(addr_rst_s) - 1;
+ struct ntp_control * cpkt;
+ u_short qdata_octets;
+
+ /*
+ * CTL_OP_READ_ORDLIST_A was first named CTL_OP_READ_IFSTATS and
+ * used only for ntpq -c ifstats. With the addition of reslist
+ * the same opcode was generalized to retrieve ordered lists
+ * which require authentication. The request data is empty or
+ * contains "ifstats" (not null terminated) to retrieve local
+ * addresses and associated stats. It is "addr_restrictions"
+ * to retrieve the IPv4 then IPv6 remote address restrictions,
+ * which are access control lists. Other request data return
+ * CERR_UNKNOWNVAR.
+ */
+ cpkt = (struct ntp_control *)&rbufp->recv_pkt;
+ qdata_octets = ntohs(cpkt->count);
+ if (0 == qdata_octets || (ifstats_chars == qdata_octets &&
+ !memcmp(ifstats_s, cpkt->u.data, ifstats_chars))) {
+ read_ifstats(rbufp);
+ return;
+ }
+ if (a_r_chars == qdata_octets &&
+ !memcmp(addr_rst_s, cpkt->u.data, a_r_chars)) {
+ read_addr_restrictions(rbufp);
+ return;
+ }
+ ctl_error(CERR_UNKNOWNVAR);
+}
+
+
+/*
+ * req_nonce - CTL_OP_REQ_NONCE for ntpq -c mrulist prerequisite.
+ */
+static void req_nonce(
+ struct recvbuf * rbufp,
+ int restrict_mask
+ )
+{
+ char buf[64];
+
+ generate_nonce(rbufp, buf, sizeof(buf));
+ ctl_putunqstr("nonce", buf, strlen(buf));
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_clockstatus - return clock radio status
+ */
+/*ARGSUSED*/
+static void
+read_clockstatus(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+#ifndef REFCLOCK
+ /*
+ * If no refclock support, no data to return
+ */
+ ctl_error(CERR_BADASSOC);
+#else
+ const struct ctl_var * v;
+ int i;
+ struct peer * peer;
+ char * valuep;
+ u_char * wants;
+ size_t wants_alloc;
+ int gotvar;
+ const u_char * cc;
+ struct ctl_var * kv;
+ struct refclockstat cs;
+
+ if (res_associd != 0) {
+ peer = findpeerbyassoc(res_associd);
+ } else {
+ /*
+ * Find a clock for this jerk. If the system peer
+ * is a clock use it, else search peer_list for one.
+ */
+ if (sys_peer != NULL && (FLAG_REFCLOCK &
+ sys_peer->flags))
+ peer = sys_peer;
+ else
+ for (peer = peer_list;
+ peer != NULL;
+ peer = peer->p_link)
+ if (FLAG_REFCLOCK & peer->flags)
+ break;
+ }
+ if (NULL == peer || !(FLAG_REFCLOCK & peer->flags)) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ /*
+ * If we got here we have a peer which is a clock. Get his
+ * status.
+ */
+ cs.kv_list = NULL;
+ refclock_control(&peer->srcadr, NULL, &cs);
+ kv = cs.kv_list;
+ /*
+ * Look for variables in the packet.
+ */
+ rpkt.status = htons(ctlclkstatus(&cs));
+ wants_alloc = CC_MAXCODE + 1 + count_var(kv);
+ wants = emalloc_zero(wants_alloc);
+ gotvar = FALSE;
+ while (NULL != (v = ctl_getitem(clock_var, &valuep))) {
+ if (!(EOV & v->flags)) {
+ wants[v->code] = TRUE;
+ gotvar = TRUE;
+ } else {
+ v = ctl_getitem(kv, &valuep);
+ NTP_INSIST(NULL != v);
+ if (EOV & v->flags) {
+ ctl_error(CERR_UNKNOWNVAR);
+ free(wants);
+ free_varlist(cs.kv_list);
+ return;
+ }
+ wants[CC_MAXCODE + 1 + v->code] = TRUE;
+ gotvar = TRUE;
+ }
+ }
+
+ if (gotvar) {
+ for (i = 1; i <= CC_MAXCODE; i++)
+ if (wants[i])
+ ctl_putclock(i, &cs, TRUE);
+ if (kv != NULL)
+ for (i = 0; !(EOV & kv[i].flags); i++)
+ if (wants[i + CC_MAXCODE + 1])
+ ctl_putdata(kv[i].text,
+ strlen(kv[i].text),
+ FALSE);
+ } else {
+ for (cc = def_clock_var; *cc != 0; cc++)
+ ctl_putclock((int)*cc, &cs, FALSE);
+ for ( ; kv != NULL && !(EOV & kv->flags); kv++)
+ if (DEF & kv->flags)
+ ctl_putdata(kv->text, strlen(kv->text),
+ FALSE);
+ }
+
+ free(wants);
+ free_varlist(cs.kv_list);
+
+ ctl_flushpkt(0);
+#endif
+}
+
+
+/*
+ * write_clockstatus - we don't do this
+ */
+/*ARGSUSED*/
+static void
+write_clockstatus(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ ctl_error(CERR_PERMISSION);
+}
+
+/*
+ * Trap support from here on down. We send async trap messages when the
+ * upper levels report trouble. Traps can by set either by control
+ * messages or by configuration.
+ */
+/*
+ * set_trap - set a trap in response to a control message
+ */
+static void
+set_trap(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ int traptype;
+
+ /*
+ * See if this guy is allowed
+ */
+ if (restrict_mask & RES_NOTRAP) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+
+ /*
+ * Determine his allowed trap type.
+ */
+ traptype = TRAP_TYPE_PRIO;
+ if (restrict_mask & RES_LPTRAP)
+ traptype = TRAP_TYPE_NONPRIO;
+
+ /*
+ * Call ctlsettrap() to do the work. Return
+ * an error if it can't assign the trap.
+ */
+ if (!ctlsettrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype,
+ (int)res_version))
+ ctl_error(CERR_NORESOURCE);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * unset_trap - unset a trap in response to a control message
+ */
+static void
+unset_trap(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ int traptype;
+
+ /*
+ * We don't prevent anyone from removing his own trap unless the
+ * trap is configured. Note we also must be aware of the
+ * possibility that restriction flags were changed since this
+ * guy last set his trap. Set the trap type based on this.
+ */
+ traptype = TRAP_TYPE_PRIO;
+ if (restrict_mask & RES_LPTRAP)
+ traptype = TRAP_TYPE_NONPRIO;
+
+ /*
+ * Call ctlclrtrap() to clear this out.
+ */
+ if (!ctlclrtrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype))
+ ctl_error(CERR_BADASSOC);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * ctlsettrap - called to set a trap
+ */
+int
+ctlsettrap(
+ sockaddr_u *raddr,
+ struct interface *linter,
+ int traptype,
+ int version
+ )
+{
+ size_t n;
+ struct ctl_trap *tp;
+ struct ctl_trap *tptouse;
+
+ /*
+ * See if we can find this trap. If so, we only need update
+ * the flags and the time.
+ */
+ if ((tp = ctlfindtrap(raddr, linter)) != NULL) {
+ switch (traptype) {
+
+ case TRAP_TYPE_CONFIG:
+ tp->tr_flags = TRAP_INUSE|TRAP_CONFIGURED;
+ break;
+
+ case TRAP_TYPE_PRIO:
+ if (tp->tr_flags & TRAP_CONFIGURED)
+ return (1); /* don't change anything */
+ tp->tr_flags = TRAP_INUSE;
+ break;
+
+ case TRAP_TYPE_NONPRIO:
+ if (tp->tr_flags & TRAP_CONFIGURED)
+ return (1); /* don't change anything */
+ tp->tr_flags = TRAP_INUSE|TRAP_NONPRIO;
+ break;
+ }
+ tp->tr_settime = current_time;
+ tp->tr_resets++;
+ return (1);
+ }
+
+ /*
+ * First we heard of this guy. Try to find a trap structure
+ * for him to use, clearing out lesser priority guys if we
+ * have to. Clear out anyone who's expired while we're at it.
+ */
+ tptouse = NULL;
+ for (n = 0; n < COUNTOF(ctl_traps); n++) {
+ tp = &ctl_traps[n];
+ if ((TRAP_INUSE & tp->tr_flags) &&
+ !(TRAP_CONFIGURED & tp->tr_flags) &&
+ ((tp->tr_settime + CTL_TRAPTIME) > current_time)) {
+ tp->tr_flags = 0;
+ num_ctl_traps--;
+ }
+ if (!(TRAP_INUSE & tp->tr_flags)) {
+ tptouse = tp;
+ } else if (!(TRAP_CONFIGURED & tp->tr_flags)) {
+ switch (traptype) {
+
+ case TRAP_TYPE_CONFIG:
+ if (tptouse == NULL) {
+ tptouse = tp;
+ break;
+ }
+ if ((TRAP_NONPRIO & tptouse->tr_flags) &&
+ !(TRAP_NONPRIO & tp->tr_flags))
+ break;
+
+ if (!(TRAP_NONPRIO & tptouse->tr_flags)
+ && (TRAP_NONPRIO & tp->tr_flags)) {
+ tptouse = tp;
+ break;
+ }
+ if (tptouse->tr_origtime <
+ tp->tr_origtime)
+ tptouse = tp;
+ break;
+
+ case TRAP_TYPE_PRIO:
+ if ( TRAP_NONPRIO & tp->tr_flags) {
+ if (tptouse == NULL ||
+ ((TRAP_INUSE &
+ tptouse->tr_flags) &&
+ tptouse->tr_origtime <
+ tp->tr_origtime))
+ tptouse = tp;
+ }
+ break;
+
+ case TRAP_TYPE_NONPRIO:
+ break;
+ }
+ }
+ }
+
+ /*
+ * If we don't have room for him return an error.
+ */
+ if (tptouse == NULL)
+ return (0);
+
+ /*
+ * Set up this structure for him.
+ */
+ tptouse->tr_settime = tptouse->tr_origtime = current_time;
+ tptouse->tr_count = tptouse->tr_resets = 0;
+ tptouse->tr_sequence = 1;
+ tptouse->tr_addr = *raddr;
+ tptouse->tr_localaddr = linter;
+ tptouse->tr_version = (u_char) version;
+ tptouse->tr_flags = TRAP_INUSE;
+ if (traptype == TRAP_TYPE_CONFIG)
+ tptouse->tr_flags |= TRAP_CONFIGURED;
+ else if (traptype == TRAP_TYPE_NONPRIO)
+ tptouse->tr_flags |= TRAP_NONPRIO;
+ num_ctl_traps++;
+ return (1);
+}
+
+
+/*
+ * ctlclrtrap - called to clear a trap
+ */
+int
+ctlclrtrap(
+ sockaddr_u *raddr,
+ struct interface *linter,
+ int traptype
+ )
+{
+ register struct ctl_trap *tp;
+
+ if ((tp = ctlfindtrap(raddr, linter)) == NULL)
+ return (0);
+
+ if (tp->tr_flags & TRAP_CONFIGURED
+ && traptype != TRAP_TYPE_CONFIG)
+ return (0);
+
+ tp->tr_flags = 0;
+ num_ctl_traps--;
+ return (1);
+}
+
+
+/*
+ * ctlfindtrap - find a trap given the remote and local addresses
+ */
+static struct ctl_trap *
+ctlfindtrap(
+ sockaddr_u *raddr,
+ struct interface *linter
+ )
+{
+ size_t n;
+
+ for (n = 0; n < COUNTOF(ctl_traps); n++)
+ if ((ctl_traps[n].tr_flags & TRAP_INUSE)
+ && ADDR_PORT_EQ(raddr, &ctl_traps[n].tr_addr)
+ && (linter == ctl_traps[n].tr_localaddr))
+ return &ctl_traps[n];
+
+ return NULL;
+}
+
+
+/*
+ * report_event - report an event to the trappers
+ */
+void
+report_event(
+ int err, /* error code */
+ struct peer *peer, /* peer structure pointer */
+ const char *str /* protostats string */
+ )
+{
+ char statstr[NTP_MAXSTRLEN];
+ int i;
+ size_t len;
+
+ /*
+ * Report the error to the protostats file, system log and
+ * trappers.
+ */
+ if (peer == NULL) {
+
+ /*
+ * Discard a system report if the number of reports of
+ * the same type exceeds the maximum.
+ */
+ if (ctl_sys_last_event != (u_char)err)
+ ctl_sys_num_events= 0;
+ if (ctl_sys_num_events >= CTL_SYS_MAXEVENTS)
+ return;
+
+ ctl_sys_last_event = (u_char)err;
+ ctl_sys_num_events++;
+ snprintf(statstr, sizeof(statstr),
+ "0.0.0.0 %04x %02x %s",
+ ctlsysstatus(), err, eventstr(err));
+ if (str != NULL) {
+ len = strlen(statstr);
+ snprintf(statstr + len, sizeof(statstr) - len,
+ " %s", str);
+ }
+ NLOG(NLOG_SYSEVENT)
+ msyslog(LOG_INFO, "%s", statstr);
+ } else {
+
+ /*
+ * Discard a peer report if the number of reports of
+ * the same type exceeds the maximum for that peer.
+ */
+ const char * src;
+ u_char errlast;
+
+ errlast = (u_char)err & ~PEER_EVENT;
+ if (peer->last_event == errlast)
+ peer->num_events = 0;
+ if (peer->num_events >= CTL_PEER_MAXEVENTS)
+ return;
+
+ peer->last_event = errlast;
+ peer->num_events++;
+ if (ISREFCLOCKADR(&peer->srcadr))
+ src = refnumtoa(&peer->srcadr);
+ else
+ src = stoa(&peer->srcadr);
+
+ snprintf(statstr, sizeof(statstr),
+ "%s %04x %02x %s", src,
+ ctlpeerstatus(peer), err, eventstr(err));
+ if (str != NULL) {
+ len = strlen(statstr);
+ snprintf(statstr + len, sizeof(statstr) - len,
+ " %s", str);
+ }
+ NLOG(NLOG_PEEREVENT)
+ msyslog(LOG_INFO, "%s", statstr);
+ }
+ record_proto_stats(statstr);
+#if DEBUG
+ if (debug)
+ printf("event at %lu %s\n", current_time, statstr);
+#endif
+
+ /*
+ * If no trappers, return.
+ */
+ if (num_ctl_traps <= 0)
+ return;
+
+ /*
+ * Set up the outgoing packet variables
+ */
+ res_opcode = CTL_OP_ASYNCMSG;
+ res_offset = 0;
+ res_async = TRUE;
+ res_authenticate = FALSE;
+ datapt = rpkt.u.data;
+ dataend = &rpkt.u.data[CTL_MAX_DATA_LEN];
+ if (!(err & PEER_EVENT)) {
+ rpkt.associd = 0;
+ rpkt.status = htons(ctlsysstatus());
+
+ /* Include the core system variables and the list. */
+ for (i = 1; i <= CS_VARLIST; i++)
+ ctl_putsys(i);
+ } else {
+ NTP_INSIST(peer != NULL);
+ rpkt.associd = htons(peer->associd);
+ rpkt.status = htons(ctlpeerstatus(peer));
+
+ /* Dump it all. Later, maybe less. */
+ for (i = 1; i <= CP_MAX_NOAUTOKEY; i++)
+ ctl_putpeer(i, peer);
+#ifdef REFCLOCK
+ /*
+ * for clock exception events: add clock variables to
+ * reflect info on exception
+ */
+ if (err == PEVNT_CLOCK) {
+ struct refclockstat cs;
+ struct ctl_var *kv;
+
+ cs.kv_list = NULL;
+ refclock_control(&peer->srcadr, NULL, &cs);
+
+ ctl_puthex("refclockstatus",
+ ctlclkstatus(&cs));
+
+ for (i = 1; i <= CC_MAXCODE; i++)
+ ctl_putclock(i, &cs, FALSE);
+ for (kv = cs.kv_list;
+ kv != NULL && !(EOV & kv->flags);
+ kv++)
+ if (DEF & kv->flags)
+ ctl_putdata(kv->text,
+ strlen(kv->text),
+ FALSE);
+ free_varlist(cs.kv_list);
+ }
+#endif /* REFCLOCK */
+ }
+
+ /*
+ * We're done, return.
+ */
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * mprintf_event - printf-style varargs variant of report_event()
+ */
+int
+mprintf_event(
+ int evcode, /* event code */
+ struct peer * p, /* may be NULL */
+ const char * fmt, /* msnprintf format */
+ ...
+ )
+{
+ va_list ap;
+ int rc;
+ char msg[512];
+
+ va_start(ap, fmt);
+ rc = mvsnprintf(msg, sizeof(msg), fmt, ap);
+ va_end(ap);
+ report_event(evcode, p, msg);
+
+ return rc;
+}
+
+
+/*
+ * ctl_clr_stats - clear stat counters
+ */
+void
+ctl_clr_stats(void)
+{
+ ctltimereset = current_time;
+ numctlreq = 0;
+ numctlbadpkts = 0;
+ numctlresponses = 0;
+ numctlfrags = 0;
+ numctlerrors = 0;
+ numctlfrags = 0;
+ numctltooshort = 0;
+ numctlinputresp = 0;
+ numctlinputfrag = 0;
+ numctlinputerr = 0;
+ numctlbadoffset = 0;
+ numctlbadversion = 0;
+ numctldatatooshort = 0;
+ numctlbadop = 0;
+ numasyncmsgs = 0;
+}
+
+static u_short
+count_var(
+ const struct ctl_var *k
+ )
+{
+ u_int c;
+
+ if (NULL == k)
+ return 0;
+
+ c = 0;
+ while (!(EOV & (k++)->flags))
+ c++;
+
+ NTP_ENSURE(c <= USHRT_MAX);
+ return (u_short)c;
+}
+
+
+char *
+add_var(
+ struct ctl_var **kv,
+ u_long size,
+ u_short def
+ )
+{
+ u_short c;
+ struct ctl_var *k;
+ char * buf;
+
+ c = count_var(*kv);
+ *kv = erealloc(*kv, (c + 2) * sizeof(**kv));
+ k = *kv;
+ buf = emalloc(size);
+ k[c].code = c;
+ k[c].text = buf;
+ k[c].flags = def;
+ k[c + 1].code = 0;
+ k[c + 1].text = NULL;
+ k[c + 1].flags = EOV;
+
+ return buf;
+}
+
+
+void
+set_var(
+ struct ctl_var **kv,
+ const char *data,
+ u_long size,
+ u_short def
+ )
+{
+ struct ctl_var *k;
+ const char *s;
+ const char *t;
+ char *td;
+
+ if (NULL == data || !size)
+ return;
+
+ k = *kv;
+ if (k != NULL) {
+ while (!(EOV & k->flags)) {
+ if (NULL == k->text) {
+ td = emalloc(size);
+ memcpy(td, data, size);
+ k->text = td;
+ k->flags = def;
+ return;
+ } else {
+ s = data;
+ t = k->text;
+ while (*t != '=' && *s == *t) {
+ s++;
+ t++;
+ }
+ if (*s == *t && ((*t == '=') || !*t)) {
+ td = erealloc(k->text, size);
+ memcpy(td, data, size);
+ k->text = td;
+ k->flags = def;
+ return;
+ }
+ }
+ k++;
+ }
+ }
+ td = add_var(kv, size, def);
+ memcpy(td, data, size);
+}
+
+
+void
+set_sys_var(
+ const char *data,
+ u_long size,
+ u_short def
+ )
+{
+ set_var(&ext_sys_var, data, size, def);
+}
+
+
+/*
+ * get_ext_sys_var() retrieves the value of a user-defined variable or
+ * NULL if the variable has not been setvar'd.
+ */
+const char *
+get_ext_sys_var(const char *tag)
+{
+ struct ctl_var * v;
+ size_t c;
+ const char * val;
+
+ val = NULL;
+ c = strlen(tag);
+ for (v = ext_sys_var; !(EOV & v->flags); v++) {
+ if (NULL != v->text && !memcmp(tag, v->text, c)) {
+ if ('=' == v->text[c]) {
+ val = v->text + c + 1;
+ break;
+ } else if ('\0' == v->text[c]) {
+ val = "";
+ break;
+ }
+ }
+ }
+
+ return val;
+}
+
+
+void
+free_varlist(
+ struct ctl_var *kv
+ )
+{
+ struct ctl_var *k;
+ if (kv) {
+ for (k = kv; !(k->flags & EOV); k++)
+ free((void *)k->text);
+ free((void *)kv);
+ }
+}
diff --git a/ntpd/ntp_crypto.c b/ntpd/ntp_crypto.c
new file mode 100644
index 0000000..e66d5c7
--- /dev/null
+++ b/ntpd/ntp_crypto.c
@@ -0,0 +1,3987 @@
+/*
+ * ntp_crypto.c - NTP version 4 public key routines
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef AUTOKEY
+#include <stdio.h>
+#include <stdlib.h> /* strtoul */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+#include "ntp_string.h"
+#include "ntp_random.h"
+#include "ntp_assert.h"
+#include "ntp_calendar.h"
+#include "ntp_leapsec.h"
+
+#include "openssl/asn1_mac.h"
+#include "openssl/bn.h"
+#include "openssl/err.h"
+#include "openssl/evp.h"
+#include "openssl/pem.h"
+#include "openssl/rand.h"
+#include "openssl/x509v3.h"
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif /* KERNEL_PLL */
+
+/*
+ * calcomp - compare two calendar structures, ignoring yearday and weekday; like strcmp
+ * No, it's not a plotter. If you don't understand that, you're too young.
+ */
+static int calcomp(struct calendar *pjd1, struct calendar *pjd2)
+{
+ int32_t diff; /* large enough to hold the signed difference between two uint16_t values */
+
+ diff = pjd1->year - pjd2->year;
+ if (diff < 0) return -1; else if (diff > 0) return 1;
+ /* same year; compare months */
+ diff = pjd1->month - pjd2->month;
+ if (diff < 0) return -1; else if (diff > 0) return 1;
+ /* same year and month; compare monthday */
+ diff = pjd1->monthday - pjd2->monthday;
+ if (diff < 0) return -1; else if (diff > 0) return 1;
+ /* same year and month and monthday; compare time */
+ diff = pjd1->hour - pjd2->hour;
+ if (diff < 0) return -1; else if (diff > 0) return 1;
+ diff = pjd1->minute - pjd2->minute;
+ if (diff < 0) return -1; else if (diff > 0) return 1;
+ diff = pjd1->second - pjd2->second;
+ if (diff < 0) return -1; else if (diff > 0) return 1;
+ /* identical */
+ return 0;
+}
+
+/*
+ * Extension field message format
+ *
+ * These are always signed and saved before sending in network byte
+ * order. They must be converted to and from host byte order for
+ * processing.
+ *
+ * +-------+-------+
+ * | op | len | <- extension pointer
+ * +-------+-------+
+ * | associd |
+ * +---------------+
+ * | timestamp | <- value pointer
+ * +---------------+
+ * | filestamp |
+ * +---------------+
+ * | value len |
+ * +---------------+
+ * | |
+ * = value =
+ * | |
+ * +---------------+
+ * | signature len |
+ * +---------------+
+ * | |
+ * = signature =
+ * | |
+ * +---------------+
+ *
+ * The CRYPTO_RESP bit is set to 0 for requests, 1 for responses.
+ * Requests carry the association ID of the receiver; responses carry
+ * the association ID of the sender. Some messages include only the
+ * operation/length and association ID words and so have length 8
+ * octets. Ohers include the value structure and associated value and
+ * signature fields. These messages include the timestamp, filestamp,
+ * value and signature words and so have length at least 24 octets. The
+ * signature and/or value fields can be empty, in which case the
+ * respective length words are zero. An empty value with nonempty
+ * signature is syntactically valid, but semantically questionable.
+ *
+ * The filestamp represents the time when a cryptographic data file such
+ * as a public/private key pair is created. It follows every reference
+ * depending on that file and serves as a means to obsolete earlier data
+ * of the same type. The timestamp represents the time when the
+ * cryptographic data of the message were last signed. Creation of a
+ * cryptographic data file or signing a message can occur only when the
+ * creator or signor is synchronized to an authoritative source and
+ * proventicated to a trusted authority.
+ *
+ * Note there are several conditions required for server trust. First,
+ * the public key on the server certificate must be verified, which can
+ * involve a hike along the certificate trail to a trusted host. Next,
+ * the server trust must be confirmed by one of several identity
+ * schemes. Valid cryptographic values are signed with attached
+ * timestamp and filestamp. Individual packet trust is confirmed
+ * relative to these values by a message digest with keys generated by a
+ * reverse-order pseudorandom hash.
+ *
+ * State decomposition. These flags are lit in the order given. They are
+ * dim only when the association is demobilized.
+ *
+ * CRYPTO_FLAG_ENAB Lit upon acceptance of a CRYPTO_ASSOC message
+ * CRYPTO_FLAG_CERT Lit when a self-digned trusted certificate is
+ * accepted.
+ * CRYPTO_FLAG_VRFY Lit when identity is confirmed.
+ * CRYPTO_FLAG_PROV Lit when the first signature is verified.
+ * CRYPTO_FLAG_COOK Lit when a valid cookie is accepted.
+ * CRYPTO_FLAG_AUTO Lit when valid autokey values are accepted.
+ * CRYPTO_FLAG_SIGN Lit when the server signed certificate is
+ * accepted.
+ * CRYPTO_FLAG_LEAP Lit when the leapsecond values are accepted.
+ */
+/*
+ * Cryptodefines
+ */
+#define TAI_1972 10 /* initial TAI offset (s) */
+#define MAX_LEAP 100 /* max UTC leapseconds (s) */
+#define VALUE_LEN (6 * 4) /* min response field length */
+#define YEAR (60 * 60 * 24 * 365) /* seconds in year */
+
+/*
+ * Global cryptodata in host byte order
+ */
+u_int32 crypto_flags = 0x0; /* status word */
+int crypto_nid = KEY_TYPE_MD5; /* digest nid */
+char *sys_hostname = NULL;
+char *sys_groupname = NULL;
+static char *host_filename = NULL; /* host file name */
+static char *ident_filename = NULL; /* group file name */
+
+/*
+ * Global cryptodata in network byte order
+ */
+struct cert_info *cinfo = NULL; /* certificate info/value cache */
+struct cert_info *cert_host = NULL; /* host certificate */
+struct pkey_info *pkinfo = NULL; /* key info/value cache */
+struct value hostval; /* host value */
+struct value pubkey; /* public key */
+struct value tai_leap; /* leapseconds values */
+struct pkey_info *iffkey_info = NULL; /* IFF keys */
+struct pkey_info *gqkey_info = NULL; /* GQ keys */
+struct pkey_info *mvkey_info = NULL; /* MV keys */
+
+/*
+ * Private cryptodata in host byte order
+ */
+static char *passwd = NULL; /* private key password */
+static EVP_PKEY *host_pkey = NULL; /* host key */
+static EVP_PKEY *sign_pkey = NULL; /* sign key */
+static const EVP_MD *sign_digest = NULL; /* sign digest */
+static u_int sign_siglen; /* sign key length */
+static char *rand_file = NULL; /* random seed file */
+
+/*
+ * Cryptotypes
+ */
+static int crypto_verify (struct exten *, struct value *,
+ struct peer *);
+static int crypto_encrypt (struct exten *, struct value *,
+ keyid_t *);
+static int crypto_alice (struct peer *, struct value *);
+static int crypto_alice2 (struct peer *, struct value *);
+static int crypto_alice3 (struct peer *, struct value *);
+static int crypto_bob (struct exten *, struct value *);
+static int crypto_bob2 (struct exten *, struct value *);
+static int crypto_bob3 (struct exten *, struct value *);
+static int crypto_iff (struct exten *, struct peer *);
+static int crypto_gq (struct exten *, struct peer *);
+static int crypto_mv (struct exten *, struct peer *);
+static int crypto_send (struct exten *, struct value *, int);
+static tstamp_t crypto_time (void);
+static void asn_to_calendar (ASN1_TIME *, struct calendar*);
+static struct cert_info *cert_parse (const u_char *, long, tstamp_t);
+static int cert_sign (struct exten *, struct value *);
+static struct cert_info *cert_install (struct exten *, struct peer *);
+static int cert_hike (struct peer *, struct cert_info *);
+static void cert_free (struct cert_info *);
+static struct pkey_info *crypto_key (char *, char *, sockaddr_u *);
+static void bighash (BIGNUM *, BIGNUM *);
+static struct cert_info *crypto_cert (char *);
+
+#ifdef SYS_WINNT
+int
+readlink(char * link, char * file, int len) {
+ return (-1);
+}
+#endif
+
+/*
+ * session_key - generate session key
+ *
+ * This routine generates a session key from the source address,
+ * destination address, key ID and private value. The value of the
+ * session key is the MD5 hash of these values, while the next key ID is
+ * the first four octets of the hash.
+ *
+ * Returns the next key ID or 0 if there is no destination address.
+ */
+keyid_t
+session_key(
+ sockaddr_u *srcadr, /* source address */
+ sockaddr_u *dstadr, /* destination address */
+ keyid_t keyno, /* key ID */
+ keyid_t private, /* private value */
+ u_long lifetime /* key lifetime */
+ )
+{
+ EVP_MD_CTX ctx; /* message digest context */
+ u_char dgst[EVP_MAX_MD_SIZE]; /* message digest */
+ keyid_t keyid; /* key identifer */
+ u_int32 header[10]; /* data in network byte order */
+ u_int hdlen, len;
+
+ if (!dstadr)
+ return 0;
+
+ /*
+ * Generate the session key and key ID. If the lifetime is
+ * greater than zero, install the key and call it trusted.
+ */
+ hdlen = 0;
+ switch(AF(srcadr)) {
+ case AF_INET:
+ header[0] = NSRCADR(srcadr);
+ header[1] = NSRCADR(dstadr);
+ header[2] = htonl(keyno);
+ header[3] = htonl(private);
+ hdlen = 4 * sizeof(u_int32);
+ break;
+
+ case AF_INET6:
+ memcpy(&header[0], PSOCK_ADDR6(srcadr),
+ sizeof(struct in6_addr));
+ memcpy(&header[4], PSOCK_ADDR6(dstadr),
+ sizeof(struct in6_addr));
+ header[8] = htonl(keyno);
+ header[9] = htonl(private);
+ hdlen = 10 * sizeof(u_int32);
+ break;
+ }
+ EVP_DigestInit(&ctx, EVP_get_digestbynid(crypto_nid));
+ EVP_DigestUpdate(&ctx, (u_char *)header, hdlen);
+ EVP_DigestFinal(&ctx, dgst, &len);
+ memcpy(&keyid, dgst, 4);
+ keyid = ntohl(keyid);
+ if (lifetime != 0) {
+ MD5auth_setkey(keyno, crypto_nid, dgst, len);
+ authtrust(keyno, lifetime);
+ }
+ DPRINTF(2, ("session_key: %s > %s %08x %08x hash %08x life %lu\n",
+ stoa(srcadr), stoa(dstadr), keyno,
+ private, keyid, lifetime));
+
+ return (keyid);
+}
+
+
+/*
+ * make_keylist - generate key list
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_ERR protocol error
+ *
+ * This routine constructs a pseudo-random sequence by repeatedly
+ * hashing the session key starting from a given source address,
+ * destination address, private value and the next key ID of the
+ * preceeding session key. The last entry on the list is saved along
+ * with its sequence number and public signature.
+ */
+int
+make_keylist(
+ struct peer *peer, /* peer structure pointer */
+ struct interface *dstadr /* interface */
+ )
+{
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ struct autokey *ap; /* autokey pointer */
+ struct value *vp; /* value pointer */
+ keyid_t keyid = 0; /* next key ID */
+ keyid_t cookie; /* private value */
+ long lifetime;
+ u_int len, mpoll;
+ int i;
+
+ if (!dstadr)
+ return XEVNT_ERR;
+
+ /*
+ * Allocate the key list if necessary.
+ */
+ tstamp = crypto_time();
+ if (peer->keylist == NULL)
+ peer->keylist = emalloc(sizeof(keyid_t) *
+ NTP_MAXSESSION);
+
+ /*
+ * Generate an initial key ID which is unique and greater than
+ * NTP_MAXKEY.
+ */
+ while (1) {
+ keyid = ntp_random() & 0xffffffff;
+ if (keyid <= NTP_MAXKEY)
+ continue;
+
+ if (authhavekey(keyid))
+ continue;
+ break;
+ }
+
+ /*
+ * Generate up to NTP_MAXSESSION session keys. Stop if the
+ * next one would not be unique or not a session key ID or if
+ * it would expire before the next poll. The private value
+ * included in the hash is zero if broadcast mode, the peer
+ * cookie if client mode or the host cookie if symmetric modes.
+ */
+ mpoll = 1 << min(peer->ppoll, peer->hpoll);
+ lifetime = min(1U << sys_automax, NTP_MAXSESSION * mpoll);
+ if (peer->hmode == MODE_BROADCAST)
+ cookie = 0;
+ else
+ cookie = peer->pcookie;
+ for (i = 0; i < NTP_MAXSESSION; i++) {
+ peer->keylist[i] = keyid;
+ peer->keynumber = i;
+ keyid = session_key(&dstadr->sin, &peer->srcadr, keyid,
+ cookie, lifetime + mpoll);
+ lifetime -= mpoll;
+ if (auth_havekey(keyid) || keyid <= NTP_MAXKEY ||
+ lifetime < 0 || tstamp == 0)
+ break;
+ }
+
+ /*
+ * Save the last session key ID, sequence number and timestamp,
+ * then sign these values for later retrieval by the clients. Be
+ * careful not to use invalid key media. Use the public values
+ * timestamp as filestamp.
+ */
+ vp = &peer->sndval;
+ if (vp->ptr == NULL)
+ vp->ptr = emalloc(sizeof(struct autokey));
+ ap = (struct autokey *)vp->ptr;
+ ap->seq = htonl(peer->keynumber);
+ ap->key = htonl(keyid);
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = hostval.tstamp;
+ vp->vallen = htonl(sizeof(struct autokey));
+ vp->siglen = 0;
+ if (tstamp != 0) {
+ if (vp->sig == NULL)
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)vp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, sizeof(struct autokey));
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) {
+ vp->siglen = htonl(sign_siglen);
+ peer->flags |= FLAG_ASSOC;
+ }
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("make_keys: %d %08x %08x ts %u fs %u poll %d\n",
+ peer->keynumber, keyid, cookie, ntohl(vp->tstamp),
+ ntohl(vp->fstamp), peer->hpoll);
+#endif
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_recv - parse extension fields
+ *
+ * This routine is called when the packet has been matched to an
+ * association and passed sanity, format and MAC checks. We believe the
+ * extension field values only if the field has proper format and
+ * length, the timestamp and filestamp are valid and the signature has
+ * valid length and is verified. There are a few cases where some values
+ * are believed even if the signature fails, but only if the proventic
+ * bit is not set.
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_ERR protocol error
+ * XEVNT_LEN bad field format or length
+ */
+int
+crypto_recv(
+ struct peer *peer, /* peer structure pointer */
+ struct recvbuf *rbufp /* packet buffer pointer */
+ )
+{
+ const EVP_MD *dp; /* message digest algorithm */
+ u_int32 *pkt; /* receive packet pointer */
+ struct autokey *ap, *bp; /* autokey pointer */
+ struct exten *ep, *fp; /* extension pointers */
+ struct cert_info *xinfo; /* certificate info pointer */
+ int has_mac; /* length of MAC field */
+ int authlen; /* offset of MAC field */
+ associd_t associd; /* association ID */
+ tstamp_t tstamp = 0; /* timestamp */
+ tstamp_t fstamp = 0; /* filestamp */
+ u_int len; /* extension field length */
+ u_int code; /* extension field opcode */
+ u_int vallen = 0; /* value length */
+ X509 *cert; /* X509 certificate */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ keyid_t cookie; /* crumbles */
+ int hismode; /* packet mode */
+ int rval = XEVNT_OK;
+ const u_char *puch;
+ u_int32 temp32;
+
+ /*
+ * Initialize. Note that the packet has already been checked for
+ * valid format and extension field lengths. First extract the
+ * field length, command code and association ID in host byte
+ * order. These are used with all commands and modes. Then check
+ * the version number, which must be 2, and length, which must
+ * be at least 8 for requests and VALUE_LEN (24) for responses.
+ * Packets that fail either test sink without a trace. The
+ * association ID is saved only if nonzero.
+ */
+ authlen = LEN_PKT_NOMAC;
+ hismode = (int)PKT_MODE((&rbufp->recv_pkt)->li_vn_mode);
+ while ((has_mac = rbufp->recv_length - authlen) > MAX_MAC_LEN) {
+ pkt = (u_int32 *)&rbufp->recv_pkt + authlen / 4;
+ ep = (struct exten *)pkt;
+ code = ntohl(ep->opcode) & 0xffff0000;
+ len = ntohl(ep->opcode) & 0x0000ffff;
+ associd = (associd_t)ntohl(pkt[1]);
+ rval = XEVNT_OK;
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "crypto_recv: flags 0x%x ext offset %d len %u code 0x%x associd %d\n",
+ peer->crypto, authlen, len, code >> 16,
+ associd);
+#endif
+
+ /*
+ * Check version number and field length. If bad,
+ * quietly ignore the packet.
+ */
+ if (((code >> 24) & 0x3f) != CRYPTO_VN || len < 8) {
+ sys_badlength++;
+ code |= CRYPTO_ERROR;
+ }
+
+ if (len >= VALUE_LEN) {
+ tstamp = ntohl(ep->tstamp);
+ fstamp = ntohl(ep->fstamp);
+ vallen = ntohl(ep->vallen);
+ }
+ switch (code) {
+
+ /*
+ * Install status word, host name, signature scheme and
+ * association ID. In OpenSSL the signature algorithm is
+ * bound to the digest algorithm, so the NID completely
+ * defines the signature scheme. Note the request and
+ * response are identical, but neither is validated by
+ * signature. The request is processed here only in
+ * symmetric modes. The server name field might be
+ * useful to implement access controls in future.
+ */
+ case CRYPTO_ASSOC:
+
+ /*
+ * If our state machine is running when this
+ * message arrives, the other fellow might have
+ * restarted. However, this could be an
+ * intruder, so just clamp the poll interval and
+ * find out for ourselves. Otherwise, pass the
+ * extension field to the transmit side.
+ */
+ if (peer->crypto & CRYPTO_FLAG_CERT) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ if (peer->cmmd) {
+ if (peer->assoc != associd) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ }
+ fp = emalloc(len);
+ memcpy(fp, ep, len);
+ fp->associd = htonl(peer->associd);
+ peer->cmmd = fp;
+ /* fall through */
+
+ case CRYPTO_ASSOC | CRYPTO_RESP:
+
+ /*
+ * Discard the message if it has already been
+ * stored or the message has been amputated.
+ */
+ if (peer->crypto) {
+ if (peer->assoc != associd)
+ rval = XEVNT_ERR;
+ break;
+ }
+ if (vallen == 0 || vallen > MAXHOSTNAME ||
+ len < VALUE_LEN + vallen) {
+ rval = XEVNT_LEN;
+ break;
+ }
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "crypto_recv: ident host 0x%x %d server 0x%x %d\n",
+ crypto_flags, peer->associd, fstamp,
+ peer->assoc);
+#endif
+ temp32 = crypto_flags & CRYPTO_FLAG_MASK;
+
+ /*
+ * If the client scheme is PC, the server scheme
+ * must be PC. The public key and identity are
+ * presumed valid, so we skip the certificate
+ * and identity exchanges and move immediately
+ * to the cookie exchange which confirms the
+ * server signature.
+ */
+ if (crypto_flags & CRYPTO_FLAG_PRIV) {
+ if (!(fstamp & CRYPTO_FLAG_PRIV)) {
+ rval = XEVNT_KEY;
+ break;
+ }
+ fstamp |= CRYPTO_FLAG_CERT |
+ CRYPTO_FLAG_VRFY | CRYPTO_FLAG_SIGN;
+
+ /*
+ * It is an error if either peer supports
+ * identity, but the other does not.
+ */
+ } else if (hismode == MODE_ACTIVE || hismode ==
+ MODE_PASSIVE) {
+ if ((temp32 && !(fstamp &
+ CRYPTO_FLAG_MASK)) ||
+ (!temp32 && (fstamp &
+ CRYPTO_FLAG_MASK))) {
+ rval = XEVNT_KEY;
+ break;
+ }
+ }
+
+ /*
+ * Discard the message if the signature digest
+ * NID is not supported.
+ */
+ temp32 = (fstamp >> 16) & 0xffff;
+ dp =
+ (const EVP_MD *)EVP_get_digestbynid(temp32);
+ if (dp == NULL) {
+ rval = XEVNT_MD;
+ break;
+ }
+
+ /*
+ * Save status word, host name and message
+ * digest/signature type. If this is from a
+ * broadcast and the association ID has changed,
+ * request the autokey values.
+ */
+ peer->assoc = associd;
+ if (hismode == MODE_SERVER)
+ fstamp |= CRYPTO_FLAG_AUTO;
+ if (!(fstamp & CRYPTO_FLAG_TAI))
+ fstamp |= CRYPTO_FLAG_LEAP;
+ RAND_bytes((u_char *)&peer->hcookie, 4);
+ peer->crypto = fstamp;
+ peer->digest = dp;
+ if (peer->subject != NULL)
+ free(peer->subject);
+ peer->subject = emalloc(vallen + 1);
+ memcpy(peer->subject, ep->pkt, vallen);
+ peer->subject[vallen] = '\0';
+ if (peer->issuer != NULL)
+ free(peer->issuer);
+ peer->issuer = estrdup(peer->subject);
+ snprintf(statstr, sizeof(statstr),
+ "assoc %d %d host %s %s", peer->associd,
+ peer->assoc, peer->subject,
+ OBJ_nid2ln(temp32));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Decode X509 certificate in ASN.1 format and extract
+ * the data containing, among other things, subject
+ * name and public key. In the default identification
+ * scheme, the certificate trail is followed to a self
+ * signed trusted certificate.
+ */
+ case CRYPTO_CERT | CRYPTO_RESP:
+
+ /*
+ * Discard the message if empty or invalid.
+ */
+ if (len < VALUE_LEN)
+ break;
+
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * Scan the certificate list to delete old
+ * versions and link the newest version first on
+ * the list. Then, verify the signature. If the
+ * certificate is bad or missing, just ignore
+ * it.
+ */
+ if ((xinfo = cert_install(ep, peer)) == NULL) {
+ rval = XEVNT_CRT;
+ break;
+ }
+ if ((rval = cert_hike(peer, xinfo)) != XEVNT_OK)
+ break;
+
+ /*
+ * We plug in the public key and lifetime from
+ * the first certificate received. However, note
+ * that this certificate might not be signed by
+ * the server, so we can't check the
+ * signature/digest NID.
+ */
+ if (peer->pkey == NULL) {
+ puch = xinfo->cert.ptr;
+ cert = d2i_X509(NULL, &puch,
+ ntohl(xinfo->cert.vallen));
+ peer->pkey = X509_get_pubkey(cert);
+ X509_free(cert);
+ }
+ peer->flash &= ~TEST8;
+ temp32 = xinfo->nid;
+ snprintf(statstr, sizeof(statstr),
+ "cert %s %s 0x%x %s (%u) fs %u",
+ xinfo->subject, xinfo->issuer, xinfo->flags,
+ OBJ_nid2ln(temp32), temp32,
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Schnorr (IFF) identity scheme. This scheme is
+ * designed for use with shared secret server group keys
+ * and where the certificate may be generated by a third
+ * party. The client sends a challenge to the server,
+ * which performs a calculation and returns the result.
+ * A positive result is possible only if both client and
+ * server contain the same secret group key.
+ */
+ case CRYPTO_IFF | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid.
+ */
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * If the challenge matches the response, the
+ * server public key, signature and identity are
+ * all verified at the same time. The server is
+ * declared trusted, so we skip further
+ * certificate exchanges and move immediately to
+ * the cookie exchange.
+ */
+ if ((rval = crypto_iff(ep, peer)) != XEVNT_OK)
+ break;
+
+ peer->crypto |= CRYPTO_FLAG_VRFY;
+ peer->flash &= ~TEST8;
+ snprintf(statstr, sizeof(statstr), "iff %s fs %u",
+ peer->issuer, ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Guillou-Quisquater (GQ) identity scheme. This scheme
+ * is designed for use with public certificates carrying
+ * the GQ public key in an extension field. The client
+ * sends a challenge to the server, which performs a
+ * calculation and returns the result. A positive result
+ * is possible only if both client and server contain
+ * the same group key and the server has the matching GQ
+ * private key.
+ */
+ case CRYPTO_GQ | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid
+ */
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * If the challenge matches the response, the
+ * server public key, signature and identity are
+ * all verified at the same time. The server is
+ * declared trusted, so we skip further
+ * certificate exchanges and move immediately to
+ * the cookie exchange.
+ */
+ if ((rval = crypto_gq(ep, peer)) != XEVNT_OK)
+ break;
+
+ peer->crypto |= CRYPTO_FLAG_VRFY;
+ peer->flash &= ~TEST8;
+ snprintf(statstr, sizeof(statstr), "gq %s fs %u",
+ peer->issuer, ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Mu-Varadharajan (MV) identity scheme. This scheme is
+ * designed for use with three levels of trust, trusted
+ * host, server and client. The trusted host key is
+ * opaque to servers and clients; the server keys are
+ * opaque to clients and each client key is different.
+ * Client keys can be revoked without requiring new key
+ * generations.
+ */
+ case CRYPTO_MV | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid.
+ */
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * If the challenge matches the response, the
+ * server public key, signature and identity are
+ * all verified at the same time. The server is
+ * declared trusted, so we skip further
+ * certificate exchanges and move immediately to
+ * the cookie exchange.
+ */
+ if ((rval = crypto_mv(ep, peer)) != XEVNT_OK)
+ break;
+
+ peer->crypto |= CRYPTO_FLAG_VRFY;
+ peer->flash &= ~TEST8;
+ snprintf(statstr, sizeof(statstr), "mv %s fs %u",
+ peer->issuer, ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+
+ /*
+ * Cookie response in client and symmetric modes. If the
+ * cookie bit is set, the working cookie is the EXOR of
+ * the current and new values.
+ */
+ case CRYPTO_COOK | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or signature
+ * not verified with respect to the cookie
+ * values.
+ */
+ if ((rval = crypto_verify(ep, &peer->cookval,
+ peer)) != XEVNT_OK)
+ break;
+
+ /*
+ * Decrypt the cookie, hunting all the time for
+ * errors.
+ */
+ if (vallen == (u_int)EVP_PKEY_size(host_pkey)) {
+ if (RSA_private_decrypt(vallen,
+ (u_char *)ep->pkt,
+ (u_char *)&temp32,
+ host_pkey->pkey.rsa,
+ RSA_PKCS1_OAEP_PADDING) <= 0) {
+ rval = XEVNT_CKY;
+ break;
+ } else {
+ cookie = ntohl(temp32);
+ }
+ } else {
+ rval = XEVNT_CKY;
+ break;
+ }
+
+ /*
+ * Install cookie values and light the cookie
+ * bit. If this is not broadcast client mode, we
+ * are done here.
+ */
+ key_expire(peer);
+ if (hismode == MODE_ACTIVE || hismode ==
+ MODE_PASSIVE)
+ peer->pcookie = peer->hcookie ^ cookie;
+ else
+ peer->pcookie = cookie;
+ peer->crypto |= CRYPTO_FLAG_COOK;
+ peer->flash &= ~TEST8;
+ snprintf(statstr, sizeof(statstr),
+ "cook %x ts %u fs %u", peer->pcookie,
+ ntohl(ep->tstamp), ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Install autokey values in broadcast client and
+ * symmetric modes. We have to do this every time the
+ * sever/peer cookie changes or a new keylist is
+ * rolled. Ordinarily, this is automatic as this message
+ * is piggybacked on the first NTP packet sent upon
+ * either of these events. Note that a broadcast client
+ * or symmetric peer can receive this response without a
+ * matching request.
+ */
+ case CRYPTO_AUTO | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or signature
+ * not verified with respect to the receive
+ * autokey values.
+ */
+ if ((rval = crypto_verify(ep, &peer->recval,
+ peer)) != XEVNT_OK)
+ break;
+
+ /*
+ * Discard the message if a broadcast client and
+ * the association ID does not match. This might
+ * happen if a broacast server restarts the
+ * protocol. A protocol restart will occur at
+ * the next ASSOC message.
+ */
+ if ((peer->cast_flags & MDF_BCLNT) &&
+ peer->assoc != associd)
+ break;
+
+ /*
+ * Install autokey values and light the
+ * autokey bit. This is not hard.
+ */
+ if (ep->tstamp == 0)
+ break;
+
+ if (peer->recval.ptr == NULL)
+ peer->recval.ptr =
+ emalloc(sizeof(struct autokey));
+ bp = (struct autokey *)peer->recval.ptr;
+ peer->recval.tstamp = ep->tstamp;
+ peer->recval.fstamp = ep->fstamp;
+ ap = (struct autokey *)ep->pkt;
+ bp->seq = ntohl(ap->seq);
+ bp->key = ntohl(ap->key);
+ peer->pkeyid = bp->key;
+ peer->crypto |= CRYPTO_FLAG_AUTO;
+ peer->flash &= ~TEST8;
+ snprintf(statstr, sizeof(statstr),
+ "auto seq %d key %x ts %u fs %u", bp->seq,
+ bp->key, ntohl(ep->tstamp),
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * X509 certificate sign response. Validate the
+ * certificate signed by the server and install. Later
+ * this can be provided to clients of this server in
+ * lieu of the self signed certificate in order to
+ * validate the public key.
+ */
+ case CRYPTO_SIGN | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid.
+ */
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * Scan the certificate list to delete old
+ * versions and link the newest version first on
+ * the list.
+ */
+ if ((xinfo = cert_install(ep, peer)) == NULL) {
+ rval = XEVNT_CRT;
+ break;
+ }
+ peer->crypto |= CRYPTO_FLAG_SIGN;
+ peer->flash &= ~TEST8;
+ temp32 = xinfo->nid;
+ snprintf(statstr, sizeof(statstr),
+ "sign %s %s 0x%x %s (%u) fs %u",
+ xinfo->subject, xinfo->issuer, xinfo->flags,
+ OBJ_nid2ln(temp32), temp32,
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Install leapseconds values. While the leapsecond
+ * values epoch, TAI offset and values expiration epoch
+ * are retained, only the current TAI offset is provided
+ * via the kernel to other applications.
+ */
+ case CRYPTO_LEAP | CRYPTO_RESP:
+ /*
+ * Discard the message if invalid. We can't
+ * compare the value timestamps here, as they
+ * can be updated by different servers.
+ */
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * If the packet leap values are more recent
+ * than the stored ones, install the new leap
+ * values and recompute the signatures.
+ */
+ if (leapsec_add_fix(ntohl(ep->pkt[0]),
+ ntohl(ep->pkt[1]),
+ ntohl(ep->pkt[2]),
+ NULL))
+ {
+ leap_signature_t lsig;
+
+ leapsec_getsig(&lsig);
+ tai_leap.tstamp = ep->tstamp;
+ tai_leap.fstamp = ep->fstamp;
+ tai_leap.vallen = ep->vallen;
+ crypto_update();
+ mprintf_event(EVNT_TAI, peer,
+ "%d leap %s expire %s", lsig.taiof,
+ fstostr(lsig.ttime),
+ fstostr(lsig.etime));
+ }
+ peer->crypto |= CRYPTO_FLAG_LEAP;
+ peer->flash &= ~TEST8;
+ snprintf(statstr, sizeof(statstr),
+ "leap TAI offset %d at %u expire %u fs %u",
+ ntohl(ep->pkt[0]), ntohl(ep->pkt[1]),
+ ntohl(ep->pkt[2]), ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * We come here in symmetric modes for miscellaneous
+ * commands that have value fields but are processed on
+ * the transmit side. All we need do here is check for
+ * valid field length. Note that ASSOC is handled
+ * separately.
+ */
+ case CRYPTO_CERT:
+ case CRYPTO_IFF:
+ case CRYPTO_GQ:
+ case CRYPTO_MV:
+ case CRYPTO_COOK:
+ case CRYPTO_SIGN:
+ if (len < VALUE_LEN) {
+ rval = XEVNT_LEN;
+ break;
+ }
+ /* fall through */
+
+ /*
+ * We come here in symmetric modes for requests
+ * requiring a response (above plus AUTO and LEAP) and
+ * for responses. If a request, save the extension field
+ * for later; invalid requests will be caught on the
+ * transmit side. If an error or invalid response,
+ * declare a protocol error.
+ */
+ default:
+ if (code & (CRYPTO_RESP | CRYPTO_ERROR)) {
+ rval = XEVNT_ERR;
+ } else if (peer->cmmd == NULL) {
+ fp = emalloc(len);
+ memcpy(fp, ep, len);
+ peer->cmmd = fp;
+ }
+ }
+
+ /*
+ * The first error found terminates the extension field
+ * scan and we return the laundry to the caller.
+ */
+ if (rval != XEVNT_OK) {
+ snprintf(statstr, sizeof(statstr),
+ "%04x %d %02x %s", htonl(ep->opcode),
+ associd, rval, eventstr(rval));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ return (rval);
+ }
+ authlen += (len + 3) / 4 * 4;
+ }
+ return (rval);
+}
+
+
+/*
+ * crypto_xmit - construct extension fields
+ *
+ * This routine is called both when an association is configured and
+ * when one is not. The only case where this matters is to retrieve the
+ * autokey information, in which case the caller has to provide the
+ * association ID to match the association.
+ *
+ * Side effect: update the packet offset.
+ *
+ * Errors
+ * XEVNT_OK success
+ * XEVNT_CRT bad or missing certificate
+ * XEVNT_ERR protocol error
+ * XEVNT_LEN bad field format or length
+ * XEVNT_PER host certificate expired
+ */
+int
+crypto_xmit(
+ struct peer *peer, /* peer structure pointer */
+ struct pkt *xpkt, /* transmit packet pointer */
+ struct recvbuf *rbufp, /* receive buffer pointer */
+ int start, /* offset to extension field */
+ struct exten *ep, /* extension pointer */
+ keyid_t cookie /* session cookie */
+ )
+{
+ struct exten *fp; /* extension pointers */
+ struct cert_info *cp, *xp, *yp; /* cert info/value pointer */
+ sockaddr_u *srcadr_sin; /* source address */
+ u_int32 *pkt; /* packet pointer */
+ u_int opcode; /* extension field opcode */
+ char certname[MAXHOSTNAME + 1]; /* subject name buffer */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ tstamp_t tstamp;
+ struct calendar tscal;
+ u_int vallen;
+ struct value vtemp;
+ associd_t associd;
+ int rval;
+ int len;
+ keyid_t tcookie;
+
+ /*
+ * Generate the requested extension field request code, length
+ * and association ID. If this is a response and the host is not
+ * synchronized, light the error bit and go home.
+ */
+ pkt = (u_int32 *)xpkt + start / 4;
+ fp = (struct exten *)pkt;
+ opcode = ntohl(ep->opcode);
+ if (peer != NULL) {
+ srcadr_sin = &peer->srcadr;
+ if (!(opcode & CRYPTO_RESP))
+ peer->opcode = ep->opcode;
+ } else {
+ srcadr_sin = &rbufp->recv_srcadr;
+ }
+ associd = (associd_t) ntohl(ep->associd);
+ len = 8;
+ fp->opcode = htonl((opcode & 0xffff0000) | len);
+ fp->associd = ep->associd;
+ rval = XEVNT_OK;
+ tstamp = crypto_time();
+ switch (opcode & 0xffff0000) {
+
+ /*
+ * Send association request and response with status word and
+ * host name. Note, this message is not signed and the filestamp
+ * contains only the status word.
+ */
+ case CRYPTO_ASSOC:
+ case CRYPTO_ASSOC | CRYPTO_RESP:
+ len = crypto_send(fp, &hostval, start);
+ fp->fstamp = htonl(crypto_flags);
+ break;
+
+ /*
+ * Send certificate request. Use the values from the extension
+ * field.
+ */
+ case CRYPTO_CERT:
+ memset(&vtemp, 0, sizeof(vtemp));
+ vtemp.tstamp = ep->tstamp;
+ vtemp.fstamp = ep->fstamp;
+ vtemp.vallen = ep->vallen;
+ vtemp.ptr = (u_char *)ep->pkt;
+ len = crypto_send(fp, &vtemp, start);
+ break;
+
+ /*
+ * Send sign request. Use the host certificate, which is self-
+ * signed and may or may not be trusted.
+ */
+ case CRYPTO_SIGN:
+ (void)ntpcal_ntp_to_date(&tscal, tstamp, NULL);
+ if ((calcomp(&tscal, &(cert_host->first)) < 0)
+ || (calcomp(&tscal, &(cert_host->last)) > 0))
+ rval = XEVNT_PER;
+ else
+ len = crypto_send(fp, &cert_host->cert, start);
+ break;
+
+ /*
+ * Send certificate response. Use the name in the extension
+ * field to find the certificate in the cache. If the request
+ * contains no subject name, assume the name of this host. This
+ * is for backwards compatibility. Private certificates are
+ * never sent.
+ *
+ * There may be several certificates matching the request. First
+ * choice is a self-signed trusted certificate; second choice is
+ * any certificate signed by another host. There is no third
+ * choice.
+ */
+ case CRYPTO_CERT | CRYPTO_RESP:
+ vallen = ntohl(ep->vallen);
+ if (vallen == 0 || vallen > MAXHOSTNAME) {
+ rval = XEVNT_LEN;
+ break;
+ }
+
+ /*
+ * Find all public valid certificates with matching
+ * subject. If a self-signed, trusted certificate is
+ * found, use that certificate. If not, use the last non
+ * self-signed certificate.
+ */
+ memcpy(certname, ep->pkt, vallen);
+ certname[vallen] = '\0';
+ xp = yp = NULL;
+ for (cp = cinfo; cp != NULL; cp = cp->link) {
+ if (cp->flags & (CERT_PRIV | CERT_ERROR))
+ continue;
+
+ if (strcmp(certname, cp->subject) != 0)
+ continue;
+
+ if (strcmp(certname, cp->issuer) != 0)
+ yp = cp;
+ else if (cp ->flags & CERT_TRUST)
+ xp = cp;
+ continue;
+ }
+
+ /*
+ * Be careful who you trust. If the certificate is not
+ * found, return an empty response. Note that we dont
+ * enforce lifetimes here.
+ *
+ * The timestamp and filestamp are taken from the
+ * certificate value structure. For all certificates the
+ * timestamp is the latest signature update time. For
+ * host and imported certificates the filestamp is the
+ * creation epoch. For signed certificates the filestamp
+ * is the creation epoch of the trusted certificate at
+ * the root of the certificate trail. In principle, this
+ * allows strong checking for signature masquerade.
+ */
+ if (xp == NULL)
+ xp = yp;
+ if (xp == NULL)
+ break;
+
+ if (tstamp == 0)
+ break;
+
+ len = crypto_send(fp, &xp->cert, start);
+ break;
+
+ /*
+ * Send challenge in Schnorr (IFF) identity scheme.
+ */
+ case CRYPTO_IFF:
+ if (peer == NULL)
+ break; /* hack attack */
+
+ if ((rval = crypto_alice(peer, &vtemp)) == XEVNT_OK) {
+ len = crypto_send(fp, &vtemp, start);
+ value_free(&vtemp);
+ }
+ break;
+
+ /*
+ * Send response in Schnorr (IFF) identity scheme.
+ */
+ case CRYPTO_IFF | CRYPTO_RESP:
+ if ((rval = crypto_bob(ep, &vtemp)) == XEVNT_OK) {
+ len = crypto_send(fp, &vtemp, start);
+ value_free(&vtemp);
+ }
+ break;
+
+ /*
+ * Send challenge in Guillou-Quisquater (GQ) identity scheme.
+ */
+ case CRYPTO_GQ:
+ if (peer == NULL)
+ break; /* hack attack */
+
+ if ((rval = crypto_alice2(peer, &vtemp)) == XEVNT_OK) {
+ len = crypto_send(fp, &vtemp, start);
+ value_free(&vtemp);
+ }
+ break;
+
+ /*
+ * Send response in Guillou-Quisquater (GQ) identity scheme.
+ */
+ case CRYPTO_GQ | CRYPTO_RESP:
+ if ((rval = crypto_bob2(ep, &vtemp)) == XEVNT_OK) {
+ len = crypto_send(fp, &vtemp, start);
+ value_free(&vtemp);
+ }
+ break;
+
+ /*
+ * Send challenge in MV identity scheme.
+ */
+ case CRYPTO_MV:
+ if (peer == NULL)
+ break; /* hack attack */
+
+ if ((rval = crypto_alice3(peer, &vtemp)) == XEVNT_OK) {
+ len = crypto_send(fp, &vtemp, start);
+ value_free(&vtemp);
+ }
+ break;
+
+ /*
+ * Send response in MV identity scheme.
+ */
+ case CRYPTO_MV | CRYPTO_RESP:
+ if ((rval = crypto_bob3(ep, &vtemp)) == XEVNT_OK) {
+ len = crypto_send(fp, &vtemp, start);
+ value_free(&vtemp);
+ }
+ break;
+
+ /*
+ * Send certificate sign response. The integrity of the request
+ * certificate has already been verified on the receive side.
+ * Sign the response using the local server key. Use the
+ * filestamp from the request and use the timestamp as the
+ * current time. Light the error bit if the certificate is
+ * invalid or contains an unverified signature.
+ */
+ case CRYPTO_SIGN | CRYPTO_RESP:
+ if ((rval = cert_sign(ep, &vtemp)) == XEVNT_OK) {
+ len = crypto_send(fp, &vtemp, start);
+ value_free(&vtemp);
+ }
+ break;
+
+ /*
+ * Send public key and signature. Use the values from the public
+ * key.
+ */
+ case CRYPTO_COOK:
+ len = crypto_send(fp, &pubkey, start);
+ break;
+
+ /*
+ * Encrypt and send cookie and signature. Light the error bit if
+ * anything goes wrong.
+ */
+ case CRYPTO_COOK | CRYPTO_RESP:
+ if ((opcode & 0xffff) < VALUE_LEN) {
+ rval = XEVNT_LEN;
+ break;
+ }
+ if (peer == NULL)
+ tcookie = cookie;
+ else
+ tcookie = peer->hcookie;
+ if ((rval = crypto_encrypt(ep, &vtemp, &tcookie)) ==
+ XEVNT_OK) {
+ len = crypto_send(fp, &vtemp, start);
+ value_free(&vtemp);
+ }
+ break;
+
+ /*
+ * Find peer and send autokey data and signature in broadcast
+ * server and symmetric modes. Use the values in the autokey
+ * structure. If no association is found, either the server has
+ * restarted with new associations or some perp has replayed an
+ * old message, in which case light the error bit.
+ */
+ case CRYPTO_AUTO | CRYPTO_RESP:
+ if (peer == NULL) {
+ if ((peer = findpeerbyassoc(associd)) == NULL) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ }
+ peer->flags &= ~FLAG_ASSOC;
+ len = crypto_send(fp, &peer->sndval, start);
+ break;
+
+ /*
+ * Send leapseconds values and signature. Use the values from
+ * the tai structure. If no table has been loaded, just send an
+ * empty request.
+ */
+ case CRYPTO_LEAP | CRYPTO_RESP:
+ len = crypto_send(fp, &tai_leap, start);
+ break;
+
+ /*
+ * Default - Send a valid command for unknown requests; send
+ * an error response for unknown resonses.
+ */
+ default:
+ if (opcode & CRYPTO_RESP)
+ rval = XEVNT_ERR;
+ }
+
+ /*
+ * In case of error, flame the log. If a request, toss the
+ * puppy; if a response, return so the sender can flame, too.
+ */
+ if (rval != XEVNT_OK) {
+ u_int32 uint32;
+
+ uint32 = CRYPTO_ERROR;
+ opcode |= uint32;
+ fp->opcode |= htonl(uint32);
+ snprintf(statstr, sizeof(statstr),
+ "%04x %d %02x %s", opcode, associd, rval,
+ eventstr(rval));
+ record_crypto_stats(srcadr_sin, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_xmit: %s\n", statstr);
+#endif
+ if (!(opcode & CRYPTO_RESP))
+ return (0);
+ }
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "crypto_xmit: flags 0x%x offset %d len %d code 0x%x associd %d\n",
+ crypto_flags, start, len, opcode >> 16, associd);
+#endif
+ return (len);
+}
+
+
+/*
+ * crypto_verify - verify the extension field value and signature
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_ERR protocol error
+ * XEVNT_FSP bad filestamp
+ * XEVNT_LEN bad field format or length
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_SGL bad signature length
+ * XEVNT_SIG signature not verified
+ * XEVNT_TSP bad timestamp
+ */
+static int
+crypto_verify(
+ struct exten *ep, /* extension pointer */
+ struct value *vp, /* value pointer */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ EVP_PKEY *pkey; /* server public key */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp, tstamp1 = 0; /* timestamp */
+ tstamp_t fstamp, fstamp1 = 0; /* filestamp */
+ u_int vallen; /* value length */
+ u_int siglen; /* signature length */
+ u_int opcode, len;
+ int i;
+
+ /*
+ * We are extremely parannoyed. We require valid opcode, length,
+ * association ID, timestamp, filestamp, public key, digest,
+ * signature length and signature, where relevant. Note that
+ * preliminary length checks are done in the main loop.
+ */
+ len = ntohl(ep->opcode) & 0x0000ffff;
+ opcode = ntohl(ep->opcode) & 0xffff0000;
+
+ /*
+ * Check for valid value header, association ID and extension
+ * field length. Remember, it is not an error to receive an
+ * unsolicited response; however, the response ID must match
+ * the association ID.
+ */
+ if (opcode & CRYPTO_ERROR)
+ return (XEVNT_ERR);
+
+ if (len < VALUE_LEN)
+ return (XEVNT_LEN);
+
+ if (opcode == (CRYPTO_AUTO | CRYPTO_RESP) && (peer->pmode ==
+ MODE_BROADCAST || (peer->cast_flags & MDF_BCLNT))) {
+ if (ntohl(ep->associd) != peer->assoc)
+ return (XEVNT_ERR);
+ } else {
+ if (ntohl(ep->associd) != peer->associd)
+ return (XEVNT_ERR);
+ }
+
+ /*
+ * We have a valid value header. Check for valid value and
+ * signature field lengths. The extension field length must be
+ * long enough to contain the value header, value and signature.
+ * Note both the value and signature field lengths are rounded
+ * up to the next word (4 octets).
+ */
+ vallen = ntohl(ep->vallen);
+ if (vallen == 0)
+ return (XEVNT_LEN);
+
+ i = (vallen + 3) / 4;
+ siglen = ntohl(ep->pkt[i++]);
+ if (len < VALUE_LEN + ((vallen + 3) / 4) * 4 + ((siglen + 3) /
+ 4) * 4)
+ return (XEVNT_LEN);
+
+ /*
+ * Check for valid timestamp and filestamp. If the timestamp is
+ * zero, the sender is not synchronized and signatures are
+ * not possible. If nonzero the timestamp must not precede the
+ * filestamp. The timestamp and filestamp must not precede the
+ * corresponding values in the value structure, if present.
+ */
+ tstamp = ntohl(ep->tstamp);
+ fstamp = ntohl(ep->fstamp);
+ if (tstamp == 0)
+ return (XEVNT_TSP);
+
+ if (tstamp < fstamp)
+ return (XEVNT_TSP);
+
+ if (vp != NULL) {
+ tstamp1 = ntohl(vp->tstamp);
+ fstamp1 = ntohl(vp->fstamp);
+ if (tstamp1 != 0 && fstamp1 != 0) {
+ if (tstamp < tstamp1)
+ return (XEVNT_TSP);
+
+ if ((tstamp < fstamp1 || fstamp < fstamp1))
+ return (XEVNT_FSP);
+ }
+ }
+
+ /*
+ * At the time the certificate message is validated, the public
+ * key in the message is not available. Thus, don't try to
+ * verify the signature.
+ */
+ if (opcode == (CRYPTO_CERT | CRYPTO_RESP))
+ return (XEVNT_OK);
+
+ /*
+ * Check for valid signature length, public key and digest
+ * algorithm.
+ */
+ if (crypto_flags & peer->crypto & CRYPTO_FLAG_PRIV)
+ pkey = sign_pkey;
+ else
+ pkey = peer->pkey;
+ if (siglen == 0 || pkey == NULL || peer->digest == NULL)
+ return (XEVNT_ERR);
+
+ if (siglen != (u_int)EVP_PKEY_size(pkey))
+ return (XEVNT_SGL);
+
+ /*
+ * Darn, I thought we would never get here. Verify the
+ * signature. If the identity exchange is verified, light the
+ * proventic bit. What a relief.
+ */
+ EVP_VerifyInit(&ctx, peer->digest);
+ EVP_VerifyUpdate(&ctx, (u_char *)&ep->tstamp, vallen + 12);
+ if (EVP_VerifyFinal(&ctx, (u_char *)&ep->pkt[i], siglen,
+ pkey) <= 0)
+ return (XEVNT_SIG);
+
+ if (peer->crypto & CRYPTO_FLAG_VRFY)
+ peer->crypto |= CRYPTO_FLAG_PROV;
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_encrypt - construct encrypted cookie and signature from
+ * extension field and cookie
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_CKY bad or missing cookie
+ * XEVNT_PUB bad or missing public key
+ */
+static int
+crypto_encrypt(
+ struct exten *ep, /* extension pointer */
+ struct value *vp, /* value pointer */
+ keyid_t *cookie /* server cookie */
+ )
+{
+ EVP_PKEY *pkey; /* public key */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ u_int32 temp32;
+ u_int len;
+ const u_char *ptr;
+ u_char *puch;
+
+ /*
+ * Extract the public key from the request.
+ */
+ len = ntohl(ep->vallen);
+ ptr = (void *)ep->pkt;
+ pkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ptr, len);
+ if (pkey == NULL) {
+ msyslog(LOG_ERR, "crypto_encrypt: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Encrypt the cookie, encode in ASN.1 and sign.
+ */
+ memset(vp, 0, sizeof(struct value));
+ tstamp = crypto_time();
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = hostval.tstamp;
+ len = EVP_PKEY_size(pkey);
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ puch = vp->ptr;
+ temp32 = htonl(*cookie);
+ if (RSA_public_encrypt(4, (u_char *)&temp32, puch,
+ pkey->pkey.rsa, RSA_PKCS1_OAEP_PADDING) <= 0) {
+ msyslog(LOG_ERR, "crypto_encrypt: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ free(vp->ptr);
+ EVP_PKEY_free(pkey);
+ return (XEVNT_CKY);
+ }
+ EVP_PKEY_free(pkey);
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(sign_siglen);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_ident - construct extension field for identity scheme
+ *
+ * This routine determines which identity scheme is in use and
+ * constructs an extension field for that scheme.
+ *
+ * Returns
+ * CRYTPO_IFF IFF scheme
+ * CRYPTO_GQ GQ scheme
+ * CRYPTO_MV MV scheme
+ * CRYPTO_NULL no available scheme
+ */
+u_int
+crypto_ident(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ char filename[MAXFILENAME];
+ const char * scheme_name;
+ u_int scheme_id;
+
+ /*
+ * We come here after the group trusted host has been found; its
+ * name defines the group name. Search the key cache for all
+ * keys matching the same group name in order IFF, GQ and MV.
+ * Use the first one available.
+ */
+ scheme_name = NULL;
+ if (peer->crypto & CRYPTO_FLAG_IFF) {
+ scheme_name = "iff";
+ scheme_id = CRYPTO_IFF;
+ } else if (peer->crypto & CRYPTO_FLAG_GQ) {
+ scheme_name = "gq";
+ scheme_id = CRYPTO_GQ;
+ } else if (peer->crypto & CRYPTO_FLAG_MV) {
+ scheme_name = "mv";
+ scheme_id = CRYPTO_MV;
+ }
+
+ if (scheme_name != NULL) {
+ snprintf(filename, sizeof(filename), "ntpkey_%spar_%s",
+ scheme_name, peer->ident);
+ peer->ident_pkey = crypto_key(filename, NULL,
+ &peer->srcadr);
+ if (peer->ident_pkey != NULL)
+ return scheme_id;
+ }
+
+ msyslog(LOG_NOTICE,
+ "crypto_ident: no identity parameters found for group %s",
+ peer->ident);
+
+ return CRYPTO_NULL;
+}
+
+
+/*
+ * crypto_args - construct extension field from arguments
+ *
+ * This routine creates an extension field with current timestamps and
+ * specified opcode, association ID and optional string. Note that the
+ * extension field is created here, but freed after the crypto_xmit()
+ * call in the protocol module.
+ *
+ * Returns extension field pointer (no errors)
+ */
+struct exten *
+crypto_args(
+ struct peer *peer, /* peer structure pointer */
+ u_int opcode, /* operation code */
+ associd_t associd, /* association ID */
+ char *str /* argument string */
+ )
+{
+ tstamp_t tstamp; /* NTP timestamp */
+ struct exten *ep; /* extension field pointer */
+ u_int len; /* extension field length */
+
+ tstamp = crypto_time();
+ len = sizeof(struct exten);
+ if (str != NULL)
+ len += strlen(str);
+ ep = emalloc_zero(len);
+ if (opcode == 0)
+ return (ep);
+
+ ep->opcode = htonl(opcode + len);
+ ep->associd = htonl(associd);
+ ep->tstamp = htonl(tstamp);
+ ep->fstamp = hostval.tstamp;
+ ep->vallen = 0;
+ if (str != NULL) {
+ ep->vallen = htonl(strlen(str));
+ memcpy((char *)ep->pkt, str, strlen(str));
+ }
+ return (ep);
+}
+
+
+/*
+ * crypto_send - construct extension field from value components
+ *
+ * The value and signature fields are zero-padded to a word boundary.
+ * Note: it is not polite to send a nonempty signature with zero
+ * timestamp or a nonzero timestamp with an empty signature, but those
+ * rules are not enforced here.
+ */
+int
+crypto_send(
+ struct exten *ep, /* extension field pointer */
+ struct value *vp, /* value pointer */
+ int start /* buffer offset */
+ )
+{
+ u_int len, vallen, siglen, opcode;
+ int i, j;
+
+ /*
+ * Calculate extension field length and check for buffer
+ * overflow. Leave room for the MAC.
+ */
+ len = 16;
+ vallen = ntohl(vp->vallen);
+ len += ((vallen + 3) / 4 + 1) * 4;
+ siglen = ntohl(vp->siglen);
+ len += ((siglen + 3) / 4 + 1) * 4;
+ if (start + len > sizeof(struct pkt) - MAX_MAC_LEN)
+ return (0);
+
+ /*
+ * Copy timestamps.
+ */
+ ep->tstamp = vp->tstamp;
+ ep->fstamp = vp->fstamp;
+ ep->vallen = vp->vallen;
+
+ /*
+ * Copy value. If the data field is empty or zero length,
+ * encode an empty value with length zero.
+ */
+ i = 0;
+ if (vallen > 0 && vp->ptr != NULL) {
+ j = vallen / 4;
+ if (j * 4 < (int)vallen)
+ ep->pkt[i + j++] = 0;
+ memcpy(&ep->pkt[i], vp->ptr, vallen);
+ i += j;
+ }
+
+ /*
+ * Copy signature. If the signature field is empty or zero
+ * length, encode an empty signature with length zero.
+ */
+ ep->pkt[i++] = vp->siglen;
+ if (siglen > 0 && vp->sig != NULL) {
+ j = siglen / 4;
+ if (j * 4 < (int)siglen)
+ ep->pkt[i + j++] = 0;
+ memcpy(&ep->pkt[i], vp->sig, siglen);
+ i += j;
+ }
+ opcode = ntohl(ep->opcode);
+ ep->opcode = htonl((opcode & 0xffff0000) | len);
+ return (len);
+}
+
+
+/*
+ * crypto_update - compute new public value and sign extension fields
+ *
+ * This routine runs periodically, like once a day, and when something
+ * changes. It updates the timestamps on three value structures and one
+ * value structure list, then signs all the structures:
+ *
+ * hostval host name (not signed)
+ * pubkey public key
+ * cinfo certificate info/value list
+ * tai_leap leap values
+ *
+ * Filestamps are proventic data, so this routine runs only when the
+ * host is synchronized to a proventicated source. Thus, the timestamp
+ * is proventic and can be used to deflect clogging attacks.
+ *
+ * Returns void (no errors)
+ */
+void
+crypto_update(void)
+{
+ EVP_MD_CTX ctx; /* message digest context */
+ struct cert_info *cp; /* certificate info/value */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ u_int32 *ptr;
+ u_int len;
+ leap_signature_t lsig;
+
+ hostval.tstamp = htonl(crypto_time());
+ if (hostval.tstamp == 0)
+ return;
+
+
+ /*
+ * Sign public key and timestamps. The filestamp is derived from
+ * the host key file extension from wherever the file was
+ * generated.
+ */
+ if (pubkey.vallen != 0) {
+ pubkey.tstamp = hostval.tstamp;
+ pubkey.siglen = 0;
+ if (pubkey.sig == NULL)
+ pubkey.sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&pubkey, 12);
+ EVP_SignUpdate(&ctx, pubkey.ptr, ntohl(pubkey.vallen));
+ if (EVP_SignFinal(&ctx, pubkey.sig, &len, sign_pkey))
+ pubkey.siglen = htonl(sign_siglen);
+ }
+
+ /*
+ * Sign certificates and timestamps. The filestamp is derived
+ * from the certificate file extension from wherever the file
+ * was generated. Note we do not throw expired certificates
+ * away; they may have signed younger ones.
+ */
+ for (cp = cinfo; cp != NULL; cp = cp->link) {
+ cp->cert.tstamp = hostval.tstamp;
+ cp->cert.siglen = 0;
+ if (cp->cert.sig == NULL)
+ cp->cert.sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&cp->cert, 12);
+ EVP_SignUpdate(&ctx, cp->cert.ptr,
+ ntohl(cp->cert.vallen));
+ if (EVP_SignFinal(&ctx, cp->cert.sig, &len, sign_pkey))
+ cp->cert.siglen = htonl(sign_siglen);
+ }
+
+ /*
+ * Sign leapseconds values and timestamps. Note it is not an
+ * error to return null values.
+ */
+ tai_leap.tstamp = hostval.tstamp;
+ tai_leap.fstamp = hostval.fstamp;
+ len = 3 * sizeof(u_int32);
+ if (tai_leap.ptr == NULL)
+ tai_leap.ptr = emalloc(len);
+ tai_leap.vallen = htonl(len);
+ ptr = (u_int32 *)tai_leap.ptr;
+ leapsec_getsig(&lsig);
+ ptr[0] = htonl(lsig.taiof);
+ ptr[1] = htonl(lsig.ttime);
+ ptr[2] = htonl(lsig.etime);
+ if (tai_leap.sig == NULL)
+ tai_leap.sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&tai_leap, 12);
+ EVP_SignUpdate(&ctx, tai_leap.ptr, len);
+ if (EVP_SignFinal(&ctx, tai_leap.sig, &len, sign_pkey))
+ tai_leap.siglen = htonl(sign_siglen);
+ if (lsig.ttime > 0)
+ crypto_flags |= CRYPTO_FLAG_TAI;
+ snprintf(statstr, sizeof(statstr), "signature update ts %u",
+ ntohl(hostval.tstamp));
+ record_crypto_stats(NULL, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_update: %s\n", statstr);
+#endif
+}
+
+
+/*
+ * value_free - free value structure components.
+ *
+ * Returns void (no errors)
+ */
+void
+value_free(
+ struct value *vp /* value structure */
+ )
+{
+ if (vp->ptr != NULL)
+ free(vp->ptr);
+ if (vp->sig != NULL)
+ free(vp->sig);
+ memset(vp, 0, sizeof(struct value));
+}
+
+
+/*
+ * crypto_time - returns current NTP time.
+ *
+ * Returns NTP seconds if in synch, 0 otherwise
+ */
+tstamp_t
+crypto_time()
+{
+ l_fp tstamp; /* NTP time */
+
+ L_CLR(&tstamp);
+ if (sys_leap != LEAP_NOTINSYNC)
+ get_systime(&tstamp);
+ return (tstamp.l_ui);
+}
+
+
+/*
+ * asn_to_calendar - convert ASN1_TIME time structure to struct calendar.
+ *
+ */
+static
+void
+asn_to_calendar (
+ ASN1_TIME *asn1time, /* pointer to ASN1_TIME structure */
+ struct calendar *pjd /* pointer to result */
+ )
+{
+ int len; /* length of ASN1_TIME string */
+ char v[24]; /* writable copy of ASN1_TIME string */
+ unsigned long temp; /* result from strtoul */
+
+ /*
+ * Extract time string YYMMDDHHMMSSZ from ASN1 time structure.
+ * Or YYYYMMDDHHMMSSZ.
+ * Note that the YY, MM, DD fields start with one, the HH, MM,
+ * SS fields start with zero and the Z character is ignored.
+ * Also note that two-digit years less than 50 map to years greater than
+ * 100. Dontcha love ASN.1? Better than MIL-188.
+ */
+ len = asn1time->length;
+ NTP_REQUIRE(len < sizeof(v));
+ (void)strncpy(v, (char *)(asn1time->data), len);
+ NTP_REQUIRE(len >= 13);
+ temp = strtoul(v+len-3, NULL, 10);
+ pjd->second = temp;
+ v[len-3] = '\0';
+
+ temp = strtoul(v+len-5, NULL, 10);
+ pjd->minute = temp;
+ v[len-5] = '\0';
+
+ temp = strtoul(v+len-7, NULL, 10);
+ pjd->hour = temp;
+ v[len-7] = '\0';
+
+ temp = strtoul(v+len-9, NULL, 10);
+ pjd->monthday = temp;
+ v[len-9] = '\0';
+
+ temp = strtoul(v+len-11, NULL, 10);
+ pjd->month = temp;
+ v[len-11] = '\0';
+
+ temp = strtoul(v, NULL, 10);
+ /* handle two-digit years */
+ if (temp < 50UL)
+ temp += 100UL;
+ if (temp < 150UL)
+ temp += 1900UL;
+ pjd->year = temp;
+
+ pjd->yearday = pjd->weekday = 0;
+ return;
+}
+
+
+/*
+ * bigdig() - compute a BIGNUM MD5 hash of a BIGNUM number.
+ *
+ * Returns void (no errors)
+ */
+static void
+bighash(
+ BIGNUM *bn, /* BIGNUM * from */
+ BIGNUM *bk /* BIGNUM * to */
+ )
+{
+ EVP_MD_CTX ctx; /* message digest context */
+ u_char dgst[EVP_MAX_MD_SIZE]; /* message digest */
+ u_char *ptr; /* a BIGNUM as binary string */
+ u_int len;
+
+ len = BN_num_bytes(bn);
+ ptr = emalloc(len);
+ BN_bn2bin(bn, ptr);
+ EVP_DigestInit(&ctx, EVP_md5());
+ EVP_DigestUpdate(&ctx, ptr, len);
+ EVP_DigestFinal(&ctx, dgst, &len);
+ BN_bin2bn(dgst, len, bk);
+ free(ptr);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines implement the Schnorr (IFF) identity scheme *
+ * *
+ ***********************************************************************
+ *
+ * The Schnorr (IFF) identity scheme is intended for use when
+ * certificates are generated by some other trusted certificate
+ * authority and the certificate cannot be used to convey public
+ * parameters. There are two kinds of files: encrypted server files that
+ * contain private and public values and nonencrypted client files that
+ * contain only public values. New generations of server files must be
+ * securely transmitted to all servers of the group; client files can be
+ * distributed by any means. The scheme is self contained and
+ * independent of new generations of host keys, sign keys and
+ * certificates.
+ *
+ * The IFF values hide in a DSA cuckoo structure which uses the same
+ * parameters. The values are used by an identity scheme based on DSA
+ * cryptography and described in Stimson p. 285. The p is a 512-bit
+ * prime, g a generator of Zp* and q a 160-bit prime that divides p - 1
+ * and is a qth root of 1 mod p; that is, g^q = 1 mod p. The TA rolls a
+ * private random group key b (0 < b < q) and public key v = g^b, then
+ * sends (p, q, g, b) to the servers and (p, q, g, v) to the clients.
+ * Alice challenges Bob to confirm identity using the protocol described
+ * below.
+ *
+ * How it works
+ *
+ * The scheme goes like this. Both Alice and Bob have the public primes
+ * p, q and generator g. The TA gives private key b to Bob and public
+ * key v to Alice.
+ *
+ * Alice rolls new random challenge r (o < r < q) and sends to Bob in
+ * the IFF request message. Bob rolls new random k (0 < k < q), then
+ * computes y = k + b r mod q and x = g^k mod p and sends (y, hash(x))
+ * to Alice in the response message. Besides making the response
+ * shorter, the hash makes it effectivey impossible for an intruder to
+ * solve for b by observing a number of these messages.
+ *
+ * Alice receives the response and computes g^y v^r mod p. After a bit
+ * of algebra, this simplifies to g^k. If the hash of this result
+ * matches hash(x), Alice knows that Bob has the group key b. The signed
+ * response binds this knowledge to Bob's private key and the public key
+ * previously received in his certificate.
+ *
+ * crypto_alice - construct Alice's challenge in IFF scheme
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_ID bad or missing group key
+ * XEVNT_PUB bad or missing public key
+ */
+static int
+crypto_alice(
+ struct peer *peer, /* peer pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ DSA *dsa; /* IFF parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp;
+ u_int len;
+
+ /*
+ * The identity parameters must have correct format and content.
+ */
+ if (peer->ident_pkey == NULL) {
+ msyslog(LOG_NOTICE, "crypto_alice: scheme unavailable");
+ return (XEVNT_ID);
+ }
+
+ if ((dsa = peer->ident_pkey->pkey->pkey.dsa) == NULL) {
+ msyslog(LOG_NOTICE, "crypto_alice: defective key");
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Roll new random r (0 < r < q).
+ */
+ if (peer->iffval != NULL)
+ BN_free(peer->iffval);
+ peer->iffval = BN_new();
+ len = BN_num_bytes(dsa->q);
+ BN_rand(peer->iffval, len * 8, -1, 1); /* r mod q*/
+ bctx = BN_CTX_new();
+ BN_mod(peer->iffval, peer->iffval, dsa->q, bctx);
+ BN_CTX_free(bctx);
+
+ /*
+ * Sign and send to Bob. The filestamp is from the local file.
+ */
+ memset(vp, 0, sizeof(struct value));
+ tstamp = crypto_time();
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(peer->ident_pkey->fstamp);
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ BN_bn2bin(peer->iffval, vp->ptr);
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(sign_siglen);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_bob - construct Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_ERR protocol error
+ * XEVNT_ID bad or missing group key
+ */
+static int
+crypto_bob(
+ struct exten *ep, /* extension pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ DSA *dsa; /* IFF parameters */
+ DSA_SIG *sdsa; /* DSA signature context fake */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ BIGNUM *bn, *bk, *r;
+ u_char *ptr;
+ u_int len;
+
+ /*
+ * If the IFF parameters are not valid, something awful
+ * happened or we are being tormented.
+ */
+ if (iffkey_info == NULL) {
+ msyslog(LOG_NOTICE, "crypto_bob: scheme unavailable");
+ return (XEVNT_ID);
+ }
+ dsa = iffkey_info->pkey->pkey.dsa;
+
+ /*
+ * Extract r from the challenge.
+ */
+ len = ntohl(ep->vallen);
+ if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) {
+ msyslog(LOG_ERR, "crypto_bob: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_ERR);
+ }
+
+ /*
+ * Bob rolls random k (0 < k < q), computes y = k + b r mod q
+ * and x = g^k mod p, then sends (y, hash(x)) to Alice.
+ */
+ bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new();
+ sdsa = DSA_SIG_new();
+ BN_rand(bk, len * 8, -1, 1); /* k */
+ BN_mod_mul(bn, dsa->priv_key, r, dsa->q, bctx); /* b r mod q */
+ BN_add(bn, bn, bk);
+ BN_mod(bn, bn, dsa->q, bctx); /* k + b r mod q */
+ sdsa->r = BN_dup(bn);
+ BN_mod_exp(bk, dsa->g, bk, dsa->p, bctx); /* g^k mod p */
+ bighash(bk, bk);
+ sdsa->s = BN_dup(bk);
+ BN_CTX_free(bctx);
+ BN_free(r); BN_free(bn); BN_free(bk);
+#ifdef DEBUG
+ if (debug > 1)
+ DSA_print_fp(stdout, dsa, 0);
+#endif
+
+ /*
+ * Encode the values in ASN.1 and sign. The filestamp is from
+ * the local file.
+ */
+ len = i2d_DSA_SIG(sdsa, NULL);
+ if (len == 0) {
+ msyslog(LOG_ERR, "crypto_bob: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ DSA_SIG_free(sdsa);
+ return (XEVNT_ERR);
+ }
+ memset(vp, 0, sizeof(struct value));
+ tstamp = crypto_time();
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(iffkey_info->fstamp);
+ vp->vallen = htonl(len);
+ ptr = emalloc(len);
+ vp->ptr = ptr;
+ i2d_DSA_SIG(sdsa, &ptr);
+ DSA_SIG_free(sdsa);
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(sign_siglen);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_iff - verify Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_FSP bad filestamp
+ * XEVNT_ID bad or missing group key
+ * XEVNT_PUB bad or missing public key
+ */
+int
+crypto_iff(
+ struct exten *ep, /* extension pointer */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ DSA *dsa; /* IFF parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ DSA_SIG *sdsa; /* DSA parameters */
+ BIGNUM *bn, *bk;
+ u_int len;
+ const u_char *ptr;
+ int temp;
+
+ /*
+ * If the IFF parameters are not valid or no challenge was sent,
+ * something awful happened or we are being tormented.
+ */
+ if (peer->ident_pkey == NULL) {
+ msyslog(LOG_NOTICE, "crypto_iff: scheme unavailable");
+ return (XEVNT_ID);
+ }
+ if (ntohl(ep->fstamp) != peer->ident_pkey->fstamp) {
+ msyslog(LOG_NOTICE, "crypto_iff: invalid filestamp %u",
+ ntohl(ep->fstamp));
+ return (XEVNT_FSP);
+ }
+ if ((dsa = peer->ident_pkey->pkey->pkey.dsa) == NULL) {
+ msyslog(LOG_NOTICE, "crypto_iff: defective key");
+ return (XEVNT_PUB);
+ }
+ if (peer->iffval == NULL) {
+ msyslog(LOG_NOTICE, "crypto_iff: missing challenge");
+ return (XEVNT_ID);
+ }
+
+ /*
+ * Extract the k + b r and g^k values from the response.
+ */
+ bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new();
+ len = ntohl(ep->vallen);
+ ptr = (u_char *)ep->pkt;
+ if ((sdsa = d2i_DSA_SIG(NULL, &ptr, len)) == NULL) {
+ BN_free(bn); BN_free(bk); BN_CTX_free(bctx);
+ msyslog(LOG_ERR, "crypto_iff: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_ERR);
+ }
+
+ /*
+ * Compute g^(k + b r) g^(q - b)r mod p.
+ */
+ BN_mod_exp(bn, dsa->pub_key, peer->iffval, dsa->p, bctx);
+ BN_mod_exp(bk, dsa->g, sdsa->r, dsa->p, bctx);
+ BN_mod_mul(bn, bn, bk, dsa->p, bctx);
+
+ /*
+ * Verify the hash of the result matches hash(x).
+ */
+ bighash(bn, bn);
+ temp = BN_cmp(bn, sdsa->s);
+ BN_free(bn); BN_free(bk); BN_CTX_free(bctx);
+ BN_free(peer->iffval);
+ peer->iffval = NULL;
+ DSA_SIG_free(sdsa);
+ if (temp == 0)
+ return (XEVNT_OK);
+
+ msyslog(LOG_NOTICE, "crypto_iff: identity not verified");
+ return (XEVNT_ID);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines implement the Guillou-Quisquater (GQ) *
+ * identity scheme *
+ * *
+ ***********************************************************************
+ *
+ * The Guillou-Quisquater (GQ) identity scheme is intended for use when
+ * the certificate can be used to convey public parameters. The scheme
+ * uses a X509v3 certificate extension field do convey the public key of
+ * a private key known only to servers. There are two kinds of files:
+ * encrypted server files that contain private and public values and
+ * nonencrypted client files that contain only public values. New
+ * generations of server files must be securely transmitted to all
+ * servers of the group; client files can be distributed by any means.
+ * The scheme is self contained and independent of new generations of
+ * host keys and sign keys. The scheme is self contained and independent
+ * of new generations of host keys and sign keys.
+ *
+ * The GQ parameters hide in a RSA cuckoo structure which uses the same
+ * parameters. The values are used by an identity scheme based on RSA
+ * cryptography and described in Stimson p. 300 (with errors). The 512-
+ * bit public modulus is n = p q, where p and q are secret large primes.
+ * The TA rolls private random group key b as RSA exponent. These values
+ * are known to all group members.
+ *
+ * When rolling new certificates, a server recomputes the private and
+ * public keys. The private key u is a random roll, while the public key
+ * is the inverse obscured by the group key v = (u^-1)^b. These values
+ * replace the private and public keys normally generated by the RSA
+ * scheme. Alice challenges Bob to confirm identity using the protocol
+ * described below.
+ *
+ * How it works
+ *
+ * The scheme goes like this. Both Alice and Bob have the same modulus n
+ * and some random b as the group key. These values are computed and
+ * distributed in advance via secret means, although only the group key
+ * b is truly secret. Each has a private random private key u and public
+ * key (u^-1)^b, although not necessarily the same ones. Bob and Alice
+ * can regenerate the key pair from time to time without affecting
+ * operations. The public key is conveyed on the certificate in an
+ * extension field; the private key is never revealed.
+ *
+ * Alice rolls new random challenge r and sends to Bob in the GQ
+ * request message. Bob rolls new random k, then computes y = k u^r mod
+ * n and x = k^b mod n and sends (y, hash(x)) to Alice in the response
+ * message. Besides making the response shorter, the hash makes it
+ * effectivey impossible for an intruder to solve for b by observing
+ * a number of these messages.
+ *
+ * Alice receives the response and computes y^b v^r mod n. After a bit
+ * of algebra, this simplifies to k^b. If the hash of this result
+ * matches hash(x), Alice knows that Bob has the group key b. The signed
+ * response binds this knowledge to Bob's private key and the public key
+ * previously received in his certificate.
+ *
+ * crypto_alice2 - construct Alice's challenge in GQ scheme
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_ID bad or missing group key
+ * XEVNT_PUB bad or missing public key
+ */
+static int
+crypto_alice2(
+ struct peer *peer, /* peer pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ RSA *rsa; /* GQ parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp;
+ u_int len;
+
+ /*
+ * The identity parameters must have correct format and content.
+ */
+ if (peer->ident_pkey == NULL)
+ return (XEVNT_ID);
+
+ if ((rsa = peer->ident_pkey->pkey->pkey.rsa) == NULL) {
+ msyslog(LOG_NOTICE, "crypto_alice2: defective key");
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Roll new random r (0 < r < n).
+ */
+ if (peer->iffval != NULL)
+ BN_free(peer->iffval);
+ peer->iffval = BN_new();
+ len = BN_num_bytes(rsa->n);
+ BN_rand(peer->iffval, len * 8, -1, 1); /* r mod n */
+ bctx = BN_CTX_new();
+ BN_mod(peer->iffval, peer->iffval, rsa->n, bctx);
+ BN_CTX_free(bctx);
+
+ /*
+ * Sign and send to Bob. The filestamp is from the local file.
+ */
+ memset(vp, 0, sizeof(struct value));
+ tstamp = crypto_time();
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(peer->ident_pkey->fstamp);
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ BN_bn2bin(peer->iffval, vp->ptr);
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(sign_siglen);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_bob2 - construct Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_ERR protocol error
+ * XEVNT_ID bad or missing group key
+ */
+static int
+crypto_bob2(
+ struct exten *ep, /* extension pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ RSA *rsa; /* GQ parameters */
+ DSA_SIG *sdsa; /* DSA parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ BIGNUM *r, *k, *g, *y;
+ u_char *ptr;
+ u_int len;
+ int s_len;
+
+ /*
+ * If the GQ parameters are not valid, something awful
+ * happened or we are being tormented.
+ */
+ if (gqkey_info == NULL) {
+ msyslog(LOG_NOTICE, "crypto_bob2: scheme unavailable");
+ return (XEVNT_ID);
+ }
+ rsa = gqkey_info->pkey->pkey.rsa;
+
+ /*
+ * Extract r from the challenge.
+ */
+ len = ntohl(ep->vallen);
+ if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) {
+ msyslog(LOG_ERR, "crypto_bob2: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_ERR);
+ }
+
+ /*
+ * Bob rolls random k (0 < k < n), computes y = k u^r mod n and
+ * x = k^b mod n, then sends (y, hash(x)) to Alice.
+ */
+ bctx = BN_CTX_new(); k = BN_new(); g = BN_new(); y = BN_new();
+ sdsa = DSA_SIG_new();
+ BN_rand(k, len * 8, -1, 1); /* k */
+ BN_mod(k, k, rsa->n, bctx);
+ BN_mod_exp(y, rsa->p, r, rsa->n, bctx); /* u^r mod n */
+ BN_mod_mul(y, k, y, rsa->n, bctx); /* k u^r mod n */
+ sdsa->r = BN_dup(y);
+ BN_mod_exp(g, k, rsa->e, rsa->n, bctx); /* k^b mod n */
+ bighash(g, g);
+ sdsa->s = BN_dup(g);
+ BN_CTX_free(bctx);
+ BN_free(r); BN_free(k); BN_free(g); BN_free(y);
+#ifdef DEBUG
+ if (debug > 1)
+ RSA_print_fp(stdout, rsa, 0);
+#endif
+
+ /*
+ * Encode the values in ASN.1 and sign. The filestamp is from
+ * the local file.
+ */
+ len = s_len = i2d_DSA_SIG(sdsa, NULL);
+ if (s_len <= 0) {
+ msyslog(LOG_ERR, "crypto_bob2: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ DSA_SIG_free(sdsa);
+ return (XEVNT_ERR);
+ }
+ memset(vp, 0, sizeof(struct value));
+ tstamp = crypto_time();
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(gqkey_info->fstamp);
+ vp->vallen = htonl(len);
+ ptr = emalloc(len);
+ vp->ptr = ptr;
+ i2d_DSA_SIG(sdsa, &ptr);
+ DSA_SIG_free(sdsa);
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(sign_siglen);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_gq - verify Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_ERR protocol error
+ * XEVNT_FSP bad filestamp
+ * XEVNT_ID bad or missing group keys
+ * XEVNT_PUB bad or missing public key
+ */
+int
+crypto_gq(
+ struct exten *ep, /* extension pointer */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ RSA *rsa; /* GQ parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ DSA_SIG *sdsa; /* RSA signature context fake */
+ BIGNUM *y, *v;
+ const u_char *ptr;
+ long len;
+ u_int temp;
+
+ /*
+ * If the GQ parameters are not valid or no challenge was sent,
+ * something awful happened or we are being tormented. Note that
+ * the filestamp on the local key file can be greater than on
+ * the remote parameter file if the keys have been refreshed.
+ */
+ if (peer->ident_pkey == NULL) {
+ msyslog(LOG_NOTICE, "crypto_gq: scheme unavailable");
+ return (XEVNT_ID);
+ }
+ if (ntohl(ep->fstamp) < peer->ident_pkey->fstamp) {
+ msyslog(LOG_NOTICE, "crypto_gq: invalid filestamp %u",
+ ntohl(ep->fstamp));
+ return (XEVNT_FSP);
+ }
+ if ((rsa = peer->ident_pkey->pkey->pkey.rsa) == NULL) {
+ msyslog(LOG_NOTICE, "crypto_gq: defective key");
+ return (XEVNT_PUB);
+ }
+ if (peer->iffval == NULL) {
+ msyslog(LOG_NOTICE, "crypto_gq: missing challenge");
+ return (XEVNT_ID);
+ }
+
+ /*
+ * Extract the y = k u^r and hash(x = k^b) values from the
+ * response.
+ */
+ bctx = BN_CTX_new(); y = BN_new(); v = BN_new();
+ len = ntohl(ep->vallen);
+ ptr = (u_char *)ep->pkt;
+ if ((sdsa = d2i_DSA_SIG(NULL, &ptr, len)) == NULL) {
+ BN_CTX_free(bctx); BN_free(y); BN_free(v);
+ msyslog(LOG_ERR, "crypto_gq: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_ERR);
+ }
+
+ /*
+ * Compute v^r y^b mod n.
+ */
+ if (peer->grpkey == NULL) {
+ msyslog(LOG_NOTICE, "crypto_gq: missing group key");
+ return (XEVNT_ID);
+ }
+ BN_mod_exp(v, peer->grpkey, peer->iffval, rsa->n, bctx);
+ /* v^r mod n */
+ BN_mod_exp(y, sdsa->r, rsa->e, rsa->n, bctx); /* y^b mod n */
+ BN_mod_mul(y, v, y, rsa->n, bctx); /* v^r y^b mod n */
+
+ /*
+ * Verify the hash of the result matches hash(x).
+ */
+ bighash(y, y);
+ temp = BN_cmp(y, sdsa->s);
+ BN_CTX_free(bctx); BN_free(y); BN_free(v);
+ BN_free(peer->iffval);
+ peer->iffval = NULL;
+ DSA_SIG_free(sdsa);
+ if (temp == 0)
+ return (XEVNT_OK);
+
+ msyslog(LOG_NOTICE, "crypto_gq: identity not verified");
+ return (XEVNT_ID);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines implement the Mu-Varadharajan (MV) identity *
+ * scheme *
+ * *
+ ***********************************************************************
+ *
+ * The Mu-Varadharajan (MV) cryptosystem was originally intended when
+ * servers broadcast messages to clients, but clients never send
+ * messages to servers. There is one encryption key for the server and a
+ * separate decryption key for each client. It operated something like a
+ * pay-per-view satellite broadcasting system where the session key is
+ * encrypted by the broadcaster and the decryption keys are held in a
+ * tamperproof set-top box.
+ *
+ * The MV parameters and private encryption key hide in a DSA cuckoo
+ * structure which uses the same parameters, but generated in a
+ * different way. The values are used in an encryption scheme similar to
+ * El Gamal cryptography and a polynomial formed from the expansion of
+ * product terms (x - x[j]), as described in Mu, Y., and V.
+ * Varadharajan: Robust and Secure Broadcasting, Proc. Indocrypt 2001,
+ * 223-231. The paper has significant errors and serious omissions.
+ *
+ * Let q be the product of n distinct primes s1[j] (j = 1...n), where
+ * each s1[j] has m significant bits. Let p be a prime p = 2 * q + 1, so
+ * that q and each s1[j] divide p - 1 and p has M = n * m + 1
+ * significant bits. Let g be a generator of Zp; that is, gcd(g, p - 1)
+ * = 1 and g^q = 1 mod p. We do modular arithmetic over Zq and then
+ * project into Zp* as exponents of g. Sometimes we have to compute an
+ * inverse b^-1 of random b in Zq, but for that purpose we require
+ * gcd(b, q) = 1. We expect M to be in the 500-bit range and n
+ * relatively small, like 30. These are the parameters of the scheme and
+ * they are expensive to compute.
+ *
+ * We set up an instance of the scheme as follows. A set of random
+ * values x[j] mod q (j = 1...n), are generated as the zeros of a
+ * polynomial of order n. The product terms (x - x[j]) are expanded to
+ * form coefficients a[i] mod q (i = 0...n) in powers of x. These are
+ * used as exponents of the generator g mod p to generate the private
+ * encryption key A. The pair (gbar, ghat) of public server keys and the
+ * pairs (xbar[j], xhat[j]) (j = 1...n) of private client keys are used
+ * to construct the decryption keys. The devil is in the details.
+ *
+ * This routine generates a private server encryption file including the
+ * private encryption key E and partial decryption keys gbar and ghat.
+ * It then generates public client decryption files including the public
+ * keys xbar[j] and xhat[j] for each client j. The partial decryption
+ * files are used to compute the inverse of E. These values are suitably
+ * blinded so secrets are not revealed.
+ *
+ * The distinguishing characteristic of this scheme is the capability to
+ * revoke keys. Included in the calculation of E, gbar and ghat is the
+ * product s = prod(s1[j]) (j = 1...n) above. If the factor s1[j] is
+ * subsequently removed from the product and E, gbar and ghat
+ * recomputed, the jth client will no longer be able to compute E^-1 and
+ * thus unable to decrypt the messageblock.
+ *
+ * How it works
+ *
+ * The scheme goes like this. Bob has the server values (p, E, q, gbar,
+ * ghat) and Alice has the client values (p, xbar, xhat).
+ *
+ * Alice rolls new random nonce r mod p and sends to Bob in the MV
+ * request message. Bob rolls random nonce k mod q, encrypts y = r E^k
+ * mod p and sends (y, gbar^k, ghat^k) to Alice.
+ *
+ * Alice receives the response and computes the inverse (E^k)^-1 from
+ * the partial decryption keys gbar^k, ghat^k, xbar and xhat. She then
+ * decrypts y and verifies it matches the original r. The signed
+ * response binds this knowledge to Bob's private key and the public key
+ * previously received in his certificate.
+ *
+ * crypto_alice3 - construct Alice's challenge in MV scheme
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_ID bad or missing group key
+ * XEVNT_PUB bad or missing public key
+ */
+static int
+crypto_alice3(
+ struct peer *peer, /* peer pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ DSA *dsa; /* MV parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp;
+ u_int len;
+
+ /*
+ * The identity parameters must have correct format and content.
+ */
+ if (peer->ident_pkey == NULL)
+ return (XEVNT_ID);
+
+ if ((dsa = peer->ident_pkey->pkey->pkey.dsa) == NULL) {
+ msyslog(LOG_NOTICE, "crypto_alice3: defective key");
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Roll new random r (0 < r < q).
+ */
+ if (peer->iffval != NULL)
+ BN_free(peer->iffval);
+ peer->iffval = BN_new();
+ len = BN_num_bytes(dsa->p);
+ BN_rand(peer->iffval, len * 8, -1, 1); /* r mod p */
+ bctx = BN_CTX_new();
+ BN_mod(peer->iffval, peer->iffval, dsa->p, bctx);
+ BN_CTX_free(bctx);
+
+ /*
+ * Sign and send to Bob. The filestamp is from the local file.
+ */
+ memset(vp, 0, sizeof(struct value));
+ tstamp = crypto_time();
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(peer->ident_pkey->fstamp);
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ BN_bn2bin(peer->iffval, vp->ptr);
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(sign_siglen);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_bob3 - construct Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_ERR protocol error
+ */
+static int
+crypto_bob3(
+ struct exten *ep, /* extension pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ DSA *dsa; /* MV parameters */
+ DSA *sdsa; /* DSA signature context fake */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ BIGNUM *r, *k, *u;
+ u_char *ptr;
+ u_int len;
+
+ /*
+ * If the MV parameters are not valid, something awful
+ * happened or we are being tormented.
+ */
+ if (mvkey_info == NULL) {
+ msyslog(LOG_NOTICE, "crypto_bob3: scheme unavailable");
+ return (XEVNT_ID);
+ }
+ dsa = mvkey_info->pkey->pkey.dsa;
+
+ /*
+ * Extract r from the challenge.
+ */
+ len = ntohl(ep->vallen);
+ if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) {
+ msyslog(LOG_ERR, "crypto_bob3: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_ERR);
+ }
+
+ /*
+ * Bob rolls random k (0 < k < q), making sure it is not a
+ * factor of q. He then computes y = r A^k and sends (y, gbar^k,
+ * and ghat^k) to Alice.
+ */
+ bctx = BN_CTX_new(); k = BN_new(); u = BN_new();
+ sdsa = DSA_new();
+ sdsa->p = BN_new(); sdsa->q = BN_new(); sdsa->g = BN_new();
+ while (1) {
+ BN_rand(k, BN_num_bits(dsa->q), 0, 0);
+ BN_mod(k, k, dsa->q, bctx);
+ BN_gcd(u, k, dsa->q, bctx);
+ if (BN_is_one(u))
+ break;
+ }
+ BN_mod_exp(u, dsa->g, k, dsa->p, bctx); /* A^k r */
+ BN_mod_mul(sdsa->p, u, r, dsa->p, bctx);
+ BN_mod_exp(sdsa->q, dsa->priv_key, k, dsa->p, bctx); /* gbar */
+ BN_mod_exp(sdsa->g, dsa->pub_key, k, dsa->p, bctx); /* ghat */
+ BN_CTX_free(bctx); BN_free(k); BN_free(r); BN_free(u);
+#ifdef DEBUG
+ if (debug > 1)
+ DSA_print_fp(stdout, sdsa, 0);
+#endif
+
+ /*
+ * Encode the values in ASN.1 and sign. The filestamp is from
+ * the local file.
+ */
+ memset(vp, 0, sizeof(struct value));
+ tstamp = crypto_time();
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(mvkey_info->fstamp);
+ len = i2d_DSAparams(sdsa, NULL);
+ if (len == 0) {
+ msyslog(LOG_ERR, "crypto_bob3: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ DSA_free(sdsa);
+ return (XEVNT_ERR);
+ }
+ vp->vallen = htonl(len);
+ ptr = emalloc(len);
+ vp->ptr = ptr;
+ i2d_DSAparams(sdsa, &ptr);
+ DSA_free(sdsa);
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(sign_siglen);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_mv - verify Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_ERR protocol error
+ * XEVNT_FSP bad filestamp
+ * XEVNT_ID bad or missing group key
+ * XEVNT_PUB bad or missing public key
+ */
+int
+crypto_mv(
+ struct exten *ep, /* extension pointer */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ DSA *dsa; /* MV parameters */
+ DSA *sdsa; /* DSA parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ BIGNUM *k, *u, *v;
+ u_int len;
+ const u_char *ptr;
+ int temp;
+
+ /*
+ * If the MV parameters are not valid or no challenge was sent,
+ * something awful happened or we are being tormented.
+ */
+ if (peer->ident_pkey == NULL) {
+ msyslog(LOG_NOTICE, "crypto_mv: scheme unavailable");
+ return (XEVNT_ID);
+ }
+ if (ntohl(ep->fstamp) != peer->ident_pkey->fstamp) {
+ msyslog(LOG_NOTICE, "crypto_mv: invalid filestamp %u",
+ ntohl(ep->fstamp));
+ return (XEVNT_FSP);
+ }
+ if ((dsa = peer->ident_pkey->pkey->pkey.dsa) == NULL) {
+ msyslog(LOG_NOTICE, "crypto_mv: defective key");
+ return (XEVNT_PUB);
+ }
+ if (peer->iffval == NULL) {
+ msyslog(LOG_NOTICE, "crypto_mv: missing challenge");
+ return (XEVNT_ID);
+ }
+
+ /*
+ * Extract the y, gbar and ghat values from the response.
+ */
+ bctx = BN_CTX_new(); k = BN_new(); u = BN_new(); v = BN_new();
+ len = ntohl(ep->vallen);
+ ptr = (u_char *)ep->pkt;
+ if ((sdsa = d2i_DSAparams(NULL, &ptr, len)) == NULL) {
+ msyslog(LOG_ERR, "crypto_mv: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_ERR);
+ }
+
+ /*
+ * Compute (gbar^xhat ghat^xbar) mod p.
+ */
+ BN_mod_exp(u, sdsa->q, dsa->pub_key, dsa->p, bctx);
+ BN_mod_exp(v, sdsa->g, dsa->priv_key, dsa->p, bctx);
+ BN_mod_mul(u, u, v, dsa->p, bctx);
+ BN_mod_mul(u, u, sdsa->p, dsa->p, bctx);
+
+ /*
+ * The result should match r.
+ */
+ temp = BN_cmp(u, peer->iffval);
+ BN_CTX_free(bctx); BN_free(k); BN_free(u); BN_free(v);
+ BN_free(peer->iffval);
+ peer->iffval = NULL;
+ DSA_free(sdsa);
+ if (temp == 0)
+ return (XEVNT_OK);
+
+ msyslog(LOG_NOTICE, "crypto_mv: identity not verified");
+ return (XEVNT_ID);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines are used to manipulate certificates *
+ * *
+ ***********************************************************************
+ */
+/*
+ * cert_sign - sign x509 certificate equest and update value structure.
+ *
+ * The certificate request includes a copy of the host certificate,
+ * which includes the version number, subject name and public key of the
+ * host. The resulting certificate includes these values plus the
+ * serial number, issuer name and valid interval of the server. The
+ * valid interval extends from the current time to the same time one
+ * year hence. This may extend the life of the signed certificate beyond
+ * that of the signer certificate.
+ *
+ * It is convenient to use the NTP seconds of the current time as the
+ * serial number. In the value structure the timestamp is the current
+ * time and the filestamp is taken from the extension field. Note this
+ * routine is called only when the client clock is synchronized to a
+ * proventic source, so timestamp comparisons are valid.
+ *
+ * The host certificate is valid from the time it was generated for a
+ * period of one year. A signed certificate is valid from the time of
+ * signature for a period of one year, but only the host certificate (or
+ * sign certificate if used) is actually used to encrypt and decrypt
+ * signatures. The signature trail is built from the client via the
+ * intermediate servers to the trusted server. Each signature on the
+ * trail must be valid at the time of signature, but it could happen
+ * that a signer certificate expire before the signed certificate, which
+ * remains valid until its expiration.
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_CRT bad or missing certificate
+ * XEVNT_PER host certificate expired
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_VFY certificate not verified
+ */
+static int
+cert_sign(
+ struct exten *ep, /* extension field pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ X509 *req; /* X509 certificate request */
+ X509 *cert; /* X509 certificate */
+ X509_EXTENSION *ext; /* certificate extension */
+ ASN1_INTEGER *serial; /* serial number */
+ X509_NAME *subj; /* distinguished (common) name */
+ EVP_PKEY *pkey; /* public key */
+ EVP_MD_CTX ctx; /* message digest context */
+ tstamp_t tstamp; /* NTP timestamp */
+ struct calendar tscal;
+ u_int len;
+ const u_char *cptr;
+ u_char *ptr;
+ int i, temp;
+
+ /*
+ * Decode ASN.1 objects and construct certificate structure.
+ * Make sure the system clock is synchronized to a proventic
+ * source.
+ */
+ tstamp = crypto_time();
+ if (tstamp == 0)
+ return (XEVNT_TSP);
+
+ cptr = (void *)ep->pkt;
+ if ((req = d2i_X509(NULL, &cptr, ntohl(ep->vallen))) == NULL) {
+ msyslog(LOG_ERR, "cert_sign: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_CRT);
+ }
+ /*
+ * Extract public key and check for errors.
+ */
+ if ((pkey = X509_get_pubkey(req)) == NULL) {
+ msyslog(LOG_ERR, "cert_sign: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ X509_free(req);
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Generate X509 certificate signed by this server. If this is a
+ * trusted host, the issuer name is the group name; otherwise,
+ * it is the host name. Also copy any extensions that might be
+ * present.
+ */
+ cert = X509_new();
+ X509_set_version(cert, X509_get_version(req));
+ serial = ASN1_INTEGER_new();
+ ASN1_INTEGER_set(serial, tstamp);
+ X509_set_serialNumber(cert, serial);
+ X509_gmtime_adj(X509_get_notBefore(cert), 0L);
+ X509_gmtime_adj(X509_get_notAfter(cert), YEAR);
+ subj = X509_get_issuer_name(cert);
+ X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC,
+ hostval.ptr, strlen(hostval.ptr), -1, 0);
+ subj = X509_get_subject_name(req);
+ X509_set_subject_name(cert, subj);
+ X509_set_pubkey(cert, pkey);
+ temp = X509_get_ext_count(req);
+ for (i = 0; i < temp; i++) {
+ ext = X509_get_ext(req, i);
+ INSIST(X509_add_ext(cert, ext, -1));
+ }
+ X509_free(req);
+
+ /*
+ * Sign and verify the client certificate, but only if the host
+ * certificate has not expired.
+ */
+ (void)ntpcal_ntp_to_date(&tscal, tstamp, NULL);
+ if ((calcomp(&tscal, &(cert_host->first)) < 0)
+ || (calcomp(&tscal, &(cert_host->last)) > 0)) {
+ X509_free(cert);
+ return (XEVNT_PER);
+ }
+ X509_sign(cert, sign_pkey, sign_digest);
+ if (X509_verify(cert, sign_pkey) <= 0) {
+ msyslog(LOG_ERR, "cert_sign: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ X509_free(cert);
+ return (XEVNT_VFY);
+ }
+ len = i2d_X509(cert, NULL);
+
+ /*
+ * Build and sign the value structure. We have to sign it here,
+ * since the response has to be returned right away. This is a
+ * clogging hazard.
+ */
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = ep->fstamp;
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ ptr = vp->ptr;
+ i2d_X509(cert, &ptr);
+ vp->siglen = 0;
+ if (tstamp != 0) {
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)vp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(sign_siglen);
+ }
+#ifdef DEBUG
+ if (debug > 1)
+ X509_print_fp(stdout, cert);
+#endif
+ X509_free(cert);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * cert_install - install certificate in certificate cache
+ *
+ * This routine encodes an extension field into a certificate info/value
+ * structure. It searches the certificate list for duplicates and
+ * expunges whichever is older. Finally, it inserts this certificate
+ * first on the list.
+ *
+ * Returns certificate info pointer if valid, NULL if not.
+ */
+struct cert_info *
+cert_install(
+ struct exten *ep, /* cert info/value */
+ struct peer *peer /* peer structure */
+ )
+{
+ struct cert_info *cp, *xp, **zp;
+
+ /*
+ * Parse and validate the signed certificate. If valid,
+ * construct the info/value structure; otherwise, scamper home
+ * empty handed.
+ */
+ if ((cp = cert_parse((u_char *)ep->pkt, (long)ntohl(ep->vallen),
+ (tstamp_t)ntohl(ep->fstamp))) == NULL)
+ return (NULL);
+
+ /*
+ * Scan certificate list looking for another certificate with
+ * the same subject and issuer. If another is found with the
+ * same or older filestamp, unlink it and return the goodies to
+ * the heap. If another is found with a later filestamp, discard
+ * the new one and leave the building with the old one.
+ *
+ * Make a note to study this issue again. An earlier certificate
+ * with a long lifetime might be overtaken by a later
+ * certificate with a short lifetime, thus invalidating the
+ * earlier signature. However, we gotta find a way to leak old
+ * stuff from the cache, so we do it anyway.
+ */
+ zp = &cinfo;
+ for (xp = cinfo; xp != NULL; xp = xp->link) {
+ if (strcmp(cp->subject, xp->subject) == 0 &&
+ strcmp(cp->issuer, xp->issuer) == 0) {
+ if (ntohl(cp->cert.fstamp) <=
+ ntohl(xp->cert.fstamp)) {
+ cert_free(cp);
+ cp = xp;
+ } else {
+ *zp = xp->link;
+ cert_free(xp);
+ xp = NULL;
+ }
+ break;
+ }
+ zp = &xp->link;
+ }
+ if (xp == NULL) {
+ cp->link = cinfo;
+ cinfo = cp;
+ }
+ cp->flags |= CERT_VALID;
+ crypto_update();
+ return (cp);
+}
+
+
+/*
+ * cert_hike - verify the signature using the issuer public key
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_CRT bad or missing certificate
+ * XEVNT_PER host certificate expired
+ * XEVNT_VFY certificate not verified
+ */
+int
+cert_hike(
+ struct peer *peer, /* peer structure pointer */
+ struct cert_info *yp /* issuer certificate */
+ )
+{
+ struct cert_info *xp; /* subject certificate */
+ X509 *cert; /* X509 certificate */
+ const u_char *ptr;
+
+ /*
+ * Save the issuer on the new certificate, but remember the old
+ * one.
+ */
+ if (peer->issuer != NULL)
+ free(peer->issuer);
+ peer->issuer = estrdup(yp->issuer);
+ xp = peer->xinfo;
+ peer->xinfo = yp;
+
+ /*
+ * If subject Y matches issuer Y, then the certificate trail is
+ * complete. If Y is not trusted, the server certificate has yet
+ * been signed, so keep trying. Otherwise, save the group key
+ * and light the valid bit. If the host certificate is trusted,
+ * do not execute a sign exchange. If no identity scheme is in
+ * use, light the identity and proventic bits.
+ */
+ if (strcmp(yp->subject, yp->issuer) == 0) {
+ if (!(yp->flags & CERT_TRUST))
+ return (XEVNT_OK);
+
+ /*
+ * If the server has an an identity scheme, fetch the
+ * identity credentials. If not, the identity is
+ * verified only by the trusted certificate. The next
+ * signature will set the server proventic.
+ */
+ peer->crypto |= CRYPTO_FLAG_CERT;
+ peer->grpkey = yp->grpkey;
+ if (peer->ident == NULL || !(peer->crypto &
+ CRYPTO_FLAG_MASK))
+ peer->crypto |= CRYPTO_FLAG_VRFY;
+ }
+
+ /*
+ * If X exists, verify signature X using public key Y.
+ */
+ if (xp == NULL)
+ return (XEVNT_OK);
+
+ ptr = (u_char *)xp->cert.ptr;
+ cert = d2i_X509(NULL, &ptr, ntohl(xp->cert.vallen));
+ if (cert == NULL) {
+ xp->flags |= CERT_ERROR;
+ return (XEVNT_CRT);
+ }
+ if (X509_verify(cert, yp->pkey) <= 0) {
+ X509_free(cert);
+ xp->flags |= CERT_ERROR;
+ return (XEVNT_VFY);
+ }
+ X509_free(cert);
+
+ /*
+ * Signature X is valid only if it begins during the
+ * lifetime of Y.
+ */
+ if ((calcomp(&(xp->first), &(yp->first)) < 0)
+ || (calcomp(&(xp->first), &(yp->last)) > 0)) {
+ xp->flags |= CERT_ERROR;
+ return (XEVNT_PER);
+ }
+ xp->flags |= CERT_SIGN;
+ return (XEVNT_OK);
+}
+
+
+/*
+ * cert_parse - parse x509 certificate and create info/value structures.
+ *
+ * The server certificate includes the version number, issuer name,
+ * subject name, public key and valid date interval. If the issuer name
+ * is the same as the subject name, the certificate is self signed and
+ * valid only if the server is configured as trustable. If the names are
+ * different, another issuer has signed the server certificate and
+ * vouched for it. In this case the server certificate is valid if
+ * verified by the issuer public key.
+ *
+ * Returns certificate info/value pointer if valid, NULL if not.
+ */
+struct cert_info * /* certificate information structure */
+cert_parse(
+ const u_char *asn1cert, /* X509 certificate */
+ long len, /* certificate length */
+ tstamp_t fstamp /* filestamp */
+ )
+{
+ X509 *cert; /* X509 certificate */
+ X509_EXTENSION *ext; /* X509v3 extension */
+ struct cert_info *ret; /* certificate info/value */
+ BIO *bp;
+ char pathbuf[MAXFILENAME];
+ const u_char *ptr;
+ char *pch;
+ int temp, cnt, i;
+ struct calendar fscal;
+
+ /*
+ * Decode ASN.1 objects and construct certificate structure.
+ */
+ ptr = asn1cert;
+ if ((cert = d2i_X509(NULL, &ptr, len)) == NULL) {
+ msyslog(LOG_ERR, "cert_parse: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (NULL);
+ }
+#ifdef DEBUG
+ if (debug > 1)
+ X509_print_fp(stdout, cert);
+#endif
+
+ /*
+ * Extract version, subject name and public key.
+ */
+ ret = emalloc_zero(sizeof(*ret));
+ if ((ret->pkey = X509_get_pubkey(cert)) == NULL) {
+ msyslog(LOG_ERR, "cert_parse: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+ ret->version = X509_get_version(cert);
+ X509_NAME_oneline(X509_get_subject_name(cert), pathbuf,
+ sizeof(pathbuf));
+ pch = strstr(pathbuf, "CN=");
+ if (NULL == pch) {
+ msyslog(LOG_NOTICE, "cert_parse: invalid subject %s",
+ pathbuf);
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+ ret->subject = estrdup(pch + 3);
+
+ /*
+ * Extract remaining objects. Note that the NTP serial number is
+ * the NTP seconds at the time of signing, but this might not be
+ * the case for other authority. We don't bother to check the
+ * objects at this time, since the real crunch can happen only
+ * when the time is valid but not yet certificated.
+ */
+ ret->nid = OBJ_obj2nid(cert->cert_info->signature->algorithm);
+ ret->digest = (const EVP_MD *)EVP_get_digestbynid(ret->nid);
+ ret->serial =
+ (u_long)ASN1_INTEGER_get(X509_get_serialNumber(cert));
+ X509_NAME_oneline(X509_get_issuer_name(cert), pathbuf,
+ sizeof(pathbuf));
+ if ((pch = strstr(pathbuf, "CN=")) == NULL) {
+ msyslog(LOG_NOTICE, "cert_parse: invalid issuer %s",
+ pathbuf);
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+ ret->issuer = estrdup(pch + 3);
+ asn_to_calendar(X509_get_notBefore(cert), &(ret->first));
+ asn_to_calendar(X509_get_notAfter(cert), &(ret->last));
+
+ /*
+ * Extract extension fields. These are ad hoc ripoffs of
+ * currently assigned functions and will certainly be changed
+ * before prime time.
+ */
+ cnt = X509_get_ext_count(cert);
+ for (i = 0; i < cnt; i++) {
+ ext = X509_get_ext(cert, i);
+ temp = OBJ_obj2nid(ext->object);
+ switch (temp) {
+
+ /*
+ * If a key_usage field is present, we decode whether
+ * this is a trusted or private certificate. This is
+ * dorky; all we want is to compare NIDs, but OpenSSL
+ * insists on BIO text strings.
+ */
+ case NID_ext_key_usage:
+ bp = BIO_new(BIO_s_mem());
+ X509V3_EXT_print(bp, ext, 0, 0);
+ BIO_gets(bp, pathbuf, sizeof(pathbuf));
+ BIO_free(bp);
+ if (strcmp(pathbuf, "Trust Root") == 0)
+ ret->flags |= CERT_TRUST;
+ else if (strcmp(pathbuf, "Private") == 0)
+ ret->flags |= CERT_PRIV;
+#if DEBUG
+ if (debug)
+ printf("cert_parse: %s: %s\n",
+ OBJ_nid2ln(temp), pathbuf);
+#endif
+ break;
+
+ /*
+ * If a NID_subject_key_identifier field is present, it
+ * contains the GQ public key.
+ */
+ case NID_subject_key_identifier:
+ ret->grpkey = BN_bin2bn(&ext->value->data[2],
+ ext->value->length - 2, NULL);
+ /* fall through */
+#if DEBUG
+ default:
+ if (debug)
+ printf("cert_parse: %s\n",
+ OBJ_nid2ln(temp));
+#endif
+ }
+ }
+ if (strcmp(ret->subject, ret->issuer) == 0) {
+
+ /*
+ * If certificate is self signed, verify signature.
+ */
+ if (X509_verify(cert, ret->pkey) <= 0) {
+ msyslog(LOG_NOTICE,
+ "cert_parse: signature not verified %s",
+ ret->subject);
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+ } else {
+
+ /*
+ * Check for a certificate loop.
+ */
+ if (strcmp(hostval.ptr, ret->issuer) == 0) {
+ msyslog(LOG_NOTICE,
+ "cert_parse: certificate trail loop %s",
+ ret->subject);
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+ }
+
+ /*
+ * Verify certificate valid times. Note that certificates cannot
+ * be retroactive.
+ */
+ (void)ntpcal_ntp_to_date(&fscal, fstamp, NULL);
+ if ((calcomp(&(ret->first), &(ret->last)) > 0)
+ || (calcomp(&(ret->first), &fscal) < 0)) {
+ msyslog(LOG_NOTICE,
+ "cert_parse: invalid times %s first %u-%02u-%02uT%02u:%02u:%02u last %u-%02u-%02uT%02u:%02u:%02u fstamp %u-%02u-%02uT%02u:%02u:%02u",
+ ret->subject,
+ ret->first.year, ret->first.month, ret->first.monthday,
+ ret->first.hour, ret->first.minute, ret->first.second,
+ ret->last.year, ret->last.month, ret->last.monthday,
+ ret->last.hour, ret->last.minute, ret->last.second,
+ fscal.year, fscal.month, fscal.monthday,
+ fscal.hour, fscal.minute, fscal.second);
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+
+ /*
+ * Build the value structure to sign and send later.
+ */
+ ret->cert.fstamp = htonl(fstamp);
+ ret->cert.vallen = htonl(len);
+ ret->cert.ptr = emalloc(len);
+ memcpy(ret->cert.ptr, asn1cert, len);
+ X509_free(cert);
+ return (ret);
+}
+
+
+/*
+ * cert_free - free certificate information structure
+ */
+void
+cert_free(
+ struct cert_info *cinf /* certificate info/value structure */
+ )
+{
+ if (cinf->pkey != NULL)
+ EVP_PKEY_free(cinf->pkey);
+ if (cinf->subject != NULL)
+ free(cinf->subject);
+ if (cinf->issuer != NULL)
+ free(cinf->issuer);
+ if (cinf->grpkey != NULL)
+ BN_free(cinf->grpkey);
+ value_free(&cinf->cert);
+ free(cinf);
+}
+
+
+/*
+ * crypto_key - load cryptographic parameters and keys
+ *
+ * This routine searches the key cache for matching name in the form
+ * ntpkey_<key>_<name>, where <key> is one of host, sign, iff, gq, mv,
+ * and <name> is the host/group name. If not found, it tries to load a
+ * PEM-encoded file of the same name and extracts the filestamp from
+ * the first line of the file name. It returns the key pointer if valid,
+ * NULL if not.
+ */
+static struct pkey_info *
+crypto_key(
+ char *cp, /* file name */
+ char *passwd1, /* password */
+ sockaddr_u *addr /* IP address */
+ )
+{
+ FILE *str; /* file handle */
+ struct pkey_info *pkp; /* generic key */
+ EVP_PKEY *pkey = NULL; /* public/private key */
+ tstamp_t fstamp;
+ char filename[MAXFILENAME]; /* name of key file */
+ char linkname[MAXFILENAME]; /* filestamp buffer) */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ char *ptr;
+
+ /*
+ * Search the key cache for matching key and name.
+ */
+ for (pkp = pkinfo; pkp != NULL; pkp = pkp->link) {
+ if (strcmp(cp, pkp->name) == 0)
+ return (pkp);
+ }
+
+ /*
+ * Open the key file. If the first character of the file name is
+ * not '/', prepend the keys directory string. If something goes
+ * wrong, abandon ship.
+ */
+ if (*cp == '/')
+ strlcpy(filename, cp, sizeof(filename));
+ else
+ snprintf(filename, sizeof(filename), "%s/%s", keysdir,
+ cp);
+ str = fopen(filename, "r");
+ if (str == NULL)
+ return (NULL);
+
+ /*
+ * Read the filestamp, which is contained in the first line.
+ */
+ if ((ptr = fgets(linkname, sizeof(linkname), str)) == NULL) {
+ msyslog(LOG_ERR, "crypto_key: empty file %s",
+ filename);
+ fclose(str);
+ return (NULL);
+ }
+ if ((ptr = strrchr(ptr, '.')) == NULL) {
+ msyslog(LOG_ERR, "crypto_key: no filestamp %s",
+ filename);
+ fclose(str);
+ return (NULL);
+ }
+ if (sscanf(++ptr, "%u", &fstamp) != 1) {
+ msyslog(LOG_ERR, "crypto_key: invalid filestamp %s",
+ filename);
+ fclose(str);
+ return (NULL);
+ }
+
+ /*
+ * Read and decrypt PEM-encoded private key. If it fails to
+ * decrypt, game over.
+ */
+ pkey = PEM_read_PrivateKey(str, NULL, NULL, passwd1);
+ fclose(str);
+ if (pkey == NULL) {
+ msyslog(LOG_ERR, "crypto_key: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ exit (-1);
+ }
+
+ /*
+ * Make a new entry in the key cache.
+ */
+ pkp = emalloc(sizeof(struct pkey_info));
+ pkp->link = pkinfo;
+ pkinfo = pkp;
+ pkp->pkey = pkey;
+ pkp->name = estrdup(cp);
+ pkp->fstamp = fstamp;
+
+ /*
+ * Leave tracks in the cryptostats.
+ */
+ if ((ptr = strrchr(linkname, '\n')) != NULL)
+ *ptr = '\0';
+ snprintf(statstr, sizeof(statstr), "%s mod %d", &linkname[2],
+ EVP_PKEY_size(pkey) * 8);
+ record_crypto_stats(addr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_key: %s\n", statstr);
+ if (debug > 1) {
+ if (pkey->type == EVP_PKEY_DSA)
+ DSA_print_fp(stdout, pkey->pkey.dsa, 0);
+ else if (pkey->type == EVP_PKEY_RSA)
+ RSA_print_fp(stdout, pkey->pkey.rsa, 0);
+ }
+#endif
+ return (pkp);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines are used only at initialization time *
+ * *
+ ***********************************************************************
+ */
+/*
+ * crypto_cert - load certificate from file
+ *
+ * This routine loads an X.509 RSA or DSA certificate from a file and
+ * constructs a info/cert value structure for this machine. The
+ * structure includes a filestamp extracted from the file name. Later
+ * the certificate can be sent to another machine on request.
+ *
+ * Returns certificate info/value pointer if valid, NULL if not.
+ */
+static struct cert_info * /* certificate information */
+crypto_cert(
+ char *cp /* file name */
+ )
+{
+ struct cert_info *ret; /* certificate information */
+ FILE *str; /* file handle */
+ char filename[MAXFILENAME]; /* name of certificate file */
+ char linkname[MAXFILENAME]; /* filestamp buffer */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ tstamp_t fstamp; /* filestamp */
+ long len;
+ char *ptr;
+ char *name, *header;
+ u_char *data;
+
+ /*
+ * Open the certificate file. If the first character of the file
+ * name is not '/', prepend the keys directory string. If
+ * something goes wrong, abandon ship.
+ */
+ if (*cp == '/')
+ strlcpy(filename, cp, sizeof(filename));
+ else
+ snprintf(filename, sizeof(filename), "%s/%s", keysdir,
+ cp);
+ str = fopen(filename, "r");
+ if (str == NULL)
+ return (NULL);
+
+ /*
+ * Read the filestamp, which is contained in the first line.
+ */
+ if ((ptr = fgets(linkname, sizeof(linkname), str)) == NULL) {
+ msyslog(LOG_ERR, "crypto_cert: empty file %s",
+ filename);
+ fclose(str);
+ return (NULL);
+ }
+ if ((ptr = strrchr(ptr, '.')) == NULL) {
+ msyslog(LOG_ERR, "crypto_cert: no filestamp %s",
+ filename);
+ fclose(str);
+ return (NULL);
+ }
+ if (sscanf(++ptr, "%u", &fstamp) != 1) {
+ msyslog(LOG_ERR, "crypto_cert: invalid filestamp %s",
+ filename);
+ fclose(str);
+ return (NULL);
+ }
+
+ /*
+ * Read PEM-encoded certificate and install.
+ */
+ if (!PEM_read(str, &name, &header, &data, &len)) {
+ msyslog(LOG_ERR, "crypto_cert: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ fclose(str);
+ return (NULL);
+ }
+ fclose(str);
+ free(header);
+ if (strcmp(name, "CERTIFICATE") != 0) {
+ msyslog(LOG_NOTICE, "crypto_cert: wrong PEM type %s",
+ name);
+ free(name);
+ free(data);
+ return (NULL);
+ }
+ free(name);
+
+ /*
+ * Parse certificate and generate info/value structure. The
+ * pointer and copy nonsense is due something broken in Solaris.
+ */
+ ret = cert_parse(data, len, fstamp);
+ free(data);
+ if (ret == NULL)
+ return (NULL);
+
+ if ((ptr = strrchr(linkname, '\n')) != NULL)
+ *ptr = '\0';
+ snprintf(statstr, sizeof(statstr), "%s 0x%x len %lu",
+ &linkname[2], ret->flags, len);
+ record_crypto_stats(NULL, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_cert: %s\n", statstr);
+#endif
+ return (ret);
+}
+
+
+/*
+ * crypto_setup - load keys, certificate and identity parameters
+ *
+ * This routine loads the public/private host key and certificate. If
+ * available, it loads the public/private sign key, which defaults to
+ * the host key. The host key must be RSA, but the sign key can be
+ * either RSA or DSA. If a trusted certificate, it loads the identity
+ * parameters. In either case, the public key on the certificate must
+ * agree with the sign key.
+ *
+ * Required but missing files and inconsistent data and errors are
+ * fatal. Allowing configuration to continue would be hazardous and
+ * require really messy error checks.
+ */
+void
+crypto_setup(void)
+{
+ struct pkey_info *pinfo; /* private/public key */
+ char filename[MAXFILENAME]; /* file name buffer */
+ char hostname[MAXFILENAME]; /* host name buffer */
+ char *randfile;
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ l_fp seed; /* crypto PRNG seed as NTP timestamp */
+ u_int len;
+ int bytes;
+ u_char *ptr;
+
+ /*
+ * Check for correct OpenSSL version and avoid initialization in
+ * the case of multiple crypto commands.
+ */
+ if (crypto_flags & CRYPTO_FLAG_ENAB) {
+ msyslog(LOG_NOTICE,
+ "crypto_setup: spurious crypto command");
+ return;
+ }
+ ssl_check_version();
+
+ /*
+ * Load required random seed file and seed the random number
+ * generator. Be default, it is found as .rnd in the user home
+ * directory. The root home directory may be / or /root,
+ * depending on the system. Wiggle the contents a bit and write
+ * it back so the sequence does not repeat when we next restart.
+ */
+ if (!RAND_status()) {
+ if (rand_file == NULL) {
+ RAND_file_name(filename, sizeof(filename));
+ randfile = filename;
+ } else if (*rand_file != '/') {
+ snprintf(filename, sizeof(filename), "%s/%s",
+ keysdir, rand_file);
+ randfile = filename;
+ } else
+ randfile = rand_file;
+
+ if ((bytes = RAND_load_file(randfile, -1)) == 0) {
+ msyslog(LOG_ERR,
+ "crypto_setup: random seed file %s missing",
+ randfile);
+ exit (-1);
+ }
+ get_systime(&seed);
+ RAND_seed(&seed, sizeof(l_fp));
+ RAND_write_file(randfile);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "crypto_setup: OpenSSL version %lx random seed file %s bytes read %d\n",
+ SSLeay(), randfile, bytes);
+#endif
+ }
+
+ /*
+ * Initialize structures.
+ */
+ gethostname(hostname, sizeof(hostname));
+ if (host_filename != NULL)
+ strlcpy(hostname, host_filename, sizeof(hostname));
+ if (passwd == NULL)
+ passwd = estrdup(hostname);
+ memset(&hostval, 0, sizeof(hostval));
+ memset(&pubkey, 0, sizeof(pubkey));
+ memset(&tai_leap, 0, sizeof(tai_leap));
+
+ /*
+ * Load required host key from file "ntpkey_host_<hostname>". If
+ * no host key file is not found or has invalid password, life
+ * as we know it ends. The host key also becomes the default
+ * sign key.
+ */
+ snprintf(filename, sizeof(filename), "ntpkey_host_%s", hostname);
+ pinfo = crypto_key(filename, passwd, NULL);
+ if (pinfo == NULL) {
+ msyslog(LOG_ERR,
+ "crypto_setup: host key file %s not found or corrupt",
+ filename);
+ exit (-1);
+ }
+ if (pinfo->pkey->type != EVP_PKEY_RSA) {
+ msyslog(LOG_ERR,
+ "crypto_setup: host key is not RSA key type");
+ exit (-1);
+ }
+ host_pkey = pinfo->pkey;
+ sign_pkey = host_pkey;
+ hostval.fstamp = htonl(pinfo->fstamp);
+
+ /*
+ * Construct public key extension field for agreement scheme.
+ */
+ len = i2d_PublicKey(host_pkey, NULL);
+ ptr = emalloc(len);
+ pubkey.ptr = ptr;
+ i2d_PublicKey(host_pkey, &ptr);
+ pubkey.fstamp = hostval.fstamp;
+ pubkey.vallen = htonl(len);
+
+ /*
+ * Load optional sign key from file "ntpkey_sign_<hostname>". If
+ * available, it becomes the sign key.
+ */
+ snprintf(filename, sizeof(filename), "ntpkey_sign_%s", hostname);
+ pinfo = crypto_key(filename, passwd, NULL);
+ if (pinfo != NULL)
+ sign_pkey = pinfo->pkey;
+
+ /*
+ * Load required certificate from file "ntpkey_cert_<hostname>".
+ */
+ snprintf(filename, sizeof(filename), "ntpkey_cert_%s", hostname);
+ cinfo = crypto_cert(filename);
+ if (cinfo == NULL) {
+ msyslog(LOG_ERR,
+ "crypto_setup: certificate file %s not found or corrupt",
+ filename);
+ exit (-1);
+ }
+ cert_host = cinfo;
+ sign_digest = cinfo->digest;
+ sign_siglen = EVP_PKEY_size(sign_pkey);
+ if (cinfo->flags & CERT_PRIV)
+ crypto_flags |= CRYPTO_FLAG_PRIV;
+
+ /*
+ * The certificate must be self-signed.
+ */
+ if (strcmp(cinfo->subject, cinfo->issuer) != 0) {
+ msyslog(LOG_ERR,
+ "crypto_setup: certificate %s is not self-signed",
+ filename);
+ exit (-1);
+ }
+ hostval.ptr = estrdup(cinfo->subject);
+ hostval.vallen = htonl(strlen(cinfo->subject));
+ sys_hostname = hostval.ptr;
+ ptr = (u_char *)strchr(sys_hostname, '@');
+ if (ptr != NULL)
+ sys_groupname = estrdup((char *)++ptr);
+ if (ident_filename != NULL)
+ strlcpy(hostname, ident_filename, sizeof(hostname));
+
+ /*
+ * Load optional IFF parameters from file
+ * "ntpkey_iffkey_<hostname>".
+ */
+ snprintf(filename, sizeof(filename), "ntpkey_iffkey_%s",
+ hostname);
+ iffkey_info = crypto_key(filename, passwd, NULL);
+ if (iffkey_info != NULL)
+ crypto_flags |= CRYPTO_FLAG_IFF;
+
+ /*
+ * Load optional GQ parameters from file
+ * "ntpkey_gqkey_<hostname>".
+ */
+ snprintf(filename, sizeof(filename), "ntpkey_gqkey_%s",
+ hostname);
+ gqkey_info = crypto_key(filename, passwd, NULL);
+ if (gqkey_info != NULL)
+ crypto_flags |= CRYPTO_FLAG_GQ;
+
+ /*
+ * Load optional MV parameters from file
+ * "ntpkey_mvkey_<hostname>".
+ */
+ snprintf(filename, sizeof(filename), "ntpkey_mvkey_%s",
+ hostname);
+ mvkey_info = crypto_key(filename, passwd, NULL);
+ if (mvkey_info != NULL)
+ crypto_flags |= CRYPTO_FLAG_MV;
+
+ /*
+ * We met the enemy and he is us. Now strike up the dance.
+ */
+ crypto_flags |= CRYPTO_FLAG_ENAB | (cinfo->nid << 16);
+ snprintf(statstr, sizeof(statstr), "setup 0x%x host %s %s",
+ crypto_flags, hostname, OBJ_nid2ln(cinfo->nid));
+ record_crypto_stats(NULL, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_setup: %s\n", statstr);
+#endif
+}
+
+
+/*
+ * crypto_config - configure data from the crypto command.
+ */
+void
+crypto_config(
+ int item, /* configuration item */
+ char *cp /* item name */
+ )
+{
+ int nid;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("crypto_config: item %d %s\n", item, cp);
+#endif
+ switch (item) {
+
+ /*
+ * Set host name (host).
+ */
+ case CRYPTO_CONF_PRIV:
+ if (NULL != host_filename)
+ free(host_filename);
+ host_filename = estrdup(cp);
+ break;
+
+ /*
+ * Set group name (ident).
+ */
+ case CRYPTO_CONF_IDENT:
+ if (NULL != ident_filename)
+ free(ident_filename);
+ ident_filename = estrdup(cp);
+ break;
+
+ /*
+ * Set private key password (pw).
+ */
+ case CRYPTO_CONF_PW:
+ if (NULL != passwd)
+ free(passwd);
+ passwd = estrdup(cp);
+ break;
+
+ /*
+ * Set random seed file name (randfile).
+ */
+ case CRYPTO_CONF_RAND:
+ if (NULL != rand_file)
+ free(rand_file);
+ rand_file = estrdup(cp);
+ break;
+
+ /*
+ * Set message digest NID.
+ */
+ case CRYPTO_CONF_NID:
+ nid = OBJ_sn2nid(cp);
+ if (nid == 0)
+ msyslog(LOG_ERR,
+ "crypto_config: invalid digest name %s", cp);
+ else
+ crypto_nid = nid;
+ break;
+ }
+}
+# else /* !AUTOKEY follows */
+int ntp_crypto_bs_pubkey;
+# endif /* !AUTOKEY */
diff --git a/ntpd/ntp_filegen.c b/ntpd/ntp_filegen.c
new file mode 100644
index 0000000..a1703a9
--- /dev/null
+++ b/ntpd/ntp_filegen.c
@@ -0,0 +1,645 @@
+/*
+ * ntp_filegen.c,v 3.12 1994/01/25 19:06:11 kardel Exp
+ *
+ * implements file generations support for NTP
+ * logfiles and statistic files
+ *
+ *
+ * Copyright (C) 1992, 1996 by Rainer Pruy
+ * Friedrich-Alexander Universitaet Erlangen-Nuernberg, Germany
+ *
+ * This code may be modified and used freely
+ * provided credits remain intact.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_string.h"
+#include "ntp_calendar.h"
+#include "ntp_filegen.h"
+#include "ntp_stdlib.h"
+
+/*
+ * NTP is intended to run long periods of time without restart.
+ * Thus log and statistic files generated by NTP will grow large.
+ *
+ * this set of routines provides a central interface
+ * to generating files using file generations
+ *
+ * the generation of a file is changed according to file generation type
+ */
+
+
+/*
+ * redefine this if your system dislikes filename suffixes like
+ * X.19910101 or X.1992W50 or ....
+ */
+#define SUFFIX_SEP '.'
+
+static void filegen_open (FILEGEN *, u_int32, const time_t*);
+static int valid_fileref (const char *, const char *);
+static void filegen_init (const char *, const char *, FILEGEN *);
+#ifdef DEBUG
+static void filegen_uninit (FILEGEN *);
+#endif /* DEBUG */
+
+
+/*
+ * filegen_init
+ */
+
+static void
+filegen_init(
+ const char * dir,
+ const char * fname,
+ FILEGEN * fgp
+ )
+{
+ fgp->fp = NULL;
+ fgp->dir = estrdup(dir);
+ fgp->fname = estrdup(fname);
+ fgp->id_lo = 0;
+ fgp->id_hi = 0;
+ fgp->type = FILEGEN_DAY;
+ fgp->flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
+}
+
+
+/*
+ * filegen_uninit - free memory allocated by filegen_init
+ */
+#ifdef DEBUG
+static void
+filegen_uninit(
+ FILEGEN *fgp
+ )
+{
+ free(fgp->dir);
+ free(fgp->fname);
+}
+#endif
+
+
+/*
+ * open a file generation according to the current settings of gen
+ * will also provide a link to basename if requested to do so
+ */
+
+static void
+filegen_open(
+ FILEGEN * gen,
+ u_int32 stamp,
+ const time_t * pivot
+ )
+{
+ char *savename; /* temp store for name collision handling */
+ char *fullname; /* name with any designation extension */
+ char *filename; /* name without designation extension */
+ char *suffix; /* where to print suffix extension */
+ u_int len, suflen;
+ FILE *fp;
+ struct calendar cal;
+ struct isodate iso;
+
+ /* get basic filename in buffer, leave room for extensions */
+ len = strlen(gen->dir) + strlen(gen->fname) + 65;
+ filename = emalloc(len);
+ fullname = emalloc(len);
+ savename = NULL;
+ snprintf(filename, len, "%s%s", gen->dir, gen->fname);
+
+ /* where to place suffix */
+ suflen = strlcpy(fullname, filename, len);
+ suffix = fullname + suflen;
+ suflen = len - suflen;
+
+ /* last octet of fullname set to '\0' for truncation check */
+ fullname[len - 1] = '\0';
+
+ switch (gen->type) {
+
+ default:
+ msyslog(LOG_ERR,
+ "unsupported file generations type %d for "
+ "\"%s\" - reverting to FILEGEN_NONE",
+ gen->type, filename);
+ gen->type = FILEGEN_NONE;
+ break;
+
+ case FILEGEN_NONE:
+ /* no suffix, all set */
+ break;
+
+ case FILEGEN_PID:
+ gen->id_lo = getpid();
+ gen->id_hi = 0;
+ snprintf(suffix, suflen, "%c#%ld",
+ SUFFIX_SEP, gen->id_lo);
+ break;
+
+ case FILEGEN_DAY:
+ /*
+ * You can argue here in favor of using MJD, but I
+ * would assume it to be easier for humans to interpret
+ * dates in a format they are used to in everyday life.
+ */
+ ntpcal_ntp_to_date(&cal, stamp, pivot);
+ snprintf(suffix, suflen, "%c%04d%02d%02d",
+ SUFFIX_SEP, cal.year, cal.month, cal.monthday);
+ cal.hour = cal.minute = cal.second = 0;
+ gen->id_lo = ntpcal_date_to_ntp(&cal);
+ gen->id_hi = (u_int32)(gen->id_lo + SECSPERDAY);
+ break;
+
+ case FILEGEN_WEEK:
+ isocal_ntp_to_date(&iso, stamp, pivot);
+ snprintf(suffix, suflen, "%c%04dw%02d",
+ SUFFIX_SEP, iso.year, iso.week);
+ iso.hour = iso.minute = iso.second = 0;
+ iso.weekday = 1;
+ gen->id_lo = isocal_date_to_ntp(&iso);
+ gen->id_hi = (u_int32)(gen->id_lo + 7 * SECSPERDAY);
+ break;
+
+ case FILEGEN_MONTH:
+ ntpcal_ntp_to_date(&cal, stamp, pivot);
+ snprintf(suffix, suflen, "%c%04d%02d",
+ SUFFIX_SEP, cal.year, cal.month);
+ cal.hour = cal.minute = cal.second = 0;
+ cal.monthday = 1;
+ gen->id_lo = ntpcal_date_to_ntp(&cal);
+ cal.month++;
+ gen->id_hi = ntpcal_date_to_ntp(&cal);
+ break;
+
+ case FILEGEN_YEAR:
+ ntpcal_ntp_to_date(&cal, stamp, pivot);
+ snprintf(suffix, suflen, "%c%04d",
+ SUFFIX_SEP, cal.year);
+ cal.hour = cal.minute = cal.second = 0;
+ cal.month = cal.monthday = 1;
+ gen->id_lo = ntpcal_date_to_ntp(&cal);
+ cal.year++;
+ gen->id_hi = ntpcal_date_to_ntp(&cal);
+ break;
+
+ case FILEGEN_AGE:
+ gen->id_lo = current_time - (current_time % SECSPERDAY);
+ gen->id_hi = gen->id_lo + SECSPERDAY;
+ snprintf(suffix, suflen, "%ca%08ld",
+ SUFFIX_SEP, gen->id_lo);
+ }
+
+ /* check possible truncation */
+ if ('\0' != fullname[len - 1]) {
+ fullname[len - 1] = '\0';
+ msyslog(LOG_ERR, "logfile name truncated: \"%s\"",
+ fullname);
+ }
+
+ if (FILEGEN_NONE != gen->type) {
+ /*
+ * check for existence of a file with name 'basename'
+ * as we disallow such a file
+ * if FGEN_FLAG_LINK is set create a link
+ */
+ struct stat stats;
+ /*
+ * try to resolve name collisions
+ */
+ static u_long conflicts = 0;
+
+#ifndef S_ISREG
+#define S_ISREG(mode) (((mode) & S_IFREG) == S_IFREG)
+#endif
+ if (stat(filename, &stats) == 0) {
+ /* Hm, file exists... */
+ if (S_ISREG(stats.st_mode)) {
+ if (stats.st_nlink <= 1) {
+ /*
+ * Oh, it is not linked - try to save it
+ */
+ savename = emalloc(len);
+ snprintf(savename, len,
+ "%s%c%dC%lu",
+ filename, SUFFIX_SEP,
+ (int)getpid(), conflicts++);
+
+ if (rename(filename, savename) != 0)
+ msyslog(LOG_ERR,
+ "couldn't save %s: %m",
+ filename);
+ free(savename);
+ } else {
+ /*
+ * there is at least a second link to
+ * this file.
+ * just remove the conflicting one
+ */
+ if (
+#if !defined(VMS)
+ unlink(filename) != 0
+#else
+ delete(filename) != 0
+#endif
+ )
+ msyslog(LOG_ERR,
+ "couldn't unlink %s: %m",
+ filename);
+ }
+ } else {
+ /*
+ * Ehh? Not a regular file ?? strange !!!!
+ */
+ msyslog(LOG_ERR,
+ "expected regular file for %s "
+ "(found mode 0%lo)",
+ filename,
+ (unsigned long)stats.st_mode);
+ }
+ } else {
+ /*
+ * stat(..) failed, but it is absolutely correct for
+ * 'basename' not to exist
+ */
+ if (ENOENT != errno)
+ msyslog(LOG_ERR, "stat(%s) failed: %m",
+ filename);
+ }
+ }
+
+ /*
+ * now, try to open new file generation...
+ */
+ DPRINTF(4, ("opening filegen (type=%d/stamp=%u) \"%s\"\n",
+ gen->type, stamp, fullname));
+
+ fp = fopen(fullname, "a");
+
+ if (NULL == fp) {
+ /* open failed -- keep previous state
+ *
+ * If the file was open before keep the previous generation.
+ * This will cause output to end up in the 'wrong' file,
+ * but I think this is still better than losing output
+ *
+ * ignore errors due to missing directories
+ */
+
+ if (ENOENT != errno)
+ msyslog(LOG_ERR, "can't open %s: %m", fullname);
+ } else {
+ if (NULL != gen->fp) {
+ fclose(gen->fp);
+ gen->fp = NULL;
+ }
+ gen->fp = fp;
+
+ if (gen->flag & FGEN_FLAG_LINK) {
+ /*
+ * need to link file to basename
+ * have to use hardlink for now as I want to allow
+ * gen->basename spanning directory levels
+ * this would make it more complex to get the correct
+ * fullname for symlink
+ *
+ * Ok, it would just mean taking the part following
+ * the last '/' in the name.... Should add it later....
+ */
+
+ /* Windows NT does not support file links -Greg Schueman 1/18/97 */
+
+#if defined(SYS_WINNT) || defined(SYS_VXWORKS)
+ SetLastError(0); /* On WinNT, don't support FGEN_FLAG_LINK */
+#elif defined(VMS)
+ errno = 0; /* On VMS, don't support FGEN_FLAG_LINK */
+#else /* not (VMS) / VXWORKS / WINNT ; DO THE LINK) */
+ if (link(fullname, filename) != 0)
+ if (EEXIST != errno)
+ msyslog(LOG_ERR,
+ "can't link(%s, %s): %m",
+ fullname, filename);
+#endif /* SYS_WINNT || VXWORKS */
+ } /* flags & FGEN_FLAG_LINK */
+ } /* else fp == NULL */
+
+ free(filename);
+ free(fullname);
+ return;
+}
+
+/*
+ * this function sets up gen->fp to point to the correct
+ * generation of the file for the time specified by 'now'
+ *
+ * 'now' usually is interpreted as second part of a l_fp as is in the cal...
+ * library routines
+ */
+
+void
+filegen_setup(
+ FILEGEN * gen,
+ u_int32 now
+ )
+{
+ int current;
+ time_t pivot;
+
+ if (!(gen->flag & FGEN_FLAG_ENABLED)) {
+ if (NULL != gen->fp) {
+ fclose(gen->fp);
+ gen->fp = NULL;
+ }
+ return;
+ }
+
+ switch (gen->type) {
+
+ default:
+ case FILEGEN_NONE:
+ current = TRUE;
+ break;
+
+ case FILEGEN_PID:
+ current = ((int)gen->id_lo == getpid());
+ break;
+
+ case FILEGEN_AGE:
+ current = (gen->id_lo <= current_time) &&
+ (gen->id_hi > current_time);
+ break;
+
+ case FILEGEN_DAY:
+ case FILEGEN_WEEK:
+ case FILEGEN_MONTH:
+ case FILEGEN_YEAR:
+ current = (gen->id_lo <= now) &&
+ (gen->id_hi > now);
+ break;
+ }
+ /*
+ * try to open file if not yet open
+ * reopen new file generation file on change of generation id
+ */
+ if (NULL == gen->fp || !current) {
+ DPRINTF(1, ("filegen %0x %u\n", gen->type, now));
+ pivot = time(NULL);
+ filegen_open(gen, now, &pivot);
+ }
+}
+
+
+/*
+ * change settings for filegen files
+ */
+void
+filegen_config(
+ FILEGEN * gen,
+ const char * dir,
+ const char * fname,
+ u_int type,
+ u_int flag
+ )
+{
+ int file_existed;
+ l_fp now;
+
+
+ /*
+ * if nothing would be changed...
+ */
+ if (strcmp(dir, gen->dir) == 0 && strcmp(fname, gen->fname) == 0
+ && type == gen->type && flag == gen->flag)
+ return;
+
+ /*
+ * validate parameters
+ */
+ if (!valid_fileref(dir, fname))
+ return;
+
+ if (NULL != gen->fp) {
+ fclose(gen->fp);
+ gen->fp = NULL;
+ file_existed = TRUE;
+ } else {
+ file_existed = FALSE;
+ }
+
+ DPRINTF(3, ("configuring filegen:\n"
+ "\tdir:\t%s -> %s\n"
+ "\tfname:\t%s -> %s\n"
+ "\ttype:\t%d -> %d\n"
+ "\tflag: %x -> %x\n",
+ gen->dir, dir,
+ gen->fname, fname,
+ gen->type, type,
+ gen->flag, flag));
+
+ if (strcmp(gen->dir, dir) != 0) {
+ free(gen->dir);
+ gen->dir = estrdup(dir);
+ }
+
+ if (strcmp(gen->fname, fname) != 0) {
+ free(gen->fname);
+ gen->fname = estrdup(fname);
+ }
+ gen->type = (u_char)type;
+ gen->flag = (u_char)flag;
+
+ /*
+ * make filegen use the new settings
+ * special action is only required when a generation file
+ * is currently open
+ * otherwise the new settings will be used anyway at the next open
+ */
+ if (file_existed) {
+ get_systime(&now);
+ filegen_setup(gen, now.l_ui);
+ }
+}
+
+
+/*
+ * check whether concatenating prefix and basename
+ * yields a legal filename
+ */
+static int
+valid_fileref(
+ const char * dir,
+ const char * fname
+ )
+{
+ /*
+ * dir cannot be changed dynamically
+ * (within the context of filegen)
+ * so just reject basenames containing '..'
+ *
+ * ASSUMPTION:
+ * file system parts 'below' dir may be
+ * specified without infringement of security
+ *
+ * restricting dir to legal values
+ * has to be ensured by other means
+ * (however, it would be possible to perform some checks here...)
+ */
+ const char *p;
+
+ /*
+ * Just to catch, dumb errors opening up the world...
+ */
+ if (NULL == dir || '\0' == dir[0])
+ return FALSE;
+
+ if (NULL == fname)
+ return FALSE;
+
+#ifdef SYS_WINNT
+ /*
+ * Windows treats / equivalent to \, reject any / to ensure
+ * check below for DIR_SEP (\ on windows) are adequate.
+ */
+ if (strchr(fname, '/')) {
+ msyslog(LOG_ERR,
+ "filegen filenames must not contain '/': %s",
+ fname);
+ return FALSE;
+ }
+#endif
+
+ for (p = fname; p != NULL; p = strchr(p, DIR_SEP)) {
+ if ('.' == p[0] && '.' == p[1]
+ && ('\0' == p[2] || DIR_SEP == p[2]))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * filegen registry
+ */
+
+static struct filegen_entry {
+ char * name;
+ FILEGEN * filegen;
+ struct filegen_entry * next;
+} *filegen_registry = NULL;
+
+
+FILEGEN *
+filegen_get(
+ const char * name
+ )
+{
+ struct filegen_entry *f = filegen_registry;
+
+ while (f) {
+ if (f->name == name || strcmp(name, f->name) == 0) {
+ DPRINTF(4, ("filegen_get(%s) = %p\n",
+ name, f->filegen));
+ return f->filegen;
+ }
+ f = f->next;
+ }
+ DPRINTF(4, ("filegen_get(%s) = NULL\n", name));
+ return NULL;
+}
+
+
+void
+filegen_register(
+ const char * dir,
+ const char * name,
+ FILEGEN * filegen
+ )
+{
+ struct filegen_entry **ppfe;
+
+ DPRINTF(4, ("filegen_register(%s, %p)\n", name, filegen));
+
+ filegen_init(dir, name, filegen);
+
+ ppfe = &filegen_registry;
+ while (NULL != *ppfe) {
+ if ((*ppfe)->name == name
+ || !strcmp((*ppfe)->name, name)) {
+
+ DPRINTF(5, ("replacing filegen %p\n",
+ (*ppfe)->filegen));
+
+ (*ppfe)->filegen = filegen;
+ return;
+ }
+ ppfe = &((*ppfe)->next);
+ }
+
+ *ppfe = emalloc(sizeof **ppfe);
+
+ (*ppfe)->next = NULL;
+ (*ppfe)->name = estrdup(name);
+ (*ppfe)->filegen = filegen;
+
+ DPRINTF(6, ("adding new filegen\n"));
+
+ return;
+}
+
+
+/*
+ * filegen_statsdir() - reset each filegen entry's dir to statsdir.
+ */
+void
+filegen_statsdir(void)
+{
+ struct filegen_entry *f;
+
+ for (f = filegen_registry; f != NULL; f = f->next)
+ filegen_config(f->filegen, statsdir, f->filegen->fname,
+ f->filegen->type, f->filegen->flag);
+}
+
+
+/*
+ * filegen_unregister frees memory allocated by filegen_register for
+ * name.
+ */
+#ifdef DEBUG
+void
+filegen_unregister(
+ char *name
+ )
+{
+ struct filegen_entry ** ppfe;
+ struct filegen_entry * pfe;
+ FILEGEN * fg;
+
+ DPRINTF(4, ("filegen_unregister(%s)\n", name));
+
+ ppfe = &filegen_registry;
+
+ while (NULL != *ppfe) {
+ if ((*ppfe)->name == name
+ || !strcmp((*ppfe)->name, name)) {
+ pfe = *ppfe;
+ *ppfe = (*ppfe)->next;
+ fg = pfe->filegen;
+ free(pfe->name);
+ free(pfe);
+ filegen_uninit(fg);
+ break;
+ }
+ ppfe = &((*ppfe)->next);
+ }
+}
+#endif /* DEBUG */
diff --git a/ntpd/ntp_io.c b/ntpd/ntp_io.c
new file mode 100644
index 0000000..0f335ea
--- /dev/null
+++ b/ntpd/ntp_io.c
@@ -0,0 +1,4688 @@
+/*
+ * ntp_io.c - input/output routines for ntpd. The socket-opening code
+ * was shamelessly stolen from ntpd.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+#ifdef HAVE_FNMATCH_H
+# include <fnmatch.h>
+# if !defined(FNM_CASEFOLD) && defined(FNM_IGNORECASE)
+# define FNM_CASEFOLD FNM_IGNORECASE
+# endif
+#endif
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H /* UXPV: SIOC* #defines (Frank Vance <fvance@waii.com>) */
+# include <sys/sockio.h>
+#endif
+#ifdef HAVE_SYS_UIO_H
+# include <sys/uio.h>
+#endif
+
+#include "ntp_machine.h"
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "iosignal.h"
+#include "ntp_lists.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_worker.h"
+#include "ntp_request.h"
+#include "ntp_assert.h"
+#include "timevalops.h"
+#include "timespecops.h"
+#include "ntpd-opts.h"
+
+/* Don't include ISC's version of IPv6 variables and structures */
+#define ISC_IPV6_H 1
+#include <isc/mem.h>
+#include <isc/interfaceiter.h>
+#include <isc/netaddr.h>
+#include <isc/result.h>
+#include <isc/sockaddr.h>
+
+#ifdef SIM
+#include "ntpsim.h"
+#endif
+
+#ifdef HAS_ROUTING_SOCKET
+# include <net/route.h>
+# ifdef HAVE_RTNETLINK
+# include <linux/rtnetlink.h>
+# endif
+#endif
+
+
+/*
+ * setsockopt does not always have the same arg declaration
+ * across all platforms. If it's not defined we make it empty
+ */
+
+#ifndef SETSOCKOPT_ARG_CAST
+#define SETSOCKOPT_ARG_CAST
+#endif
+
+extern int listen_to_virtual_ips;
+
+/*
+ * NIC rule entry
+ */
+typedef struct nic_rule_tag nic_rule;
+
+struct nic_rule_tag {
+ nic_rule * next;
+ nic_rule_action action;
+ nic_rule_match match_type;
+ char * if_name;
+ sockaddr_u addr;
+ int prefixlen;
+};
+
+/*
+ * NIC rule listhead. Entries are added at the head so that the first
+ * match in the list is the last matching rule specified.
+ */
+nic_rule *nic_rule_list;
+
+
+#if defined(SO_BINTIME) && defined(SCM_BINTIME) && defined(CMSG_FIRSTHDR)
+# define HAVE_PACKET_TIMESTAMP
+# define HAVE_BINTIME
+# ifdef BINTIME_CTLMSGBUF_SIZE
+# define CMSG_BUFSIZE BINTIME_CTLMSGBUF_SIZE
+# else
+# define CMSG_BUFSIZE 1536 /* moderate default */
+# endif
+#elif defined(SO_TIMESTAMPNS) && defined(SCM_TIMESTAMPNS) && defined(CMSG_FIRSTHDR)
+# define HAVE_PACKET_TIMESTAMP
+# define HAVE_TIMESTAMPNS
+# ifdef TIMESTAMPNS_CTLMSGBUF_SIZE
+# define CMSG_BUFSIZE TIMESTAMPNS_CTLMSGBUF_SIZE
+# else
+# define CMSG_BUFSIZE 1536 /* moderate default */
+# endif
+#elif defined(SO_TIMESTAMP) && defined(SCM_TIMESTAMP) && defined(CMSG_FIRSTHDR)
+# define HAVE_PACKET_TIMESTAMP
+# define HAVE_TIMESTAMP
+# ifdef TIMESTAMP_CTLMSGBUF_SIZE
+# define CMSG_BUFSIZE TIMESTAMP_CTLMSGBUF_SIZE
+# else
+# define CMSG_BUFSIZE 1536 /* moderate default */
+# endif
+#else
+/* fill in for old/other timestamp interfaces */
+#endif
+
+#if defined(SYS_WINNT)
+#include "win32_io.h"
+#include <isc/win32os.h>
+#endif
+
+/*
+ * We do asynchronous input using the SIGIO facility. A number of
+ * recvbuf buffers are preallocated for input. In the signal
+ * handler we poll to see which sockets are ready and read the
+ * packets from them into the recvbuf's along with a time stamp and
+ * an indication of the source host and the interface it was received
+ * through. This allows us to get as accurate receive time stamps
+ * as possible independent of other processing going on.
+ *
+ * We watch the number of recvbufs available to the signal handler
+ * and allocate more when this number drops below the low water
+ * mark. If the signal handler should run out of buffers in the
+ * interim it will drop incoming frames, the idea being that it is
+ * better to drop a packet than to be inaccurate.
+ */
+
+
+/*
+ * Other statistics of possible interest
+ */
+volatile u_long packets_dropped; /* total number of packets dropped on reception */
+volatile u_long packets_ignored; /* packets received on wild card interface */
+volatile u_long packets_received; /* total number of packets received */
+ u_long packets_sent; /* total number of packets sent */
+ u_long packets_notsent; /* total number of packets which couldn't be sent */
+
+volatile u_long handler_calls; /* number of calls to interrupt handler */
+volatile u_long handler_pkts; /* number of pkts received by handler */
+u_long io_timereset; /* time counters were reset */
+
+/*
+ * Interface stuff
+ */
+endpt * any_interface; /* wildcard ipv4 interface */
+endpt * any6_interface; /* wildcard ipv6 interface */
+endpt * loopback_interface; /* loopback ipv4 interface */
+
+isc_boolean_t broadcast_client_enabled; /* is broadcast client enabled */
+u_int sys_ifnum; /* next .ifnum to assign */
+int ninterfaces; /* Total number of interfaces */
+
+int disable_dynamic_updates; /* scan interfaces once only */
+
+#ifdef REFCLOCK
+/*
+ * Refclock stuff. We keep a chain of structures with data concerning
+ * the guys we are doing I/O for.
+ */
+static struct refclockio *refio;
+#endif /* REFCLOCK */
+
+/*
+ * File descriptor masks etc. for call to select
+ * Not needed for I/O Completion Ports or anything outside this file
+ */
+static fd_set activefds;
+static int maxactivefd;
+
+/*
+ * bit alternating value to detect verified interfaces during an update cycle
+ */
+static u_short sys_interphase = 0;
+
+static endpt * new_interface(endpt *);
+static void add_interface(endpt *);
+static int update_interfaces(u_short, interface_receiver_t,
+ void *);
+static void remove_interface(endpt *);
+static endpt * create_interface(u_short, endpt *);
+
+static int is_wildcard_addr (const sockaddr_u *);
+
+/*
+ * Multicast functions
+ */
+static isc_boolean_t addr_ismulticast (sockaddr_u *);
+static isc_boolean_t is_anycast (sockaddr_u *,
+ const char *);
+
+/*
+ * Not all platforms support multicast
+ */
+#ifdef MCAST
+static isc_boolean_t socket_multicast_enable (endpt *, sockaddr_u *);
+static isc_boolean_t socket_multicast_disable(endpt *, sockaddr_u *);
+#endif
+
+#ifdef DEBUG
+static void interface_dump (const endpt *);
+static void sockaddr_dump (const sockaddr_u *);
+static void print_interface (const endpt *, char *, char *);
+#define DPRINT_INTERFACE(level, args) do { if (debug >= (level)) { print_interface args; } } while (0)
+#else
+#define DPRINT_INTERFACE(level, args) do {} while (0)
+#endif
+
+typedef struct vsock vsock_t;
+enum desc_type { FD_TYPE_SOCKET, FD_TYPE_FILE };
+
+struct vsock {
+ vsock_t * link;
+ SOCKET fd;
+ enum desc_type type;
+};
+
+vsock_t *fd_list;
+
+#if !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET)
+/*
+ * async notification processing (e. g. routing sockets)
+ */
+/*
+ * support for receiving data on fd that is not a refclock or a socket
+ * like e. g. routing sockets
+ */
+struct asyncio_reader {
+ struct asyncio_reader *link; /* the list this is being kept in */
+ SOCKET fd; /* fd to be read */
+ void *data; /* possibly local data */
+ void (*receiver)(struct asyncio_reader *); /* input handler */
+};
+
+struct asyncio_reader *asyncio_reader_list;
+
+static void delete_asyncio_reader (struct asyncio_reader *);
+static struct asyncio_reader *new_asyncio_reader (void);
+static void add_asyncio_reader (struct asyncio_reader *, enum desc_type);
+static void remove_asyncio_reader (struct asyncio_reader *);
+
+#endif /* !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET) */
+
+static void init_async_notifications (void);
+
+static int addr_eqprefix (const sockaddr_u *, const sockaddr_u *,
+ int);
+static int addr_samesubnet (const sockaddr_u *, const sockaddr_u *,
+ const sockaddr_u *, const sockaddr_u *);
+static int create_sockets (u_short);
+static SOCKET open_socket (sockaddr_u *, int, int, endpt *);
+static char * fdbits (int, fd_set *);
+static void set_reuseaddr (int);
+static isc_boolean_t socket_broadcast_enable (struct interface *, SOCKET, sockaddr_u *);
+static isc_boolean_t socket_broadcast_disable (struct interface *, sockaddr_u *);
+
+typedef struct remaddr remaddr_t;
+
+struct remaddr {
+ remaddr_t * link;
+ sockaddr_u addr;
+ endpt * ep;
+};
+
+remaddr_t * remoteaddr_list;
+endpt * ep_list; /* complete endpt list */
+endpt * mc4_list; /* IPv4 mcast-capable unicast endpts */
+endpt * mc6_list; /* IPv6 mcast-capable unicast endpts */
+
+static endpt * wildipv4;
+static endpt * wildipv6;
+
+#ifdef SYS_WINNT
+int accept_wildcard_if_for_winnt;
+#else
+const int accept_wildcard_if_for_winnt = FALSE;
+#endif
+
+static void add_fd_to_list (SOCKET, enum desc_type);
+static endpt * find_addr_in_list (sockaddr_u *);
+static endpt * find_flagged_addr_in_list(sockaddr_u *, u_int32);
+static void delete_addr_from_list (sockaddr_u *);
+static void delete_interface_from_list(endpt *);
+static void close_and_delete_fd_from_list(SOCKET);
+static void add_addr_to_list (sockaddr_u *, endpt *);
+static void create_wildcards (u_short);
+static endpt * findlocalinterface (sockaddr_u *, int, int);
+static endpt * findclosestinterface (sockaddr_u *, int);
+#ifdef DEBUG
+static const char * action_text (nic_rule_action);
+#endif
+static nic_rule_action interface_action(char *, sockaddr_u *, u_int32);
+static void convert_isc_if (isc_interface_t *,
+ endpt *, u_short);
+static void calc_addr_distance(sockaddr_u *,
+ const sockaddr_u *,
+ const sockaddr_u *);
+static int cmp_addr_distance(const sockaddr_u *,
+ const sockaddr_u *);
+
+/*
+ * Routines to read the ntp packets
+ */
+#if !defined(HAVE_IO_COMPLETION_PORT)
+static inline int read_network_packet (SOCKET, struct interface *, l_fp);
+static void ntpd_addremove_io_fd (int, int, int);
+static input_handler_t input_handler;
+#ifdef REFCLOCK
+static inline int read_refclock_packet (SOCKET, struct refclockio *, l_fp);
+#endif
+#endif
+
+
+
+#ifndef HAVE_IO_COMPLETION_PORT
+void
+maintain_activefds(
+ int fd,
+ int closing
+ )
+{
+ int i;
+
+ if (fd < 0 || fd >= FD_SETSIZE) {
+ msyslog(LOG_ERR,
+ "Too many sockets in use, FD_SETSIZE %d exceeded by fd %d",
+ FD_SETSIZE, fd);
+ exit(1);
+ }
+
+ if (!closing) {
+ FD_SET(fd, &activefds);
+ maxactivefd = max(fd, maxactivefd);
+ } else {
+ FD_CLR(fd, &activefds);
+ if (maxactivefd && fd == maxactivefd) {
+ for (i = maxactivefd - 1; i >= 0; i--)
+ if (FD_ISSET(i, &activefds)) {
+ maxactivefd = i;
+ break;
+ }
+ NTP_INSIST(fd != maxactivefd);
+ }
+ }
+}
+#endif /* !HAVE_IO_COMPLETION_PORT */
+
+
+#ifdef DEBUG_TIMING
+/*
+ * collect timing information for various processing
+ * paths. currently we only pass them on to the file
+ * for later processing. this could also do histogram
+ * based analysis in other to reduce the load (and skew)
+ * dur to the file output
+ */
+void
+collect_timing(struct recvbuf *rb, const char *tag, int count, l_fp *dts)
+{
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "%s %d %s %s",
+ (rb != NULL)
+ ? ((rb->dstadr != NULL)
+ ? stoa(&rb->recv_srcadr)
+ : "-REFCLOCK-")
+ : "-",
+ count, lfptoa(dts, 9), tag);
+ record_timing_stats(buf);
+}
+#endif
+
+/*
+ * About dynamic interfaces, sockets, reception and more...
+ *
+ * the code solves following tasks:
+ *
+ * - keep a current list of active interfaces in order
+ * to bind to to the interface address on NTP_PORT so that
+ * all wild and specific bindings for NTP_PORT are taken by ntpd
+ * to avoid other daemons messing with the time or sockets.
+ * - all interfaces keep a list of peers that are referencing
+ * the interface in order to quickly re-assign the peers to
+ * new interface in case an interface is deleted (=> gone from system or
+ * down)
+ * - have a preconfigured socket ready with the right local address
+ * for transmission and reception
+ * - have an address list for all destination addresses used within ntpd
+ * to find the "right" preconfigured socket.
+ * - facilitate updating the internal interface list with respect to
+ * the current kernel state
+ *
+ * special issues:
+ *
+ * - mapping of multicast addresses to the interface affected is not always
+ * one to one - especially on hosts with multiple interfaces
+ * the code here currently allocates a separate interface entry for those
+ * multicast addresses
+ * iff it is able to bind to a *new* socket with the multicast address (flags |= MCASTIF)
+ * in case of failure the multicast address is bound to an existing interface.
+ * - on some systems it is perfectly legal to assign the same address to
+ * multiple interfaces. Therefore this code does not keep a list of interfaces
+ * but a list of interfaces that represent a unique address as determined by the kernel
+ * by the procedure in findlocalinterface. Thus it is perfectly legal to see only
+ * one representative of a group of real interfaces if they share the same address.
+ *
+ * Frank Kardel 20050910
+ */
+
+/*
+ * init_io - initialize I/O module.
+ */
+void
+init_io(void)
+{
+ /* Init buffer free list and stat counters */
+ init_recvbuff(RECV_INIT);
+ /* update interface every 5 minutes as default */
+ interface_interval = 300;
+
+#ifdef WORK_PIPE
+ addremove_io_fd = &ntpd_addremove_io_fd;
+#endif
+
+#ifdef SYS_WINNT
+ init_io_completion_port();
+#endif
+
+#if defined(HAVE_SIGNALED_IO)
+ (void) set_signal(input_handler);
+#endif
+}
+
+
+static void
+ntpd_addremove_io_fd(
+ int fd,
+ int is_pipe,
+ int remove_it
+ )
+{
+ UNUSED_ARG(is_pipe);
+
+#ifdef HAVE_SIGNALED_IO
+ init_socket_sig(fd);
+#endif /* not HAVE_SIGNALED_IO */
+
+ maintain_activefds(fd, remove_it);
+}
+
+
+/*
+ * io_open_sockets - call socket creation routine
+ */
+void
+io_open_sockets(void)
+{
+ static int already_opened;
+
+ if (already_opened || HAVE_OPT( SAVECONFIGQUIT ))
+ return;
+
+ already_opened = 1;
+
+ /*
+ * Create the sockets
+ */
+ BLOCKIO();
+ create_sockets(NTP_PORT);
+ UNBLOCKIO();
+
+ init_async_notifications();
+
+ DPRINTF(3, ("io_open_sockets: maxactivefd %d\n", maxactivefd));
+}
+
+
+#ifdef DEBUG
+/*
+ * function to dump the contents of the interface structure
+ * for debugging use only.
+ */
+void
+interface_dump(const endpt *itf)
+{
+ printf("Dumping interface: %p\n", itf);
+ printf("fd = %d\n", itf->fd);
+ printf("bfd = %d\n", itf->bfd);
+ printf("sin = %s,\n", stoa(&itf->sin));
+ sockaddr_dump(&itf->sin);
+ printf("bcast = %s,\n", stoa(&itf->bcast));
+ sockaddr_dump(&itf->bcast);
+ printf("mask = %s,\n", stoa(&itf->mask));
+ sockaddr_dump(&itf->mask);
+ printf("name = %s\n", itf->name);
+ printf("flags = 0x%08x\n", itf->flags);
+ printf("last_ttl = %d\n", itf->last_ttl);
+ printf("addr_refid = %08x\n", itf->addr_refid);
+ printf("num_mcast = %d\n", itf->num_mcast);
+ printf("received = %ld\n", itf->received);
+ printf("sent = %ld\n", itf->sent);
+ printf("notsent = %ld\n", itf->notsent);
+ printf("ifindex = %u\n", itf->ifindex);
+ printf("peercnt = %u\n", itf->peercnt);
+ printf("phase = %u\n", itf->phase);
+}
+
+/*
+ * sockaddr_dump - hex dump the start of a sockaddr_u
+ */
+static void
+sockaddr_dump(const sockaddr_u *psau)
+{
+ /* Limit the size of the sockaddr_in6 hex dump */
+ const int maxsize = min(32, sizeof(psau->sa6));
+ const u_char * cp;
+ int i;
+
+ /* XXX: Should we limit maxsize based on psau->saX.sin_family? */
+ cp = (const void *)&psau->sa6;
+
+ for(i = 0; i < maxsize; i++) {
+ printf("%02x", *cp++);
+ if (!((i + 1) % 4))
+ printf(" ");
+ }
+ printf("\n");
+}
+
+/*
+ * print_interface - helper to output debug information
+ */
+static void
+print_interface(const endpt *iface, char *pfx, char *sfx)
+{
+ printf("%sinterface #%d: fd=%d, bfd=%d, name=%s, flags=0x%x, ifindex=%u, sin=%s",
+ pfx,
+ iface->ifnum,
+ iface->fd,
+ iface->bfd,
+ iface->name,
+ iface->flags,
+ iface->ifindex,
+ stoa(&iface->sin));
+ if (AF_INET == iface->family) {
+ if (iface->flags & INT_BROADCAST)
+ printf(", bcast=%s", stoa(&iface->bcast));
+ printf(", mask=%s", stoa(&iface->mask));
+ }
+ printf(", %s:%s",
+ (iface->ignore_packets)
+ ? "Disabled"
+ : "Enabled",
+ sfx);
+ if (debug > 4) /* in-depth debugging only */
+ interface_dump(iface);
+}
+#endif
+
+#if !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET)
+/*
+ * create an asyncio_reader structure
+ */
+static struct asyncio_reader *
+new_asyncio_reader(void)
+{
+ struct asyncio_reader *reader;
+
+ reader = emalloc_zero(sizeof(*reader));
+ reader->fd = INVALID_SOCKET;
+
+ return reader;
+}
+
+/*
+ * delete a reader
+ */
+static void
+delete_asyncio_reader(
+ struct asyncio_reader *reader
+ )
+{
+ free(reader);
+}
+
+/*
+ * add asynchio_reader
+ */
+static void
+add_asyncio_reader(
+ struct asyncio_reader * reader,
+ enum desc_type type)
+{
+ LINK_SLIST(asyncio_reader_list, reader, link);
+ add_fd_to_list(reader->fd, type);
+}
+
+/*
+ * remove asynchio_reader
+ */
+static void
+remove_asyncio_reader(
+ struct asyncio_reader *reader
+ )
+{
+ struct asyncio_reader *unlinked;
+
+ UNLINK_SLIST(unlinked, asyncio_reader_list, reader, link,
+ struct asyncio_reader);
+
+ if (reader->fd != INVALID_SOCKET)
+ close_and_delete_fd_from_list(reader->fd);
+
+ reader->fd = INVALID_SOCKET;
+}
+#endif /* !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET) */
+
+
+/* compare two sockaddr prefixes */
+static int
+addr_eqprefix(
+ const sockaddr_u * a,
+ const sockaddr_u * b,
+ int prefixlen
+ )
+{
+ isc_netaddr_t isc_a;
+ isc_netaddr_t isc_b;
+ isc_sockaddr_t isc_sa;
+
+ ZERO(isc_sa);
+ memcpy(&isc_sa.type, a, min(sizeof(isc_sa.type), sizeof(*a)));
+ isc_netaddr_fromsockaddr(&isc_a, &isc_sa);
+
+ ZERO(isc_sa);
+ memcpy(&isc_sa.type, b, min(sizeof(isc_sa.type), sizeof(*b)));
+ isc_netaddr_fromsockaddr(&isc_b, &isc_sa);
+
+ return (int)isc_netaddr_eqprefix(&isc_a, &isc_b,
+ (u_int)prefixlen);
+}
+
+
+static int
+addr_samesubnet(
+ const sockaddr_u * a,
+ const sockaddr_u * a_mask,
+ const sockaddr_u * b,
+ const sockaddr_u * b_mask
+ )
+{
+ const u_int32 * pa;
+ const u_int32 * pa_limit;
+ const u_int32 * pb;
+ const u_int32 * pm;
+ size_t loops;
+
+ NTP_REQUIRE(AF(a) == AF(a_mask));
+ NTP_REQUIRE(AF(b) == AF(b_mask));
+ /*
+ * With address and mask families verified to match, comparing
+ * the masks also validates the address's families match.
+ */
+ if (!SOCK_EQ(a_mask, b_mask))
+ return FALSE;
+
+ if (IS_IPV6(a)) {
+ loops = sizeof(NSRCADR6(a)) / sizeof(*pa);
+ pa = (const void *)&NSRCADR6(a);
+ pb = (const void *)&NSRCADR6(b);
+ pm = (const void *)&NSRCADR6(a_mask);
+ } else {
+ loops = sizeof(NSRCADR(a)) / sizeof(*pa);
+ pa = (const void *)&NSRCADR(a);
+ pb = (const void *)&NSRCADR(b);
+ pm = (const void *)&NSRCADR(a_mask);
+ }
+ for (pa_limit = pa + loops; pa < pa_limit; pa++, pb++, pm++)
+ if ((*pa & *pm) != (*pb & *pm))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/*
+ * Code to tell if we have an IP address
+ * If we have then return the sockaddr structure
+ * and set the return value
+ * see the bind9/getaddresses.c for details
+ */
+int
+is_ip_address(
+ const char * host,
+ u_short af,
+ sockaddr_u * addr
+ )
+{
+ struct in_addr in4;
+ struct in6_addr in6;
+ struct addrinfo hints;
+ struct addrinfo *result;
+ struct sockaddr_in6 *resaddr6;
+ char tmpbuf[128];
+ char *pch;
+
+ NTP_REQUIRE(host != NULL);
+ NTP_REQUIRE(addr != NULL);
+
+ ZERO_SOCK(addr);
+
+ /*
+ * Try IPv4, then IPv6. In order to handle the extended format
+ * for IPv6 scoped addresses (address%scope_ID), we'll use a local
+ * working buffer of 128 bytes. The length is an ad-hoc value, but
+ * should be enough for this purpose; the buffer can contain a string
+ * of at least 80 bytes for scope_ID in addition to any IPv6 numeric
+ * addresses (up to 46 bytes), the delimiter character and the
+ * terminating NULL character.
+ */
+ if (AF_UNSPEC == af || AF_INET == af)
+ if (inet_pton(AF_INET, host, &in4) == 1) {
+ AF(addr) = AF_INET;
+ SET_ADDR4N(addr, in4.s_addr);
+
+ return TRUE;
+ }
+
+ if (AF_UNSPEC == af || AF_INET6 == af)
+ if (sizeof(tmpbuf) > strlen(host)) {
+ if ('[' == host[0]) {
+ strlcpy(tmpbuf, &host[1], sizeof(tmpbuf));
+ pch = strchr(tmpbuf, ']');
+ if (pch != NULL)
+ *pch = '\0';
+ } else {
+ strlcpy(tmpbuf, host, sizeof(tmpbuf));
+ }
+ ZERO(hints);
+ hints.ai_family = AF_INET6;
+ hints.ai_flags |= AI_NUMERICHOST;
+ if (getaddrinfo(tmpbuf, NULL, &hints, &result) == 0) {
+ AF(addr) = AF_INET6;
+ resaddr6 = (struct sockaddr_in6 *)result->ai_addr;
+ SET_ADDR6N(addr, resaddr6->sin6_addr);
+ SET_SCOPE(addr, resaddr6->sin6_scope_id);
+
+ freeaddrinfo(result);
+ return TRUE;
+ }
+ }
+ /*
+ * If we got here it was not an IP address
+ */
+ return FALSE;
+}
+
+
+/*
+ * interface list enumerator - visitor pattern
+ */
+void
+interface_enumerate(
+ interface_receiver_t receiver,
+ void * data
+ )
+{
+ interface_info_t ifi;
+
+ ifi.action = IFS_EXISTS;
+ for (ifi.ep = ep_list; ifi.ep != NULL; ifi.ep = ifi.ep->elink)
+ (*receiver)(data, &ifi);
+}
+
+/*
+ * do standard initialization of interface structure
+ */
+static void
+init_interface(
+ endpt *ep
+ )
+{
+ ZERO(*ep);
+ ep->fd = INVALID_SOCKET;
+ ep->bfd = INVALID_SOCKET;
+ ep->phase = sys_interphase;
+}
+
+
+/*
+ * create new interface structure initialize from
+ * template structure or via standard initialization
+ * function
+ */
+static struct interface *
+new_interface(
+ struct interface *interface
+ )
+{
+ struct interface * iface;
+
+ iface = emalloc(sizeof(*iface));
+
+ if (NULL == interface)
+ init_interface(iface);
+ else /* use the template */
+ memcpy(iface, interface, sizeof(*iface));
+
+ /* count every new instance of an interface in the system */
+ iface->ifnum = sys_ifnum++;
+ iface->starttime = current_time;
+
+ return iface;
+}
+
+
+/*
+ * return interface storage into free memory pool
+ */
+static inline void
+delete_interface(
+ endpt *ep
+ )
+{
+ free(ep);
+}
+
+
+/*
+ * link interface into list of known interfaces
+ */
+static void
+add_interface(
+ endpt * ep
+ )
+{
+ endpt ** pmclisthead;
+ endpt * scan;
+ endpt * scan_next;
+ endpt * unlinked;
+ sockaddr_u * addr;
+ int ep_local;
+ int scan_local;
+ int same_subnet;
+ int ep_univ_iid; /* iface ID from MAC address */
+ int scan_univ_iid; /* see RFC 4291 */
+ int ep_privacy; /* random local iface ID */
+ int scan_privacy; /* see RFC 4941 */
+ int rc;
+
+ /* Calculate the refid */
+ ep->addr_refid = addr2refid(&ep->sin);
+ /* link at tail so ntpdc -c ifstats index increases each row */
+ LINK_TAIL_SLIST(ep_list, ep, elink, endpt);
+ ninterfaces++;
+#ifdef MCAST
+ /* the rest is for enabled multicast-capable addresses only */
+ if (ep->ignore_packets || !(INT_MULTICAST & ep->flags) ||
+ INT_LOOPBACK & ep->flags)
+ return;
+# ifndef INCLUDE_IPV6_MULTICAST_SUPPORT
+ if (AF_INET6 == ep->family)
+ return;
+# endif
+ pmclisthead = (AF_INET == ep->family)
+ ? &mc4_list
+ : &mc6_list;
+
+ if (AF_INET6 == ep->family) {
+ ep_local =
+ IN6_IS_ADDR_LINKLOCAL(PSOCK_ADDR6(&ep->sin)) ||
+ IN6_IS_ADDR_SITELOCAL(PSOCK_ADDR6(&ep->sin));
+ ep_univ_iid = IS_IID_UNIV(&ep->sin);
+ ep_privacy = !!(INT_PRIVACY & ep->flags);
+ } else {
+ ep_local = FALSE;
+ ep_univ_iid = FALSE;
+ ep_privacy = FALSE;
+ }
+ DPRINTF(4, ("add_interface mcast-capable %s%s%s%s\n",
+ stoa(&ep->sin),
+ (ep_local) ? " link/scope-local" : "",
+ (ep_univ_iid) ? " univ-IID" : "",
+ (ep_privacy) ? " privacy" : ""));
+ /*
+ * If we have multiple local addresses on the same network
+ * interface, and some are link- or site-local, do not multicast
+ * out from the link-/site-local addresses by default, to avoid
+ * duplicate manycastclient associations between v6 peers using
+ * link-local and global addresses. link-local can still be
+ * chosen using "nic ignore myv6globalprefix::/64".
+ * Similarly, if we have multiple global addresses from the same
+ * prefix on the same network interface, multicast from one,
+ * preferring EUI-64, then static, then least RFC 4941 privacy
+ * addresses.
+ */
+ for (scan = *pmclisthead; scan != NULL; scan = scan_next) {
+ scan_next = scan->mclink;
+ if (ep->family != scan->family)
+ continue;
+ if (strcmp(ep->name, scan->name))
+ continue;
+ same_subnet = addr_samesubnet(&ep->sin, &ep->mask,
+ &scan->sin, &scan->mask);
+ if (AF_INET6 == ep->family) {
+ addr = &scan->sin;
+ scan_local =
+ IN6_IS_ADDR_LINKLOCAL(PSOCK_ADDR6(addr)) ||
+ IN6_IS_ADDR_SITELOCAL(PSOCK_ADDR6(addr));
+ scan_univ_iid = IS_IID_UNIV(addr);
+ scan_privacy = !!(INT_PRIVACY & scan->flags);
+ } else {
+ scan_local = FALSE;
+ scan_univ_iid = FALSE;
+ scan_privacy = FALSE;
+ }
+ DPRINTF(4, ("add_interface mcast-capable scan %s%s%s%s\n",
+ stoa(&scan->sin),
+ (scan_local) ? " link/scope-local" : "",
+ (scan_univ_iid) ? " univ-IID" : "",
+ (scan_privacy) ? " privacy" : ""));
+ if ((ep_local && !scan_local) || (same_subnet &&
+ ((ep_privacy && !scan_privacy) ||
+ (!ep_univ_iid && scan_univ_iid)))) {
+ DPRINTF(4, ("did not add %s to %s of IPv6 multicast-capable list which already has %s\n",
+ stoa(&ep->sin),
+ (ep_local)
+ ? "tail"
+ : "head",
+ stoa(&scan->sin)));
+ return;
+ }
+ if ((scan_local && !ep_local) || (same_subnet &&
+ ((scan_privacy && !ep_privacy) ||
+ (!scan_univ_iid && ep_univ_iid)))) {
+ UNLINK_SLIST(unlinked, *pmclisthead,
+ scan, mclink, endpt);
+ DPRINTF(4, ("%s %s from IPv6 multicast-capable list to add %s\n",
+ (unlinked != scan)
+ ? "Failed to remove"
+ : "removed",
+ stoa(&scan->sin), stoa(&ep->sin)));
+ }
+ }
+ /*
+ * Add link/site local at the tail of the multicast-
+ * capable unicast interfaces list, so that ntpd will
+ * send from global addresses before link-/site-local
+ * ones.
+ */
+ if (ep_local)
+ LINK_TAIL_SLIST(*pmclisthead, ep, mclink, endpt);
+ else
+ LINK_SLIST(*pmclisthead, ep, mclink);
+ DPRINTF(4, ("added %s to %s of IPv%s multicast-capable unicast local address list\n",
+ stoa(&ep->sin),
+ (ep_local)
+ ? "tail"
+ : "head",
+ (AF_INET == ep->family)
+ ? "4"
+ : "6"));
+
+ if (INVALID_SOCKET == ep->fd)
+ return;
+
+ /*
+ * select the local address from which to send to multicast.
+ */
+ switch (AF(&ep->sin)) {
+
+ case AF_INET :
+ rc = setsockopt(ep->fd, IPPROTO_IP,
+ IP_MULTICAST_IF,
+ (void *)&NSRCADR(&ep->sin),
+ sizeof(NSRCADR(&ep->sin)));
+ if (rc)
+ msyslog(LOG_ERR,
+ "setsockopt IP_MULTICAST_IF %s fails: %m",
+ stoa(&ep->sin));
+ break;
+
+# ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ case AF_INET6 :
+ rc = setsockopt(ep->fd, IPPROTO_IPV6,
+ IPV6_MULTICAST_IF,
+ (void *)&ep->ifindex,
+ sizeof(ep->ifindex));
+ /* do not complain if bound addr scope is ifindex */
+ if (rc && ep->ifindex != SCOPE(&ep->sin))
+ msyslog(LOG_ERR,
+ "setsockopt IPV6_MULTICAST_IF %u for %s fails: %m",
+ ep->ifindex, stoa(&ep->sin));
+ break;
+# endif
+ }
+#endif /* MCAST */
+}
+
+
+/*
+ * remove interface from known interface list and clean up
+ * associated resources
+ */
+static void
+remove_interface(
+ endpt * ep
+ )
+{
+ endpt * unlinked;
+ endpt ** pmclisthead;
+ sockaddr_u resmask;
+
+ UNLINK_SLIST(unlinked, ep_list, ep, elink, endpt);
+ if (!ep->ignore_packets && INT_MULTICAST & ep->flags) {
+ pmclisthead = (AF_INET == ep->family)
+ ? &mc4_list
+ : &mc6_list;
+ UNLINK_SLIST(unlinked, *pmclisthead, ep, mclink, endpt);
+ DPRINTF(4, ("%s %s IPv%s multicast-capable unicast local address list\n",
+ stoa(&ep->sin),
+ (unlinked != NULL)
+ ? "removed from"
+ : "not found on",
+ (AF_INET == ep->family)
+ ? "4"
+ : "6"));
+ }
+ delete_interface_from_list(ep);
+
+ if (ep->fd != INVALID_SOCKET) {
+ msyslog(LOG_INFO,
+ "Deleting interface #%d %s, %s#%d, interface stats: received=%ld, sent=%ld, dropped=%ld, active_time=%ld secs",
+ ep->ifnum,
+ ep->name,
+ stoa(&ep->sin),
+ SRCPORT(&ep->sin),
+ ep->received,
+ ep->sent,
+ ep->notsent,
+ current_time - ep->starttime);
+ close_and_delete_fd_from_list(ep->fd);
+ ep->fd = INVALID_SOCKET;
+ }
+
+ if (ep->bfd != INVALID_SOCKET) {
+ msyslog(LOG_INFO,
+ "stop listening for broadcasts to %s on interface #%d %s",
+ stoa(&ep->bcast), ep->ifnum, ep->name);
+ close_and_delete_fd_from_list(ep->bfd);
+ ep->bfd = INVALID_SOCKET;
+ ep->flags &= ~INT_BCASTOPEN;
+ }
+
+ ninterfaces--;
+ mon_clearinterface(ep);
+
+ /* remove restrict interface entry */
+ SET_HOSTMASK(&resmask, AF(&ep->sin));
+ hack_restrict(RESTRICT_REMOVEIF, &ep->sin, &resmask,
+ RESM_NTPONLY | RESM_INTERFACE, RES_IGNORE, 0);
+}
+
+
+static void
+log_listen_address(
+ endpt * ep
+ )
+{
+ msyslog(LOG_INFO, "%s on %d %s %s",
+ (ep->ignore_packets)
+ ? "Listen and drop"
+ : "Listen normally",
+ ep->ifnum,
+ ep->name,
+ sptoa(&ep->sin));
+}
+
+
+static void
+create_wildcards(
+ u_short port
+ )
+{
+ int v4wild;
+#ifdef INCLUDE_IPV6_SUPPORT
+ int v6wild;
+#endif
+ sockaddr_u wildaddr;
+ nic_rule_action action;
+ struct interface * wildif;
+
+ /*
+ * silence "potentially uninitialized" warnings from VC9
+ * failing to follow the logic. Ideally action could remain
+ * uninitialized, and the memset be the first statement under
+ * the first if (v4wild).
+ */
+ action = ACTION_LISTEN;
+ ZERO(wildaddr);
+
+#ifdef INCLUDE_IPV6_SUPPORT
+ /*
+ * create pseudo-interface with wildcard IPv6 address
+ */
+ v6wild = ipv6_works;
+ if (v6wild) {
+ /* set wildaddr to the v6 wildcard address :: */
+ ZERO(wildaddr);
+ AF(&wildaddr) = AF_INET6;
+ SET_ADDR6N(&wildaddr, in6addr_any);
+ SET_PORT(&wildaddr, port);
+ SET_SCOPE(&wildaddr, 0);
+
+ /* check for interface/nic rules affecting the wildcard */
+ action = interface_action(NULL, &wildaddr, 0);
+ v6wild = (ACTION_IGNORE != action);
+ }
+ if (v6wild) {
+ wildif = new_interface(NULL);
+
+ strlcpy(wildif->name, "v6wildcard", sizeof(wildif->name));
+ memcpy(&wildif->sin, &wildaddr, sizeof(wildif->sin));
+ wildif->family = AF_INET6;
+ AF(&wildif->mask) = AF_INET6;
+ SET_ONESMASK(&wildif->mask);
+
+ wildif->flags = INT_UP | INT_WILDCARD;
+ wildif->ignore_packets = (ACTION_DROP == action);
+
+ wildif->fd = open_socket(&wildif->sin, 0, 1, wildif);
+
+ if (wildif->fd != INVALID_SOCKET) {
+ wildipv6 = wildif;
+ any6_interface = wildif;
+ add_addr_to_list(&wildif->sin, wildif);
+ add_interface(wildif);
+ log_listen_address(wildif);
+ } else {
+ msyslog(LOG_ERR,
+ "unable to bind to wildcard address %s - another process may be running - EXITING",
+ stoa(&wildif->sin));
+ exit(1);
+ }
+ DPRINT_INTERFACE(2, (wildif, "created ", "\n"));
+ }
+#endif
+
+ /*
+ * create pseudo-interface with wildcard IPv4 address
+ */
+ v4wild = ipv4_works;
+ if (v4wild) {
+ /* set wildaddr to the v4 wildcard address 0.0.0.0 */
+ AF(&wildaddr) = AF_INET;
+ SET_ADDR4N(&wildaddr, INADDR_ANY);
+ SET_PORT(&wildaddr, port);
+
+ /* check for interface/nic rules affecting the wildcard */
+ action = interface_action(NULL, &wildaddr, 0);
+ v4wild = (ACTION_IGNORE != action);
+ }
+ if (v4wild) {
+ wildif = new_interface(NULL);
+
+ strlcpy(wildif->name, "v4wildcard", sizeof(wildif->name));
+ memcpy(&wildif->sin, &wildaddr, sizeof(wildif->sin));
+ wildif->family = AF_INET;
+ AF(&wildif->mask) = AF_INET;
+ SET_ONESMASK(&wildif->mask);
+
+ wildif->flags = INT_BROADCAST | INT_UP | INT_WILDCARD;
+ wildif->ignore_packets = (ACTION_DROP == action);
+#if defined(MCAST)
+ /*
+ * enable multicast reception on the broadcast socket
+ */
+ AF(&wildif->bcast) = AF_INET;
+ SET_ADDR4N(&wildif->bcast, INADDR_ANY);
+ SET_PORT(&wildif->bcast, port);
+#endif /* MCAST */
+ wildif->fd = open_socket(&wildif->sin, 0, 1, wildif);
+
+ if (wildif->fd != INVALID_SOCKET) {
+ wildipv4 = wildif;
+ any_interface = wildif;
+
+ add_addr_to_list(&wildif->sin, wildif);
+ add_interface(wildif);
+ log_listen_address(wildif);
+ } else {
+ msyslog(LOG_ERR,
+ "unable to bind to wildcard address %s - another process may be running - EXITING",
+ stoa(&wildif->sin));
+ exit(1);
+ }
+ DPRINT_INTERFACE(2, (wildif, "created ", "\n"));
+ }
+}
+
+
+/*
+ * add_nic_rule() -- insert a rule entry at the head of nic_rule_list.
+ */
+void
+add_nic_rule(
+ nic_rule_match match_type,
+ const char * if_name, /* interface name or numeric address */
+ int prefixlen,
+ nic_rule_action action
+ )
+{
+ nic_rule * rule;
+ isc_boolean_t is_ip;
+
+ rule = emalloc_zero(sizeof(*rule));
+ rule->match_type = match_type;
+ rule->prefixlen = prefixlen;
+ rule->action = action;
+
+ if (MATCH_IFNAME == match_type) {
+ NTP_REQUIRE(NULL != if_name);
+ rule->if_name = estrdup(if_name);
+ } else if (MATCH_IFADDR == match_type) {
+ NTP_REQUIRE(NULL != if_name);
+ /* set rule->addr */
+ is_ip = is_ip_address(if_name, AF_UNSPEC, &rule->addr);
+ NTP_REQUIRE(is_ip);
+ } else
+ NTP_REQUIRE(NULL == if_name);
+
+ LINK_SLIST(nic_rule_list, rule, next);
+}
+
+
+#ifdef DEBUG
+static const char *
+action_text(
+ nic_rule_action action
+ )
+{
+ const char *t;
+
+ switch (action) {
+
+ default:
+ t = "ERROR"; /* quiet uninit warning */
+ DPRINTF(1, ("fatal: unknown nic_rule_action %d\n",
+ action));
+ NTP_ENSURE(0);
+ break;
+
+ case ACTION_LISTEN:
+ t = "listen";
+ break;
+
+ case ACTION_IGNORE:
+ t = "ignore";
+ break;
+
+ case ACTION_DROP:
+ t = "drop";
+ break;
+ }
+
+ return t;
+}
+#endif /* DEBUG */
+
+
+static nic_rule_action
+interface_action(
+ char * if_name,
+ sockaddr_u * if_addr,
+ u_int32 if_flags
+ )
+{
+ nic_rule * rule;
+ int isloopback;
+ int iswildcard;
+
+ DPRINTF(4, ("interface_action: interface %s ",
+ (if_name != NULL) ? if_name : "wildcard"));
+
+ iswildcard = is_wildcard_addr(if_addr);
+ isloopback = !!(INT_LOOPBACK & if_flags);
+
+ /*
+ * Find any matching NIC rule from --interface / -I or ntp.conf
+ * interface/nic rules.
+ */
+ for (rule = nic_rule_list; rule != NULL; rule = rule->next) {
+
+ switch (rule->match_type) {
+
+ case MATCH_ALL:
+ /* loopback and wildcard excluded from "all" */
+ if (isloopback || iswildcard)
+ break;
+ DPRINTF(4, ("nic all %s\n",
+ action_text(rule->action)));
+ return rule->action;
+
+ case MATCH_IPV4:
+ if (IS_IPV4(if_addr)) {
+ DPRINTF(4, ("nic ipv4 %s\n",
+ action_text(rule->action)));
+ return rule->action;
+ }
+ break;
+
+ case MATCH_IPV6:
+ if (IS_IPV6(if_addr)) {
+ DPRINTF(4, ("nic ipv6 %s\n",
+ action_text(rule->action)));
+ return rule->action;
+ }
+ break;
+
+ case MATCH_WILDCARD:
+ if (iswildcard) {
+ DPRINTF(4, ("nic wildcard %s\n",
+ action_text(rule->action)));
+ return rule->action;
+ }
+ break;
+
+ case MATCH_IFADDR:
+ if (rule->prefixlen != -1) {
+ if (addr_eqprefix(if_addr, &rule->addr,
+ rule->prefixlen)) {
+
+ DPRINTF(4, ("subnet address match - %s\n",
+ action_text(rule->action)));
+ return rule->action;
+ }
+ } else
+ if (SOCK_EQ(if_addr, &rule->addr)) {
+
+ DPRINTF(4, ("address match - %s\n",
+ action_text(rule->action)));
+ return rule->action;
+ }
+ break;
+
+ case MATCH_IFNAME:
+ if (if_name != NULL
+#if defined(HAVE_FNMATCH) && defined(FNM_CASEFOLD)
+ && !fnmatch(rule->if_name, if_name, FNM_CASEFOLD)
+#else
+ && !strcasecmp(if_name, rule->if_name)
+#endif
+ ) {
+
+ DPRINTF(4, ("interface name match - %s\n",
+ action_text(rule->action)));
+ return rule->action;
+ }
+ break;
+ }
+ }
+
+ /*
+ * Unless explicitly disabled such as with "nic ignore ::1"
+ * listen on loopback addresses. Since ntpq and ntpdc query
+ * "localhost" by default, which typically resolves to ::1 and
+ * 127.0.0.1, it's useful to default to listening on both.
+ */
+ if (isloopback) {
+ DPRINTF(4, ("default loopback listen\n"));
+ return ACTION_LISTEN;
+ }
+
+ /*
+ * Treat wildcard addresses specially. If there is no explicit
+ * "nic ... wildcard" or "nic ... 0.0.0.0" or "nic ... ::" rule
+ * default to drop.
+ */
+ if (iswildcard) {
+ DPRINTF(4, ("default wildcard drop\n"));
+ return ACTION_DROP;
+ }
+
+ /*
+ * Check for "virtual IP" (colon in the interface name) after
+ * the rules so that "ntpd --interface eth0:1 -novirtualips"
+ * does indeed listen on eth0:1's addresses.
+ */
+ if (!listen_to_virtual_ips && if_name != NULL
+ && (strchr(if_name, ':') != NULL)) {
+
+ DPRINTF(4, ("virtual ip - ignore\n"));
+ return ACTION_IGNORE;
+ }
+
+ /*
+ * If there are no --interface/-I command-line options and no
+ * interface/nic rules in ntp.conf, the default action is to
+ * listen. In the presence of rules from either, the default
+ * is to ignore. This implements ntpd's traditional listen-
+ * every default with no interface listen configuration, and
+ * ensures a single -I eth0 or "nic listen eth0" means do not
+ * listen on any other addresses.
+ */
+ if (NULL == nic_rule_list) {
+ DPRINTF(4, ("default listen\n"));
+ return ACTION_LISTEN;
+ }
+
+ DPRINTF(4, ("implicit ignore\n"));
+ return ACTION_IGNORE;
+}
+
+
+static void
+convert_isc_if(
+ isc_interface_t *isc_if,
+ endpt *itf,
+ u_short port
+ )
+{
+ const u_char v6loop[16] = {0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1};
+
+ strlcpy(itf->name, isc_if->name, sizeof(itf->name));
+ itf->ifindex = isc_if->ifindex;
+ itf->family = (u_short)isc_if->af;
+ AF(&itf->sin) = itf->family;
+ AF(&itf->mask) = itf->family;
+ AF(&itf->bcast) = itf->family;
+ SET_PORT(&itf->sin, port);
+ SET_PORT(&itf->mask, port);
+ SET_PORT(&itf->bcast, port);
+
+ if (IS_IPV4(&itf->sin)) {
+ NSRCADR(&itf->sin) = isc_if->address.type.in.s_addr;
+ NSRCADR(&itf->mask) = isc_if->netmask.type.in.s_addr;
+
+ if (isc_if->flags & INTERFACE_F_BROADCAST) {
+ itf->flags |= INT_BROADCAST;
+ NSRCADR(&itf->bcast) =
+ isc_if->broadcast.type.in.s_addr;
+ }
+ }
+#ifdef INCLUDE_IPV6_SUPPORT
+ else if (IS_IPV6(&itf->sin)) {
+ SET_ADDR6N(&itf->sin, isc_if->address.type.in6);
+ SET_ADDR6N(&itf->mask, isc_if->netmask.type.in6);
+
+ SET_SCOPE(&itf->sin, isc_if->address.zone);
+ }
+#endif /* INCLUDE_IPV6_SUPPORT */
+
+
+ /* Process the rest of the flags */
+
+ itf->flags |=
+ ((INTERFACE_F_UP & isc_if->flags)
+ ? INT_UP : 0)
+ | ((INTERFACE_F_LOOPBACK & isc_if->flags)
+ ? INT_LOOPBACK : 0)
+ | ((INTERFACE_F_POINTTOPOINT & isc_if->flags)
+ ? INT_PPP : 0)
+ | ((INTERFACE_F_MULTICAST & isc_if->flags)
+ ? INT_MULTICAST : 0)
+ | ((INTERFACE_F_PRIVACY & isc_if->flags)
+ ? INT_PRIVACY : 0)
+ ;
+
+ /*
+ * Clear the loopback flag if the address is not localhost.
+ * http://bugs.ntp.org/1683
+ */
+ if (INT_LOOPBACK & itf->flags) {
+ if (AF_INET == itf->family) {
+ if (127 != (SRCADR(&itf->sin) >> 24))
+ itf->flags &= ~INT_LOOPBACK;
+ } else {
+ if (memcmp(v6loop, NSRCADR6(&itf->sin),
+ sizeof(NSRCADR6(&itf->sin))))
+ itf->flags &= ~INT_LOOPBACK;
+ }
+ }
+}
+
+
+/*
+ * refresh_interface
+ *
+ * some OSes have been observed to keep
+ * cached routes even when more specific routes
+ * become available.
+ * this can be mitigated by re-binding
+ * the socket.
+ */
+static int
+refresh_interface(
+ struct interface * interface
+ )
+{
+#ifdef OS_MISSES_SPECIFIC_ROUTE_UPDATES
+ if (interface->fd != INVALID_SOCKET) {
+ int bcast = (interface->flags & INT_BCASTXMIT) != 0;
+ /* as we forcibly close() the socket remove the
+ broadcast permission indication */
+ if (bcast)
+ socket_broadcast_disable(interface, &interface->sin);
+
+ close_and_delete_fd_from_list(interface->fd);
+
+ /* create new socket picking up a new first hop binding
+ at connect() time */
+ interface->fd = open_socket(&interface->sin,
+ bcast, 0, interface);
+ /*
+ * reset TTL indication so TTL is is set again
+ * next time around
+ */
+ interface->last_ttl = 0;
+ return (interface->fd != INVALID_SOCKET);
+ } else
+ return 0; /* invalid sockets are not refreshable */
+#else /* !OS_MISSES_SPECIFIC_ROUTE_UPDATES */
+ return (interface->fd != INVALID_SOCKET);
+#endif /* !OS_MISSES_SPECIFIC_ROUTE_UPDATES */
+}
+
+/*
+ * interface_update - externally callable update function
+ */
+void
+interface_update(
+ interface_receiver_t receiver,
+ void * data)
+{
+ int new_interface_found;
+
+ if (disable_dynamic_updates)
+ return;
+
+ BLOCKIO();
+ new_interface_found = update_interfaces(NTP_PORT, receiver, data);
+ UNBLOCKIO();
+
+ if (!new_interface_found)
+ return;
+
+#ifdef DEBUG
+ msyslog(LOG_DEBUG, "new interface(s) found: waking up resolver");
+#endif
+ interrupt_worker_sleep();
+}
+
+
+/*
+ * sau_from_netaddr() - convert network address on-wire formats.
+ * Convert from libisc's isc_netaddr_t to NTP's sockaddr_u
+ */
+void
+sau_from_netaddr(
+ sockaddr_u *psau,
+ const isc_netaddr_t *pna
+ )
+{
+ ZERO_SOCK(psau);
+ AF(psau) = (u_short)pna->family;
+ switch (pna->family) {
+
+ case AF_INET:
+ memcpy(&psau->sa4.sin_addr, &pna->type.in,
+ sizeof(psau->sa4.sin_addr));
+ break;
+
+ case AF_INET6:
+ memcpy(&psau->sa6.sin6_addr, &pna->type.in6,
+ sizeof(psau->sa6.sin6_addr));
+ break;
+ }
+}
+
+
+static int
+is_wildcard_addr(
+ const sockaddr_u *psau
+ )
+{
+ if (IS_IPV4(psau) && !NSRCADR(psau))
+ return 1;
+
+#ifdef INCLUDE_IPV6_SUPPORT
+ if (IS_IPV6(psau) && S_ADDR6_EQ(psau, &in6addr_any))
+ return 1;
+#endif
+
+ return 0;
+}
+
+
+#ifdef OS_NEEDS_REUSEADDR_FOR_IFADDRBIND
+/*
+ * enable/disable re-use of wildcard address socket
+ */
+static void
+set_wildcard_reuse(
+ u_short family,
+ int on
+ )
+{
+ struct interface *any;
+ SOCKET fd = INVALID_SOCKET;
+
+ any = ANY_INTERFACE_BYFAM(family);
+ if (any != NULL)
+ fd = any->fd;
+
+ if (fd != INVALID_SOCKET) {
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof(on)))
+ msyslog(LOG_ERR,
+ "set_wildcard_reuse: setsockopt(SO_REUSEADDR, %s) failed: %m",
+ on ? "on" : "off");
+
+ DPRINTF(4, ("set SO_REUSEADDR to %s on %s\n",
+ on ? "on" : "off",
+ stoa(&any->sin)));
+ }
+}
+#endif /* OS_NEEDS_REUSEADDR_FOR_IFADDRBIND */
+
+
+static isc_boolean_t
+is_anycast(
+ sockaddr_u *psau,
+ const char *name
+ )
+{
+#if defined(INCLUDE_IPV6_SUPPORT) && defined(SIOCGIFAFLAG_IN6) && \
+ defined(IN6_IFF_ANYCAST)
+ struct in6_ifreq ifr6;
+ int fd;
+ u_int32 flags6;
+
+ if (psau->sa.sa_family != AF_INET6)
+ return ISC_FALSE;
+ if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ return ISC_FALSE;
+ ZERO(ifr6);
+ memcpy(&ifr6.ifr_addr, &psau->sa6, sizeof(ifr6.ifr_addr));
+ strlcpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name));
+ if (ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
+ close(fd);
+ return ISC_FALSE;
+ }
+ close(fd);
+ flags6 = ifr6.ifr_ifru.ifru_flags6;
+ if ((flags6 & IN6_IFF_ANYCAST) != 0)
+ return ISC_TRUE;
+#endif /* INCLUDE_IPV6_SUPPORT && SIOCGIFAFLAG_IN6 && IN6_IFF_ANYCAST */
+ return ISC_FALSE;
+}
+
+
+/*
+ * update_interface strategy
+ *
+ * toggle configuration phase
+ *
+ * Phase 1:
+ * forall currently existing interfaces
+ * if address is known:
+ * drop socket - rebind again
+ *
+ * if address is NOT known:
+ * attempt to create a new interface entry
+ *
+ * Phase 2:
+ * forall currently known non MCAST and WILDCARD interfaces
+ * if interface does not match configuration phase (not seen in phase 1):
+ * remove interface from known interface list
+ * forall peers associated with this interface
+ * disconnect peer from this interface
+ *
+ * Phase 3:
+ * attempt to re-assign interfaces to peers
+ *
+ */
+
+static int
+update_interfaces(
+ u_short port,
+ interface_receiver_t receiver,
+ void * data
+ )
+{
+ isc_mem_t * mctx = (void *)-1;
+ interface_info_t ifi;
+ isc_interfaceiter_t * iter;
+ isc_result_t result;
+ isc_interface_t isc_if;
+ int new_interface_found;
+ unsigned int family;
+ endpt enumep;
+ endpt * ep;
+ endpt * next_ep;
+
+ DPRINTF(3, ("update_interfaces(%d)\n", port));
+
+ /*
+ * phase one - scan interfaces
+ * - create those that are not found
+ * - update those that are found
+ */
+
+ new_interface_found = FALSE;
+ iter = NULL;
+ result = isc_interfaceiter_create(mctx, &iter);
+
+ if (result != ISC_R_SUCCESS)
+ return 0;
+
+ /*
+ * Toggle system interface scan phase to find untouched
+ * interfaces to be deleted.
+ */
+ sys_interphase ^= 0x1;
+
+ for (result = isc_interfaceiter_first(iter);
+ ISC_R_SUCCESS == result;
+ result = isc_interfaceiter_next(iter)) {
+
+ result = isc_interfaceiter_current(iter, &isc_if);
+
+ if (result != ISC_R_SUCCESS)
+ break;
+
+ /* See if we have a valid family to use */
+ family = isc_if.address.family;
+ if (AF_INET != family && AF_INET6 != family)
+ continue;
+ if (AF_INET == family && !ipv4_works)
+ continue;
+ if (AF_INET6 == family && !ipv6_works)
+ continue;
+
+ /* create prototype */
+ init_interface(&enumep);
+
+ convert_isc_if(&isc_if, &enumep, port);
+
+ DPRINT_INTERFACE(4, (&enumep, "examining ", "\n"));
+
+ /*
+ * Check if and how we are going to use the interface.
+ */
+ switch (interface_action(enumep.name, &enumep.sin,
+ enumep.flags)) {
+
+ case ACTION_IGNORE:
+ DPRINTF(4, ("ignoring interface %s (%s) - by nic rules\n",
+ enumep.name, stoa(&enumep.sin)));
+ continue;
+
+ case ACTION_LISTEN:
+ DPRINTF(4, ("listen interface %s (%s) - by nic rules\n",
+ enumep.name, stoa(&enumep.sin)));
+ enumep.ignore_packets = ISC_FALSE;
+ break;
+
+ case ACTION_DROP:
+ DPRINTF(4, ("drop on interface %s (%s) - by nic rules\n",
+ enumep.name, stoa(&enumep.sin)));
+ enumep.ignore_packets = ISC_TRUE;
+ break;
+ }
+
+ /* interfaces must be UP to be usable */
+ if (!(enumep.flags & INT_UP)) {
+ DPRINTF(4, ("skipping interface %s (%s) - DOWN\n",
+ enumep.name, stoa(&enumep.sin)));
+ continue;
+ }
+
+ /*
+ * skip any interfaces UP and bound to a wildcard
+ * address - some dhcp clients produce that in the
+ * wild
+ */
+ if (is_wildcard_addr(&enumep.sin))
+ continue;
+
+ if (is_anycast(&enumep.sin, isc_if.name))
+ continue;
+
+ /*
+ * map to local *address* in order to map all duplicate
+ * interfaces to an endpt structure with the appropriate
+ * socket. Our name space is (ip-address), NOT
+ * (interface name, ip-address).
+ */
+ ep = getinterface(&enumep.sin, INT_WILDCARD);
+
+ if (ep != NULL && refresh_interface(ep)) {
+ /*
+ * found existing and up to date interface -
+ * mark present.
+ */
+ if (ep->phase != sys_interphase) {
+ /*
+ * On a new round we reset the name so
+ * the interface name shows up again if
+ * this address is no longer shared.
+ * We reset ignore_packets from the
+ * new prototype to respect any runtime
+ * changes to the nic rules.
+ */
+ strlcpy(ep->name, enumep.name,
+ sizeof(ep->name));
+ ep->ignore_packets =
+ enumep.ignore_packets;
+ } else {
+ /* name collision - rename interface */
+ strlcpy(ep->name, "*multiple*",
+ sizeof(ep->name));
+ }
+
+ DPRINT_INTERFACE(4, (ep, "updating ",
+ " present\n"));
+
+ if (ep->ignore_packets !=
+ enumep.ignore_packets) {
+ /*
+ * We have conflicting configurations
+ * for the interface address. This is
+ * caused by using -I <interfacename>
+ * for an interface that shares its
+ * address with other interfaces. We
+ * can not disambiguate incoming
+ * packets delivered to this socket
+ * without extra syscalls/features.
+ * These are not (commonly) available.
+ * Note this is a more unusual
+ * configuration where several
+ * interfaces share an address but
+ * filtering via interface name is
+ * attempted. We resolve the
+ * configuration conflict by disabling
+ * the processing of received packets.
+ * This leads to no service on the
+ * interface address where the conflict
+ * occurs.
+ */
+ msyslog(LOG_ERR,
+ "WARNING: conflicting enable configuration for interfaces %s and %s for address %s - unsupported configuration - address DISABLED",
+ enumep.name, ep->name,
+ stoa(&enumep.sin));
+
+ ep->ignore_packets = ISC_TRUE;
+ }
+
+ ep->phase = sys_interphase;
+
+ ifi.action = IFS_EXISTS;
+ ifi.ep = ep;
+ if (receiver != NULL)
+ (*receiver)(data, &ifi);
+ } else {
+ /*
+ * This is new or refreshing failed - add to
+ * our interface list. If refreshing failed we
+ * will delete the interface structure in phase
+ * 2 as the interface was not marked current.
+ * We can bind to the address as the refresh
+ * code already closed the offending socket
+ */
+ ep = create_interface(port, &enumep);
+
+ if (ep != NULL) {
+ ifi.action = IFS_CREATED;
+ ifi.ep = ep;
+ if (receiver != NULL)
+ (*receiver)(data, &ifi);
+
+ new_interface_found = TRUE;
+ DPRINT_INTERFACE(3,
+ (ep, "updating ",
+ " new - created\n"));
+ } else {
+ DPRINT_INTERFACE(3,
+ (&enumep, "updating ",
+ " new - creation FAILED"));
+
+ msyslog(LOG_INFO,
+ "failed to init interface for address %s",
+ stoa(&enumep.sin));
+ continue;
+ }
+ }
+ }
+
+ isc_interfaceiter_destroy(&iter);
+
+ /*
+ * phase 2 - delete gone interfaces - reassigning peers to
+ * other interfaces
+ */
+ for (ep = ep_list; ep != NULL; ep = next_ep) {
+ next_ep = ep->elink;
+
+ /*
+ * if phase does not match sys_phase this interface was
+ * not enumerated during the last interface scan - so it
+ * is gone and will be deleted here unless it did not
+ * originate from interface enumeration (INT_WILDCARD,
+ * INT_MCASTIF).
+ */
+ if (((INT_WILDCARD | INT_MCASTIF) & ep->flags) ||
+ ep->phase == sys_interphase)
+ continue;
+
+ DPRINT_INTERFACE(3, (ep, "updating ",
+ "GONE - deleting\n"));
+ remove_interface(ep);
+
+ ifi.action = IFS_DELETED;
+ ifi.ep = ep;
+ if (receiver != NULL)
+ (*receiver)(data, &ifi);
+
+ /* disconnect peers from deleted endpt. */
+ while (ep->peers != NULL)
+ set_peerdstadr(ep->peers, NULL);
+
+ /*
+ * update globals in case we lose
+ * a loopback interface
+ */
+ if (ep == loopback_interface)
+ loopback_interface = NULL;
+
+ delete_interface(ep);
+ }
+
+ /*
+ * phase 3 - re-configure as the world has possibly changed
+ *
+ * never ever make this conditional again - it is needed to track
+ * routing updates. see bug #2506
+ */
+ refresh_all_peerinterfaces();
+
+ if (broadcast_client_enabled)
+ io_setbclient();
+
+ return new_interface_found;
+}
+
+
+/*
+ * create_sockets - create a socket for each interface plus a default
+ * socket for when we don't know where to send
+ */
+static int
+create_sockets(
+ u_short port
+ )
+{
+#ifndef HAVE_IO_COMPLETION_PORT
+ /*
+ * I/O Completion Ports don't care about the select and FD_SET
+ */
+ maxactivefd = 0;
+ FD_ZERO(&activefds);
+#endif
+
+ DPRINTF(2, ("create_sockets(%d)\n", port));
+
+ create_wildcards(port);
+
+ update_interfaces(port, NULL, NULL);
+
+ /*
+ * Now that we have opened all the sockets, turn off the reuse
+ * flag for security.
+ */
+ set_reuseaddr(0);
+
+ DPRINTF(2, ("create_sockets: Total interfaces = %d\n", ninterfaces));
+
+ return ninterfaces;
+}
+
+/*
+ * create_interface - create a new interface for a given prototype
+ * binding the socket.
+ */
+static struct interface *
+create_interface(
+ u_short port,
+ struct interface * protot
+ )
+{
+ sockaddr_u resmask;
+ endpt * iface;
+#if defined(MCAST) && defined(MULTICAST_NONEWSOCKET)
+ remaddr_t * entry;
+ remaddr_t * next_entry;
+#endif
+ DPRINTF(2, ("create_interface(%s#%d)\n", stoa(&protot->sin),
+ port));
+
+ /* build an interface */
+ iface = new_interface(protot);
+
+ /*
+ * create socket
+ */
+ iface->fd = open_socket(&iface->sin, 0, 0, iface);
+
+ if (iface->fd != INVALID_SOCKET)
+ log_listen_address(iface);
+
+ if ((INT_BROADCAST & iface->flags)
+ && iface->bfd != INVALID_SOCKET)
+ msyslog(LOG_INFO, "Listening on broadcast address %s#%d",
+ stoa((&iface->bcast)), port);
+
+ if (INVALID_SOCKET == iface->fd
+ && INVALID_SOCKET == iface->bfd) {
+ msyslog(LOG_ERR, "unable to create socket on %s (%d) for %s#%d",
+ iface->name,
+ iface->ifnum,
+ stoa((&iface->sin)),
+ port);
+ delete_interface(iface);
+ return NULL;
+ }
+
+ /*
+ * Blacklist our own addresses, no use talking to ourself
+ */
+ SET_HOSTMASK(&resmask, AF(&iface->sin));
+ hack_restrict(RESTRICT_FLAGS, &iface->sin, &resmask,
+ RESM_NTPONLY | RESM_INTERFACE, RES_IGNORE, 0);
+
+ /*
+ * set globals with the first found
+ * loopback interface of the appropriate class
+ */
+ if (NULL == loopback_interface && AF_INET == iface->family
+ && (INT_LOOPBACK & iface->flags))
+ loopback_interface = iface;
+
+ /*
+ * put into our interface list
+ */
+ add_addr_to_list(&iface->sin, iface);
+ add_interface(iface);
+
+#if defined(MCAST) && defined(MULTICAST_NONEWSOCKET)
+ /*
+ * Join any previously-configured compatible multicast groups.
+ */
+ if (INT_MULTICAST & iface->flags &&
+ !((INT_LOOPBACK | INT_WILDCARD) & iface->flags) &&
+ !iface->ignore_packets) {
+ for (entry = remoteaddr_list;
+ entry != NULL;
+ entry = next_entry) {
+ next_entry = entry->link;
+ if (AF(&iface->sin) != AF(&entry->addr) ||
+ !IS_MCAST(&entry->addr))
+ continue;
+ if (socket_multicast_enable(iface,
+ &entry->addr))
+ msyslog(LOG_INFO,
+ "Joined %s socket to multicast group %s",
+ stoa(&iface->sin),
+ stoa(&entry->addr));
+ else
+ msyslog(LOG_ERR,
+ "Failed to join %s socket to multicast group %s",
+ stoa(&iface->sin),
+ stoa(&entry->addr));
+ }
+ }
+#endif /* MCAST && MCAST_NONEWSOCKET */
+
+ DPRINT_INTERFACE(2, (iface, "created ", "\n"));
+ return iface;
+}
+
+
+#ifdef SO_EXCLUSIVEADDRUSE
+static void
+set_excladdruse(
+ SOCKET fd
+ )
+{
+ int one = 1;
+ int failed;
+#ifdef SYS_WINNT
+ DWORD err;
+#endif
+
+ failed = setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
+ (char *)&one, sizeof(one));
+
+ if (!failed)
+ return;
+
+#ifdef SYS_WINNT
+ /*
+ * Prior to Windows XP setting SO_EXCLUSIVEADDRUSE can fail with
+ * error WSAINVAL depending on service pack level and whether
+ * the user account is in the Administrators group. Do not
+ * complain if it fails that way on versions prior to XP (5.1).
+ */
+ err = GetLastError();
+
+ if (isc_win32os_versioncheck(5, 1, 0, 0) < 0 /* < 5.1/XP */
+ && WSAEINVAL == err)
+ return;
+
+ SetLastError(err);
+#endif
+ msyslog(LOG_ERR,
+ "setsockopt(%d, SO_EXCLUSIVEADDRUSE, on): %m",
+ (int)fd);
+}
+#endif /* SO_EXCLUSIVEADDRUSE */
+
+
+/*
+ * set_reuseaddr() - set/clear REUSEADDR on all sockets
+ * NB possible hole - should we be doing this on broadcast
+ * fd's also?
+ */
+static void
+set_reuseaddr(
+ int flag
+ )
+{
+#ifndef SO_EXCLUSIVEADDRUSE
+ endpt *ep;
+
+ for (ep = ep_list; ep != NULL; ep = ep->elink) {
+ if (ep->flags & INT_WILDCARD)
+ continue;
+
+ /*
+ * if ep->fd is INVALID_SOCKET, we might have a adapter
+ * configured but not present
+ */
+ DPRINTF(4, ("setting SO_REUSEADDR on %.16s@%s to %s\n",
+ ep->name, stoa(&ep->sin),
+ flag ? "on" : "off"));
+
+ if (ep->fd != INVALID_SOCKET) {
+ if (setsockopt(ep->fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&flag, sizeof(flag))) {
+ msyslog(LOG_ERR, "set_reuseaddr: setsockopt(%s, SO_REUSEADDR, %s) failed: %m",
+ stoa(&ep->sin), flag ? "on" : "off");
+ }
+ }
+ }
+#endif /* ! SO_EXCLUSIVEADDRUSE */
+}
+
+/*
+ * This is just a wrapper around an internal function so we can
+ * make other changes as necessary later on
+ */
+void
+enable_broadcast(
+ struct interface * iface,
+ sockaddr_u * baddr
+ )
+{
+#ifdef OPEN_BCAST_SOCKET
+ socket_broadcast_enable(iface, iface->fd, baddr);
+#endif
+}
+
+#ifdef OPEN_BCAST_SOCKET
+/*
+ * Enable a broadcast address to a given socket
+ * The socket is in the ep_list all we need to do is enable
+ * broadcasting. It is not this function's job to select the socket
+ */
+static isc_boolean_t
+socket_broadcast_enable(
+ struct interface * iface,
+ SOCKET fd,
+ sockaddr_u * baddr
+ )
+{
+#ifdef SO_BROADCAST
+ int on = 1;
+
+ if (IS_IPV4(baddr)) {
+ /* if this interface can support broadcast, set SO_BROADCAST */
+ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST,
+ (char *)&on, sizeof(on)))
+ msyslog(LOG_ERR,
+ "setsockopt(SO_BROADCAST) enable failure on address %s: %m",
+ stoa(baddr));
+ else
+ DPRINTF(2, ("Broadcast enabled on socket %d for address %s\n",
+ fd, stoa(baddr)));
+ }
+ iface->flags |= INT_BCASTXMIT;
+ return ISC_TRUE;
+#else
+ return ISC_FALSE;
+#endif /* SO_BROADCAST */
+}
+
+/*
+ * Remove a broadcast address from a given socket
+ * The socket is in the ep_list all we need to do is disable
+ * broadcasting. It is not this function's job to select the socket
+ */
+static isc_boolean_t
+socket_broadcast_disable(
+ struct interface * iface,
+ sockaddr_u * baddr
+ )
+{
+#ifdef SO_BROADCAST
+ int off = 0; /* This seems to be OK as an int */
+
+ if (IS_IPV4(baddr) && setsockopt(iface->fd, SOL_SOCKET,
+ SO_BROADCAST, (char *)&off, sizeof(off)))
+ msyslog(LOG_ERR,
+ "setsockopt(SO_BROADCAST) disable failure on address %s: %m",
+ stoa(baddr));
+
+ iface->flags &= ~INT_BCASTXMIT;
+ return ISC_TRUE;
+#else
+ return ISC_FALSE;
+#endif /* SO_BROADCAST */
+}
+
+#endif /* OPEN_BCAST_SOCKET */
+
+/*
+ * return the broadcast client flag value
+ */
+isc_boolean_t
+get_broadcastclient_flag(void)
+{
+ return (broadcast_client_enabled);
+}
+/*
+ * Check to see if the address is a multicast address
+ */
+static isc_boolean_t
+addr_ismulticast(
+ sockaddr_u *maddr
+ )
+{
+ isc_boolean_t result;
+
+#ifndef INCLUDE_IPV6_MULTICAST_SUPPORT
+ /*
+ * If we don't have IPV6 support any IPV6 addr is not multicast
+ */
+ if (IS_IPV6(maddr))
+ result = ISC_FALSE;
+ else
+#endif
+ result = IS_MCAST(maddr);
+
+ if (!result)
+ DPRINTF(4, ("address %s is not multicast\n",
+ stoa(maddr)));
+
+ return result;
+}
+
+/*
+ * Multicast servers need to set the appropriate Multicast interface
+ * socket option in order for it to know which interface to use for
+ * send the multicast packet.
+ */
+void
+enable_multicast_if(
+ struct interface * iface,
+ sockaddr_u * maddr
+ )
+{
+#ifdef MCAST
+#ifdef IP_MULTICAST_LOOP
+ TYPEOF_IP_MULTICAST_LOOP off = 0;
+#endif
+#ifdef IPV6_MULTICAST_LOOP
+ u_int off6 = 0;
+#endif
+
+ NTP_REQUIRE(AF(maddr) == AF(&iface->sin));
+
+ switch (AF(&iface->sin)) {
+
+ case AF_INET:
+#ifdef IP_MULTICAST_LOOP
+ /*
+ * Don't send back to itself, but allow failure to set
+ */
+ if (setsockopt(iface->fd, IPPROTO_IP,
+ IP_MULTICAST_LOOP,
+ SETSOCKOPT_ARG_CAST &off,
+ sizeof(off))) {
+
+ msyslog(LOG_ERR,
+ "setsockopt IP_MULTICAST_LOOP failed: %m on socket %d, addr %s for multicast address %s",
+ iface->fd, stoa(&iface->sin),
+ stoa(maddr));
+ }
+#endif
+ break;
+
+ case AF_INET6:
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+#ifdef IPV6_MULTICAST_LOOP
+ /*
+ * Don't send back to itself, but allow failure to set
+ */
+ if (setsockopt(iface->fd, IPPROTO_IPV6,
+ IPV6_MULTICAST_LOOP,
+ (char *) &off6, sizeof(off6))) {
+
+ msyslog(LOG_ERR,
+ "setsockopt IPV6_MULTICAST_LOOP failed: %m on socket %d, addr %s for multicast address %s",
+ iface->fd, stoa(&iface->sin),
+ stoa(maddr));
+ }
+#endif
+ break;
+#else
+ return;
+#endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */
+ }
+ return;
+#endif
+}
+
+/*
+ * Add a multicast address to a given socket
+ * The socket is in the ep_list all we need to do is enable
+ * multicasting. It is not this function's job to select the socket
+ */
+#if defined(MCAST)
+static isc_boolean_t
+socket_multicast_enable(
+ endpt * iface,
+ sockaddr_u * maddr
+ )
+{
+ struct ip_mreq mreq;
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ struct ipv6_mreq mreq6;
+#endif
+ switch (AF(maddr)) {
+
+ case AF_INET:
+ ZERO(mreq);
+ mreq.imr_multiaddr = SOCK_ADDR4(maddr);
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ if (setsockopt(iface->fd,
+ IPPROTO_IP,
+ IP_ADD_MEMBERSHIP,
+ (char *)&mreq,
+ sizeof(mreq))) {
+ msyslog(LOG_ERR,
+ "setsockopt IP_ADD_MEMBERSHIP failed: %m on socket %d, addr %s for %x / %x (%s)",
+ iface->fd, stoa(&iface->sin),
+ mreq.imr_multiaddr.s_addr,
+ mreq.imr_interface.s_addr,
+ stoa(maddr));
+ return ISC_FALSE;
+ }
+ DPRINTF(4, ("Added IPv4 multicast membership on socket %d, addr %s for %x / %x (%s)\n",
+ iface->fd, stoa(&iface->sin),
+ mreq.imr_multiaddr.s_addr,
+ mreq.imr_interface.s_addr, stoa(maddr)));
+ break;
+
+ case AF_INET6:
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ /*
+ * Enable reception of multicast packets.
+ * If the address is link-local we can get the
+ * interface index from the scope id. Don't do this
+ * for other types of multicast addresses. For now let
+ * the kernel figure it out.
+ */
+ ZERO(mreq6);
+ mreq6.ipv6mr_multiaddr = SOCK_ADDR6(maddr);
+ mreq6.ipv6mr_interface = iface->ifindex;
+
+ if (setsockopt(iface->fd, IPPROTO_IPV6,
+ IPV6_JOIN_GROUP, (char *)&mreq6,
+ sizeof(mreq6))) {
+ msyslog(LOG_ERR,
+ "setsockopt IPV6_JOIN_GROUP failed: %m on socket %d, addr %s for interface %u (%s)",
+ iface->fd, stoa(&iface->sin),
+ mreq6.ipv6mr_interface, stoa(maddr));
+ return ISC_FALSE;
+ }
+ DPRINTF(4, ("Added IPv6 multicast group on socket %d, addr %s for interface %u (%s)\n",
+ iface->fd, stoa(&iface->sin),
+ mreq6.ipv6mr_interface, stoa(maddr)));
+#else
+ return ISC_FALSE;
+#endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */
+ }
+ iface->flags |= INT_MCASTOPEN;
+ iface->num_mcast++;
+
+ return ISC_TRUE;
+}
+#endif /* MCAST */
+
+
+/*
+ * Remove a multicast address from a given socket
+ * The socket is in the ep_list all we need to do is disable
+ * multicasting. It is not this function's job to select the socket
+ */
+#ifdef MCAST
+static isc_boolean_t
+socket_multicast_disable(
+ struct interface * iface,
+ sockaddr_u * maddr
+ )
+{
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ struct ipv6_mreq mreq6;
+#endif
+ struct ip_mreq mreq;
+
+ ZERO(mreq);
+
+ if (find_addr_in_list(maddr) == NULL) {
+ DPRINTF(4, ("socket_multicast_disable(%s): not found\n",
+ stoa(maddr)));
+ return ISC_TRUE;
+ }
+
+ switch (AF(maddr)) {
+
+ case AF_INET:
+ mreq.imr_multiaddr = SOCK_ADDR4(maddr);
+ mreq.imr_interface = SOCK_ADDR4(&iface->sin);
+ if (setsockopt(iface->fd, IPPROTO_IP,
+ IP_DROP_MEMBERSHIP, (char *)&mreq,
+ sizeof(mreq))) {
+
+ msyslog(LOG_ERR,
+ "setsockopt IP_DROP_MEMBERSHIP failed: %m on socket %d, addr %s for %x / %x (%s)",
+ iface->fd, stoa(&iface->sin),
+ SRCADR(maddr), SRCADR(&iface->sin),
+ stoa(maddr));
+ return ISC_FALSE;
+ }
+ break;
+ case AF_INET6:
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ /*
+ * Disable reception of multicast packets
+ * If the address is link-local we can get the
+ * interface index from the scope id. Don't do this
+ * for other types of multicast addresses. For now let
+ * the kernel figure it out.
+ */
+ mreq6.ipv6mr_multiaddr = SOCK_ADDR6(maddr);
+ mreq6.ipv6mr_interface = iface->ifindex;
+
+ if (setsockopt(iface->fd, IPPROTO_IPV6,
+ IPV6_LEAVE_GROUP, (char *)&mreq6,
+ sizeof(mreq6))) {
+
+ msyslog(LOG_ERR,
+ "setsockopt IPV6_LEAVE_GROUP failure: %m on socket %d, addr %s for %d (%s)",
+ iface->fd, stoa(&iface->sin),
+ iface->ifindex, stoa(maddr));
+ return ISC_FALSE;
+ }
+ break;
+#else
+ return ISC_FALSE;
+#endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */
+ }
+
+ iface->num_mcast--;
+ if (!iface->num_mcast)
+ iface->flags &= ~INT_MCASTOPEN;
+
+ return ISC_TRUE;
+}
+#endif /* MCAST */
+
+/*
+ * io_setbclient - open the broadcast client sockets
+ */
+void
+io_setbclient(void)
+{
+#ifdef OPEN_BCAST_SOCKET
+ struct interface * interf;
+ int nif;
+
+ nif = 0;
+ set_reuseaddr(1);
+
+ for (interf = ep_list;
+ interf != NULL;
+ interf = interf->elink) {
+
+ if (interf->flags & (INT_WILDCARD | INT_LOOPBACK))
+ continue;
+
+ /* use only allowed addresses */
+ if (interf->ignore_packets)
+ continue;
+
+ /* Need a broadcast-capable interface */
+ if (!(interf->flags & INT_BROADCAST))
+ continue;
+
+ /* Only IPv4 addresses are valid for broadcast */
+ NTP_REQUIRE(IS_IPV4(&interf->sin));
+
+ /* Do we already have the broadcast address open? */
+ if (interf->flags & INT_BCASTOPEN) {
+ /*
+ * account for already open interfaces to avoid
+ * misleading warning below
+ */
+ nif++;
+ continue;
+ }
+
+ /*
+ * Try to open the broadcast address
+ */
+ interf->family = AF_INET;
+ interf->bfd = open_socket(&interf->bcast, 1, 0, interf);
+
+ /*
+ * If we succeeded then we use it otherwise enable
+ * broadcast on the interface address
+ */
+ if (interf->bfd != INVALID_SOCKET) {
+ nif++;
+ interf->flags |= INT_BCASTOPEN;
+ msyslog(LOG_INFO,
+ "Listen for broadcasts to %s on interface #%d %s",
+ stoa(&interf->bcast), interf->ifnum, interf->name);
+ } else {
+ /* silently ignore EADDRINUSE as we probably opened
+ the socket already for an address in the same network */
+ if (errno != EADDRINUSE)
+ msyslog(LOG_INFO,
+ "failed to listen for broadcasts to %s on interface #%d %s",
+ stoa(&interf->bcast), interf->ifnum, interf->name);
+ }
+ }
+ set_reuseaddr(0);
+ if (nif > 0) {
+ broadcast_client_enabled = ISC_TRUE;
+ DPRINTF(1, ("io_setbclient: listening to %d broadcast addresses\n", nif));
+ }
+ else if (!nif) {
+ broadcast_client_enabled = ISC_FALSE;
+ msyslog(LOG_ERR,
+ "Unable to listen for broadcasts, no broadcast interfaces available");
+ }
+#else
+ msyslog(LOG_ERR,
+ "io_setbclient: Broadcast Client disabled by build");
+#endif /* OPEN_BCAST_SOCKET */
+}
+
+/*
+ * io_unsetbclient - close the broadcast client sockets
+ */
+void
+io_unsetbclient(void)
+{
+ endpt *ep;
+
+ for (ep = ep_list; ep != NULL; ep = ep->elink) {
+ if (INT_WILDCARD & ep->flags)
+ continue;
+ if (!(INT_BCASTOPEN & ep->flags))
+ continue;
+
+ if (ep->bfd != INVALID_SOCKET) {
+ /* destroy broadcast listening socket */
+ msyslog(LOG_INFO,
+ "stop listening for broadcasts to %s on interface #%d %s",
+ stoa(&ep->bcast), ep->ifnum, ep->name);
+ close_and_delete_fd_from_list(ep->bfd);
+ ep->bfd = INVALID_SOCKET;
+ ep->flags &= ~INT_BCASTOPEN;
+ }
+ }
+ broadcast_client_enabled = ISC_FALSE;
+}
+
+/*
+ * io_multicast_add() - add multicast group address
+ */
+void
+io_multicast_add(
+ sockaddr_u *addr
+ )
+{
+#ifdef MCAST
+ endpt * ep;
+ endpt * one_ep;
+
+ /*
+ * Check to see if this is a multicast address
+ */
+ if (!addr_ismulticast(addr))
+ return;
+
+ /* If we already have it we can just return */
+ if (NULL != find_flagged_addr_in_list(addr, INT_MCASTOPEN)) {
+ msyslog(LOG_INFO,
+ "Duplicate request found for multicast address %s",
+ stoa(addr));
+ return;
+ }
+
+#ifndef MULTICAST_NONEWSOCKET
+ ep = new_interface(NULL);
+
+ /*
+ * Open a new socket for the multicast address
+ */
+ ep->sin = *addr;
+ SET_PORT(&ep->sin, NTP_PORT);
+ ep->family = AF(&ep->sin);
+ AF(&ep->mask) = ep->family;
+ SET_ONESMASK(&ep->mask);
+
+ set_reuseaddr(1);
+ ep->bfd = INVALID_SOCKET;
+ ep->fd = open_socket(&ep->sin, 0, 0, ep);
+ if (ep->fd != INVALID_SOCKET) {
+ ep->ignore_packets = ISC_FALSE;
+ ep->flags |= INT_MCASTIF;
+
+ strlcpy(ep->name, "multicast", sizeof(ep->name));
+ DPRINT_INTERFACE(2, (ep, "multicast add ", "\n"));
+ add_interface(ep);
+ log_listen_address(ep);
+ } else {
+ /* bind failed, re-use wildcard interface */
+ delete_interface(ep);
+
+ if (IS_IPV4(addr))
+ ep = wildipv4;
+ else if (IS_IPV6(addr))
+ ep = wildipv6;
+ else
+ ep = NULL;
+
+ if (ep != NULL) {
+ /* HACK ! -- stuff in an address */
+ /* because we don't bind addr? DH */
+ ep->bcast = *addr;
+ msyslog(LOG_ERR,
+ "multicast address %s using wildcard interface #%d %s",
+ stoa(addr), ep->ifnum, ep->name);
+ } else {
+ msyslog(LOG_ERR,
+ "No multicast socket available to use for address %s",
+ stoa(addr));
+ return;
+ }
+ }
+ { /* in place of the { following for in #else clause */
+ one_ep = ep;
+#else /* MULTICAST_NONEWSOCKET follows */
+ /*
+ * For the case where we can't use a separate socket (Windows)
+ * join each applicable endpoint socket to the group address.
+ */
+ if (IS_IPV4(addr))
+ one_ep = wildipv4;
+ else
+ one_ep = wildipv6;
+ for (ep = ep_list; ep != NULL; ep = ep->elink) {
+ if (ep->ignore_packets || AF(&ep->sin) != AF(addr) ||
+ !(INT_MULTICAST & ep->flags) ||
+ (INT_LOOPBACK | INT_WILDCARD) & ep->flags)
+ continue;
+ one_ep = ep;
+#endif /* MULTICAST_NONEWSOCKET */
+ if (socket_multicast_enable(ep, addr))
+ msyslog(LOG_INFO,
+ "Joined %s socket to multicast group %s",
+ stoa(&ep->sin),
+ stoa(addr));
+ else
+ msyslog(LOG_ERR,
+ "Failed to join %s socket to multicast group %s",
+ stoa(&ep->sin),
+ stoa(addr));
+ }
+
+ add_addr_to_list(addr, one_ep);
+#else /* !MCAST follows*/
+ msyslog(LOG_ERR,
+ "Can not add multicast address %s: no multicast support",
+ stoa(addr));
+#endif
+ return;
+}
+
+
+/*
+ * io_multicast_del() - delete multicast group address
+ */
+void
+io_multicast_del(
+ sockaddr_u * addr
+ )
+{
+#ifdef MCAST
+ endpt *iface;
+
+ /*
+ * Check to see if this is a multicast address
+ */
+ if (!addr_ismulticast(addr)) {
+ msyslog(LOG_ERR, "invalid multicast address %s",
+ stoa(addr));
+ return;
+ }
+
+ /*
+ * Disable reception of multicast packets
+ */
+ while ((iface = find_flagged_addr_in_list(addr, INT_MCASTOPEN))
+ != NULL)
+ socket_multicast_disable(iface, addr);
+
+ delete_addr_from_list(addr);
+
+#else /* not MCAST */
+ msyslog(LOG_ERR,
+ "Can not delete multicast address %s: no multicast support",
+ stoa(addr));
+#endif /* not MCAST */
+}
+
+
+/*
+ * open_socket - open a socket, returning the file descriptor
+ */
+
+static SOCKET
+open_socket(
+ sockaddr_u * addr,
+ int bcast,
+ int turn_off_reuse,
+ endpt * interf
+ )
+{
+ SOCKET fd;
+ int errval;
+ char scopetext[16];
+ /*
+ * int is OK for REUSEADR per
+ * http://www.kohala.com/start/mcast.api.txt
+ */
+ int on = 1;
+ int off = 0;
+
+#ifndef IPTOS_DSCP_EF
+#define IPTOS_DSCP_EF 0xb8
+#endif
+ int qos = IPTOS_DSCP_EF; /* QoS RFC3246 */
+
+ if (IS_IPV6(addr) && !ipv6_works)
+ return INVALID_SOCKET;
+
+ /* create a datagram (UDP) socket */
+ fd = socket(AF(addr), SOCK_DGRAM, 0);
+ if (INVALID_SOCKET == fd) {
+ errval = socket_errno();
+ msyslog(LOG_ERR,
+ "socket(AF_INET%s, SOCK_DGRAM, 0) failed on address %s: %m",
+ IS_IPV6(addr) ? "6" : "", stoa(addr));
+
+ if (errval == EPROTONOSUPPORT ||
+ errval == EAFNOSUPPORT ||
+ errval == EPFNOSUPPORT)
+ return (INVALID_SOCKET);
+
+ errno = errval;
+ msyslog(LOG_ERR,
+ "unexpected socket() error %m code %d (not EPROTONOSUPPORT nor EAFNOSUPPORT nor EPFNOSUPPORT) - exiting",
+ errno);
+ exit(1);
+ }
+
+#ifdef SYS_WINNT
+ connection_reset_fix(fd, addr);
+#endif
+ /*
+ * Fixup the file descriptor for some systems
+ * See bug #530 for details of the issue.
+ */
+ fd = move_fd(fd);
+
+ /*
+ * set SO_REUSEADDR since we will be binding the same port
+ * number on each interface according to turn_off_reuse.
+ * This is undesirable on Windows versions starting with
+ * Windows XP (numeric version 5.1).
+ */
+#ifdef SYS_WINNT
+ if (isc_win32os_versioncheck(5, 1, 0, 0) < 0) /* before 5.1 */
+#endif
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)((turn_off_reuse)
+ ? &off
+ : &on),
+ sizeof(on))) {
+
+ msyslog(LOG_ERR,
+ "setsockopt SO_REUSEADDR %s fails for address %s: %m",
+ (turn_off_reuse)
+ ? "off"
+ : "on",
+ stoa(addr));
+ closesocket(fd);
+ return INVALID_SOCKET;
+ }
+#ifdef SO_EXCLUSIVEADDRUSE
+ /*
+ * setting SO_EXCLUSIVEADDRUSE on the wildcard we open
+ * first will cause more specific binds to fail.
+ */
+ if (!(interf->flags & INT_WILDCARD))
+ set_excladdruse(fd);
+#endif
+
+ /*
+ * IPv4 specific options go here
+ */
+ if (IS_IPV4(addr)) {
+#if defined(IPPROTO_IP) && defined(IP_TOS)
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, (char*)&qos,
+ sizeof(qos)))
+ msyslog(LOG_ERR,
+ "setsockopt IP_TOS (%02x) fails on address %s: %m",
+ qos, stoa(addr));
+#endif /* IPPROTO_IP && IP_TOS */
+ if (bcast)
+ socket_broadcast_enable(interf, fd, addr);
+ }
+
+ /*
+ * IPv6 specific options go here
+ */
+ if (IS_IPV6(addr)) {
+#if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS)
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, (char*)&qos,
+ sizeof(qos)))
+ msyslog(LOG_ERR,
+ "setsockopt IPV6_TCLASS (%02x) fails on address %s: %m",
+ qos, stoa(addr));
+#endif /* IPPROTO_IPV6 && IPV6_TCLASS */
+#ifdef IPV6_V6ONLY
+ if (isc_net_probe_ipv6only() == ISC_R_SUCCESS
+ && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char*)&on, sizeof(on)))
+ msyslog(LOG_ERR,
+ "setsockopt IPV6_V6ONLY on fails on address %s: %m",
+ stoa(addr));
+#endif
+#ifdef IPV6_BINDV6ONLY
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_BINDV6ONLY,
+ (char*)&on, sizeof(on)))
+ msyslog(LOG_ERR,
+ "setsockopt IPV6_BINDV6ONLY on fails on address %s: %m",
+ stoa(addr));
+#endif
+ }
+
+#ifdef OS_NEEDS_REUSEADDR_FOR_IFADDRBIND
+ /*
+ * some OSes don't allow binding to more specific
+ * addresses if a wildcard address already bound
+ * to the port and SO_REUSEADDR is not set
+ */
+ if (!is_wildcard_addr(addr))
+ set_wildcard_reuse(AF(addr), 1);
+#endif
+
+ /*
+ * bind the local address.
+ */
+ errval = bind(fd, &addr->sa, SOCKLEN(addr));
+
+#ifdef OS_NEEDS_REUSEADDR_FOR_IFADDRBIND
+ if (!is_wildcard_addr(addr))
+ set_wildcard_reuse(AF(addr), 0);
+#endif
+
+ if (errval < 0) {
+ /*
+ * Don't log this under all conditions
+ */
+ if (turn_off_reuse == 0
+#ifdef DEBUG
+ || debug > 1
+#endif
+ ) {
+ if (SCOPE(addr))
+ snprintf(scopetext, sizeof(scopetext),
+ "%%%d", SCOPE(addr));
+ else
+ scopetext[0] = 0;
+
+ msyslog(LOG_ERR,
+ "bind(%d) AF_INET%s %s%s#%d%s flags 0x%x failed: %m",
+ fd, IS_IPV6(addr) ? "6" : "",
+ stoa(addr), scopetext, SRCPORT(addr),
+ IS_MCAST(addr) ? " (multicast)" : "",
+ interf->flags);
+ }
+
+ closesocket(fd);
+
+ return INVALID_SOCKET;
+ }
+
+#ifdef HAVE_TIMESTAMP
+ {
+ if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP,
+ (char*)&on, sizeof(on)))
+ msyslog(LOG_DEBUG,
+ "setsockopt SO_TIMESTAMP on fails on address %s: %m",
+ stoa(addr));
+ else
+ DPRINTF(4, ("setsockopt SO_TIMESTAMP enabled on fd %d address %s\n",
+ fd, stoa(addr)));
+ }
+#endif
+#ifdef HAVE_TIMESTAMPNS
+ {
+ if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS,
+ (char*)&on, sizeof(on)))
+ msyslog(LOG_DEBUG,
+ "setsockopt SO_TIMESTAMPNS on fails on address %s: %m",
+ stoa(addr));
+ else
+ DPRINTF(4, ("setsockopt SO_TIMESTAMPNS enabled on fd %d address %s\n",
+ fd, stoa(addr)));
+ }
+#endif
+#ifdef HAVE_BINTIME
+ {
+ if (setsockopt(fd, SOL_SOCKET, SO_BINTIME,
+ (char*)&on, sizeof(on)))
+ msyslog(LOG_DEBUG,
+ "setsockopt SO_BINTIME on fails on address %s: %m",
+ stoa(addr));
+ else
+ DPRINTF(4, ("setsockopt SO_BINTIME enabled on fd %d address %s\n",
+ fd, stoa(addr)));
+ }
+#endif
+
+ DPRINTF(4, ("bind(%d) AF_INET%s, addr %s%%%d#%d, flags 0x%x\n",
+ fd, IS_IPV6(addr) ? "6" : "", stoa(addr),
+ SCOPE(addr), SRCPORT(addr), interf->flags));
+
+ make_socket_nonblocking(fd);
+
+#ifdef HAVE_SIGNALED_IO
+ init_socket_sig(fd);
+#endif /* not HAVE_SIGNALED_IO */
+
+ add_fd_to_list(fd, FD_TYPE_SOCKET);
+
+#if !defined(SYS_WINNT) && !defined(VMS)
+ DPRINTF(4, ("flags for fd %d: 0x%x\n", fd,
+ fcntl(fd, F_GETFL, 0)));
+#endif /* SYS_WINNT || VMS */
+
+#if defined (HAVE_IO_COMPLETION_PORT)
+/*
+ * Add the socket to the completion port
+ */
+ if (io_completion_port_add_socket(fd, interf)) {
+ msyslog(LOG_ERR, "unable to set up io completion port - EXITING");
+ exit(1);
+ }
+#endif
+ return fd;
+}
+
+
+#ifdef SYS_WINNT
+#define sendto(fd, buf, len, flags, dest, destsz) \
+ io_completion_port_sendto(fd, buf, len, (sockaddr_u *)(dest))
+#endif
+
+/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
+/*
+ * sendpkt - send a packet to the specified destination. Maintain a
+ * send error cache so that only the first consecutive error for a
+ * destination is logged.
+ */
+void
+sendpkt(
+ sockaddr_u * dest,
+ struct interface * ep,
+ int ttl,
+ struct pkt * pkt,
+ int len
+ )
+{
+ endpt * src;
+ int ismcast;
+ int cc;
+ int rc;
+ u_char cttl;
+
+ ismcast = IS_MCAST(dest);
+ if (!ismcast)
+ src = ep;
+ else
+ src = (IS_IPV4(dest))
+ ? mc4_list
+ : mc6_list;
+
+ if (NULL == src) {
+ /*
+ * unbound peer - drop request and wait for better
+ * network conditions
+ */
+ DPRINTF(2, ("%ssendpkt(dst=%s, ttl=%d, len=%d): no interface - IGNORED\n",
+ ismcast ? "\tMCAST\t***** " : "",
+ stoa(dest), ttl, len));
+ return;
+ }
+
+ do {
+ DPRINTF(2, ("%ssendpkt(%d, dst=%s, src=%s, ttl=%d, len=%d)\n",
+ ismcast ? "\tMCAST\t***** " : "", src->fd,
+ stoa(dest), stoa(&src->sin), ttl, len));
+#ifdef MCAST
+ /*
+ * for the moment we use the bcast option to set multicast ttl
+ */
+ if (ismcast && ttl > 0 && ttl != src->last_ttl) {
+ /*
+ * set the multicast ttl for outgoing packets
+ */
+ switch (AF(&src->sin)) {
+
+ case AF_INET :
+ cttl = (u_char)ttl;
+ rc = setsockopt(src->fd, IPPROTO_IP,
+ IP_MULTICAST_TTL,
+ (void *)&cttl,
+ sizeof(cttl));
+ break;
+
+# ifdef INCLUDE_IPV6_SUPPORT
+ case AF_INET6 :
+ rc = setsockopt(src->fd, IPPROTO_IPV6,
+ IPV6_MULTICAST_HOPS,
+ (void *)&ttl,
+ sizeof(ttl));
+ break;
+# endif /* INCLUDE_IPV6_SUPPORT */
+
+ default:
+ rc = 0;
+ }
+
+ if (!rc)
+ src->last_ttl = ttl;
+ else
+ msyslog(LOG_ERR,
+ "setsockopt IP_MULTICAST_TTL/IPV6_MULTICAST_HOPS fails on address %s: %m",
+ stoa(&src->sin));
+ }
+#endif /* MCAST */
+
+#ifdef SIM
+ cc = simulate_server(dest, src, pkt);
+#else
+ cc = sendto(src->fd, (char *)pkt, (u_int)len, 0,
+ &dest->sa, SOCKLEN(dest));
+#endif
+ if (cc == -1) {
+ src->notsent++;
+ packets_notsent++;
+ } else {
+ src->sent++;
+ packets_sent++;
+ }
+ if (ismcast)
+ src = src->mclink;
+ } while (ismcast && src != NULL);
+}
+
+
+#if !defined(HAVE_IO_COMPLETION_PORT)
+/*
+ * fdbits - generate ascii representation of fd_set (FAU debug support)
+ * HFDF format - highest fd first.
+ */
+static char *
+fdbits(
+ int count,
+ fd_set *set
+ )
+{
+ static char buffer[256];
+ char * buf = buffer;
+
+ count = min(count, 255);
+
+ while (count >= 0) {
+ *buf++ = FD_ISSET(count, set) ? '#' : '-';
+ count--;
+ }
+ *buf = '\0';
+
+ return buffer;
+}
+
+
+#ifdef REFCLOCK
+/*
+ * Routine to read the refclock packets for a specific interface
+ * Return the number of bytes read. That way we know if we should
+ * read it again or go on to the next one if no bytes returned
+ */
+static inline int
+read_refclock_packet(
+ SOCKET fd,
+ struct refclockio * rp,
+ l_fp ts
+ )
+{
+ int i;
+ int buflen;
+ int saved_errno;
+ int consumed;
+ struct recvbuf * rb;
+
+ rb = get_free_recv_buffer();
+
+ if (NULL == rb) {
+ /*
+ * No buffer space available - just drop the packet
+ */
+ char buf[RX_BUFF_SIZE];
+
+ buflen = read(fd, buf, sizeof buf);
+ packets_dropped++;
+ return (buflen);
+ }
+
+ i = (rp->datalen == 0
+ || rp->datalen > sizeof(rb->recv_space))
+ ? sizeof(rb->recv_space)
+ : rp->datalen;
+ do {
+ buflen = read(fd, (char *)&rb->recv_space, (u_int)i);
+ } while (buflen < 0 && EINTR == errno);
+
+ if (buflen <= 0) {
+ saved_errno = errno;
+ freerecvbuf(rb);
+ errno = saved_errno;
+ return buflen;
+ }
+
+ /*
+ * Got one. Mark how and when it got here,
+ * put it on the full list and do bookkeeping.
+ */
+ rb->recv_length = buflen;
+ rb->recv_peer = rp->srcclock;
+ rb->dstadr = 0;
+ rb->fd = fd;
+ rb->recv_time = ts;
+ rb->receiver = rp->clock_recv;
+
+ consumed = indicate_refclock_packet(rp, rb);
+ if (!consumed) {
+ rp->recvcount++;
+ packets_received++;
+ }
+
+ return buflen;
+}
+#endif /* REFCLOCK */
+
+
+#ifdef HAVE_PACKET_TIMESTAMP
+/*
+ * extract timestamps from control message buffer
+ */
+static l_fp
+fetch_timestamp(
+ struct recvbuf * rb,
+ struct msghdr * msghdr,
+ l_fp ts
+ )
+{
+ struct cmsghdr * cmsghdr;
+#ifdef HAVE_BINTIME
+ struct bintime * btp;
+#endif
+#ifdef HAVE_TIMESTAMPNS
+ struct timespec * tsp;
+#endif
+#ifdef HAVE_TIMESTAMP
+ struct timeval * tvp;
+#endif
+ unsigned long ticks;
+ double fuzz;
+ l_fp lfpfuzz;
+ l_fp nts;
+#ifdef DEBUG_TIMING
+ l_fp dts;
+#endif
+
+ cmsghdr = CMSG_FIRSTHDR(msghdr);
+ while (cmsghdr != NULL) {
+ switch (cmsghdr->cmsg_type)
+ {
+#ifdef HAVE_BINTIME
+ case SCM_BINTIME:
+#endif /* HAVE_BINTIME */
+#ifdef HAVE_TIMESTAMPNS
+ case SCM_TIMESTAMPNS:
+#endif /* HAVE_TIMESTAMPNS */
+#ifdef HAVE_TIMESTAMP
+ case SCM_TIMESTAMP:
+#endif /* HAVE_TIMESTAMP */
+#if defined(HAVE_BINTIME) || defined (HAVE_TIMESTAMPNS) || defined(HAVE_TIMESTAMP)
+ switch (cmsghdr->cmsg_type)
+ {
+#ifdef HAVE_BINTIME
+ case SCM_BINTIME:
+ btp = (struct bintime *)CMSG_DATA(cmsghdr);
+ /*
+ * bintime documentation is at http://phk.freebsd.dk/pubs/timecounter.pdf
+ */
+ nts.l_i = btp->sec + JAN_1970;
+ nts.l_uf = (u_int32)(btp->frac >> 32);
+ if (sys_tick > measured_tick &&
+ sys_tick > 1e-9) {
+ ticks = (unsigned long)(nts.l_uf / (unsigned long)(sys_tick * FRAC));
+ nts.l_uf = (unsigned long)(ticks * (unsigned long)(sys_tick * FRAC));
+ }
+ DPRINTF(4, ("fetch_timestamp: system bintime network time stamp: %ld.%09lu\n",
+ btp->sec, (unsigned long)((nts.l_uf / FRAC) * 1e9)));
+ break;
+#endif /* HAVE_BINTIME */
+#ifdef HAVE_TIMESTAMPNS
+ case SCM_TIMESTAMPNS:
+ tsp = (struct timespec *)CMSG_DATA(cmsghdr);
+ if (sys_tick > measured_tick &&
+ sys_tick > 1e-9) {
+ ticks = (unsigned long)((tsp->tv_nsec * 1e-9) /
+ sys_tick);
+ tsp->tv_nsec = (long)(ticks * 1e9 *
+ sys_tick);
+ }
+ DPRINTF(4, ("fetch_timestamp: system nsec network time stamp: %ld.%09ld\n",
+ tsp->tv_sec, tsp->tv_nsec));
+ nts = tspec_stamp_to_lfp(*tsp);
+ break;
+#endif /* HAVE_TIMESTAMPNS */
+#ifdef HAVE_TIMESTAMP
+ case SCM_TIMESTAMP:
+ tvp = (struct timeval *)CMSG_DATA(cmsghdr);
+ if (sys_tick > measured_tick &&
+ sys_tick > 1e-6) {
+ ticks = (unsigned long)((tvp->tv_usec * 1e-6) /
+ sys_tick);
+ tvp->tv_usec = (long)(ticks * 1e6 *
+ sys_tick);
+ }
+ DPRINTF(4, ("fetch_timestamp: system usec network time stamp: %ld.%06ld\n",
+ tvp->tv_sec, tvp->tv_usec));
+ nts = tval_stamp_to_lfp(*tvp);
+ break;
+#endif /* HAVE_TIMESTAMP */
+ }
+ fuzz = ntp_random() * 2. / FRAC * sys_fuzz;
+ DTOLFP(fuzz, &lfpfuzz);
+ L_ADD(&nts, &lfpfuzz);
+#ifdef DEBUG_TIMING
+ dts = ts;
+ L_SUB(&dts, &nts);
+ collect_timing(rb, "input processing delay", 1,
+ &dts);
+ DPRINTF(4, ("fetch_timestamp: timestamp delta: %s (incl. fuzz)\n",
+ lfptoa(&dts, 9)));
+#endif /* DEBUG_TIMING */
+ ts = nts; /* network time stamp */
+ break;
+#endif /* HAVE_BINTIME || HAVE_TIMESTAMPNS || HAVE_TIMESTAMP */
+
+ default:
+ DPRINTF(4, ("fetch_timestamp: skipping control message 0x%x\n",
+ cmsghdr->cmsg_type));
+ }
+ cmsghdr = CMSG_NXTHDR(msghdr, cmsghdr);
+ }
+ return ts;
+}
+#endif /* HAVE_PACKET_TIMESTAMP */
+
+
+/*
+ * Routine to read the network NTP packets for a specific interface
+ * Return the number of bytes read. That way we know if we should
+ * read it again or go on to the next one if no bytes returned
+ */
+static inline int
+read_network_packet(
+ SOCKET fd,
+ struct interface * itf,
+ l_fp ts
+ )
+{
+ GETSOCKNAME_SOCKLEN_TYPE fromlen;
+ int buflen;
+ register struct recvbuf *rb;
+#ifdef HAVE_PACKET_TIMESTAMP
+ struct msghdr msghdr;
+ struct iovec iovec;
+ char control[CMSG_BUFSIZE];
+#endif
+
+ /*
+ * Get a buffer and read the frame. If we
+ * haven't got a buffer, or this is received
+ * on a disallowed socket, just dump the
+ * packet.
+ */
+
+ rb = get_free_recv_buffer();
+ if (NULL == rb || itf->ignore_packets) {
+ char buf[RX_BUFF_SIZE];
+ sockaddr_u from;
+
+ if (rb != NULL)
+ freerecvbuf(rb);
+
+ fromlen = sizeof(from);
+ buflen = recvfrom(fd, buf, sizeof(buf), 0,
+ &from.sa, &fromlen);
+ DPRINTF(4, ("%s on (%lu) fd=%d from %s\n",
+ (itf->ignore_packets)
+ ? "ignore"
+ : "drop",
+ free_recvbuffs(), fd, stoa(&from)));
+ if (itf->ignore_packets)
+ packets_ignored++;
+ else
+ packets_dropped++;
+ return (buflen);
+ }
+
+ fromlen = sizeof(rb->recv_srcadr);
+
+#ifndef HAVE_PACKET_TIMESTAMP
+ rb->recv_length = recvfrom(fd, (char *)&rb->recv_space,
+ sizeof(rb->recv_space), 0,
+ &rb->recv_srcadr.sa, &fromlen);
+#else
+ iovec.iov_base = &rb->recv_space;
+ iovec.iov_len = sizeof(rb->recv_space);
+ msghdr.msg_name = &rb->recv_srcadr;
+ msghdr.msg_namelen = fromlen;
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_control = (void *)&control;
+ msghdr.msg_controllen = sizeof(control);
+ msghdr.msg_flags = 0;
+ rb->recv_length = recvmsg(fd, &msghdr, 0);
+#endif
+
+ buflen = rb->recv_length;
+
+ if (buflen == 0 || (buflen == -1 &&
+ (EWOULDBLOCK == errno
+#ifdef EAGAIN
+ || EAGAIN == errno
+#endif
+ ))) {
+ freerecvbuf(rb);
+ return (buflen);
+ } else if (buflen < 0) {
+ msyslog(LOG_ERR, "recvfrom(%s) fd=%d: %m",
+ stoa(&rb->recv_srcadr), fd);
+ DPRINTF(5, ("read_network_packet: fd=%d dropped (bad recvfrom)\n",
+ fd));
+ freerecvbuf(rb);
+ return (buflen);
+ }
+
+ DPRINTF(3, ("read_network_packet: fd=%d length %d from %s\n",
+ fd, buflen, stoa(&rb->recv_srcadr)));
+
+ /*
+ * Got one. Mark how and when it got here,
+ * put it on the full list and do bookkeeping.
+ */
+ rb->dstadr = itf;
+ rb->fd = fd;
+#ifdef HAVE_PACKET_TIMESTAMP
+ /* pick up a network time stamp if possible */
+ ts = fetch_timestamp(rb, &msghdr, ts);
+#endif
+ rb->recv_time = ts;
+ rb->receiver = receive;
+
+ add_full_recv_buffer(rb);
+
+ itf->received++;
+ packets_received++;
+ return (buflen);
+}
+
+/*
+ * attempt to handle io (select()/signaled IO)
+ */
+void
+io_handler(void)
+{
+# ifndef HAVE_SIGNALED_IO
+ fd_set rdfdes;
+ int nfound;
+
+ /*
+ * Use select() on all on all input fd's for unlimited
+ * time. select() will terminate on SIGALARM or on the
+ * reception of input. Using select() means we can't do
+ * robust signal handling and we get a potential race
+ * between checking for alarms and doing the select().
+ * Mostly harmless, I think.
+ */
+ /*
+ * On VMS, I suspect that select() can't be interrupted
+ * by a "signal" either, so I take the easy way out and
+ * have select() time out after one second.
+ * System clock updates really aren't time-critical,
+ * and - lacking a hardware reference clock - I have
+ * yet to learn about anything else that is.
+ */
+ rdfdes = activefds;
+# if !defined(VMS) && !defined(SYS_VXWORKS)
+ nfound = select(maxactivefd + 1, &rdfdes, NULL,
+ NULL, NULL);
+# else /* VMS, VxWorks */
+ /* make select() wake up after one second */
+ {
+ struct timeval t1;
+
+ t1.tv_sec = 1;
+ t1.tv_usec = 0;
+ nfound = select(maxactivefd + 1,
+ &rdfdes, NULL, NULL,
+ &t1);
+ }
+# endif /* VMS, VxWorks */
+ if (nfound > 0) {
+ l_fp ts;
+
+ get_systime(&ts);
+
+ input_handler(&ts);
+ } else if (nfound == -1 && errno != EINTR) {
+ msyslog(LOG_ERR, "select() error: %m");
+ }
+# ifdef DEBUG
+ else if (debug > 4) {
+ msyslog(LOG_DEBUG, "select(): nfound=%d, error: %m", nfound);
+ } else {
+ DPRINTF(1, ("select() returned %d: %m\n", nfound));
+ }
+# endif /* DEBUG */
+# else /* HAVE_SIGNALED_IO */
+ wait_for_signal();
+# endif /* HAVE_SIGNALED_IO */
+}
+
+/*
+ * input_handler - receive packets asynchronously
+ */
+static void
+input_handler(
+ l_fp * cts
+ )
+{
+ int buflen;
+ int n;
+ u_int idx;
+ int doing;
+ SOCKET fd;
+ blocking_child *c;
+ struct timeval tvzero;
+ l_fp ts; /* Timestamp at BOselect() gob */
+#ifdef DEBUG_TIMING
+ l_fp ts_e; /* Timestamp at EOselect() gob */
+#endif
+ fd_set fds;
+ size_t select_count;
+ endpt * ep;
+#ifdef REFCLOCK
+ struct refclockio *rp;
+ int saved_errno;
+ const char * clk;
+#endif
+#ifdef HAS_ROUTING_SOCKET
+ struct asyncio_reader * asyncio_reader;
+ struct asyncio_reader * next_asyncio_reader;
+#endif
+
+ handler_calls++;
+ select_count = 0;
+
+ /*
+ * If we have something to do, freeze a timestamp.
+ * See below for the other cases (nothing left to do or error)
+ */
+ ts = *cts;
+
+ /*
+ * Do a poll to see who has data
+ */
+
+ fds = activefds;
+ tvzero.tv_sec = tvzero.tv_usec = 0;
+
+ n = select(maxactivefd + 1, &fds, (fd_set *)0, (fd_set *)0,
+ &tvzero);
+
+ /*
+ * If there are no packets waiting just return
+ */
+ if (n < 0) {
+ int err = errno;
+ int j, b, prior;
+ /*
+ * extended FAU debugging output
+ */
+ if (err != EINTR)
+ msyslog(LOG_ERR,
+ "select(%d, %s, 0L, 0L, &0.0) error: %m",
+ maxactivefd + 1,
+ fdbits(maxactivefd, &activefds));
+ if (err != EBADF)
+ goto ih_return;
+ for (j = 0, prior = 0; j <= maxactivefd; j++) {
+ if (FD_ISSET(j, &activefds)) {
+ if (-1 != read(j, &b, 0)) {
+ prior = j;
+ continue;
+ }
+ msyslog(LOG_ERR,
+ "Removing bad file descriptor %d from select set",
+ j);
+ FD_CLR(j, &activefds);
+ if (j == maxactivefd)
+ maxactivefd = prior;
+ }
+ }
+ goto ih_return;
+ }
+ else if (n == 0)
+ goto ih_return;
+
+ ++handler_pkts;
+
+#ifdef REFCLOCK
+ /*
+ * Check out the reference clocks first, if any
+ */
+
+ if (refio != NULL) {
+ for (rp = refio; rp != NULL; rp = rp->next) {
+ fd = rp->fd;
+
+ if (!FD_ISSET(fd, &fds))
+ continue;
+ ++select_count;
+ buflen = read_refclock_packet(fd, rp, ts);
+ /*
+ * The first read must succeed after select()
+ * indicates readability, or we've reached
+ * a permanent EOF. http://bugs.ntp.org/1732
+ * reported ntpd munching CPU after a USB GPS
+ * was unplugged because select was indicating
+ * EOF but ntpd didn't remove the descriptor
+ * from the activefds set.
+ */
+ if (buflen < 0 && EAGAIN != errno) {
+ saved_errno = errno;
+ clk = refnumtoa(&rp->srcclock->srcadr);
+ errno = saved_errno;
+ msyslog(LOG_ERR, "%s read: %m", clk);
+ maintain_activefds(fd, TRUE);
+ } else if (0 == buflen) {
+ clk = refnumtoa(&rp->srcclock->srcadr);
+ msyslog(LOG_ERR, "%s read EOF", clk);
+ maintain_activefds(fd, TRUE);
+ } else {
+ /* drain any remaining refclock input */
+ do {
+ buflen = read_refclock_packet(fd, rp, ts);
+ } while (buflen > 0);
+ }
+ }
+ }
+#endif /* REFCLOCK */
+
+ /*
+ * Loop through the interfaces looking for data to read.
+ */
+ for (ep = ep_list; ep != NULL; ep = ep->elink) {
+ for (doing = 0; doing < 2; doing++) {
+ if (!doing) {
+ fd = ep->fd;
+ } else {
+ if (!(ep->flags & INT_BCASTOPEN))
+ break;
+ fd = ep->bfd;
+ }
+ if (fd < 0)
+ continue;
+ if (FD_ISSET(fd, &fds))
+ do {
+ ++select_count;
+ buflen = read_network_packet(
+ fd, ep, ts);
+ } while (buflen > 0);
+ /* Check more interfaces */
+ }
+ }
+
+#ifdef HAS_ROUTING_SOCKET
+ /*
+ * scan list of asyncio readers - currently only used for routing sockets
+ */
+ asyncio_reader = asyncio_reader_list;
+
+ while (asyncio_reader != NULL) {
+ /* callback may unlink and free asyncio_reader */
+ next_asyncio_reader = asyncio_reader->link;
+ if (FD_ISSET(asyncio_reader->fd, &fds)) {
+ ++select_count;
+ (*asyncio_reader->receiver)(asyncio_reader);
+ }
+ asyncio_reader = next_asyncio_reader;
+ }
+#endif /* HAS_ROUTING_SOCKET */
+
+ /*
+ * Check for a response from a blocking child
+ */
+ for (idx = 0; idx < blocking_children_alloc; idx++) {
+ c = blocking_children[idx];
+ if (NULL == c || -1 == c->resp_read_pipe)
+ continue;
+ if (FD_ISSET(c->resp_read_pipe, &fds)) {
+ select_count++;
+ process_blocking_resp(c);
+ }
+ }
+
+ /*
+ * Done everything from that select.
+ * If nothing to do, just return.
+ * If an error occurred, complain and return.
+ */
+ if (select_count == 0) { /* We really had nothing to do */
+#ifdef DEBUG
+ if (debug)
+ msyslog(LOG_DEBUG, "input_handler: select() returned 0");
+#endif /* DEBUG */
+ goto ih_return;
+ }
+ /* We've done our work */
+#ifdef DEBUG_TIMING
+ get_systime(&ts_e);
+ /*
+ * (ts_e - ts) is the amount of time we spent
+ * processing this gob of file descriptors. Log
+ * it.
+ */
+ L_SUB(&ts_e, &ts);
+ collect_timing(NULL, "input handler", 1, &ts_e);
+ if (debug > 3)
+ msyslog(LOG_DEBUG,
+ "input_handler: Processed a gob of fd's in %s msec",
+ lfptoms(&ts_e, 6));
+#endif /* DEBUG_TIMING */
+ /* We're done... */
+ ih_return:
+ return;
+}
+#endif /* !HAVE_IO_COMPLETION_PORT */
+
+
+/*
+ * find an interface suitable for the src address
+ */
+endpt *
+select_peerinterface(
+ struct peer * peer,
+ sockaddr_u * srcadr,
+ endpt * dstadr
+ )
+{
+ endpt *ep;
+#ifndef SIM
+ endpt *wild;
+
+ wild = ANY_INTERFACE_CHOOSE(srcadr);
+
+ /*
+ * Initialize the peer structure and dance the interface jig.
+ * Reference clocks step the loopback waltz, the others
+ * squaredance around the interface list looking for a buddy. If
+ * the dance peters out, there is always the wildcard interface.
+ * This might happen in some systems and would preclude proper
+ * operation with public key cryptography.
+ */
+ if (ISREFCLOCKADR(srcadr)) {
+ ep = loopback_interface;
+ } else if (peer->cast_flags &
+ (MDF_BCLNT | MDF_ACAST | MDF_MCAST | MDF_BCAST)) {
+ ep = findbcastinter(srcadr);
+ if (ep != NULL)
+ DPRINTF(4, ("Found *-cast interface %s for address %s\n",
+ stoa(&ep->sin), stoa(srcadr)));
+ else
+ DPRINTF(4, ("No *-cast local address found for address %s\n",
+ stoa(srcadr)));
+ } else {
+ ep = dstadr;
+ if (NULL == ep)
+ ep = wild;
+ }
+ /*
+ * If it is a multicast address, findbcastinter() may not find
+ * it. For unicast, we get to find the interface when dstadr is
+ * given to us as the wildcard (ANY_INTERFACE_CHOOSE). Either
+ * way, try a little harder.
+ */
+ if (wild == ep)
+ ep = findinterface(srcadr);
+ /*
+ * we do not bind to the wildcard interfaces for output
+ * as our (network) source address would be undefined and
+ * crypto will not work without knowing the own transmit address
+ */
+ if (ep != NULL && INT_WILDCARD & ep->flags)
+ if (!accept_wildcard_if_for_winnt)
+ ep = NULL;
+#else /* SIM follows */
+ ep = loopback_interface;
+#endif
+
+ return ep;
+}
+
+
+/*
+ * findinterface - find local interface corresponding to address
+ */
+endpt *
+findinterface(
+ sockaddr_u *addr
+ )
+{
+ endpt *iface;
+
+ iface = findlocalinterface(addr, INT_WILDCARD, 0);
+
+ if (NULL == iface) {
+ DPRINTF(4, ("Found no interface for address %s - returning wildcard\n",
+ stoa(addr)));
+
+ iface = ANY_INTERFACE_CHOOSE(addr);
+ } else
+ DPRINTF(4, ("Found interface #%d %s for address %s\n",
+ iface->ifnum, iface->name, stoa(addr)));
+
+ return iface;
+}
+
+/*
+ * findlocalinterface - find local interface corresponding to addr,
+ * which does not have any of flags set. If bast is nonzero, addr is
+ * a broadcast address.
+ *
+ * This code attempts to find the local sending address for an outgoing
+ * address by connecting a new socket to destinationaddress:NTP_PORT
+ * and reading the sockname of the resulting connect.
+ * the complicated sequence simulates the routing table lookup
+ * for to first hop without duplicating any of the routing logic into
+ * ntpd. preferably we would have used an API call - but its not there -
+ * so this is the best we can do here short of duplicating to entire routing
+ * logic in ntpd which would be a silly and really unportable thing to do.
+ *
+ */
+static endpt *
+findlocalinterface(
+ sockaddr_u * addr,
+ int flags,
+ int bcast
+ )
+{
+ GETSOCKNAME_SOCKLEN_TYPE sockaddrlen;
+ endpt * iface;
+ sockaddr_u saddr;
+ SOCKET s;
+ int rtn;
+ int on;
+
+ DPRINTF(4, ("Finding interface for addr %s in list of addresses\n",
+ stoa(addr)));
+
+ s = socket(AF(addr), SOCK_DGRAM, 0);
+ if (INVALID_SOCKET == s)
+ return NULL;
+
+ /*
+ * If we are looking for broadcast interface we need to set this
+ * socket to allow broadcast
+ */
+ if (bcast) {
+ on = 1;
+ if (SOCKET_ERROR == setsockopt(s, SOL_SOCKET,
+ SO_BROADCAST,
+ (char *)&on,
+ sizeof(on))) {
+ closesocket(s);
+ return NULL;
+ }
+ }
+
+ rtn = connect(s, &addr->sa, SOCKLEN(addr));
+ if (SOCKET_ERROR == rtn) {
+ closesocket(s);
+ return NULL;
+ }
+
+ sockaddrlen = sizeof(saddr);
+ rtn = getsockname(s, &saddr.sa, &sockaddrlen);
+ closesocket(s);
+ if (SOCKET_ERROR == rtn)
+ return NULL;
+
+ DPRINTF(4, ("findlocalinterface: kernel maps %s to %s\n",
+ stoa(addr), stoa(&saddr)));
+
+ iface = getinterface(&saddr, flags);
+
+ /*
+ * if we didn't find an exact match on saddr, find the closest
+ * available local address. This handles the case of the
+ * address suggested by the kernel being excluded by nic rules
+ * or the user's -I and -L options to ntpd.
+ * See http://bugs.ntp.org/1184 and http://bugs.ntp.org/1683
+ * for more background.
+ */
+ if (NULL == iface || iface->ignore_packets)
+ iface = findclosestinterface(&saddr,
+ flags | INT_LOOPBACK);
+
+ /* Don't use an interface which will ignore replies */
+ if (iface != NULL && iface->ignore_packets)
+ iface = NULL;
+
+ return iface;
+}
+
+
+/*
+ * findclosestinterface
+ *
+ * If there are -I/--interface or -L/novirtualips command-line options,
+ * or "nic" or "interface" rules in ntp.conf, findlocalinterface() may
+ * find the kernel's preferred local address for a given peer address is
+ * administratively unavailable to ntpd, and punt to this routine's more
+ * expensive search.
+ *
+ * Find the numerically closest local address to the one connect()
+ * suggested. This matches an address on the same subnet first, as
+ * needed by Bug 1184, and provides a consistent choice if there are
+ * multiple feasible local addresses, regardless of the order ntpd
+ * enumerated them.
+ */
+endpt *
+findclosestinterface(
+ sockaddr_u * addr,
+ int flags
+ )
+{
+ endpt * ep;
+ endpt * winner;
+ sockaddr_u addr_dist;
+ sockaddr_u min_dist;
+
+ ZERO_SOCK(&min_dist);
+ winner = NULL;
+
+ for (ep = ep_list; ep != NULL; ep = ep->elink) {
+ if (ep->ignore_packets ||
+ AF(addr) != ep->family ||
+ flags & ep->flags)
+ continue;
+
+ calc_addr_distance(&addr_dist, addr, &ep->sin);
+ if (NULL == winner ||
+ -1 == cmp_addr_distance(&addr_dist, &min_dist)) {
+ min_dist = addr_dist;
+ winner = ep;
+ }
+ }
+ if (NULL == winner)
+ DPRINTF(4, ("findclosestinterface(%s) failed\n",
+ stoa(addr)));
+ else
+ DPRINTF(4, ("findclosestinterface(%s) -> %s\n",
+ stoa(addr), stoa(&winner->sin)));
+
+ return winner;
+}
+
+
+/*
+ * calc_addr_distance - calculate the distance between two addresses,
+ * the absolute value of the difference between
+ * the addresses numerically, stored as an address.
+ */
+static void
+calc_addr_distance(
+ sockaddr_u * dist,
+ const sockaddr_u * a1,
+ const sockaddr_u * a2
+ )
+{
+ u_int32 a1val;
+ u_int32 a2val;
+ u_int32 v4dist;
+ int found_greater;
+ int a1_greater;
+ int i;
+
+ NTP_REQUIRE(AF(a1) == AF(a2));
+
+ ZERO_SOCK(dist);
+ AF(dist) = AF(a1);
+
+ /* v4 can be done a bit simpler */
+ if (IS_IPV4(a1)) {
+ a1val = SRCADR(a1);
+ a2val = SRCADR(a2);
+ v4dist = (a1val > a2val)
+ ? a1val - a2val
+ : a2val - a1val;
+ SET_ADDR4(dist, v4dist);
+
+ return;
+ }
+
+ found_greater = FALSE;
+ a1_greater = FALSE; /* suppress pot. uninit. warning */
+ for (i = 0; i < sizeof(NSRCADR6(a1)); i++) {
+ if (!found_greater &&
+ NSRCADR6(a1)[i] != NSRCADR6(a2)[i]) {
+ found_greater = TRUE;
+ a1_greater = (NSRCADR6(a1)[i] > NSRCADR6(a2)[i]);
+ }
+ if (!found_greater) {
+ NSRCADR6(dist)[i] = 0;
+ } else {
+ if (a1_greater)
+ NSRCADR6(dist)[i] = NSRCADR6(a1)[i] -
+ NSRCADR6(a2)[i];
+ else
+ NSRCADR6(dist)[i] = NSRCADR6(a2)[i] -
+ NSRCADR6(a1)[i];
+ }
+ }
+}
+
+
+/*
+ * cmp_addr_distance - compare two address distances, returning -1, 0,
+ * 1 to indicate their relationship.
+ */
+static int
+cmp_addr_distance(
+ const sockaddr_u * d1,
+ const sockaddr_u * d2
+ )
+{
+ int i;
+
+ NTP_REQUIRE(AF(d1) == AF(d2));
+
+ if (IS_IPV4(d1)) {
+ if (SRCADR(d1) < SRCADR(d2))
+ return -1;
+ else if (SRCADR(d1) == SRCADR(d2))
+ return 0;
+ else
+ return 1;
+ }
+
+ for (i = 0; i < sizeof(NSRCADR6(d1)); i++) {
+ if (NSRCADR6(d1)[i] < NSRCADR6(d2)[i])
+ return -1;
+ else if (NSRCADR6(d1)[i] > NSRCADR6(d2)[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * fetch an interface structure the matches the
+ * address and has the given flags NOT set
+ */
+endpt *
+getinterface(
+ sockaddr_u * addr,
+ u_int32 flags
+ )
+{
+ endpt *iface;
+
+ iface = find_addr_in_list(addr);
+
+ if (iface != NULL && (iface->flags & flags))
+ iface = NULL;
+
+ return iface;
+}
+
+
+/*
+ * findbcastinter - find broadcast interface corresponding to address
+ */
+endpt *
+findbcastinter(
+ sockaddr_u *addr
+ )
+{
+ endpt * iface;
+
+ iface = NULL;
+#if !defined(MPE) && (defined(SIOCGIFCONF) || defined(SYS_WINNT))
+ DPRINTF(4, ("Finding broadcast/multicast interface for addr %s in list of addresses\n",
+ stoa(addr)));
+
+ iface = findlocalinterface(addr, INT_LOOPBACK | INT_WILDCARD,
+ 1);
+ if (iface != NULL) {
+ DPRINTF(4, ("Easily found bcast-/mcast- interface index #%d %s\n",
+ iface->ifnum, iface->name));
+ return iface;
+ }
+
+ /*
+ * plan B - try to find something reasonable in our lists in
+ * case kernel lookup doesn't help
+ */
+ for (iface = ep_list; iface != NULL; iface = iface->elink) {
+ if (iface->flags & INT_WILDCARD)
+ continue;
+
+ /* Don't bother with ignored interfaces */
+ if (iface->ignore_packets)
+ continue;
+
+ /*
+ * First look if this is the correct family
+ */
+ if(AF(&iface->sin) != AF(addr))
+ continue;
+
+ /* Skip the loopback addresses */
+ if (iface->flags & INT_LOOPBACK)
+ continue;
+
+ /*
+ * If we are looking to match a multicast address and
+ * this interface is one...
+ */
+ if (addr_ismulticast(addr)
+ && (iface->flags & INT_MULTICAST)) {
+#ifdef INCLUDE_IPV6_SUPPORT
+ /*
+ * ...it is the winner unless we're looking for
+ * an interface to use for link-local multicast
+ * and its address is not link-local.
+ */
+ if (IS_IPV6(addr)
+ && IN6_IS_ADDR_MC_LINKLOCAL(PSOCK_ADDR6(addr))
+ && !IN6_IS_ADDR_LINKLOCAL(PSOCK_ADDR6(&iface->sin)))
+ continue;
+#endif
+ break;
+ }
+
+ /*
+ * We match only those interfaces marked as
+ * broadcastable and either the explicit broadcast
+ * address or the network portion of the IP address.
+ * Sloppy.
+ */
+ if (IS_IPV4(addr)) {
+ if (SOCK_EQ(&iface->bcast, addr))
+ break;
+
+ if ((NSRCADR(&iface->sin) & NSRCADR(&iface->mask))
+ == (NSRCADR(addr) & NSRCADR(&iface->mask)))
+ break;
+ }
+#ifdef INCLUDE_IPV6_SUPPORT
+ else if (IS_IPV6(addr)) {
+ if (SOCK_EQ(&iface->bcast, addr))
+ break;
+
+ if (SOCK_EQ(netof(&iface->sin), netof(addr)))
+ break;
+ }
+#endif
+ }
+#endif /* SIOCGIFCONF */
+ if (NULL == iface) {
+ DPRINTF(4, ("No bcast interface found for %s\n",
+ stoa(addr)));
+ iface = ANY_INTERFACE_CHOOSE(addr);
+ } else {
+ DPRINTF(4, ("Found bcast-/mcast- interface index #%d %s\n",
+ iface->ifnum, iface->name));
+ }
+
+ return iface;
+}
+
+
+/*
+ * io_clr_stats - clear I/O module statistics
+ */
+void
+io_clr_stats(void)
+{
+ packets_dropped = 0;
+ packets_ignored = 0;
+ packets_received = 0;
+ packets_sent = 0;
+ packets_notsent = 0;
+
+ handler_calls = 0;
+ handler_pkts = 0;
+ io_timereset = current_time;
+}
+
+
+#ifdef REFCLOCK
+/*
+ * io_addclock - add a reference clock to the list and arrange that we
+ * get SIGIO interrupts from it.
+ */
+int
+io_addclock(
+ struct refclockio *rio
+ )
+{
+ BLOCKIO();
+
+ /*
+ * Stuff the I/O structure in the list and mark the descriptor
+ * in use. There is a harmless (I hope) race condition here.
+ */
+ rio->active = TRUE;
+
+# ifdef HAVE_SIGNALED_IO
+ if (init_clock_sig(rio)) {
+ UNBLOCKIO();
+ return 0;
+ }
+# elif defined(HAVE_IO_COMPLETION_PORT)
+ if (io_completion_port_add_clock_io(rio)) {
+ UNBLOCKIO();
+ return 0;
+ }
+# endif
+
+ /*
+ * enqueue
+ */
+ LINK_SLIST(refio, rio, next);
+
+ /*
+ * register fd
+ */
+ add_fd_to_list(rio->fd, FD_TYPE_FILE);
+
+ UNBLOCKIO();
+ return 1;
+}
+
+
+/*
+ * io_closeclock - close the clock in the I/O structure given
+ */
+void
+io_closeclock(
+ struct refclockio *rio
+ )
+{
+ struct refclockio *unlinked;
+
+ BLOCKIO();
+
+ /*
+ * Remove structure from the list
+ */
+ rio->active = FALSE;
+ UNLINK_SLIST(unlinked, refio, rio, next, struct refclockio);
+ if (NULL != unlinked) {
+ purge_recv_buffers_for_fd(rio->fd);
+ /*
+ * Close the descriptor.
+ */
+ close_and_delete_fd_from_list(rio->fd);
+ }
+ rio->fd = -1;
+
+ UNBLOCKIO();
+}
+#endif /* REFCLOCK */
+
+
+/*
+ * On NT a SOCKET is an unsigned int so we cannot possibly keep it in
+ * an array. So we use one of the ISC_LIST functions to hold the
+ * socket value and use that when we want to enumerate it.
+ *
+ * This routine is called by the forked intres child process to close
+ * all open sockets. On Windows there's no need as intres runs in
+ * the same process as a thread.
+ */
+#ifndef SYS_WINNT
+void
+kill_asyncio(
+ int startfd
+ )
+{
+ BLOCKIO();
+
+ /*
+ * In the child process we do not maintain activefds and
+ * maxactivefd. Zeroing maxactivefd disables code which
+ * maintains it in close_and_delete_fd_from_list().
+ */
+ maxactivefd = 0;
+
+ while (fd_list != NULL)
+ close_and_delete_fd_from_list(fd_list->fd);
+
+ UNBLOCKIO();
+}
+#endif /* !SYS_WINNT */
+
+
+/*
+ * Add and delete functions for the list of open sockets
+ */
+static void
+add_fd_to_list(
+ SOCKET fd,
+ enum desc_type type
+ )
+{
+ vsock_t *lsock = emalloc(sizeof(*lsock));
+
+ lsock->fd = fd;
+ lsock->type = type;
+
+ LINK_SLIST(fd_list, lsock, link);
+ maintain_activefds(fd, 0);
+}
+
+
+static void
+close_and_delete_fd_from_list(
+ SOCKET fd
+ )
+{
+ vsock_t *lsock;
+
+ UNLINK_EXPR_SLIST(lsock, fd_list, fd ==
+ UNLINK_EXPR_SLIST_CURRENT()->fd, link, vsock_t);
+
+ if (NULL == lsock)
+ return;
+
+ switch (lsock->type) {
+
+ case FD_TYPE_SOCKET:
+ closesocket(lsock->fd);
+ break;
+
+ case FD_TYPE_FILE:
+ closeserial(lsock->fd);
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "internal error - illegal descriptor type %d - EXITING",
+ (int)lsock->type);
+ exit(1);
+ }
+
+ free(lsock);
+ /*
+ * remove from activefds
+ */
+ maintain_activefds(fd, 1);
+}
+
+
+static void
+add_addr_to_list(
+ sockaddr_u * addr,
+ endpt * ep
+ )
+{
+ remaddr_t *laddr;
+
+#ifdef DEBUG
+ if (find_addr_in_list(addr) == NULL) {
+#endif
+ /* not there yet - add to list */
+ laddr = emalloc(sizeof(*laddr));
+ laddr->addr = *addr;
+ laddr->ep = ep;
+
+ LINK_SLIST(remoteaddr_list, laddr, link);
+
+ DPRINTF(4, ("Added addr %s to list of addresses\n",
+ stoa(addr)));
+#ifdef DEBUG
+ } else
+ DPRINTF(4, ("WARNING: Attempt to add duplicate addr %s to address list\n",
+ stoa(addr)));
+#endif
+}
+
+
+static void
+delete_addr_from_list(
+ sockaddr_u *addr
+ )
+{
+ remaddr_t *unlinked;
+
+ UNLINK_EXPR_SLIST(unlinked, remoteaddr_list, SOCK_EQ(addr,
+ &(UNLINK_EXPR_SLIST_CURRENT()->addr)), link, remaddr_t);
+
+ if (unlinked != NULL) {
+ DPRINTF(4, ("Deleted addr %s from list of addresses\n",
+ stoa(addr)));
+ free(unlinked);
+ }
+}
+
+
+static void
+delete_interface_from_list(
+ endpt *iface
+ )
+{
+ remaddr_t *unlinked;
+
+ for (;;) {
+ UNLINK_EXPR_SLIST(unlinked, remoteaddr_list, iface ==
+ UNLINK_EXPR_SLIST_CURRENT()->ep, link,
+ remaddr_t);
+
+ if (unlinked == NULL)
+ break;
+ DPRINTF(4, ("Deleted addr %s for interface #%d %s from list of addresses\n",
+ stoa(&unlinked->addr), iface->ifnum,
+ iface->name));
+ free(unlinked);
+ }
+}
+
+
+static struct interface *
+find_addr_in_list(
+ sockaddr_u *addr
+ )
+{
+ remaddr_t *entry;
+
+ DPRINTF(4, ("Searching for addr %s in list of addresses - ",
+ stoa(addr)));
+
+ for (entry = remoteaddr_list;
+ entry != NULL;
+ entry = entry->link)
+ if (SOCK_EQ(&entry->addr, addr)) {
+ DPRINTF(4, ("FOUND\n"));
+ return entry->ep;
+ }
+
+ DPRINTF(4, ("NOT FOUND\n"));
+ return NULL;
+}
+
+
+/*
+ * Find the given address with the all given flags set in the list
+ */
+static endpt *
+find_flagged_addr_in_list(
+ sockaddr_u * addr,
+ u_int32 flags
+ )
+{
+ remaddr_t *entry;
+
+ DPRINTF(4, ("Finding addr %s with flags %d in list: ",
+ stoa(addr), flags));
+
+ for (entry = remoteaddr_list;
+ entry != NULL;
+ entry = entry->link)
+
+ if (SOCK_EQ(&entry->addr, addr)
+ && (entry->ep->flags & flags) == flags) {
+
+ DPRINTF(4, ("FOUND\n"));
+ return entry->ep;
+ }
+
+ DPRINTF(4, ("NOT FOUND\n"));
+ return NULL;
+}
+
+
+const char *
+localaddrtoa(
+ endpt *la
+ )
+{
+ return (NULL == la)
+ ? "<null>"
+ : stoa(&la->sin);
+}
+
+
+#ifdef HAS_ROUTING_SOCKET
+# ifndef UPDATE_GRACE
+# define UPDATE_GRACE 2 /* wait UPDATE_GRACE seconds before scanning */
+# endif
+
+static void
+process_routing_msgs(struct asyncio_reader *reader)
+{
+ char buffer[5120];
+ int cnt, msg_type;
+#ifdef HAVE_RTNETLINK
+ struct nlmsghdr *nh;
+#else
+ struct rt_msghdr rtm;
+ char *p;
+#endif
+
+ if (disable_dynamic_updates) {
+ /*
+ * discard ourselves if we are not needed any more
+ * usually happens when running unprivileged
+ */
+ remove_asyncio_reader(reader);
+ delete_asyncio_reader(reader);
+ return;
+ }
+
+ cnt = read(reader->fd, buffer, sizeof(buffer));
+
+ if (cnt < 0) {
+ msyslog(LOG_ERR,
+ "i/o error on routing socket %m - disabling");
+ remove_asyncio_reader(reader);
+ delete_asyncio_reader(reader);
+ return;
+ }
+
+ /*
+ * process routing message
+ */
+#ifdef HAVE_RTNETLINK
+ for (nh = (struct nlmsghdr *)buffer;
+ NLMSG_OK(nh, cnt);
+ nh = NLMSG_NEXT(nh, cnt)) {
+ msg_type = nh->nlmsg_type;
+#else
+ for (p = buffer;
+ (p + sizeof(struct rt_msghdr)) <= (buffer + cnt);
+ p += rtm.rtm_msglen) {
+ memcpy(&rtm, p, sizeof(rtm));
+ if (rtm.rtm_version != RTM_VERSION) {
+ msyslog(LOG_ERR,
+ "version mismatch (got %d - expected %d) on routing socket - disabling",
+ rtm.rtm_version, RTM_VERSION);
+
+ remove_asyncio_reader(reader);
+ delete_asyncio_reader(reader);
+ return;
+ }
+ msg_type = rtm.rtm_type;
+#endif
+ switch (msg_type) {
+#ifdef RTM_NEWADDR
+ case RTM_NEWADDR:
+#endif
+#ifdef RTM_DELADDR
+ case RTM_DELADDR:
+#endif
+#ifdef RTM_ADD
+ case RTM_ADD:
+#endif
+#ifdef RTM_DELETE
+ case RTM_DELETE:
+#endif
+#ifdef RTM_REDIRECT
+ case RTM_REDIRECT:
+#endif
+#ifdef RTM_CHANGE
+ case RTM_CHANGE:
+#endif
+#ifdef RTM_LOSING
+ case RTM_LOSING:
+#endif
+#ifdef RTM_IFINFO
+ case RTM_IFINFO:
+#endif
+#ifdef RTM_IFANNOUNCE
+ case RTM_IFANNOUNCE:
+#endif
+#ifdef RTM_NEWLINK
+ case RTM_NEWLINK:
+#endif
+#ifdef RTM_DELLINK
+ case RTM_DELLINK:
+#endif
+#ifdef RTM_NEWROUTE
+ case RTM_NEWROUTE:
+#endif
+#ifdef RTM_DELROUTE
+ case RTM_DELROUTE:
+#endif
+ /*
+ * we are keen on new and deleted addresses and
+ * if an interface goes up and down or routing
+ * changes
+ */
+ DPRINTF(3, ("routing message op = %d: scheduling interface update\n",
+ msg_type));
+ timer_interfacetimeout(current_time + UPDATE_GRACE);
+ break;
+#ifdef HAVE_RTNETLINK
+ case NLMSG_DONE:
+ /* end of multipart message */
+ return;
+#endif
+ default:
+ /*
+ * the rest doesn't bother us.
+ */
+ DPRINTF(4, ("routing message op = %d: ignored\n",
+ msg_type));
+ break;
+ }
+ }
+}
+
+/*
+ * set up routing notifications
+ */
+static void
+init_async_notifications()
+{
+ struct asyncio_reader *reader;
+#ifdef HAVE_RTNETLINK
+ int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ struct sockaddr_nl sa;
+#else
+ int fd = socket(PF_ROUTE, SOCK_RAW, 0);
+#endif
+ if (fd < 0) {
+ msyslog(LOG_ERR,
+ "unable to open routing socket (%m) - using polled interface update");
+ return;
+ }
+
+ fd = move_fd(fd);
+#ifdef HAVE_RTNETLINK
+ ZERO(sa);
+ sa.nl_family = PF_NETLINK;
+ sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR
+ | RTMGRP_IPV6_IFADDR | RTMGRP_IPV4_ROUTE
+ | RTMGRP_IPV4_MROUTE | RTMGRP_IPV6_ROUTE
+ | RTMGRP_IPV6_MROUTE;
+ if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
+ msyslog(LOG_ERR,
+ "bind failed on routing socket (%m) - using polled interface update");
+ return;
+ }
+#endif
+ make_socket_nonblocking(fd);
+#if defined(HAVE_SIGNALED_IO)
+ init_socket_sig(fd);
+#endif /* HAVE_SIGNALED_IO */
+
+ reader = new_asyncio_reader();
+
+ reader->fd = fd;
+ reader->receiver = process_routing_msgs;
+
+ add_asyncio_reader(reader, FD_TYPE_SOCKET);
+ msyslog(LOG_INFO,
+ "Listening on routing socket on fd #%d for interface updates",
+ fd);
+}
+#else
+/* HAS_ROUTING_SOCKET not defined */
+static void
+init_async_notifications(void)
+{
+}
+#endif
+
diff --git a/ntpd/ntp_keyword.h b/ntpd/ntp_keyword.h
new file mode 100644
index 0000000..8b82ec0
--- /dev/null
+++ b/ntpd/ntp_keyword.h
@@ -0,0 +1,1032 @@
+/*
+ * ntp_keyword.h
+ *
+ * NOTE: edit this file with caution, it is generated by keyword-gen.c
+ * Generated 2013-08-04 04:56:14 UTC diff_ignore_line
+ *
+ */
+#include "ntp_scanner.h"
+#include "ntp_parser.h"
+
+#define LOWEST_KEYWORD_ID 258
+
+const char * const keyword_text[186] = {
+ /* 0 258 T_Abbrev */ "abbrev",
+ /* 1 259 T_Age */ "age",
+ /* 2 260 T_All */ "all",
+ /* 3 261 T_Allan */ "allan",
+ /* 4 262 T_Allpeers */ "allpeers",
+ /* 5 263 T_Auth */ "auth",
+ /* 6 264 T_Autokey */ "autokey",
+ /* 7 265 T_Automax */ "automax",
+ /* 8 266 T_Average */ "average",
+ /* 9 267 T_Bclient */ "bclient",
+ /* 10 268 T_Beacon */ "beacon",
+ /* 11 269 T_Broadcast */ "broadcast",
+ /* 12 270 T_Broadcastclient */ "broadcastclient",
+ /* 13 271 T_Broadcastdelay */ "broadcastdelay",
+ /* 14 272 T_Burst */ "burst",
+ /* 15 273 T_Calibrate */ "calibrate",
+ /* 16 274 T_Ceiling */ "ceiling",
+ /* 17 275 T_Clockstats */ "clockstats",
+ /* 18 276 T_Cohort */ "cohort",
+ /* 19 277 T_ControlKey */ "controlkey",
+ /* 20 278 T_Crypto */ "crypto",
+ /* 21 279 T_Cryptostats */ "cryptostats",
+ /* 22 280 T_Ctl */ "ctl",
+ /* 23 281 T_Day */ "day",
+ /* 24 282 T_Default */ "default",
+ /* 25 283 T_Digest */ "digest",
+ /* 26 284 T_Disable */ "disable",
+ /* 27 285 T_Discard */ "discard",
+ /* 28 286 T_Dispersion */ "dispersion",
+ /* 29 287 T_Double */ NULL,
+ /* 30 288 T_Driftfile */ "driftfile",
+ /* 31 289 T_Drop */ "drop",
+ /* 32 290 T_Ellipsis */ "...",
+ /* 33 291 T_Enable */ "enable",
+ /* 34 292 T_End */ "end",
+ /* 35 293 T_False */ NULL,
+ /* 36 294 T_File */ "file",
+ /* 37 295 T_Filegen */ "filegen",
+ /* 38 296 T_Filenum */ "filenum",
+ /* 39 297 T_Flag1 */ "flag1",
+ /* 40 298 T_Flag2 */ "flag2",
+ /* 41 299 T_Flag3 */ "flag3",
+ /* 42 300 T_Flag4 */ "flag4",
+ /* 43 301 T_Flake */ "flake",
+ /* 44 302 T_Floor */ "floor",
+ /* 45 303 T_Freq */ "freq",
+ /* 46 304 T_Fudge */ "fudge",
+ /* 47 305 T_Host */ "host",
+ /* 48 306 T_Huffpuff */ "huffpuff",
+ /* 49 307 T_Iburst */ "iburst",
+ /* 50 308 T_Ident */ "ident",
+ /* 51 309 T_Ignore */ "ignore",
+ /* 52 310 T_Incalloc */ "incalloc",
+ /* 53 311 T_Incmem */ "incmem",
+ /* 54 312 T_Initalloc */ "initalloc",
+ /* 55 313 T_Initmem */ "initmem",
+ /* 56 314 T_Includefile */ "includefile",
+ /* 57 315 T_Integer */ NULL,
+ /* 58 316 T_Interface */ "interface",
+ /* 59 317 T_Intrange */ NULL,
+ /* 60 318 T_Io */ "io",
+ /* 61 319 T_Ipv4 */ "ipv4",
+ /* 62 320 T_Ipv4_flag */ "-4",
+ /* 63 321 T_Ipv6 */ "ipv6",
+ /* 64 322 T_Ipv6_flag */ "-6",
+ /* 65 323 T_Kernel */ "kernel",
+ /* 66 324 T_Key */ "key",
+ /* 67 325 T_Keys */ "keys",
+ /* 68 326 T_Keysdir */ "keysdir",
+ /* 69 327 T_Kod */ "kod",
+ /* 70 328 T_Mssntp */ "mssntp",
+ /* 71 329 T_Leapfile */ "leapfile",
+ /* 72 330 T_Limited */ "limited",
+ /* 73 331 T_Link */ "link",
+ /* 74 332 T_Listen */ "listen",
+ /* 75 333 T_Logconfig */ "logconfig",
+ /* 76 334 T_Logfile */ "logfile",
+ /* 77 335 T_Loopstats */ "loopstats",
+ /* 78 336 T_Lowpriotrap */ "lowpriotrap",
+ /* 79 337 T_Manycastclient */ "manycastclient",
+ /* 80 338 T_Manycastserver */ "manycastserver",
+ /* 81 339 T_Mask */ "mask",
+ /* 82 340 T_Maxage */ "maxage",
+ /* 83 341 T_Maxclock */ "maxclock",
+ /* 84 342 T_Maxdepth */ "maxdepth",
+ /* 85 343 T_Maxdist */ "maxdist",
+ /* 86 344 T_Maxmem */ "maxmem",
+ /* 87 345 T_Maxpoll */ "maxpoll",
+ /* 88 346 T_Mem */ "mem",
+ /* 89 347 T_Memlock */ "memlock",
+ /* 90 348 T_Minclock */ "minclock",
+ /* 91 349 T_Mindepth */ "mindepth",
+ /* 92 350 T_Mindist */ "mindist",
+ /* 93 351 T_Minimum */ "minimum",
+ /* 94 352 T_Minpoll */ "minpoll",
+ /* 95 353 T_Minsane */ "minsane",
+ /* 96 354 T_Mode */ "mode",
+ /* 97 355 T_Mode7 */ "mode7",
+ /* 98 356 T_Monitor */ "monitor",
+ /* 99 357 T_Month */ "month",
+ /* 100 358 T_Mru */ "mru",
+ /* 101 359 T_Multicastclient */ "multicastclient",
+ /* 102 360 T_Nic */ "nic",
+ /* 103 361 T_Nolink */ "nolink",
+ /* 104 362 T_Nomodify */ "nomodify",
+ /* 105 363 T_Nomrulist */ "nomrulist",
+ /* 106 364 T_None */ "none",
+ /* 107 365 T_Nonvolatile */ "nonvolatile",
+ /* 108 366 T_Nopeer */ "nopeer",
+ /* 109 367 T_Noquery */ "noquery",
+ /* 110 368 T_Noselect */ "noselect",
+ /* 111 369 T_Noserve */ "noserve",
+ /* 112 370 T_Notrap */ "notrap",
+ /* 113 371 T_Notrust */ "notrust",
+ /* 114 372 T_Ntp */ "ntp",
+ /* 115 373 T_Ntpport */ "ntpport",
+ /* 116 374 T_NtpSignDsocket */ "ntpsigndsocket",
+ /* 117 375 T_Orphan */ "orphan",
+ /* 118 376 T_Orphanwait */ "orphanwait",
+ /* 119 377 T_Panic */ "panic",
+ /* 120 378 T_Peer */ "peer",
+ /* 121 379 T_Peerstats */ "peerstats",
+ /* 122 380 T_Phone */ "phone",
+ /* 123 381 T_Pid */ "pid",
+ /* 124 382 T_Pidfile */ "pidfile",
+ /* 125 383 T_Pool */ "pool",
+ /* 126 384 T_Port */ "port",
+ /* 127 385 T_Preempt */ "preempt",
+ /* 128 386 T_Prefer */ "prefer",
+ /* 129 387 T_Protostats */ "protostats",
+ /* 130 388 T_Pw */ "pw",
+ /* 131 389 T_Randfile */ "randfile",
+ /* 132 390 T_Rawstats */ "rawstats",
+ /* 133 391 T_Refid */ "refid",
+ /* 134 392 T_Requestkey */ "requestkey",
+ /* 135 393 T_Reset */ "reset",
+ /* 136 394 T_Restrict */ "restrict",
+ /* 137 395 T_Revoke */ "revoke",
+ /* 138 396 T_Rlimit */ "rlimit",
+ /* 139 397 T_Saveconfigdir */ "saveconfigdir",
+ /* 140 398 T_Server */ "server",
+ /* 141 399 T_Setvar */ "setvar",
+ /* 142 400 T_Source */ "source",
+ /* 143 401 T_Stacksize */ "stacksize",
+ /* 144 402 T_Statistics */ "statistics",
+ /* 145 403 T_Stats */ "stats",
+ /* 146 404 T_Statsdir */ "statsdir",
+ /* 147 405 T_Step */ "step",
+ /* 148 406 T_Stepout */ "stepout",
+ /* 149 407 T_Stratum */ "stratum",
+ /* 150 408 T_String */ NULL,
+ /* 151 409 T_Sys */ "sys",
+ /* 152 410 T_Sysstats */ "sysstats",
+ /* 153 411 T_Tick */ "tick",
+ /* 154 412 T_Time1 */ "time1",
+ /* 155 413 T_Time2 */ "time2",
+ /* 156 414 T_Timer */ "timer",
+ /* 157 415 T_Timingstats */ "timingstats",
+ /* 158 416 T_Tinker */ "tinker",
+ /* 159 417 T_Tos */ "tos",
+ /* 160 418 T_Trap */ "trap",
+ /* 161 419 T_True */ "true",
+ /* 162 420 T_Trustedkey */ "trustedkey",
+ /* 163 421 T_Ttl */ "ttl",
+ /* 164 422 T_Type */ "type",
+ /* 165 423 T_U_int */ NULL,
+ /* 166 424 T_Unconfig */ "unconfig",
+ /* 167 425 T_Unpeer */ "unpeer",
+ /* 168 426 T_Version */ "version",
+ /* 169 427 T_WanderThreshold */ NULL,
+ /* 170 428 T_Week */ "week",
+ /* 171 429 T_Wildcard */ "wildcard",
+ /* 172 430 T_Xleave */ "xleave",
+ /* 173 431 T_Year */ "year",
+ /* 174 432 T_Flag */ NULL,
+ /* 175 433 T_EOC */ NULL,
+ /* 176 434 T_Simulate */ "simulate",
+ /* 177 435 T_Beep_Delay */ "beep_delay",
+ /* 178 436 T_Sim_Duration */ "simulation_duration",
+ /* 179 437 T_Server_Offset */ "server_offset",
+ /* 180 438 T_Duration */ "duration",
+ /* 181 439 T_Freq_Offset */ "freq_offset",
+ /* 182 440 T_Wander */ "wander",
+ /* 183 441 T_Jitter */ "jitter",
+ /* 184 442 T_Prop_Delay */ "prop_delay",
+ /* 185 443 T_Proc_Delay */ "proc_delay"
+};
+
+#define SCANNER_INIT_S 822
+
+const scan_state sst[825] = {
+/*SS_T( ch, f-by, match, other ), */
+ 0, /* 0 */
+ S_ST( '-', 3, 322, 0 ), /* 1 */
+ S_ST( '.', 3, 3, 1 ), /* 2 */
+ S_ST( '.', 3, 290, 0 ), /* 3 . */
+ S_ST( 'a', 3, 23, 2 ), /* 4 */
+ S_ST( 'b', 3, 6, 0 ), /* 5 a */
+ S_ST( 'b', 3, 7, 0 ), /* 6 ab */
+ S_ST( 'r', 3, 8, 0 ), /* 7 abb */
+ S_ST( 'e', 3, 258, 0 ), /* 8 abbr */
+ S_ST( 'g', 3, 259, 5 ), /* 9 a */
+ S_ST( 'l', 3, 260, 9 ), /* 10 a */
+ S_ST( 'a', 3, 261, 0 ), /* 11 all */
+ S_ST( 'p', 3, 13, 11 ), /* 12 all */
+ S_ST( 'e', 3, 14, 0 ), /* 13 allp */
+ S_ST( 'e', 3, 15, 0 ), /* 14 allpe */
+ S_ST( 'r', 3, 262, 0 ), /* 15 allpee */
+ S_ST( 'u', 3, 17, 10 ), /* 16 a */
+ S_ST( 't', 3, 18, 0 ), /* 17 au */
+ S_ST( 'o', 3, 21, 263 ), /* 18 aut */
+ S_ST( 'k', 3, 20, 0 ), /* 19 auto */
+ S_ST( 'e', 3, 264, 0 ), /* 20 autok */
+ S_ST( 'm', 3, 22, 19 ), /* 21 auto */
+ S_ST( 'a', 3, 265, 0 ), /* 22 autom */
+ S_ST( 'v', 3, 24, 16 ), /* 23 a */
+ S_ST( 'e', 3, 25, 0 ), /* 24 av */
+ S_ST( 'r', 3, 26, 0 ), /* 25 ave */
+ S_ST( 'a', 3, 27, 0 ), /* 26 aver */
+ S_ST( 'g', 3, 266, 0 ), /* 27 avera */
+ S_ST( 'b', 3, 61, 4 ), /* 28 */
+ S_ST( 'c', 3, 30, 0 ), /* 29 b */
+ S_ST( 'l', 3, 31, 0 ), /* 30 bc */
+ S_ST( 'i', 3, 32, 0 ), /* 31 bcl */
+ S_ST( 'e', 3, 33, 0 ), /* 32 bcli */
+ S_ST( 'n', 3, 267, 0 ), /* 33 bclie */
+ S_ST( 'e', 3, 38, 29 ), /* 34 b */
+ S_ST( 'a', 3, 36, 0 ), /* 35 be */
+ S_ST( 'c', 3, 37, 0 ), /* 36 bea */
+ S_ST( 'o', 3, 268, 0 ), /* 37 beac */
+ S_ST( 'e', 3, 39, 35 ), /* 38 be */
+ S_ST( 'p', 3, 40, 0 ), /* 39 bee */
+ S_ST( '_', 3, 41, 0 ), /* 40 beep */
+ S_ST( 'd', 3, 42, 0 ), /* 41 beep_ */
+ S_ST( 'e', 3, 43, 0 ), /* 42 beep_d */
+ S_ST( 'l', 3, 44, 0 ), /* 43 beep_de */
+ S_ST( 'a', 3, 435, 0 ), /* 44 beep_del */
+ S_ST( 'r', 3, 46, 34 ), /* 45 b */
+ S_ST( 'o', 3, 47, 0 ), /* 46 br */
+ S_ST( 'a', 3, 48, 0 ), /* 47 bro */
+ S_ST( 'd', 3, 49, 0 ), /* 48 broa */
+ S_ST( 'c', 3, 50, 0 ), /* 49 broad */
+ S_ST( 'a', 3, 51, 0 ), /* 50 broadc */
+ S_ST( 's', 3, 269, 0 ), /* 51 broadca */
+ S_ST( 'c', 3, 53, 0 ), /* 52 broadcast */
+ S_ST( 'l', 3, 54, 0 ), /* 53 broadcastc */
+ S_ST( 'i', 3, 55, 0 ), /* 54 broadcastcl */
+ S_ST( 'e', 3, 56, 0 ), /* 55 broadcastcli */
+ S_ST( 'n', 3, 270, 0 ), /* 56 broadcastclie */
+ S_ST( 'd', 3, 58, 52 ), /* 57 broadcast */
+ S_ST( 'e', 3, 59, 0 ), /* 58 broadcastd */
+ S_ST( 'l', 3, 60, 0 ), /* 59 broadcastde */
+ S_ST( 'a', 3, 271, 0 ), /* 60 broadcastdel */
+ S_ST( 'u', 3, 62, 45 ), /* 61 b */
+ S_ST( 'r', 3, 63, 0 ), /* 62 bu */
+ S_ST( 's', 3, 272, 0 ), /* 63 bur */
+ S_ST( 'c', 3, 104, 28 ), /* 64 */
+ S_ST( 'a', 3, 66, 0 ), /* 65 c */
+ S_ST( 'l', 3, 67, 0 ), /* 66 ca */
+ S_ST( 'i', 3, 68, 0 ), /* 67 cal */
+ S_ST( 'b', 3, 69, 0 ), /* 68 cali */
+ S_ST( 'r', 3, 70, 0 ), /* 69 calib */
+ S_ST( 'a', 3, 71, 0 ), /* 70 calibr */
+ S_ST( 't', 3, 273, 0 ), /* 71 calibra */
+ S_ST( 'e', 3, 73, 65 ), /* 72 c */
+ S_ST( 'i', 3, 74, 0 ), /* 73 ce */
+ S_ST( 'l', 3, 75, 0 ), /* 74 cei */
+ S_ST( 'i', 3, 76, 0 ), /* 75 ceil */
+ S_ST( 'n', 3, 274, 0 ), /* 76 ceili */
+ S_ST( 'l', 3, 78, 72 ), /* 77 c */
+ S_ST( 'o', 3, 79, 0 ), /* 78 cl */
+ S_ST( 'c', 3, 80, 0 ), /* 79 clo */
+ S_ST( 'k', 3, 81, 0 ), /* 80 cloc */
+ S_ST( 's', 3, 82, 0 ), /* 81 clock */
+ S_ST( 't', 3, 83, 0 ), /* 82 clocks */
+ S_ST( 'a', 3, 84, 0 ), /* 83 clockst */
+ S_ST( 't', 3, 275, 0 ), /* 84 clocksta */
+ S_ST( 'o', 3, 89, 77 ), /* 85 c */
+ S_ST( 'h', 3, 87, 0 ), /* 86 co */
+ S_ST( 'o', 3, 88, 0 ), /* 87 coh */
+ S_ST( 'r', 3, 276, 0 ), /* 88 coho */
+ S_ST( 'n', 3, 90, 86 ), /* 89 co */
+ S_ST( 't', 3, 91, 0 ), /* 90 con */
+ S_ST( 'r', 3, 92, 0 ), /* 91 cont */
+ S_ST( 'o', 3, 93, 0 ), /* 92 contr */
+ S_ST( 'l', 3, 94, 0 ), /* 93 contro */
+ S_ST( 'k', 3, 95, 0 ), /* 94 control */
+ S_ST( 'e', 3, 277, 0 ), /* 95 controlk */
+ S_ST( 'r', 3, 97, 85 ), /* 96 c */
+ S_ST( 'y', 3, 98, 0 ), /* 97 cr */
+ S_ST( 'p', 3, 99, 0 ), /* 98 cry */
+ S_ST( 't', 3, 278, 0 ), /* 99 cryp */
+ S_ST( 's', 3, 101, 0 ), /* 100 crypto */
+ S_ST( 't', 3, 102, 0 ), /* 101 cryptos */
+ S_ST( 'a', 3, 103, 0 ), /* 102 cryptost */
+ S_ST( 't', 3, 279, 0 ), /* 103 cryptosta */
+ S_ST( 't', 3, 280, 96 ), /* 104 c */
+ S_ST( 'd', 3, 137, 64 ), /* 105 */
+ S_ST( 'a', 3, 281, 0 ), /* 106 d */
+ S_ST( 'e', 3, 108, 106 ), /* 107 d */
+ S_ST( 'f', 3, 109, 0 ), /* 108 de */
+ S_ST( 'a', 3, 110, 0 ), /* 109 def */
+ S_ST( 'u', 3, 111, 0 ), /* 110 defa */
+ S_ST( 'l', 3, 282, 0 ), /* 111 defau */
+ S_ST( 'i', 3, 116, 107 ), /* 112 d */
+ S_ST( 'g', 3, 114, 0 ), /* 113 di */
+ S_ST( 'e', 3, 115, 0 ), /* 114 dig */
+ S_ST( 's', 3, 283, 0 ), /* 115 dige */
+ S_ST( 's', 3, 123, 113 ), /* 116 di */
+ S_ST( 'a', 3, 118, 0 ), /* 117 dis */
+ S_ST( 'b', 3, 119, 0 ), /* 118 disa */
+ S_ST( 'l', 3, 284, 0 ), /* 119 disab */
+ S_ST( 'c', 3, 121, 117 ), /* 120 dis */
+ S_ST( 'a', 3, 122, 0 ), /* 121 disc */
+ S_ST( 'r', 3, 285, 0 ), /* 122 disca */
+ S_ST( 'p', 3, 124, 120 ), /* 123 dis */
+ S_ST( 'e', 3, 125, 0 ), /* 124 disp */
+ S_ST( 'r', 3, 126, 0 ), /* 125 dispe */
+ S_ST( 's', 3, 127, 0 ), /* 126 disper */
+ S_ST( 'i', 3, 128, 0 ), /* 127 dispers */
+ S_ST( 'o', 3, 286, 0 ), /* 128 dispersi */
+ S_ST( 'r', 3, 136, 112 ), /* 129 d */
+ S_ST( 'i', 3, 131, 0 ), /* 130 dr */
+ S_ST( 'f', 3, 132, 0 ), /* 131 dri */
+ S_ST( 't', 3, 133, 0 ), /* 132 drif */
+ S_ST( 'f', 3, 134, 0 ), /* 133 drift */
+ S_ST( 'i', 3, 135, 0 ), /* 134 driftf */
+ S_ST( 'l', 3, 288, 0 ), /* 135 driftfi */
+ S_ST( 'o', 3, 289, 130 ), /* 136 dr */
+ S_ST( 'u', 3, 138, 129 ), /* 137 d */
+ S_ST( 'r', 3, 139, 0 ), /* 138 du */
+ S_ST( 'a', 3, 140, 0 ), /* 139 dur */
+ S_ST( 't', 3, 141, 0 ), /* 140 dura */
+ S_ST( 'i', 3, 142, 0 ), /* 141 durat */
+ S_ST( 'o', 3, 438, 0 ), /* 142 durati */
+ S_ST( 'e', 3, 144, 105 ), /* 143 */
+ S_ST( 'n', 3, 292, 0 ), /* 144 e */
+ S_ST( 'a', 3, 146, 0 ), /* 145 en */
+ S_ST( 'b', 3, 147, 0 ), /* 146 ena */
+ S_ST( 'l', 3, 291, 0 ), /* 147 enab */
+ S_ST( 'f', 3, 169, 143 ), /* 148 */
+ S_ST( 'i', 3, 150, 0 ), /* 149 f */
+ S_ST( 'l', 3, 294, 0 ), /* 150 fi */
+ S_ST( 'g', 3, 152, 0 ), /* 151 file */
+ S_ST( 'e', 3, 295, 0 ), /* 152 fileg */
+ S_ST( 'n', 3, 154, 151 ), /* 153 file */
+ S_ST( 'u', 3, 296, 0 ), /* 154 filen */
+ S_ST( 'l', 3, 159, 149 ), /* 155 f */
+ S_ST( 'a', 3, 158, 0 ), /* 156 fl */
+ S_ST( 'g', 3, 300, 0 ), /* 157 fla */
+ S_ST( 'k', 3, 301, 157 ), /* 158 fla */
+ S_ST( 'o', 3, 160, 156 ), /* 159 fl */
+ S_ST( 'o', 3, 302, 0 ), /* 160 flo */
+ S_ST( 'r', 3, 162, 155 ), /* 161 f */
+ S_ST( 'e', 3, 303, 0 ), /* 162 fr */
+ S_ST( '_', 3, 164, 0 ), /* 163 freq */
+ S_ST( 'o', 3, 165, 0 ), /* 164 freq_ */
+ S_ST( 'f', 3, 166, 0 ), /* 165 freq_o */
+ S_ST( 'f', 3, 167, 0 ), /* 166 freq_of */
+ S_ST( 's', 3, 168, 0 ), /* 167 freq_off */
+ S_ST( 'e', 3, 439, 0 ), /* 168 freq_offs */
+ S_ST( 'u', 3, 170, 161 ), /* 169 f */
+ S_ST( 'd', 3, 171, 0 ), /* 170 fu */
+ S_ST( 'g', 3, 304, 0 ), /* 171 fud */
+ S_ST( 'h', 3, 175, 148 ), /* 172 */
+ S_ST( 'o', 3, 174, 0 ), /* 173 h */
+ S_ST( 's', 3, 305, 0 ), /* 174 ho */
+ S_ST( 'u', 3, 176, 173 ), /* 175 h */
+ S_ST( 'f', 3, 177, 0 ), /* 176 hu */
+ S_ST( 'f', 3, 178, 0 ), /* 177 huf */
+ S_ST( 'p', 3, 179, 0 ), /* 178 huff */
+ S_ST( 'u', 3, 180, 0 ), /* 179 huffp */
+ S_ST( 'f', 3, 306, 0 ), /* 180 huffpu */
+ S_ST( 'i', 3, 222, 172 ), /* 181 */
+ S_ST( 'b', 3, 183, 0 ), /* 182 i */
+ S_ST( 'u', 3, 184, 0 ), /* 183 ib */
+ S_ST( 'r', 3, 185, 0 ), /* 184 ibu */
+ S_ST( 's', 3, 307, 0 ), /* 185 ibur */
+ S_ST( 'd', 3, 187, 182 ), /* 186 i */
+ S_ST( 'e', 3, 188, 0 ), /* 187 id */
+ S_ST( 'n', 3, 308, 0 ), /* 188 ide */
+ S_ST( 'g', 3, 190, 186 ), /* 189 i */
+ S_ST( 'n', 3, 191, 0 ), /* 190 ig */
+ S_ST( 'o', 3, 192, 0 ), /* 191 ign */
+ S_ST( 'r', 3, 309, 0 ), /* 192 igno */
+ S_ST( 'n', 3, 216, 189 ), /* 193 i */
+ S_ST( 'c', 3, 206, 0 ), /* 194 in */
+ S_ST( 'a', 3, 196, 0 ), /* 195 inc */
+ S_ST( 'l', 3, 197, 0 ), /* 196 inca */
+ S_ST( 'l', 3, 198, 0 ), /* 197 incal */
+ S_ST( 'o', 3, 310, 0 ), /* 198 incall */
+ S_ST( 'l', 3, 200, 195 ), /* 199 inc */
+ S_ST( 'u', 3, 201, 0 ), /* 200 incl */
+ S_ST( 'd', 3, 202, 0 ), /* 201 inclu */
+ S_ST( 'e', 3, 203, 0 ), /* 202 includ */
+ S_ST( 'f', 3, 204, 0 ), /* 203 include */
+ S_ST( 'i', 3, 205, 0 ), /* 204 includef */
+ S_ST( 'l', 3, 314, 0 ), /* 205 includefi */
+ S_ST( 'm', 3, 207, 199 ), /* 206 inc */
+ S_ST( 'e', 3, 311, 0 ), /* 207 incm */
+ S_ST( 'i', 3, 209, 194 ), /* 208 in */
+ S_ST( 't', 3, 214, 0 ), /* 209 ini */
+ S_ST( 'a', 3, 211, 0 ), /* 210 init */
+ S_ST( 'l', 3, 212, 0 ), /* 211 inita */
+ S_ST( 'l', 3, 213, 0 ), /* 212 inital */
+ S_ST( 'o', 3, 312, 0 ), /* 213 initall */
+ S_ST( 'm', 3, 215, 210 ), /* 214 init */
+ S_ST( 'e', 3, 313, 0 ), /* 215 initm */
+ S_ST( 't', 3, 217, 208 ), /* 216 in */
+ S_ST( 'e', 3, 218, 0 ), /* 217 int */
+ S_ST( 'r', 3, 219, 0 ), /* 218 inte */
+ S_ST( 'f', 3, 220, 0 ), /* 219 inter */
+ S_ST( 'a', 3, 221, 0 ), /* 220 interf */
+ S_ST( 'c', 3, 316, 0 ), /* 221 interfa */
+ S_ST( 'p', 3, 223, 318 ), /* 222 i */
+ S_ST( 'v', 3, 321, 0 ), /* 223 ip */
+ S_ST( 'j', 3, 225, 181 ), /* 224 */
+ S_ST( 'i', 3, 226, 0 ), /* 225 j */
+ S_ST( 't', 3, 227, 0 ), /* 226 ji */
+ S_ST( 't', 3, 228, 0 ), /* 227 jit */
+ S_ST( 'e', 3, 441, 0 ), /* 228 jitt */
+ S_ST( 'k', 3, 236, 224 ), /* 229 */
+ S_ST( 'e', 3, 324, 0 ), /* 230 k */
+ S_ST( 'r', 3, 232, 0 ), /* 231 ke */
+ S_ST( 'n', 3, 233, 0 ), /* 232 ker */
+ S_ST( 'e', 3, 323, 0 ), /* 233 kern */
+ S_ST( 'd', 3, 235, 0 ), /* 234 keys */
+ S_ST( 'i', 3, 326, 0 ), /* 235 keysd */
+ S_ST( 'o', 3, 327, 230 ), /* 236 k */
+ S_ST( 'l', 3, 253, 229 ), /* 237 */
+ S_ST( 'e', 3, 239, 0 ), /* 238 l */
+ S_ST( 'a', 3, 240, 0 ), /* 239 le */
+ S_ST( 'p', 3, 241, 0 ), /* 240 lea */
+ S_ST( 'f', 3, 242, 0 ), /* 241 leap */
+ S_ST( 'i', 3, 243, 0 ), /* 242 leapf */
+ S_ST( 'l', 3, 329, 0 ), /* 243 leapfi */
+ S_ST( 'i', 3, 250, 238 ), /* 244 l */
+ S_ST( 'm', 3, 246, 0 ), /* 245 li */
+ S_ST( 'i', 3, 247, 0 ), /* 246 lim */
+ S_ST( 't', 3, 248, 0 ), /* 247 limi */
+ S_ST( 'e', 3, 330, 0 ), /* 248 limit */
+ S_ST( 'n', 3, 331, 245 ), /* 249 li */
+ S_ST( 's', 3, 251, 249 ), /* 250 li */
+ S_ST( 't', 3, 252, 0 ), /* 251 lis */
+ S_ST( 'e', 3, 332, 0 ), /* 252 list */
+ S_ST( 'o', 3, 446, 244 ), /* 253 l */
+ S_ST( 'g', 3, 315, 0 ), /* 254 lo */
+ S_ST( 'c', 3, 256, 0 ), /* 255 log */
+ S_ST( 'o', 3, 257, 0 ), /* 256 logc */
+ S_ST( 'n', 3, 287, 0 ), /* 257 logco */
+ S_ST( 'v', 1, 0, 0 ), /* 258 T_Abbrev */
+ S_ST( 'e', 0, 0, 0 ), /* 259 T_Age */
+ S_ST( 'l', 0, 12, 0 ), /* 260 T_All */
+ S_ST( 'n', 0, 0, 0 ), /* 261 T_Allan */
+ S_ST( 's', 0, 0, 0 ), /* 262 T_Allpeers */
+ S_ST( 'h', 0, 0, 0 ), /* 263 T_Auth */
+ S_ST( 'y', 0, 0, 0 ), /* 264 T_Autokey */
+ S_ST( 'x', 0, 0, 0 ), /* 265 T_Automax */
+ S_ST( 'e', 0, 0, 0 ), /* 266 T_Average */
+ S_ST( 't', 0, 0, 0 ), /* 267 T_Bclient */
+ S_ST( 'n', 0, 0, 0 ), /* 268 T_Beacon */
+ S_ST( 't', 1, 57, 0 ), /* 269 T_Broadcast */
+ S_ST( 't', 0, 0, 0 ), /* 270 T_Broadcastclient */
+ S_ST( 'y', 0, 0, 0 ), /* 271 T_Broadcastdelay */
+ S_ST( 't', 0, 0, 0 ), /* 272 T_Burst */
+ S_ST( 'e', 0, 0, 0 ), /* 273 T_Calibrate */
+ S_ST( 'g', 0, 0, 0 ), /* 274 T_Ceiling */
+ S_ST( 's', 0, 0, 0 ), /* 275 T_Clockstats */
+ S_ST( 't', 0, 0, 0 ), /* 276 T_Cohort */
+ S_ST( 'y', 0, 0, 0 ), /* 277 T_ControlKey */
+ S_ST( 'o', 0, 100, 0 ), /* 278 T_Crypto */
+ S_ST( 's', 0, 0, 0 ), /* 279 T_Cryptostats */
+ S_ST( 'l', 0, 0, 0 ), /* 280 T_Ctl */
+ S_ST( 'y', 0, 0, 0 ), /* 281 T_Day */
+ S_ST( 't', 0, 0, 0 ), /* 282 T_Default */
+ S_ST( 't', 1, 0, 0 ), /* 283 T_Digest */
+ S_ST( 'e', 0, 0, 0 ), /* 284 T_Disable */
+ S_ST( 'd', 0, 0, 0 ), /* 285 T_Discard */
+ S_ST( 'n', 0, 0, 0 ), /* 286 T_Dispersion */
+ S_ST( 'f', 3, 293, 0 ), /* 287 logcon */
+ S_ST( 'e', 1, 0, 0 ), /* 288 T_Driftfile */
+ S_ST( 'p', 0, 0, 0 ), /* 289 T_Drop */
+ S_ST( '.', 0, 0, 0 ), /* 290 T_Ellipsis */
+ S_ST( 'e', 0, 0, 0 ), /* 291 T_Enable */
+ S_ST( 'd', 0, 0, 145 ), /* 292 T_End */
+ S_ST( 'i', 3, 333, 0 ), /* 293 logconf */
+ S_ST( 'e', 1, 153, 0 ), /* 294 T_File */
+ S_ST( 'n', 0, 0, 0 ), /* 295 T_Filegen */
+ S_ST( 'm', 0, 0, 0 ), /* 296 T_Filenum */
+ S_ST( '1', 0, 0, 0 ), /* 297 T_Flag1 */
+ S_ST( '2', 0, 0, 297 ), /* 298 T_Flag2 */
+ S_ST( '3', 0, 0, 298 ), /* 299 T_Flag3 */
+ S_ST( '4', 0, 0, 299 ), /* 300 T_Flag4 */
+ S_ST( 'e', 0, 0, 0 ), /* 301 T_Flake */
+ S_ST( 'r', 0, 0, 0 ), /* 302 T_Floor */
+ S_ST( 'q', 0, 163, 0 ), /* 303 T_Freq */
+ S_ST( 'e', 1, 0, 0 ), /* 304 T_Fudge */
+ S_ST( 't', 1, 0, 0 ), /* 305 T_Host */
+ S_ST( 'f', 0, 0, 0 ), /* 306 T_Huffpuff */
+ S_ST( 't', 0, 0, 0 ), /* 307 T_Iburst */
+ S_ST( 't', 1, 0, 0 ), /* 308 T_Ident */
+ S_ST( 'e', 0, 0, 0 ), /* 309 T_Ignore */
+ S_ST( 'c', 0, 0, 0 ), /* 310 T_Incalloc */
+ S_ST( 'm', 0, 0, 0 ), /* 311 T_Incmem */
+ S_ST( 'c', 0, 0, 0 ), /* 312 T_Initalloc */
+ S_ST( 'm', 0, 0, 0 ), /* 313 T_Initmem */
+ S_ST( 'e', 1, 0, 0 ), /* 314 T_Includefile */
+ S_ST( 'f', 3, 317, 255 ), /* 315 log */
+ S_ST( 'e', 0, 0, 0 ), /* 316 T_Interface */
+ S_ST( 'i', 3, 408, 0 ), /* 317 logf */
+ S_ST( 'o', 0, 0, 193 ), /* 318 T_Io */
+ S_ST( '4', 0, 0, 0 ), /* 319 T_Ipv4 */
+ S_ST( '4', 0, 0, 0 ), /* 320 T_Ipv4_flag */
+ S_ST( '6', 0, 0, 319 ), /* 321 T_Ipv6 */
+ S_ST( '6', 0, 0, 320 ), /* 322 T_Ipv6_flag */
+ S_ST( 'l', 0, 0, 0 ), /* 323 T_Kernel */
+ S_ST( 'y', 0, 325, 231 ), /* 324 T_Key */
+ S_ST( 's', 1, 234, 0 ), /* 325 T_Keys */
+ S_ST( 'r', 1, 0, 0 ), /* 326 T_Keysdir */
+ S_ST( 'd', 0, 0, 0 ), /* 327 T_Kod */
+ S_ST( 'p', 0, 0, 0 ), /* 328 T_Mssntp */
+ S_ST( 'e', 1, 0, 0 ), /* 329 T_Leapfile */
+ S_ST( 'd', 0, 0, 0 ), /* 330 T_Limited */
+ S_ST( 'k', 0, 0, 0 ), /* 331 T_Link */
+ S_ST( 'n', 0, 0, 0 ), /* 332 T_Listen */
+ S_ST( 'g', 2, 0, 0 ), /* 333 T_Logconfig */
+ S_ST( 'e', 1, 0, 0 ), /* 334 T_Logfile */
+ S_ST( 's', 0, 0, 0 ), /* 335 T_Loopstats */
+ S_ST( 'p', 0, 0, 0 ), /* 336 T_Lowpriotrap */
+ S_ST( 't', 1, 0, 0 ), /* 337 T_Manycastclient */
+ S_ST( 'r', 2, 0, 0 ), /* 338 T_Manycastserver */
+ S_ST( 'k', 0, 0, 0 ), /* 339 T_Mask */
+ S_ST( 'e', 0, 0, 0 ), /* 340 T_Maxage */
+ S_ST( 'k', 0, 0, 0 ), /* 341 T_Maxclock */
+ S_ST( 'h', 0, 0, 0 ), /* 342 T_Maxdepth */
+ S_ST( 't', 0, 0, 0 ), /* 343 T_Maxdist */
+ S_ST( 'm', 0, 0, 0 ), /* 344 T_Maxmem */
+ S_ST( 'l', 0, 0, 0 ), /* 345 T_Maxpoll */
+ S_ST( 'm', 0, 492, 0 ), /* 346 T_Mem */
+ S_ST( 'k', 0, 0, 0 ), /* 347 T_Memlock */
+ S_ST( 'k', 0, 0, 0 ), /* 348 T_Minclock */
+ S_ST( 'h', 0, 0, 0 ), /* 349 T_Mindepth */
+ S_ST( 't', 0, 0, 0 ), /* 350 T_Mindist */
+ S_ST( 'm', 0, 0, 0 ), /* 351 T_Minimum */
+ S_ST( 'l', 0, 0, 0 ), /* 352 T_Minpoll */
+ S_ST( 'e', 0, 0, 0 ), /* 353 T_Minsane */
+ S_ST( 'e', 0, 355, 0 ), /* 354 T_Mode */
+ S_ST( '7', 0, 0, 0 ), /* 355 T_Mode7 */
+ S_ST( 'r', 0, 0, 0 ), /* 356 T_Monitor */
+ S_ST( 'h', 0, 0, 0 ), /* 357 T_Month */
+ S_ST( 'u', 0, 0, 0 ), /* 358 T_Mru */
+ S_ST( 't', 2, 0, 0 ), /* 359 T_Multicastclient */
+ S_ST( 'c', 0, 0, 0 ), /* 360 T_Nic */
+ S_ST( 'k', 0, 0, 0 ), /* 361 T_Nolink */
+ S_ST( 'y', 0, 0, 0 ), /* 362 T_Nomodify */
+ S_ST( 't', 0, 0, 0 ), /* 363 T_Nomrulist */
+ S_ST( 'e', 0, 0, 0 ), /* 364 T_None */
+ S_ST( 'e', 0, 0, 0 ), /* 365 T_Nonvolatile */
+ S_ST( 'r', 0, 0, 0 ), /* 366 T_Nopeer */
+ S_ST( 'y', 0, 0, 0 ), /* 367 T_Noquery */
+ S_ST( 't', 0, 0, 0 ), /* 368 T_Noselect */
+ S_ST( 'e', 0, 0, 0 ), /* 369 T_Noserve */
+ S_ST( 'p', 0, 0, 0 ), /* 370 T_Notrap */
+ S_ST( 't', 0, 0, 0 ), /* 371 T_Notrust */
+ S_ST( 'p', 0, 588, 0 ), /* 372 T_Ntp */
+ S_ST( 't', 0, 0, 0 ), /* 373 T_Ntpport */
+ S_ST( 't', 1, 0, 0 ), /* 374 T_NtpSignDsocket */
+ S_ST( 'n', 0, 603, 0 ), /* 375 T_Orphan */
+ S_ST( 't', 0, 0, 0 ), /* 376 T_Orphanwait */
+ S_ST( 'c', 0, 0, 0 ), /* 377 T_Panic */
+ S_ST( 'r', 1, 612, 0 ), /* 378 T_Peer */
+ S_ST( 's', 0, 0, 0 ), /* 379 T_Peerstats */
+ S_ST( 'e', 2, 0, 0 ), /* 380 T_Phone */
+ S_ST( 'd', 0, 620, 0 ), /* 381 T_Pid */
+ S_ST( 'e', 1, 0, 0 ), /* 382 T_Pidfile */
+ S_ST( 'l', 1, 0, 0 ), /* 383 T_Pool */
+ S_ST( 't', 0, 0, 0 ), /* 384 T_Port */
+ S_ST( 't', 0, 0, 0 ), /* 385 T_Preempt */
+ S_ST( 'r', 0, 0, 0 ), /* 386 T_Prefer */
+ S_ST( 's', 0, 0, 0 ), /* 387 T_Protostats */
+ S_ST( 'w', 1, 0, 626 ), /* 388 T_Pw */
+ S_ST( 'e', 1, 0, 0 ), /* 389 T_Randfile */
+ S_ST( 's', 0, 0, 0 ), /* 390 T_Rawstats */
+ S_ST( 'd', 1, 0, 0 ), /* 391 T_Refid */
+ S_ST( 'y', 0, 0, 0 ), /* 392 T_Requestkey */
+ S_ST( 't', 0, 0, 0 ), /* 393 T_Reset */
+ S_ST( 't', 0, 0, 0 ), /* 394 T_Restrict */
+ S_ST( 'e', 0, 0, 0 ), /* 395 T_Revoke */
+ S_ST( 't', 0, 0, 0 ), /* 396 T_Rlimit */
+ S_ST( 'r', 1, 0, 0 ), /* 397 T_Saveconfigdir */
+ S_ST( 'r', 1, 703, 0 ), /* 398 T_Server */
+ S_ST( 'r', 1, 0, 0 ), /* 399 T_Setvar */
+ S_ST( 'e', 0, 0, 0 ), /* 400 T_Source */
+ S_ST( 'e', 0, 0, 0 ), /* 401 T_Stacksize */
+ S_ST( 's', 0, 0, 0 ), /* 402 T_Statistics */
+ S_ST( 's', 0, 746, 741 ), /* 403 T_Stats */
+ S_ST( 'r', 1, 0, 0 ), /* 404 T_Statsdir */
+ S_ST( 'p', 0, 749, 0 ), /* 405 T_Step */
+ S_ST( 't', 0, 0, 0 ), /* 406 T_Stepout */
+ S_ST( 'm', 0, 0, 0 ), /* 407 T_Stratum */
+ S_ST( 'l', 3, 334, 0 ), /* 408 logfi */
+ S_ST( 's', 0, 756, 0 ), /* 409 T_Sys */
+ S_ST( 's', 0, 0, 0 ), /* 410 T_Sysstats */
+ S_ST( 'k', 0, 0, 0 ), /* 411 T_Tick */
+ S_ST( '1', 0, 0, 0 ), /* 412 T_Time1 */
+ S_ST( '2', 0, 0, 412 ), /* 413 T_Time2 */
+ S_ST( 'r', 0, 0, 413 ), /* 414 T_Timer */
+ S_ST( 's', 0, 0, 0 ), /* 415 T_Timingstats */
+ S_ST( 'r', 0, 0, 0 ), /* 416 T_Tinker */
+ S_ST( 's', 0, 0, 0 ), /* 417 T_Tos */
+ S_ST( 'p', 1, 0, 0 ), /* 418 T_Trap */
+ S_ST( 'e', 0, 0, 0 ), /* 419 T_True */
+ S_ST( 'y', 0, 0, 0 ), /* 420 T_Trustedkey */
+ S_ST( 'l', 0, 0, 0 ), /* 421 T_Ttl */
+ S_ST( 'e', 0, 0, 0 ), /* 422 T_Type */
+ S_ST( 'o', 3, 427, 254 ), /* 423 lo */
+ S_ST( 'g', 1, 0, 0 ), /* 424 T_Unconfig */
+ S_ST( 'r', 1, 0, 0 ), /* 425 T_Unpeer */
+ S_ST( 'n', 0, 0, 0 ), /* 426 T_Version */
+ S_ST( 'p', 3, 432, 0 ), /* 427 loo */
+ S_ST( 'k', 0, 0, 0 ), /* 428 T_Week */
+ S_ST( 'd', 0, 0, 0 ), /* 429 T_Wildcard */
+ S_ST( 'e', 0, 0, 0 ), /* 430 T_Xleave */
+ S_ST( 'r', 0, 0, 0 ), /* 431 T_Year */
+ S_ST( 's', 3, 433, 0 ), /* 432 loop */
+ S_ST( 't', 3, 444, 0 ), /* 433 loops */
+ S_ST( 'e', 0, 0, 0 ), /* 434 T_Simulate */
+ S_ST( 'y', 0, 0, 0 ), /* 435 T_Beep_Delay */
+ S_ST( 'n', 0, 0, 0 ), /* 436 T_Sim_Duration */
+ S_ST( 't', 0, 0, 0 ), /* 437 T_Server_Offset */
+ S_ST( 'n', 0, 0, 0 ), /* 438 T_Duration */
+ S_ST( 't', 0, 0, 0 ), /* 439 T_Freq_Offset */
+ S_ST( 'r', 0, 0, 0 ), /* 440 T_Wander */
+ S_ST( 'r', 0, 0, 0 ), /* 441 T_Jitter */
+ S_ST( 'y', 0, 0, 0 ), /* 442 T_Prop_Delay */
+ S_ST( 'y', 0, 0, 0 ), /* 443 T_Proc_Delay */
+ S_ST( 'a', 3, 445, 0 ), /* 444 loopst */
+ S_ST( 't', 3, 335, 0 ), /* 445 loopsta */
+ S_ST( 'w', 3, 447, 423 ), /* 446 lo */
+ S_ST( 'p', 3, 448, 0 ), /* 447 low */
+ S_ST( 'r', 3, 449, 0 ), /* 448 lowp */
+ S_ST( 'i', 3, 450, 0 ), /* 449 lowpr */
+ S_ST( 'o', 3, 451, 0 ), /* 450 lowpri */
+ S_ST( 't', 3, 452, 0 ), /* 451 lowprio */
+ S_ST( 'r', 3, 453, 0 ), /* 452 lowpriot */
+ S_ST( 'a', 3, 336, 0 ), /* 453 lowpriotr */
+ S_ST( 'm', 3, 528, 237 ), /* 454 */
+ S_ST( 'a', 3, 473, 0 ), /* 455 m */
+ S_ST( 'n', 3, 457, 0 ), /* 456 ma */
+ S_ST( 'y', 3, 458, 0 ), /* 457 man */
+ S_ST( 'c', 3, 459, 0 ), /* 458 many */
+ S_ST( 'a', 3, 460, 0 ), /* 459 manyc */
+ S_ST( 's', 3, 461, 0 ), /* 460 manyca */
+ S_ST( 't', 3, 467, 0 ), /* 461 manycas */
+ S_ST( 'c', 3, 463, 0 ), /* 462 manycast */
+ S_ST( 'l', 3, 464, 0 ), /* 463 manycastc */
+ S_ST( 'i', 3, 465, 0 ), /* 464 manycastcl */
+ S_ST( 'e', 3, 466, 0 ), /* 465 manycastcli */
+ S_ST( 'n', 3, 337, 0 ), /* 466 manycastclie */
+ S_ST( 's', 3, 468, 462 ), /* 467 manycast */
+ S_ST( 'e', 3, 469, 0 ), /* 468 manycasts */
+ S_ST( 'r', 3, 470, 0 ), /* 469 manycastse */
+ S_ST( 'v', 3, 471, 0 ), /* 470 manycastser */
+ S_ST( 'e', 3, 338, 0 ), /* 471 manycastserv */
+ S_ST( 's', 3, 339, 456 ), /* 472 ma */
+ S_ST( 'x', 3, 488, 472 ), /* 473 ma */
+ S_ST( 'a', 3, 475, 0 ), /* 474 max */
+ S_ST( 'g', 3, 340, 0 ), /* 475 maxa */
+ S_ST( 'c', 3, 477, 474 ), /* 476 max */
+ S_ST( 'l', 3, 478, 0 ), /* 477 maxc */
+ S_ST( 'o', 3, 479, 0 ), /* 478 maxcl */
+ S_ST( 'c', 3, 341, 0 ), /* 479 maxclo */
+ S_ST( 'd', 3, 484, 476 ), /* 480 max */
+ S_ST( 'e', 3, 482, 0 ), /* 481 maxd */
+ S_ST( 'p', 3, 483, 0 ), /* 482 maxde */
+ S_ST( 't', 3, 342, 0 ), /* 483 maxdep */
+ S_ST( 'i', 3, 485, 481 ), /* 484 maxd */
+ S_ST( 's', 3, 343, 0 ), /* 485 maxdi */
+ S_ST( 'm', 3, 487, 480 ), /* 486 max */
+ S_ST( 'e', 3, 344, 0 ), /* 487 maxm */
+ S_ST( 'p', 3, 489, 486 ), /* 488 max */
+ S_ST( 'o', 3, 490, 0 ), /* 489 maxp */
+ S_ST( 'l', 3, 345, 0 ), /* 490 maxpo */
+ S_ST( 'e', 3, 346, 455 ), /* 491 m */
+ S_ST( 'l', 3, 493, 0 ), /* 492 mem */
+ S_ST( 'o', 3, 494, 0 ), /* 493 meml */
+ S_ST( 'c', 3, 347, 0 ), /* 494 memlo */
+ S_ST( 'i', 3, 496, 491 ), /* 495 m */
+ S_ST( 'n', 3, 513, 0 ), /* 496 mi */
+ S_ST( 'c', 3, 498, 0 ), /* 497 min */
+ S_ST( 'l', 3, 499, 0 ), /* 498 minc */
+ S_ST( 'o', 3, 500, 0 ), /* 499 mincl */
+ S_ST( 'c', 3, 348, 0 ), /* 500 minclo */
+ S_ST( 'd', 3, 505, 497 ), /* 501 min */
+ S_ST( 'e', 3, 503, 0 ), /* 502 mind */
+ S_ST( 'p', 3, 504, 0 ), /* 503 minde */
+ S_ST( 't', 3, 349, 0 ), /* 504 mindep */
+ S_ST( 'i', 3, 506, 502 ), /* 505 mind */
+ S_ST( 's', 3, 350, 0 ), /* 506 mindi */
+ S_ST( 'i', 3, 508, 501 ), /* 507 min */
+ S_ST( 'm', 3, 509, 0 ), /* 508 mini */
+ S_ST( 'u', 3, 351, 0 ), /* 509 minim */
+ S_ST( 'p', 3, 511, 507 ), /* 510 min */
+ S_ST( 'o', 3, 512, 0 ), /* 511 minp */
+ S_ST( 'l', 3, 352, 0 ), /* 512 minpo */
+ S_ST( 's', 3, 514, 510 ), /* 513 min */
+ S_ST( 'a', 3, 515, 0 ), /* 514 mins */
+ S_ST( 'n', 3, 353, 0 ), /* 515 minsa */
+ S_ST( 'o', 3, 518, 495 ), /* 516 m */
+ S_ST( 'd', 3, 354, 0 ), /* 517 mo */
+ S_ST( 'n', 3, 522, 517 ), /* 518 mo */
+ S_ST( 'i', 3, 520, 0 ), /* 519 mon */
+ S_ST( 't', 3, 521, 0 ), /* 520 moni */
+ S_ST( 'o', 3, 356, 0 ), /* 521 monit */
+ S_ST( 't', 3, 357, 519 ), /* 522 mon */
+ S_ST( 'r', 3, 358, 516 ), /* 523 m */
+ S_ST( 's', 3, 525, 523 ), /* 524 m */
+ S_ST( 's', 3, 526, 0 ), /* 525 ms */
+ S_ST( 'n', 3, 527, 0 ), /* 526 mss */
+ S_ST( 't', 3, 328, 0 ), /* 527 mssn */
+ S_ST( 'u', 3, 529, 524 ), /* 528 m */
+ S_ST( 'l', 3, 530, 0 ), /* 529 mu */
+ S_ST( 't', 3, 531, 0 ), /* 530 mul */
+ S_ST( 'i', 3, 532, 0 ), /* 531 mult */
+ S_ST( 'c', 3, 533, 0 ), /* 532 multi */
+ S_ST( 'a', 3, 534, 0 ), /* 533 multic */
+ S_ST( 's', 3, 535, 0 ), /* 534 multica */
+ S_ST( 't', 3, 536, 0 ), /* 535 multicas */
+ S_ST( 'c', 3, 537, 0 ), /* 536 multicast */
+ S_ST( 'l', 3, 538, 0 ), /* 537 multicastc */
+ S_ST( 'i', 3, 539, 0 ), /* 538 multicastcl */
+ S_ST( 'e', 3, 540, 0 ), /* 539 multicastcli */
+ S_ST( 'n', 3, 359, 0 ), /* 540 multicastclie */
+ S_ST( 'n', 3, 584, 454 ), /* 541 */
+ S_ST( 'i', 3, 360, 0 ), /* 542 n */
+ S_ST( 'o', 3, 579, 542 ), /* 543 n */
+ S_ST( 'l', 3, 545, 0 ), /* 544 no */
+ S_ST( 'i', 3, 546, 0 ), /* 545 nol */
+ S_ST( 'n', 3, 361, 0 ), /* 546 noli */
+ S_ST( 'm', 3, 552, 544 ), /* 547 no */
+ S_ST( 'o', 3, 549, 0 ), /* 548 nom */
+ S_ST( 'd', 3, 550, 0 ), /* 549 nomo */
+ S_ST( 'i', 3, 551, 0 ), /* 550 nomod */
+ S_ST( 'f', 3, 362, 0 ), /* 551 nomodi */
+ S_ST( 'r', 3, 553, 548 ), /* 552 nom */
+ S_ST( 'u', 3, 554, 0 ), /* 553 nomr */
+ S_ST( 'l', 3, 555, 0 ), /* 554 nomru */
+ S_ST( 'i', 3, 556, 0 ), /* 555 nomrul */
+ S_ST( 's', 3, 363, 0 ), /* 556 nomruli */
+ S_ST( 'n', 3, 558, 547 ), /* 557 no */
+ S_ST( 'v', 3, 559, 364 ), /* 558 non */
+ S_ST( 'o', 3, 560, 0 ), /* 559 nonv */
+ S_ST( 'l', 3, 561, 0 ), /* 560 nonvo */
+ S_ST( 'a', 3, 562, 0 ), /* 561 nonvol */
+ S_ST( 't', 3, 563, 0 ), /* 562 nonvola */
+ S_ST( 'i', 3, 564, 0 ), /* 563 nonvolat */
+ S_ST( 'l', 3, 365, 0 ), /* 564 nonvolati */
+ S_ST( 'p', 3, 566, 557 ), /* 565 no */
+ S_ST( 'e', 3, 567, 0 ), /* 566 nop */
+ S_ST( 'e', 3, 366, 0 ), /* 567 nope */
+ S_ST( 'q', 3, 569, 565 ), /* 568 no */
+ S_ST( 'u', 3, 570, 0 ), /* 569 noq */
+ S_ST( 'e', 3, 571, 0 ), /* 570 noqu */
+ S_ST( 'r', 3, 367, 0 ), /* 571 noque */
+ S_ST( 's', 3, 573, 568 ), /* 572 no */
+ S_ST( 'e', 3, 577, 0 ), /* 573 nos */
+ S_ST( 'l', 3, 575, 0 ), /* 574 nose */
+ S_ST( 'e', 3, 576, 0 ), /* 575 nosel */
+ S_ST( 'c', 3, 368, 0 ), /* 576 nosele */
+ S_ST( 'r', 3, 578, 574 ), /* 577 nose */
+ S_ST( 'v', 3, 369, 0 ), /* 578 noser */
+ S_ST( 't', 3, 580, 572 ), /* 579 no */
+ S_ST( 'r', 3, 582, 0 ), /* 580 not */
+ S_ST( 'a', 3, 370, 0 ), /* 581 notr */
+ S_ST( 'u', 3, 583, 581 ), /* 582 notr */
+ S_ST( 's', 3, 371, 0 ), /* 583 notru */
+ S_ST( 't', 3, 372, 543 ), /* 584 n */
+ S_ST( 'p', 3, 586, 0 ), /* 585 ntp */
+ S_ST( 'o', 3, 587, 0 ), /* 586 ntpp */
+ S_ST( 'r', 3, 373, 0 ), /* 587 ntppo */
+ S_ST( 's', 3, 589, 585 ), /* 588 ntp */
+ S_ST( 'i', 3, 590, 0 ), /* 589 ntps */
+ S_ST( 'g', 3, 591, 0 ), /* 590 ntpsi */
+ S_ST( 'n', 3, 592, 0 ), /* 591 ntpsig */
+ S_ST( 'd', 3, 593, 0 ), /* 592 ntpsign */
+ S_ST( 's', 3, 594, 0 ), /* 593 ntpsignd */
+ S_ST( 'o', 3, 595, 0 ), /* 594 ntpsignds */
+ S_ST( 'c', 3, 596, 0 ), /* 595 ntpsigndso */
+ S_ST( 'k', 3, 597, 0 ), /* 596 ntpsigndsoc */
+ S_ST( 'e', 3, 374, 0 ), /* 597 ntpsigndsock */
+ S_ST( 'o', 3, 599, 541 ), /* 598 */
+ S_ST( 'r', 3, 600, 0 ), /* 599 o */
+ S_ST( 'p', 3, 601, 0 ), /* 600 or */
+ S_ST( 'h', 3, 602, 0 ), /* 601 orp */
+ S_ST( 'a', 3, 375, 0 ), /* 602 orph */
+ S_ST( 'w', 3, 604, 0 ), /* 603 orphan */
+ S_ST( 'a', 3, 605, 0 ), /* 604 orphanw */
+ S_ST( 'i', 3, 376, 0 ), /* 605 orphanwa */
+ S_ST( 'p', 3, 388, 598 ), /* 606 */
+ S_ST( 'a', 3, 608, 0 ), /* 607 p */
+ S_ST( 'n', 3, 609, 0 ), /* 608 pa */
+ S_ST( 'i', 3, 377, 0 ), /* 609 pan */
+ S_ST( 'e', 3, 611, 607 ), /* 610 p */
+ S_ST( 'e', 3, 378, 0 ), /* 611 pe */
+ S_ST( 's', 3, 613, 0 ), /* 612 peer */
+ S_ST( 't', 3, 614, 0 ), /* 613 peers */
+ S_ST( 'a', 3, 615, 0 ), /* 614 peerst */
+ S_ST( 't', 3, 379, 0 ), /* 615 peersta */
+ S_ST( 'h', 3, 617, 610 ), /* 616 p */
+ S_ST( 'o', 3, 618, 0 ), /* 617 ph */
+ S_ST( 'n', 3, 380, 0 ), /* 618 pho */
+ S_ST( 'i', 3, 381, 616 ), /* 619 p */
+ S_ST( 'f', 3, 621, 0 ), /* 620 pid */
+ S_ST( 'i', 3, 622, 0 ), /* 621 pidf */
+ S_ST( 'l', 3, 382, 0 ), /* 622 pidfi */
+ S_ST( 'o', 3, 625, 619 ), /* 623 p */
+ S_ST( 'o', 3, 383, 0 ), /* 624 po */
+ S_ST( 'r', 3, 384, 624 ), /* 625 po */
+ S_ST( 'r', 3, 633, 623 ), /* 626 p */
+ S_ST( 'e', 3, 631, 0 ), /* 627 pr */
+ S_ST( 'e', 3, 629, 0 ), /* 628 pre */
+ S_ST( 'm', 3, 630, 0 ), /* 629 pree */
+ S_ST( 'p', 3, 385, 0 ), /* 630 preem */
+ S_ST( 'f', 3, 632, 628 ), /* 631 pre */
+ S_ST( 'e', 3, 386, 0 ), /* 632 pref */
+ S_ST( 'o', 3, 646, 627 ), /* 633 pr */
+ S_ST( 'c', 3, 635, 0 ), /* 634 pro */
+ S_ST( '_', 3, 636, 0 ), /* 635 proc */
+ S_ST( 'd', 3, 637, 0 ), /* 636 proc_ */
+ S_ST( 'e', 3, 638, 0 ), /* 637 proc_d */
+ S_ST( 'l', 3, 639, 0 ), /* 638 proc_de */
+ S_ST( 'a', 3, 443, 0 ), /* 639 proc_del */
+ S_ST( 'p', 3, 641, 634 ), /* 640 pro */
+ S_ST( '_', 3, 642, 0 ), /* 641 prop */
+ S_ST( 'd', 3, 643, 0 ), /* 642 prop_ */
+ S_ST( 'e', 3, 644, 0 ), /* 643 prop_d */
+ S_ST( 'l', 3, 645, 0 ), /* 644 prop_de */
+ S_ST( 'a', 3, 442, 0 ), /* 645 prop_del */
+ S_ST( 't', 3, 647, 640 ), /* 646 pro */
+ S_ST( 'o', 3, 648, 0 ), /* 647 prot */
+ S_ST( 's', 3, 649, 0 ), /* 648 proto */
+ S_ST( 't', 3, 650, 0 ), /* 649 protos */
+ S_ST( 'a', 3, 651, 0 ), /* 650 protost */
+ S_ST( 't', 3, 387, 0 ), /* 651 protosta */
+ S_ST( 'r', 3, 683, 606 ), /* 652 */
+ S_ST( 'a', 3, 659, 0 ), /* 653 r */
+ S_ST( 'n', 3, 655, 0 ), /* 654 ra */
+ S_ST( 'd', 3, 656, 0 ), /* 655 ran */
+ S_ST( 'f', 3, 657, 0 ), /* 656 rand */
+ S_ST( 'i', 3, 658, 0 ), /* 657 randf */
+ S_ST( 'l', 3, 389, 0 ), /* 658 randfi */
+ S_ST( 'w', 3, 660, 654 ), /* 659 ra */
+ S_ST( 's', 3, 661, 0 ), /* 660 raw */
+ S_ST( 't', 3, 662, 0 ), /* 661 raws */
+ S_ST( 'a', 3, 663, 0 ), /* 662 rawst */
+ S_ST( 't', 3, 390, 0 ), /* 663 rawsta */
+ S_ST( 'e', 3, 680, 653 ), /* 664 r */
+ S_ST( 'f', 3, 666, 0 ), /* 665 re */
+ S_ST( 'i', 3, 391, 0 ), /* 666 ref */
+ S_ST( 'q', 3, 668, 665 ), /* 667 re */
+ S_ST( 'u', 3, 669, 0 ), /* 668 req */
+ S_ST( 'e', 3, 670, 0 ), /* 669 requ */
+ S_ST( 's', 3, 671, 0 ), /* 670 reque */
+ S_ST( 't', 3, 672, 0 ), /* 671 reques */
+ S_ST( 'k', 3, 673, 0 ), /* 672 request */
+ S_ST( 'e', 3, 392, 0 ), /* 673 requestk */
+ S_ST( 's', 3, 676, 667 ), /* 674 re */
+ S_ST( 'e', 3, 393, 0 ), /* 675 res */
+ S_ST( 't', 3, 677, 675 ), /* 676 res */
+ S_ST( 'r', 3, 678, 0 ), /* 677 rest */
+ S_ST( 'i', 3, 679, 0 ), /* 678 restr */
+ S_ST( 'c', 3, 394, 0 ), /* 679 restri */
+ S_ST( 'v', 3, 681, 674 ), /* 680 re */
+ S_ST( 'o', 3, 682, 0 ), /* 681 rev */
+ S_ST( 'k', 3, 395, 0 ), /* 682 revo */
+ S_ST( 'l', 3, 684, 664 ), /* 683 r */
+ S_ST( 'i', 3, 685, 0 ), /* 684 rl */
+ S_ST( 'm', 3, 686, 0 ), /* 685 rli */
+ S_ST( 'i', 3, 396, 0 ), /* 686 rlim */
+ S_ST( 's', 3, 755, 652 ), /* 687 */
+ S_ST( 'a', 3, 689, 0 ), /* 688 s */
+ S_ST( 'v', 3, 690, 0 ), /* 689 sa */
+ S_ST( 'e', 3, 691, 0 ), /* 690 sav */
+ S_ST( 'c', 3, 692, 0 ), /* 691 save */
+ S_ST( 'o', 3, 693, 0 ), /* 692 savec */
+ S_ST( 'n', 3, 694, 0 ), /* 693 saveco */
+ S_ST( 'f', 3, 695, 0 ), /* 694 savecon */
+ S_ST( 'i', 3, 696, 0 ), /* 695 saveconf */
+ S_ST( 'g', 3, 697, 0 ), /* 696 saveconfi */
+ S_ST( 'd', 3, 698, 0 ), /* 697 saveconfig */
+ S_ST( 'i', 3, 397, 0 ), /* 698 saveconfigd */
+ S_ST( 'e', 3, 709, 688 ), /* 699 s */
+ S_ST( 'r', 3, 701, 0 ), /* 700 se */
+ S_ST( 'v', 3, 702, 0 ), /* 701 ser */
+ S_ST( 'e', 3, 398, 0 ), /* 702 serv */
+ S_ST( '_', 3, 704, 0 ), /* 703 server */
+ S_ST( 'o', 3, 705, 0 ), /* 704 server_ */
+ S_ST( 'f', 3, 706, 0 ), /* 705 server_o */
+ S_ST( 'f', 3, 707, 0 ), /* 706 server_of */
+ S_ST( 's', 3, 708, 0 ), /* 707 server_off */
+ S_ST( 'e', 3, 437, 0 ), /* 708 server_offs */
+ S_ST( 't', 3, 710, 700 ), /* 709 se */
+ S_ST( 'v', 3, 711, 0 ), /* 710 set */
+ S_ST( 'a', 3, 399, 0 ), /* 711 setv */
+ S_ST( 'i', 3, 713, 699 ), /* 712 s */
+ S_ST( 'm', 3, 714, 0 ), /* 713 si */
+ S_ST( 'u', 3, 715, 0 ), /* 714 sim */
+ S_ST( 'l', 3, 716, 0 ), /* 715 simu */
+ S_ST( 'a', 3, 717, 0 ), /* 716 simul */
+ S_ST( 't', 3, 718, 0 ), /* 717 simula */
+ S_ST( 'i', 3, 719, 434 ), /* 718 simulat */
+ S_ST( 'o', 3, 720, 0 ), /* 719 simulati */
+ S_ST( 'n', 3, 721, 0 ), /* 720 simulatio */
+ S_ST( '_', 3, 722, 0 ), /* 721 simulation */
+ S_ST( 'd', 3, 723, 0 ), /* 722 simulation_ */
+ S_ST( 'u', 3, 724, 0 ), /* 723 simulation_d */
+ S_ST( 'r', 3, 725, 0 ), /* 724 simulation_du */
+ S_ST( 'a', 3, 726, 0 ), /* 725 simulation_dur */
+ S_ST( 't', 3, 727, 0 ), /* 726 simulation_dura */
+ S_ST( 'i', 3, 728, 0 ), /* 727 simulation_durat */
+ S_ST( 'o', 3, 436, 0 ), /* 728 simulation_durati */
+ S_ST( 'o', 3, 730, 712 ), /* 729 s */
+ S_ST( 'u', 3, 731, 0 ), /* 730 so */
+ S_ST( 'r', 3, 732, 0 ), /* 731 sou */
+ S_ST( 'c', 3, 400, 0 ), /* 732 sour */
+ S_ST( 't', 3, 751, 729 ), /* 733 s */
+ S_ST( 'a', 3, 740, 0 ), /* 734 st */
+ S_ST( 'c', 3, 736, 0 ), /* 735 sta */
+ S_ST( 'k', 3, 737, 0 ), /* 736 stac */
+ S_ST( 's', 3, 738, 0 ), /* 737 stack */
+ S_ST( 'i', 3, 739, 0 ), /* 738 stacks */
+ S_ST( 'z', 3, 401, 0 ), /* 739 stacksi */
+ S_ST( 't', 3, 403, 735 ), /* 740 sta */
+ S_ST( 'i', 3, 742, 0 ), /* 741 stat */
+ S_ST( 's', 3, 743, 0 ), /* 742 stati */
+ S_ST( 't', 3, 744, 0 ), /* 743 statis */
+ S_ST( 'i', 3, 745, 0 ), /* 744 statist */
+ S_ST( 'c', 3, 402, 0 ), /* 745 statisti */
+ S_ST( 'd', 3, 747, 0 ), /* 746 stats */
+ S_ST( 'i', 3, 404, 0 ), /* 747 statsd */
+ S_ST( 'e', 3, 405, 734 ), /* 748 st */
+ S_ST( 'o', 3, 750, 0 ), /* 749 step */
+ S_ST( 'u', 3, 406, 0 ), /* 750 stepo */
+ S_ST( 'r', 3, 752, 748 ), /* 751 st */
+ S_ST( 'a', 3, 753, 0 ), /* 752 str */
+ S_ST( 't', 3, 754, 0 ), /* 753 stra */
+ S_ST( 'u', 3, 407, 0 ), /* 754 strat */
+ S_ST( 'y', 3, 409, 733 ), /* 755 s */
+ S_ST( 's', 3, 757, 0 ), /* 756 sys */
+ S_ST( 't', 3, 758, 0 ), /* 757 syss */
+ S_ST( 'a', 3, 759, 0 ), /* 758 sysst */
+ S_ST( 't', 3, 410, 0 ), /* 759 syssta */
+ S_ST( 't', 3, 786, 687 ), /* 760 */
+ S_ST( 'i', 3, 772, 0 ), /* 761 t */
+ S_ST( 'c', 3, 411, 0 ), /* 762 ti */
+ S_ST( 'm', 3, 765, 762 ), /* 763 ti */
+ S_ST( 'e', 3, 414, 0 ), /* 764 tim */
+ S_ST( 'i', 3, 766, 764 ), /* 765 tim */
+ S_ST( 'n', 3, 767, 0 ), /* 766 timi */
+ S_ST( 'g', 3, 768, 0 ), /* 767 timin */
+ S_ST( 's', 3, 769, 0 ), /* 768 timing */
+ S_ST( 't', 3, 770, 0 ), /* 769 timings */
+ S_ST( 'a', 3, 771, 0 ), /* 770 timingst */
+ S_ST( 't', 3, 415, 0 ), /* 771 timingsta */
+ S_ST( 'n', 3, 773, 763 ), /* 772 ti */
+ S_ST( 'k', 3, 774, 0 ), /* 773 tin */
+ S_ST( 'e', 3, 416, 0 ), /* 774 tink */
+ S_ST( 'o', 3, 417, 761 ), /* 775 t */
+ S_ST( 'r', 3, 778, 775 ), /* 776 t */
+ S_ST( 'a', 3, 418, 0 ), /* 777 tr */
+ S_ST( 'u', 3, 779, 777 ), /* 778 tr */
+ S_ST( 's', 3, 780, 419 ), /* 779 tru */
+ S_ST( 't', 3, 781, 0 ), /* 780 trus */
+ S_ST( 'e', 3, 782, 0 ), /* 781 trust */
+ S_ST( 'd', 3, 783, 0 ), /* 782 truste */
+ S_ST( 'k', 3, 784, 0 ), /* 783 trusted */
+ S_ST( 'e', 3, 420, 0 ), /* 784 trustedk */
+ S_ST( 't', 3, 421, 776 ), /* 785 t */
+ S_ST( 'y', 3, 787, 785 ), /* 786 t */
+ S_ST( 'p', 3, 422, 0 ), /* 787 ty */
+ S_ST( 'u', 3, 789, 760 ), /* 788 */
+ S_ST( 'n', 3, 795, 0 ), /* 789 u */
+ S_ST( 'c', 3, 791, 0 ), /* 790 un */
+ S_ST( 'o', 3, 792, 0 ), /* 791 unc */
+ S_ST( 'n', 3, 793, 0 ), /* 792 unco */
+ S_ST( 'f', 3, 794, 0 ), /* 793 uncon */
+ S_ST( 'i', 3, 424, 0 ), /* 794 unconf */
+ S_ST( 'p', 3, 796, 790 ), /* 795 un */
+ S_ST( 'e', 3, 797, 0 ), /* 796 unp */
+ S_ST( 'e', 3, 425, 0 ), /* 797 unpe */
+ S_ST( 'v', 3, 799, 788 ), /* 798 */
+ S_ST( 'e', 3, 800, 0 ), /* 799 v */
+ S_ST( 'r', 3, 801, 0 ), /* 800 ve */
+ S_ST( 's', 3, 802, 0 ), /* 801 ver */
+ S_ST( 'i', 3, 803, 0 ), /* 802 vers */
+ S_ST( 'o', 3, 426, 0 ), /* 803 versi */
+ S_ST( 'w', 3, 811, 798 ), /* 804 */
+ S_ST( 'a', 3, 806, 0 ), /* 805 w */
+ S_ST( 'n', 3, 807, 0 ), /* 806 wa */
+ S_ST( 'd', 3, 808, 0 ), /* 807 wan */
+ S_ST( 'e', 3, 440, 0 ), /* 808 wand */
+ S_ST( 'e', 3, 810, 805 ), /* 809 w */
+ S_ST( 'e', 3, 428, 0 ), /* 810 we */
+ S_ST( 'i', 3, 812, 809 ), /* 811 w */
+ S_ST( 'l', 3, 813, 0 ), /* 812 wi */
+ S_ST( 'd', 3, 814, 0 ), /* 813 wil */
+ S_ST( 'c', 3, 815, 0 ), /* 814 wild */
+ S_ST( 'a', 3, 816, 0 ), /* 815 wildc */
+ S_ST( 'r', 3, 429, 0 ), /* 816 wildca */
+ S_ST( 'x', 3, 818, 804 ), /* 817 */
+ S_ST( 'l', 3, 819, 0 ), /* 818 x */
+ S_ST( 'e', 3, 820, 0 ), /* 819 xl */
+ S_ST( 'a', 3, 821, 0 ), /* 820 xle */
+ S_ST( 'v', 3, 430, 0 ), /* 821 xlea */
+ S_ST( 'y', 3, 823, 817 ), /* 822 [initial state] */
+ S_ST( 'e', 3, 824, 0 ), /* 823 y */
+ S_ST( 'a', 3, 431, 0 ) /* 824 ye */
+};
+
diff --git a/ntpd/ntp_leapsec.c b/ntpd/ntp_leapsec.c
new file mode 100644
index 0000000..14b8cd9
--- /dev/null
+++ b/ntpd/ntp_leapsec.c
@@ -0,0 +1,1009 @@
+/*
+ * ntp_leapsec.c - leap second processing for NTPD
+ *
+ * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
+ * The contents of 'html/copyright.html' apply.
+ * ----------------------------------------------------------------------
+ * This is an attempt to get the leap second handling into a dedicated
+ * module to make the somewhat convoluted logic testable.
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+#include "ntp_types.h"
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+#include "ntp_calendar.h"
+#include "ntp_leapsec.h"
+#include "ntp.h"
+#include "vint64ops.h"
+#include "lib_strbuf.h"
+
+#include "isc/sha1.h"
+
+static const char * const logPrefix = "leapsecond file";
+
+/* ---------------------------------------------------------------------
+ * GCC is rather sticky with its 'const' attribute. We have to do it more
+ * explicit than with a cast if we want to get rid of a CONST qualifier.
+ * Greetings from the PASCAL world, where casting was only possible via
+ * untagged unions...
+ */
+static inline void*
+noconst(
+ const void* ptr
+ )
+{
+ union {
+ const void * cp;
+ void * vp;
+ } tmp;
+ tmp.cp = ptr;
+ return tmp.vp;
+}
+
+/* ---------------------------------------------------------------------
+ * Our internal data structure
+ */
+#define MAX_HIST 10 /* history of leap seconds */
+
+struct leap_info {
+ vint64 ttime; /* transition time (after the step, ntp scale) */
+ uint32_t stime; /* schedule limit (a month before transition) */
+ int16_t taiof; /* TAI offset on and after the transition */
+ uint8_t dynls; /* dynamic: inserted on peer/clock request */
+};
+typedef struct leap_info leap_info_t;
+
+struct leap_head {
+ vint64 update; /* time of information update */
+ vint64 expire; /* table expiration time */
+ uint16_t size; /* number of infos in table */
+ int16_t base_tai; /* total leaps before first entry */
+ int16_t this_tai; /* current TAI offset */
+ int16_t next_tai; /* TAI offset after 'when' */
+ vint64 dtime; /* due time (current era end) */
+ vint64 ttime; /* nominal transition time (next era start) */
+ vint64 stime; /* schedule time (when we take notice) */
+ vint64 ebase; /* base time of this leap era */
+ uint8_t dynls; /* next leap is dynamic (by peer request) */
+};
+typedef struct leap_head leap_head_t;
+
+struct leap_table {
+ leap_signature_t lsig;
+ leap_head_t head;
+ leap_info_t info[MAX_HIST];
+};
+
+/* Where we store our tables */
+static leap_table_t _ltab[2], *_lptr;
+static int/*BOOL*/ _electric;
+
+/* Forward decls of local helpers */
+static int add_range(leap_table_t*, const leap_info_t*);
+static char * get_line(leapsec_reader, void*, char*, size_t);
+static char * skipws(const char*);
+static int parsefail(const char * cp, const char * ep);
+static void reload_limits(leap_table_t*, const vint64*);
+static int betweenu32(uint32_t, uint32_t, uint32_t);
+static void reset_times(leap_table_t*);
+static int leapsec_add(leap_table_t*, const vint64*, int);
+static int leapsec_raw(leap_table_t*, const vint64 *, int, int);
+static char * lstostr(const vint64 * ts);
+
+/* =====================================================================
+ * Get & Set the current leap table
+ */
+
+/* ------------------------------------------------------------------ */
+leap_table_t *
+leapsec_get_table(
+ int alternate)
+{
+ leap_table_t *p1, *p2;
+
+ p1 = _lptr;
+ p1 = &_ltab[p1 == &_ltab[1]];
+ p2 = &_ltab[p1 == &_ltab[0]];
+ if (alternate) {
+ memcpy(p2, p1, sizeof(leap_table_t));
+ p1 = p2;
+ }
+
+ return p1;
+}
+
+/* ------------------------------------------------------------------ */
+int/*BOOL*/
+leapsec_set_table(
+ leap_table_t * pt)
+{
+ if (pt == &_ltab[0] || pt == &_ltab[1])
+ _lptr = pt;
+ return _lptr == pt;
+}
+
+/* ------------------------------------------------------------------ */
+int/*BOOL*/
+leapsec_electric(
+ int/*BOOL*/ on)
+{
+ int res = _electric;
+ if (on < 0)
+ return res;
+
+ _electric = (on != 0);
+ if (_electric == res)
+ return res;
+
+ if (_lptr == &_ltab[0] || _lptr == &_ltab[1])
+ reset_times(_lptr);
+
+ return res;
+}
+
+/* =====================================================================
+ * API functions that operate on tables
+ */
+
+/* ---------------------------------------------------------------------
+ * Clear all leap second data. Use it for init & cleanup
+ */
+void
+leapsec_clear(
+ leap_table_t * pt)
+{
+ memset(&pt->lsig, 0, sizeof(pt->lsig));
+ memset(&pt->head, 0, sizeof(pt->head));
+ reset_times(pt);
+}
+
+/* ---------------------------------------------------------------------
+ * Load a leap second file and check expiration on the go
+ */
+int/*BOOL*/
+leapsec_load(
+ leap_table_t * pt ,
+ leapsec_reader func,
+ void * farg,
+ int use_build_limit)
+{
+ char *cp, *ep, linebuf[50];
+ vint64 ttime, limit;
+ long taiof;
+ struct calendar build;
+
+ leapsec_clear(pt);
+ if (use_build_limit && ntpcal_get_build_date(&build))
+ limit = ntpcal_date_to_ntp64(&build);
+ else
+ memset(&limit, 0, sizeof(limit));
+
+ while (get_line(func, farg, linebuf, sizeof(linebuf))) {
+ cp = linebuf;
+ if (*cp == '#') {
+ cp++;
+ if (*cp == '@') {
+ cp = skipws(cp+1);
+ pt->head.expire = strtouv64(cp, &ep, 10);
+ if (parsefail(cp, ep))
+ goto fail_read;
+ pt->lsig.etime = pt->head.expire.D_s.lo;
+ } else if (*cp == '$') {
+ cp = skipws(cp+1);
+ pt->head.update = strtouv64(cp, &ep, 10);
+ if (parsefail(cp, ep))
+ goto fail_read;
+ }
+ } else if (isdigit((u_char)*cp)) {
+ ttime = strtouv64(cp, &ep, 10);
+ if (parsefail(cp, ep))
+ goto fail_read;
+ cp = skipws(ep);
+ taiof = strtol(cp, &ep, 10);
+ if ( parsefail(cp, ep)
+ || taiof > SHRT_MAX || taiof < SHRT_MIN)
+ goto fail_read;
+ if (ucmpv64(&ttime, &limit) >= 0) {
+ if (!leapsec_raw(pt, &ttime,
+ taiof, FALSE))
+ goto fail_insn;
+ } else {
+ pt->head.base_tai = (int16_t)taiof;
+ }
+ pt->lsig.ttime = ttime.D_s.lo;
+ pt->lsig.taiof = (int16_t)taiof;
+ }
+ }
+ return TRUE;
+
+fail_read:
+ errno = EILSEQ;
+fail_insn:
+ leapsec_clear(pt);
+ return FALSE;
+}
+
+/* ---------------------------------------------------------------------
+ * Dump a table in human-readable format. Use 'fprintf' and a FILE
+ * pointer if you want to get it printed into a stream.
+ */
+void
+leapsec_dump(
+ const leap_table_t * pt ,
+ leapsec_dumper func,
+ void * farg)
+{
+ int idx;
+ vint64 ts;
+ struct calendar atb, ttb;
+
+ ntpcal_ntp64_to_date(&ttb, &pt->head.expire);
+ (*func)(farg, "leap table (%u entries) expires at %04u-%02u-%02u:\n",
+ pt->head.size,
+ ttb.year, ttb.month, ttb.monthday);
+ idx = pt->head.size;
+ while (idx-- != 0) {
+ ts = pt->info[idx].ttime;
+ ntpcal_ntp64_to_date(&ttb, &ts);
+ ts = subv64u32(&ts, pt->info[idx].stime);
+ ntpcal_ntp64_to_date(&atb, &ts);
+
+ (*func)(farg, "%04u-%02u-%02u [%c] (%04u-%02u-%02u) - %d\n",
+ ttb.year, ttb.month, ttb.monthday,
+ "-*"[pt->info[idx].dynls != 0],
+ atb.year, atb.month, atb.monthday,
+ pt->info[idx].taiof);
+ }
+}
+
+/* =====================================================================
+ * usecase driven API functions
+ */
+
+int/*BOOL*/
+leapsec_query(
+ leap_result_t * qr ,
+ uint32_t ts32 ,
+ const time_t * pivot)
+{
+ leap_table_t * pt;
+ vint64 ts64, last, next;
+ uint32_t due32;
+ int fired;
+
+ /* preset things we use later on... */
+ fired = FALSE;
+ ts64 = ntpcal_ntp_to_ntp(ts32, pivot);
+ pt = leapsec_get_table(FALSE);
+ memset(qr, 0, sizeof(leap_result_t));
+
+ if (ucmpv64(&ts64, &pt->head.ebase) < 0) {
+ /* Most likely after leap frame reset. Could also be a
+ * backstep of the system clock. Anyway, get the new
+ * leap era frame.
+ */
+ reload_limits(pt, &ts64);
+ } else if (ucmpv64(&ts64, &pt->head.dtime) >= 0) {
+ /* Boundary crossed in forward direction. This might
+ * indicate a leap transition, so we prepare for that
+ * case.
+ *
+ * Some operations below are actually NOPs in electric
+ * mode, but having only one code path that works for
+ * both modes is easier to maintain.
+ */
+ last = pt->head.ttime;
+ qr->warped = (int16_t)(last.D_s.lo -
+ pt->head.dtime.D_s.lo);
+ next = addv64i32(&ts64, qr->warped);
+ reload_limits(pt, &next);
+ fired = ucmpv64(&pt->head.ebase, &last) == 0;
+ if (fired) {
+ ts64 = next;
+ ts32 = next.D_s.lo;
+ } else {
+ qr->warped = 0;
+ }
+ }
+
+ qr->tai_offs = pt->head.this_tai;
+
+ /* If before the next scheduling alert, we're done. */
+ if (ucmpv64(&ts64, &pt->head.stime) < 0)
+ return fired;
+
+ /* now start to collect the remaing data */
+ due32 = pt->head.dtime.D_s.lo;
+
+ qr->tai_diff = pt->head.next_tai - pt->head.this_tai;
+ qr->ttime = pt->head.ttime;
+ qr->ddist = due32 - ts32;
+ qr->dynamic = pt->head.dynls;
+ qr->proximity = LSPROX_SCHEDULE;
+
+ /* if not in the last day before transition, we're done. */
+ if (!betweenu32(due32 - SECSPERDAY, ts32, due32))
+ return fired;
+
+ qr->proximity = LSPROX_ANNOUNCE;
+ if (!betweenu32(due32 - 10, ts32, due32))
+ return fired;
+
+ /* The last 10s before the transition. Prepare for action! */
+ qr->proximity = LSPROX_ALERT;
+ return fired;
+}
+
+/* ------------------------------------------------------------------ */
+int/*BOOL*/
+leapsec_frame(
+ leap_result_t *qr)
+{
+ const leap_table_t * pt;
+
+ memset(qr, 0, sizeof(leap_result_t));
+ pt = leapsec_get_table(FALSE);
+ if (ucmpv64(&pt->head.ttime, &pt->head.stime) <= 0)
+ return FALSE;
+
+ qr->tai_offs = pt->head.this_tai;
+ qr->tai_diff = pt->head.next_tai - pt->head.this_tai;
+ qr->ttime = pt->head.ttime;
+ qr->dynamic = pt->head.dynls;
+
+ return TRUE;
+}
+
+/* ------------------------------------------------------------------ */
+/* Reset the current leap frame */
+void
+leapsec_reset_frame(void)
+{
+ reset_times(leapsec_get_table(FALSE));
+}
+
+/* ------------------------------------------------------------------ */
+/* load a file from a FILE pointer. Note: If hcheck is true, load
+ * only after successful signature check. The stream must be seekable
+ * or this will fail.
+ */
+int/*BOOL*/
+leapsec_load_stream(
+ FILE * ifp ,
+ const char * fname,
+ int/*BOOL*/ logall)
+{
+ leap_table_t *pt;
+ int rcheck;
+
+ if (NULL == fname)
+ fname = "<unknown>";
+
+ rcheck = leapsec_validate((leapsec_reader)getc, ifp);
+ if (logall)
+ switch (rcheck)
+ {
+ case LSVALID_GOODHASH:
+ msyslog(LOG_NOTICE, "%s ('%s'): good hash signature",
+ logPrefix, fname);
+ break;
+
+ case LSVALID_NOHASH:
+ msyslog(LOG_ERR, "%s ('%s'): no hash signature",
+ logPrefix, fname);
+ break;
+ case LSVALID_BADHASH:
+ msyslog(LOG_ERR, "%s ('%s'): signature mismatch",
+ logPrefix, fname);
+ break;
+ case LSVALID_BADFORMAT:
+ msyslog(LOG_ERR, "%s ('%s'): malformed hash signature",
+ logPrefix, fname);
+ break;
+ default:
+ msyslog(LOG_ERR, "%s ('%s'): unknown error code %d",
+ logPrefix, fname, rcheck);
+ break;
+ }
+ if (rcheck < 0)
+ return FALSE;
+
+ rewind(ifp);
+ pt = leapsec_get_table(TRUE);
+ if (!leapsec_load(pt, (leapsec_reader)getc, ifp, TRUE)) {
+ switch (errno) {
+ case EINVAL:
+ msyslog(LOG_ERR, "%s ('%s'): bad transition time",
+ logPrefix, fname);
+ break;
+ case ERANGE:
+ msyslog(LOG_ERR, "%s ('%s'): times not ascending",
+ logPrefix, fname);
+ break;
+ default:
+ msyslog(LOG_ERR, "%s ('%s'): parsing error",
+ logPrefix, fname);
+ break;
+ }
+ return FALSE;
+ }
+
+ if (pt->head.size)
+ msyslog(LOG_NOTICE, "%s ('%s'): loaded, expire=%s last=%s ofs=%d",
+ logPrefix, fname, lstostr(&pt->head.expire),
+ lstostr(&pt->info[0].ttime), pt->info[0].taiof);
+ else
+ msyslog(LOG_NOTICE,
+ "%s ('%s'): loaded, expire=%s ofs=%d (no entries after build date)",
+ logPrefix, fname, lstostr(&pt->head.expire),
+ pt->head.base_tai);
+
+ return leapsec_set_table(pt);
+}
+
+/* ------------------------------------------------------------------ */
+int/*BOOL*/
+leapsec_load_file(
+ const char * fname,
+ struct stat * sb_old,
+ int/*BOOL*/ force,
+ int/*BOOL*/ logall)
+{
+ FILE * fp;
+ struct stat sb_new;
+ int rc;
+
+ /* just do nothing if there is no leap file */
+ if ( !(fname && *fname) )
+ return FALSE;
+
+ /* try to stat the leapfile */
+ if (0 != stat(fname, &sb_new)) {
+ if (logall)
+ msyslog(LOG_ERR, "%s ('%s'): stat failed: %m",
+ logPrefix, fname);
+ return FALSE;
+ }
+
+ /* silently skip to postcheck if no new file found */
+ if (NULL != sb_old) {
+ if (!force
+ && sb_old->st_mtime == sb_new.st_mtime
+ && sb_old->st_ctime == sb_new.st_ctime
+ )
+ return FALSE;
+ *sb_old = sb_new;
+ }
+
+ /* try to open the leap file, complain if that fails
+ *
+ * [perlinger@ntp.org]
+ * coverity raises a TOCTOU (time-of-check/time-of-use) issue
+ * here, which is not entirely helpful: While there is indeed a
+ * possible race condition between the 'stat()' call above and
+ * the 'fopen)' call below, I intentionally want to omit the
+ * overhead of opening the file and calling 'fstat()', because
+ * in most cases the file would have be to closed anyway without
+ * reading the contents. I chose to disable the coverity
+ * warning instead.
+ *
+ * So unless someone comes up with a reasonable argument why
+ * this could be a real issue, I'll just try to silence coverity
+ * on that topic.
+ */
+ /* coverity[toctou] */
+ if ((fp = fopen(fname, "r")) == NULL) {
+ if (logall)
+ msyslog(LOG_ERR,
+ "%s ('%s'): open failed: %m",
+ logPrefix, fname);
+ return FALSE;
+ }
+
+ rc = leapsec_load_stream(fp, fname, logall);
+ fclose(fp);
+ return rc;
+}
+
+/* ------------------------------------------------------------------ */
+void
+leapsec_getsig(
+ leap_signature_t * psig)
+{
+ const leap_table_t * pt;
+
+ pt = leapsec_get_table(FALSE);
+ memcpy(psig, &pt->lsig, sizeof(leap_signature_t));
+}
+
+/* ------------------------------------------------------------------ */
+int/*BOOL*/
+leapsec_expired(
+ uint32_t when,
+ const time_t * tpiv)
+{
+ const leap_table_t * pt;
+ vint64 limit;
+
+ pt = leapsec_get_table(FALSE);
+ limit = ntpcal_ntp_to_ntp(when, tpiv);
+ return ucmpv64(&limit, &pt->head.expire) >= 0;
+}
+
+/* ------------------------------------------------------------------ */
+int32_t
+leapsec_daystolive(
+ uint32_t when,
+ const time_t * tpiv)
+{
+ const leap_table_t * pt;
+ vint64 limit;
+
+ pt = leapsec_get_table(FALSE);
+ limit = ntpcal_ntp_to_ntp(when, tpiv);
+ limit = subv64(&pt->head.expire, &limit);
+ return ntpcal_daysplit(&limit).hi;
+}
+
+/* ------------------------------------------------------------------ */
+int/*BOOL*/
+leapsec_add_fix(
+ int total,
+ uint32_t ttime,
+ uint32_t etime,
+ const time_t * pivot)
+{
+ time_t tpiv;
+ leap_table_t * pt;
+ vint64 tt64, et64;
+
+ if (pivot == NULL) {
+ time(&tpiv);
+ pivot = &tpiv;
+ }
+
+ et64 = ntpcal_ntp_to_ntp(etime, pivot);
+ tt64 = ntpcal_ntp_to_ntp(ttime, pivot);
+ pt = leapsec_get_table(TRUE);
+
+ if ( ucmpv64(&et64, &pt->head.expire) <= 0
+ || !leapsec_raw(pt, &tt64, total, FALSE) )
+ return FALSE;
+
+ pt->lsig.etime = etime;
+ pt->lsig.ttime = ttime;
+ pt->lsig.taiof = (int16_t)total;
+
+ pt->head.expire = et64;
+
+ return leapsec_set_table(pt);
+}
+
+/* ------------------------------------------------------------------ */
+int/*BOOL*/
+leapsec_add_dyn(
+ int insert,
+ uint32_t ntpnow,
+ const time_t * pivot )
+{
+ leap_table_t * pt;
+ vint64 now64;
+
+ pt = leapsec_get_table(TRUE);
+ now64 = ntpcal_ntp_to_ntp(ntpnow, pivot);
+ return ( leapsec_add(pt, &now64, (insert != 0))
+ && leapsec_set_table(pt));
+}
+
+/* =====================================================================
+ * internal helpers
+ */
+
+/* [internal] Reset / init the time window in the leap processor to
+ * force reload on next query. Since a leap transition cannot take place
+ * at an odd second, the value chosen avoids spurious leap transition
+ * triggers. Making all three times equal forces a reload. Using the
+ * maximum value for unsigned 64 bits makes finding the next leap frame
+ * a bit easier.
+ */
+static void
+reset_times(
+ leap_table_t * pt)
+{
+ memset(&pt->head.ebase, 0xFF, sizeof(vint64));
+ pt->head.stime = pt->head.ebase;
+ pt->head.ttime = pt->head.ebase;
+ pt->head.dtime = pt->head.ebase;
+}
+
+/* [internal] Add raw data to the table, removing old entries on the
+ * fly. This cannot fail currently.
+ */
+static int/*BOOL*/
+add_range(
+ leap_table_t * pt,
+ const leap_info_t * pi)
+{
+ /* If the table is full, make room by throwing out the oldest
+ * entry. But remember the accumulated leap seconds!
+ */
+ if (pt->head.size >= MAX_HIST) {
+ pt->head.size = MAX_HIST - 1;
+ pt->head.base_tai = pt->info[pt->head.size].taiof;
+ }
+
+ /* make room in lower end and insert item */
+ memmove(pt->info+1, pt->info, pt->head.size*sizeof(*pt->info));
+ pt->info[0] = *pi;
+ pt->head.size++;
+
+ /* invalidate the cached limit data -- we might have news ;-)
+ *
+ * This blocks a spurious transition detection. OTOH, if you add
+ * a value after the last query before a leap transition was
+ * expected to occur, this transition trigger is lost. But we
+ * can probably live with that.
+ */
+ reset_times(pt);
+ return TRUE;
+}
+
+/* [internal] given a reader function, read characters into a buffer
+ * until either EOL or EOF is reached. Makes sure that the buffer is
+ * always NUL terminated, but silently truncates excessive data. The
+ * EOL-marker ('\n') is *not* stored in the buffer.
+ *
+ * Returns the pointer to the buffer, unless EOF was reached when trying
+ * to read the first character of a line.
+ */
+static char *
+get_line(
+ leapsec_reader func,
+ void * farg,
+ char * buff,
+ size_t size)
+{
+ int ch;
+ char *ptr;
+
+ /* if we cannot even store the delimiter, declare failure */
+ if (buff == NULL || size == 0)
+ return NULL;
+
+ ptr = buff;
+ while (EOF != (ch = (*func)(farg)) && '\n' != ch)
+ if (size > 1) {
+ size--;
+ *ptr++ = (char)ch;
+ }
+ /* discard trailing whitespace */
+ while (ptr != buff && isspace((u_char)ptr[-1]))
+ ptr--;
+ *ptr = '\0';
+ return (ptr == buff && ch == EOF) ? NULL : buff;
+}
+
+/* [internal] skips whitespace characters from a character buffer. */
+static char *
+skipws(
+ const char *ptr)
+{
+ while (isspace((u_char)*ptr))
+ ptr++;
+ return (char*)noconst(ptr);
+}
+
+/* [internal] check if a strtoXYZ ended at EOL or whistespace and
+ * converted something at all. Return TRUE if something went wrong.
+ */
+static int/*BOOL*/
+parsefail(
+ const char * cp,
+ const char * ep)
+{
+ return (cp == ep)
+ || (*ep && *ep != '#' && !isspace((u_char)*ep));
+}
+
+/* [internal] reload the table limits around the given time stamp. This
+ * is where the real work is done when it comes to table lookup and
+ * evaluation. Some care has been taken to have correct code for dealing
+ * with boundary conditions and empty tables.
+ *
+ * In electric mode, transition and trip time are the same. In dumb
+ * mode, the difference of the TAI offsets must be taken into account
+ * and trip time and transition time become different. The difference
+ * becomes the warping distance when the trip time is reached.
+ */
+static void
+reload_limits(
+ leap_table_t * pt,
+ const vint64 * ts)
+{
+ int idx;
+
+ /* Get full time and search the true lower bound. Use a
+ * simple loop here, since the number of entries does
+ * not warrant a binary search. This also works for an empty
+ * table, so there is no shortcut for that case.
+ */
+ for (idx = 0; idx != pt->head.size; idx++)
+ if (ucmpv64(ts, &pt->info[idx].ttime) >= 0)
+ break;
+
+ /* get time limits with proper bound conditions. Note that the
+ * bounds of the table will be observed even if the table is
+ * empty -- no undefined condition must arise from this code.
+ */
+ if (idx >= pt->head.size) {
+ memset(&pt->head.ebase, 0x00, sizeof(vint64));
+ pt->head.this_tai = pt->head.base_tai;
+ } else {
+ pt->head.ebase = pt->info[idx].ttime;
+ pt->head.this_tai = pt->info[idx].taiof;
+ }
+ if (--idx >= 0) {
+ pt->head.next_tai = pt->info[idx].taiof;
+ pt->head.dynls = pt->info[idx].dynls;
+ pt->head.ttime = pt->info[idx].ttime;
+
+ if (_electric)
+ pt->head.dtime = pt->head.ttime;
+ else
+ pt->head.dtime = addv64i32(
+ &pt->head.ttime,
+ pt->head.next_tai - pt->head.this_tai);
+
+ pt->head.stime = subv64u32(
+ &pt->head.ttime, pt->info[idx].stime);
+
+ } else {
+ memset(&pt->head.ttime, 0xFF, sizeof(vint64));
+ pt->head.stime = pt->head.ttime;
+ pt->head.dtime = pt->head.ttime;
+ pt->head.next_tai = pt->head.this_tai;
+ pt->head.dynls = 0;
+ }
+}
+
+/* [internal] Take a time stamp and create a leap second frame for
+ * it. This will schedule a leap second for the beginning of the next
+ * month, midnight UTC. The 'insert' argument tells if a leap second is
+ * added (!=0) or removed (==0). We do not handle multiple inserts
+ * (yet?)
+ *
+ * Returns 1 if the insert worked, 0 otherwise. (It's not possible to
+ * insert a leap second into the current history -- only appending
+ * towards the future is allowed!)
+ */
+static int/*BOOL*/
+leapsec_add(
+ leap_table_t* pt ,
+ const vint64 * now64 ,
+ int insert)
+{
+ vint64 ttime, stime;
+ struct calendar fts;
+ leap_info_t li;
+
+ /* Check against the table expiration and the lates available
+ * leap entry. Do not permit inserts, only appends, and only if
+ * the extend the table beyond the expiration!
+ */
+ if ( ucmpv64(now64, &pt->head.expire) < 0
+ || (pt->head.size && ucmpv64(now64, &pt->info[0].ttime) <= 0)) {
+ errno = ERANGE;
+ return FALSE;
+ }
+
+ ntpcal_ntp64_to_date(&fts, now64);
+ /* To guard against dangling leap flags: do not accept leap
+ * second request on the 1st hour of the 1st day of the month.
+ */
+ if (fts.monthday == 1 && fts.hour == 0) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ /* Ok, do the remaining calculations */
+ fts.monthday = 1;
+ fts.hour = 0;
+ fts.minute = 0;
+ fts.second = 0;
+ stime = ntpcal_date_to_ntp64(&fts);
+ fts.month++;
+ ttime = ntpcal_date_to_ntp64(&fts);
+
+ li.ttime = ttime;
+ li.stime = ttime.D_s.lo - stime.D_s.lo;
+ li.taiof = (pt->head.size ? pt->info[0].taiof : pt->head.base_tai)
+ + (insert ? 1 : -1);
+ li.dynls = 1;
+ return add_range(pt, &li);
+}
+
+/* [internal] Given a time stamp for a leap insertion (the exact begin
+ * of the new leap era), create new leap frame and put it into the
+ * table. This is the work horse for reading a leap file and getting a
+ * leap second update via authenticated network packet.
+ */
+int/*BOOL*/
+leapsec_raw(
+ leap_table_t * pt,
+ const vint64 * ttime,
+ int taiof,
+ int dynls)
+{
+ vint64 stime;
+ struct calendar fts;
+ leap_info_t li;
+
+ /* Check that we only extend the table. Paranoia rulez! */
+ if (pt->head.size && ucmpv64(ttime, &pt->info[0].ttime) <= 0) {
+ errno = ERANGE;
+ return FALSE;
+ }
+
+ ntpcal_ntp64_to_date(&fts, ttime);
+ /* If this does not match the exact month start, bail out. */
+ if (fts.monthday != 1 || fts.hour || fts.minute || fts.second) {
+ errno = EINVAL;
+ return FALSE;
+ }
+ fts.month--; /* was in range 1..12, no overflow here! */
+ stime = ntpcal_date_to_ntp64(&fts);
+ li.ttime = *ttime;
+ li.stime = ttime->D_s.lo - stime.D_s.lo;
+ li.taiof = (int16_t)taiof;
+ li.dynls = (dynls != 0);
+ return add_range(pt, &li);
+}
+
+/* [internal] Do a wrap-around save range inclusion check.
+ * Returns TRUE if x in [lo,hi[ (intervall open on right side) with full
+ * handling of an overflow / wrap-around.
+ */
+static int/*BOOL*/
+betweenu32(
+ uint32_t lo,
+ uint32_t x,
+ uint32_t hi)
+{
+ int rc;
+
+ if (lo <= hi)
+ rc = (lo <= x) && (x < hi);
+ else
+ rc = (lo <= x) || (x < hi);
+ return rc;
+}
+
+/* =====================================================================
+ * validation stuff
+ */
+
+typedef struct {
+ unsigned char hv[ISC_SHA1_DIGESTLENGTH];
+} sha1_digest;
+
+/* [internal] parse a digest line to get the hash signature
+ * The NIST code creating the hash writes them out as 5 hex integers
+ * without leading zeros. This makes reading them back as hex-encoded
+ * BLOB impossible, because there might be less than 40 hex digits.
+ *
+ * The solution is to read the values back as integers, and then do the
+ * byte twiddle necessary to get it into an array of 20 chars. The
+ * drawback is that it permits any acceptable number syntax provided by
+ * 'scanf()' and 'strtoul()', including optional signs and '0x'
+ * prefixes.
+ */
+static int/*BOOL*/
+do_leap_hash(
+ sha1_digest * mac,
+ char const * cp )
+{
+ int wi, di, num, len;
+ unsigned long tmp[5];
+
+ memset(mac, 0, sizeof(*mac));
+ num = sscanf(cp, " %lx %lx %lx %lx %lx%n",
+ &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4],
+ &len);
+ if (num != 5 || cp[len] > ' ')
+ return FALSE;
+
+ /* now do the byte twiddle */
+ for (wi=0; wi < 5; ++wi)
+ for (di=3; di >= 0; --di) {
+ mac->hv[wi*4 + di] = (unsigned char)tmp[wi];
+ tmp[wi] >>= 8;
+ }
+ return TRUE;
+}
+
+/* [internal] add the digits of a data line to the hash, stopping at the
+ * next hash ('#') character.
+ */
+static void
+do_hash_data(
+ isc_sha1_t * mdctx,
+ char const * cp )
+{
+ unsigned char text[32]; // must be power of two!
+ unsigned int tlen = 0;
+ unsigned char ch;
+
+ while ('\0' != (ch = *cp++) && '#' != ch)
+ if (isdigit(ch)) {
+ text[tlen++] = ch;
+ tlen &= (sizeof(text)-1);
+ if (0 == tlen)
+ isc_sha1_update(
+ mdctx, text, sizeof(text));
+ }
+
+ if (0 < tlen)
+ isc_sha1_update(mdctx, text, tlen);
+}
+
+/* given a reader and a reader arg, calculate and validate the the hash
+ * signature of a NIST leap second file.
+ */
+int
+leapsec_validate(
+ leapsec_reader func,
+ void * farg)
+{
+ isc_sha1_t mdctx;
+ sha1_digest rdig, ldig; /* remote / local digests */
+ char line[50];
+ int hlseen = -1;
+
+ isc_sha1_init(&mdctx);
+ while (get_line(func, farg, line, sizeof(line))) {
+ if (!strncmp(line, "#h", 2))
+ hlseen = do_leap_hash(&rdig, line+2);
+ else if (!strncmp(line, "#@", 2))
+ do_hash_data(&mdctx, line+2);
+ else if (!strncmp(line, "#$", 2))
+ do_hash_data(&mdctx, line+2);
+ else if (isdigit(line[0]))
+ do_hash_data(&mdctx, line);
+ }
+ isc_sha1_final(&mdctx, ldig.hv);
+ isc_sha1_invalidate(&mdctx);
+
+ if (0 > hlseen)
+ return LSVALID_NOHASH;
+ if (0 == hlseen)
+ return LSVALID_BADFORMAT;
+ if (0 != memcmp(&rdig, &ldig, sizeof(sha1_digest)))
+ return LSVALID_BADHASH;
+ return LSVALID_GOODHASH;
+}
+
+/*
+ * lstostr - prettyprint NTP seconds
+ */
+static char * lstostr(
+ const vint64 * ts)
+{
+ char * buf;
+ struct calendar tm;
+
+ LIB_GETBUF(buf);
+ ntpcal_ntp64_to_date(&tm, ts);
+ snprintf(buf, LIB_BUFLENGTH, "%04d-%02d-%02dT%02d:%02dZ",
+ tm.year, tm.month, tm.monthday,
+ tm.hour, tm.minute);
+ return buf;
+}
+
+
+
+/* -*- that's all folks! -*- */
diff --git a/ntpd/ntp_leapsec.h b/ntpd/ntp_leapsec.h
new file mode 100644
index 0000000..75edcc4
--- /dev/null
+++ b/ntpd/ntp_leapsec.h
@@ -0,0 +1,218 @@
+/*
+ * ntp_leapsec.h - leap second processing for NTPD
+ *
+ * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
+ * The contents of 'html/copyright.html' apply.
+ * ----------------------------------------------------------------------
+ * This is an attempt to get the leap second handling into a dedicated
+ * module to make the somewhat convoluted logic testable.
+ */
+
+#ifndef NTP_LEAPSEC_H
+#define NTP_LEAPSEC_H
+
+struct stat;
+
+
+/* function pointer types. Note that 'fprintf' and 'getc' can be casted
+ * to the dumper resp. reader type, provided the auxiliary argument is a
+ * valid FILE pointer in hat case.
+ */
+typedef void (*leapsec_dumper)(void*, const char *fmt, ...);
+typedef int (*leapsec_reader)(void*);
+
+struct leap_table;
+typedef struct leap_table leap_table_t;
+
+/* Validate a stream containing a leap second file in the NIST / NTPD
+ * format that can also be loaded via 'leapsec_load()'. This uses
+ * the SHA1 hash and preprocessing as described in the NIST leapsecond
+ * file.
+ */
+#define LSVALID_GOODHASH 1 /* valid signature */
+#define LSVALID_NOHASH 0 /* no signature in file */
+#define LSVALID_BADHASH -1 /* signature mismatch */
+#define LSVALID_BADFORMAT -2 /* signature not parseable */
+
+extern int leapsec_validate(leapsec_reader, void*);
+
+
+/* Set/get electric mode
+ * Electric mode is defined as the operation mode where the system clock
+ * automagically manages the leap second, so we don't have to care about
+ * stepping the clock. (This should be the case with most systems,
+ * including the current implementation of the Win32 timekeeping.)
+ *
+ * The consequence of electric mode is that we do not 'see' the leap
+ * second, and no client actions are needed when crossing the leap era
+ * boundary. In manual (aka non-electric) mode the clock will simply
+ * step forward untill *we* (that is, this module) tells the client app
+ * to step at the right time. This needs a slightly different type of
+ * processing, so switching between those two modes should not be done
+ * too close to a leap second. The transition might be lost in that
+ * case. (The limit is actual 2 sec before transition.)
+ *
+ * OTOH, this is a system characteristic, so it's expected to be set
+ * properly somewhere after system start and retain the value.
+ *
+ * Simply querying the state or setting it to the same value as before
+ * does not have any unwanted side effects. You can query by giving a
+ * negative value for the switch.
+ */
+extern int/*BOOL*/ leapsec_electric(int/*BOOL*/ on);
+
+
+/* Query result for a leap second schedule
+ * 'ttime' is the transition point in full time scale, but only if
+ * 'tai_diff' is not zero. Nominal UTC time when the next leap
+ * era starts.
+ * 'ddist' is the distance to the transition, in clock seconds.
+ * This is the distance to the due time, which is different
+ * from the transition time if the mode is non-electric.
+ * Only valid if 'tai_diff' is not zero.
+ * 'tai_offs' is the CURRENT distance from clock (UTC) to TAI. Always valid.
+ * 'tai_diff' is the change in TAI offset after the next leap
+ * transition. Zero if nothing is pending or too far ahead.
+ * 'warped' is set only once, when the the leap second occurred between
+ * two queries. Always zero in electric mode. If non-zero,
+ * immediately step the clock.
+ * 'proximity' is a proximity warning. See definitions below. This is
+ * more useful than an absolute difference to the leap second.
+ * 'dynamic' != 0 if entry was requested by clock/peer
+ */
+struct leap_result {
+ vint64 ttime;
+ uint32_t ddist;
+ int16_t tai_offs;
+ int16_t tai_diff;
+ int16_t warped;
+ uint8_t proximity;
+ uint8_t dynamic;
+};
+typedef struct leap_result leap_result_t;
+
+struct leap_signature {
+ uint32_t etime; /* expiration time */
+ uint32_t ttime; /* transition time */
+ int16_t taiof; /* total offset to TAI */
+};
+typedef struct leap_signature leap_signature_t;
+
+
+#define LSPROX_NOWARN 0 /* clear radar screen */
+#define LSPROX_SCHEDULE 1 /* less than 1 month to target*/
+#define LSPROX_ANNOUNCE 2 /* less than 1 day to target */
+#define LSPROX_ALERT 3 /* less than 10 sec to target */
+
+/* Get the current or alternate table pointer. Getting the alternate
+ * pointer will automatically copy the primary table, so it can be
+ * subsequently modified.
+ */
+extern leap_table_t *leapsec_get_table(int alternate);
+
+/* Set the current leap table. Accepts only return values from
+ * 'leapsec_get_table()', so it's hard to do something wrong. Returns
+ * TRUE if the current table is the requested one.
+ */
+extern int/*BOOL*/ leapsec_set_table(leap_table_t *);
+
+/* Clear all leap second data. Use it for init & cleanup */
+extern void leapsec_clear(leap_table_t*);
+
+/* Load a leap second file. If 'blimit' is set, do not store (but
+ * register with their TAI offset) leap entries before the build date.
+ * Update the leap signature data on the fly.
+ */
+extern int/*BOOL*/ leapsec_load(leap_table_t*, leapsec_reader,
+ void*, int blimit);
+
+/* Dump the current leap table in readable format, using the provided
+ * dump formatter function.
+ */
+extern void leapsec_dump(const leap_table_t*, leapsec_dumper func, void *farg);
+
+/* Read a leap second file from stream. This is a convenience wrapper
+ * around the generic load function, 'leapsec_load()'.
+ */
+extern int/*BOOL*/ leapsec_load_stream(FILE * fp, const char * fname,
+ int/*BOOL*/logall);
+
+/* Read a leap second file from file. It checks that the file exists and
+ * (if 'force' is not applied) the ctime/mtime has changed since the
+ * last load. If the file has to be loaded, either due to 'force' or
+ * changed time stamps, the 'stat()' results of the file are stored in
+ * '*sb' for the next cycle. Returns TRUE on successful load, FALSE
+ * otherwise. Uses 'leapsec_load_stream()' internally.
+ */
+extern int/*BOOL*/ leapsec_load_file(const char * fname, struct stat * sb,
+ int/*BOOL*/force, int/*BOOL*/logall);
+
+/* Get the current leap data signature. This consists of the last
+ * ransition, the table expiration, and the total TAI difference at the
+ * last transition. This is valid even if the leap transition itself was
+ * culled due to the build date limit.
+ */
+extern void leapsec_getsig(leap_signature_t * psig);
+
+/* Check if the leap table is expired at the given time.
+ */
+extern int/*BOOL*/ leapsec_expired(uint32_t when, const time_t * pivot);
+
+/* Get the distance to expiration in days.
+ * Returns negative values if expired, zero if there are less than 24hrs
+ * left, and positive numbers otherwise.
+ */
+extern int32_t leapsec_daystolive(uint32_t when, const time_t * pivot);
+
+/* Reset the current leap frame, so the next query will do proper table
+ * lookup from fresh. Suppresses a possible leap era transition detection
+ * for the next query.
+ */
+extern void leapsec_reset_frame(void);
+
+/* Given a transition time, the TAI offset valid after that and an
+ * expiration time, try to establish a system leap transition. Only
+ * works if the existing table is extended. On success, updates the
+ * signature data.
+ */
+extern int/*BOOL*/ leapsec_add_fix(int offset, uint32_t ttime, uint32_t etime,
+ const time_t * pivot);
+
+/* Take a time stamp and create a leap second frame for it. This will
+ * schedule a leap second for the beginning of the next month, midnight
+ * UTC. The 'insert' argument tells if a leap second is added (!=0) or
+ * removed (==0). We do not handle multiple inserts (yet?)
+ *
+ * Returns 1 if the insert worked, 0 otherwise. (It's not possible to
+ * insert a leap second into the current history -- only appending
+ * towards the future is allowed!)
+ *
+ * 'ntp_now' is subject to era unfolding. The entry is marked
+ * dynamic. The leap signature is NOT updated.
+ */
+extern int/*BOOL*/ leapsec_add_dyn(int/*BOOL*/ insert, uint32_t ntp_now,
+ const time_t * pivot);
+
+/* Take a time stamp and get the associated leap information. The time
+ * stamp is subject to era unfolding around the pivot or the current
+ * system time if pivot is NULL. Sets the information in '*qr' and
+ * returns TRUE if a leap second era boundary was crossed between the
+ * last and the current query. In that case, qr->warped contains the
+ * required clock stepping, which is always zero in electric mode.
+ */
+extern int/*BOOL*/ leapsec_query(leap_result_t *qr, uint32_t ntpts,
+ const time_t * pivot);
+
+/* Get the current leap frame info. Returns TRUE if the result contains
+ * useable data, FALSE if there is currently no leap second frame.
+ * This merely replicates some results from a previous query, but since
+ * it does not check the current time, only the following entries are
+ * meaningful:
+ * qr->ttime;
+ * qr->tai_offs;
+ * qr->tai_diff;
+ * qr->dynamic;
+ */
+extern int/*BOOL*/ leapsec_frame(leap_result_t *qr);
+
+#endif /* !defined(NTP_LEAPSEC_H) */
diff --git a/ntpd/ntp_loopfilter.c b/ntpd/ntp_loopfilter.c
new file mode 100644
index 0000000..87db726
--- /dev/null
+++ b/ntpd/ntp_loopfilter.c
@@ -0,0 +1,1224 @@
+/*
+ * ntp_loopfilter.c - implements the NTP loop filter algorithm
+ *
+ * ATTENTION: Get approval from Dave Mills on all changes to this file!
+ *
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <signal.h>
+#include <setjmp.h>
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif /* KERNEL_PLL */
+
+/*
+ * This is an implementation of the clock discipline algorithm described
+ * in UDel TR 97-4-3, as amended. It operates as an adaptive parameter,
+ * hybrid phase/frequency-lock loop. A number of sanity checks are
+ * included to protect against timewarps, timespikes and general mayhem.
+ * All units are in s and s/s, unless noted otherwise.
+ */
+#define CLOCK_MAX .128 /* default step threshold (s) */
+#define CLOCK_MINSTEP 300. /* default stepout threshold (s) */
+#define CLOCK_PANIC 1000. /* default panic threshold (s) */
+#define CLOCK_PHI 15e-6 /* max frequency error (s/s) */
+#define CLOCK_PLL 16. /* PLL loop gain (log2) */
+#define CLOCK_AVG 8. /* parameter averaging constant */
+#define CLOCK_FLL .25 /* FLL loop gain */
+#define CLOCK_FLOOR .0005 /* startup offset floor (s) */
+#define CLOCK_ALLAN 11 /* Allan intercept (log2 s) */
+#define CLOCK_LIMIT 30 /* poll-adjust threshold */
+#define CLOCK_PGATE 4. /* poll-adjust gate */
+#define PPS_MAXAGE 120 /* kernel pps signal timeout (s) */
+#define FREQTOD(x) ((x) / 65536e6) /* NTP to double */
+#define DTOFREQ(x) ((int32)((x) * 65536e6)) /* double to NTP */
+
+/*
+ * Clock discipline state machine. This is used to control the
+ * synchronization behavior during initialization and following a
+ * timewarp.
+ *
+ * State < step > step Comments
+ * ========================================================
+ * NSET FREQ step, FREQ freq not set
+ *
+ * FSET SYNC step, SYNC freq set
+ *
+ * FREQ if (mu < 900) if (mu < 900) set freq direct
+ * ignore ignore
+ * else else
+ * freq, SYNC freq, step, SYNC
+ *
+ * SYNC SYNC SPIK, ignore adjust phase/freq
+ *
+ * SPIK SYNC if (mu < 900) adjust phase/freq
+ * ignore
+ * step, SYNC
+ */
+/*
+ * Kernel PLL/PPS state machine. This is used with the kernel PLL
+ * modifications described in the documentation.
+ *
+ * If kernel support for the ntp_adjtime() system call is available, the
+ * ntp_control flag is set. The ntp_enable and kern_enable flags can be
+ * set at configuration time or run time using ntpdc. If ntp_enable is
+ * false, the discipline loop is unlocked and no corrections of any kind
+ * are made. If both ntp_control and kern_enable are set, the kernel
+ * support is used as described above; if false, the kernel is bypassed
+ * entirely and the daemon discipline used instead.
+ *
+ * There have been three versions of the kernel discipline code. The
+ * first (microkernel) now in Solaris discipilnes the microseconds. The
+ * second and third (nanokernel) disciplines the clock in nanoseconds.
+ * These versions are identifed if the symbol STA_PLL is present in the
+ * header file /usr/include/sys/timex.h. The third and current version
+ * includes TAI offset and is identified by the symbol NTP_API with
+ * value 4.
+ *
+ * Each PPS time/frequency discipline can be enabled by the atom driver
+ * or another driver. If enabled, the STA_PPSTIME and STA_FREQ bits are
+ * set in the kernel status word; otherwise, these bits are cleared.
+ * These bits are also cleard if the kernel reports an error.
+ *
+ * If an external clock is present, the clock driver sets STA_CLK in the
+ * status word. When the local clock driver sees this bit, it updates
+ * via this routine, which then calls ntp_adjtime() with the STA_PLL bit
+ * set to zero, in which case the system clock is not adjusted. This is
+ * also a signal for the external clock driver to discipline the system
+ * clock. Unless specified otherwise, all times are in seconds.
+ */
+/*
+ * Program variables that can be tinkered.
+ */
+double clock_max = CLOCK_MAX; /* step threshold */
+double clock_minstep = CLOCK_MINSTEP; /* stepout threshold */
+double clock_panic = CLOCK_PANIC; /* panic threshold */
+double clock_phi = CLOCK_PHI; /* dispersion rate (s/s) */
+u_char allan_xpt = CLOCK_ALLAN; /* Allan intercept (log2 s) */
+
+/*
+ * Program variables
+ */
+static double clock_offset; /* offset */
+double clock_jitter; /* offset jitter */
+double drift_comp; /* frequency (s/s) */
+static double init_drift_comp; /* initial frequency (PPM) */
+double clock_stability; /* frequency stability (wander) (s/s) */
+double clock_codec; /* audio codec frequency (samples/s) */
+static u_long clock_epoch; /* last update */
+u_int sys_tai; /* TAI offset from UTC */
+static int loop_started; /* TRUE after LOOP_DRIFTINIT */
+static void rstclock (int, double); /* transition function */
+static double direct_freq(double); /* direct set frequency */
+static void set_freq(double); /* set frequency */
+#ifndef PATH_MAX
+# define PATH_MAX MAX_PATH
+#endif
+static char relative_path[PATH_MAX + 1]; /* relative path per recursive make */
+static char *this_file = NULL;
+
+#ifdef KERNEL_PLL
+static struct timex ntv; /* ntp_adjtime() parameters */
+int pll_status; /* last kernel status bits */
+#if defined(STA_NANO) && NTP_API == 4
+static u_int loop_tai; /* last TAI offset */
+#endif /* STA_NANO */
+static void start_kern_loop(void);
+static void stop_kern_loop(void);
+#endif /* KERNEL_PLL */
+
+/*
+ * Clock state machine control flags
+ */
+int ntp_enable = TRUE; /* clock discipline enabled */
+int pll_control; /* kernel support available */
+int kern_enable = TRUE; /* kernel support enabled */
+int hardpps_enable; /* kernel PPS discipline enabled */
+int ext_enable; /* external clock enabled */
+int pps_stratum; /* pps stratum */
+int allow_panic = FALSE; /* allow panic correction */
+int mode_ntpdate = FALSE; /* exit on first clock set */
+int freq_cnt; /* initial frequency clamp */
+int freq_set; /* initial set frequency switch */
+
+/*
+ * Clock state machine variables
+ */
+int state = 0; /* clock discipline state */
+u_char sys_poll; /* time constant/poll (log2 s) */
+int tc_counter; /* jiggle counter */
+double last_offset; /* last offset (s) */
+
+/*
+ * Huff-n'-puff filter variables
+ */
+static double *sys_huffpuff; /* huff-n'-puff filter */
+static int sys_hufflen; /* huff-n'-puff filter stages */
+static int sys_huffptr; /* huff-n'-puff filter pointer */
+static double sys_mindly; /* huff-n'-puff filter min delay */
+
+#if defined(KERNEL_PLL)
+/* Emacs cc-mode goes nuts if we split the next line... */
+#define MOD_BITS (MOD_OFFSET | MOD_MAXERROR | MOD_ESTERROR | \
+ MOD_STATUS | MOD_TIMECONST)
+#ifdef SIGSYS
+static void pll_trap (int); /* configuration trap */
+static struct sigaction sigsys; /* current sigaction status */
+static struct sigaction newsigsys; /* new sigaction status */
+static sigjmp_buf env; /* environment var. for pll_trap() */
+#endif /* SIGSYS */
+#endif /* KERNEL_PLL */
+
+/*
+ * file_name - return pointer to non-relative portion of this C file pathname
+ */
+static char *file_name(void)
+{
+ if (this_file == NULL) {
+ (void)strncpy(relative_path, __FILE__, PATH_MAX);
+ for (this_file=relative_path; *this_file && ! isalnum(*this_file); this_file++) ;
+ }
+ return this_file;
+}
+
+/*
+ * init_loopfilter - initialize loop filter data
+ */
+void
+init_loopfilter(void)
+{
+ /*
+ * Initialize state variables.
+ */
+ sys_poll = ntp_minpoll;
+ clock_jitter = LOGTOD(sys_precision);
+ freq_cnt = (int)clock_minstep;
+}
+
+#ifdef KERNEL_PLL
+/*
+ * ntp_adjtime_error_handler - process errors from ntp_adjtime
+ */
+static void
+ntp_adjtime_error_handler(
+ const char *caller, /* name of calling function */
+ struct timex *ptimex, /* pointer to struct timex */
+ int ret, /* return value from ntp_adjtime */
+ int saved_errno, /* value of errno when ntp_adjtime returned */
+ int pps_call, /* ntp_adjtime call was PPS-related */
+ int tai_call, /* ntp_adjtime call was TAI-related */
+ int line /* line number of ntp_adjtime call */
+ )
+{
+ switch (ret) {
+ case -1:
+ switch (saved_errno) {
+ case EFAULT:
+ msyslog(LOG_ERR, "%s: %s line %d: invalid struct timex pointer: 0x%lx",
+ caller, file_name(), line,
+ (long)((void *)ptimex)
+ );
+ break;
+ case EINVAL:
+ msyslog(LOG_ERR, "%s: %s line %d: invalid struct timex \"constant\" element value: %ld",
+ caller, file_name(), line,
+ (long)(ptimex->constant)
+ );
+ break;
+ case EPERM:
+ if (tai_call) {
+ errno = saved_errno;
+ msyslog(LOG_ERR,
+ "%s: ntp_adjtime(TAI) failed: %m",
+ caller);
+ }
+ errno = saved_errno;
+ msyslog(LOG_ERR, "%s: %s line %d: ntp_adjtime: %m",
+ caller, file_name(), line
+ );
+ break;
+ default:
+ msyslog(LOG_NOTICE, "%s: %s line %d: unhandled errno value %d after failed ntp_adjtime call",
+ caller, file_name(), line,
+ saved_errno
+ );
+ break;
+ }
+ break;
+#ifdef TIME_OK
+ case TIME_OK: /* 0 no leap second warning */
+ /* OK means OK */
+ break;
+#endif
+#ifdef TIME_INS
+ case TIME_INS: /* 1 positive leap second warning */
+ msyslog(LOG_INFO, "%s: %s line %d: kernel reports positive leap second warning state",
+ caller, file_name(), line
+ );
+ break;
+#endif
+#ifdef TIME_DEL
+ case TIME_DEL: /* 2 negative leap second warning */
+ msyslog(LOG_INFO, "%s: %s line %d: kernel reports negative leap second warning state",
+ caller, file_name(), line
+ );
+ break;
+#endif
+#ifdef TIME_OOP
+ case TIME_OOP: /* 3 leap second in progress */
+ msyslog(LOG_INFO, "%s: %s line %d: kernel reports leap second in progress",
+ caller, file_name(), line
+ );
+ break;
+#endif
+#ifdef TIME_WAIT
+ case TIME_WAIT: /* 4 leap second has occured */
+ msyslog(LOG_INFO, "%s: %s line %d: kernel reports leap second has occured",
+ caller, file_name(), line
+ );
+ break;
+#endif
+#ifdef TIME_ERROR
+ case TIME_ERROR: /* loss of synchronization */
+ if (pps_call && !(ptimex->status & STA_PPSSIGNAL))
+ report_event(EVNT_KERN, NULL,
+ "PPS no signal");
+ errno = saved_errno;
+ DPRINTF(1, ("kernel loop status (%s) %d %m\n",
+ k_st_flags(ptimex->status), errno));
+ break;
+#endif
+ default:
+ msyslog(LOG_NOTICE, "%s: %s line %d: unhandled return value %d from ntp_adjtime in %s at line %d",
+ caller, file_name(), line,
+ ret,
+ __func__, __LINE__
+ );
+ break;
+ }
+ return;
+}
+#endif
+
+/*
+ * local_clock - the NTP logical clock loop filter.
+ *
+ * Return codes:
+ * -1 update ignored: exceeds panic threshold
+ * 0 update ignored: popcorn or exceeds step threshold
+ * 1 clock was slewed
+ * 2 clock was stepped
+ *
+ * LOCKCLOCK: The only thing this routine does is set the
+ * sys_rootdisp variable equal to the peer dispersion.
+ */
+int
+local_clock(
+ struct peer *peer, /* synch source peer structure */
+ double fp_offset /* clock offset (s) */
+ )
+{
+ int rval; /* return code */
+ int osys_poll; /* old system poll */
+ int ntp_adj_ret; /* returned by ntp_adjtime */
+ double mu; /* interval since last update */
+ double clock_frequency; /* clock frequency */
+ double dtemp, etemp; /* double temps */
+ char tbuf[80]; /* report buffer */
+
+ /*
+ * If the loop is opened or the NIST LOCKCLOCK is in use,
+ * monitor and record the offsets anyway in order to determine
+ * the open-loop response and then go home.
+ */
+#ifdef LOCKCLOCK
+ {
+#else
+ if (!ntp_enable) {
+#endif /* LOCKCLOCK */
+ record_loop_stats(fp_offset, drift_comp, clock_jitter,
+ clock_stability, sys_poll);
+ return (0);
+ }
+
+#ifndef LOCKCLOCK
+ /*
+ * If the clock is way off, panic is declared. The clock_panic
+ * defaults to 1000 s; if set to zero, the panic will never
+ * occur. The allow_panic defaults to FALSE, so the first panic
+ * will exit. It can be set TRUE by a command line option, in
+ * which case the clock will be set anyway and time marches on.
+ * But, allow_panic will be set FALSE when the update is less
+ * than the step threshold; so, subsequent panics will exit.
+ */
+ if (fabs(fp_offset) > clock_panic && clock_panic > 0 &&
+ !allow_panic) {
+ snprintf(tbuf, sizeof(tbuf),
+ "%+.0f s; set clock manually within %.0f s.",
+ fp_offset, clock_panic);
+ report_event(EVNT_SYSFAULT, NULL, tbuf);
+ return (-1);
+ }
+
+ /*
+ * This section simulates ntpdate. If the offset exceeds the
+ * step threshold (128 ms), step the clock to that time and
+ * exit. Otherwise, slew the clock to that time and exit. Note
+ * that the slew will persist and eventually complete beyond the
+ * life of this program. Note that while ntpdate is active, the
+ * terminal does not detach, so the termination message prints
+ * directly to the terminal.
+ */
+ if (mode_ntpdate) {
+ if (fabs(fp_offset) > clock_max && clock_max > 0) {
+ step_systime(fp_offset);
+ msyslog(LOG_NOTICE, "ntpd: time set %+.6f s",
+ fp_offset);
+ printf("ntpd: time set %+.6fs\n", fp_offset);
+ } else {
+ adj_systime(fp_offset);
+ msyslog(LOG_NOTICE, "ntpd: time slew %+.6f s",
+ fp_offset);
+ printf("ntpd: time slew %+.6fs\n", fp_offset);
+ }
+ record_loop_stats(fp_offset, drift_comp, clock_jitter,
+ clock_stability, sys_poll);
+ exit (0);
+ }
+
+ /*
+ * The huff-n'-puff filter finds the lowest delay in the recent
+ * interval. This is used to correct the offset by one-half the
+ * difference between the sample delay and minimum delay. This
+ * is most effective if the delays are highly assymetric and
+ * clockhopping is avoided and the clock frequency wander is
+ * relatively small.
+ */
+ if (sys_huffpuff != NULL) {
+ if (peer->delay < sys_huffpuff[sys_huffptr])
+ sys_huffpuff[sys_huffptr] = peer->delay;
+ if (peer->delay < sys_mindly)
+ sys_mindly = peer->delay;
+ if (fp_offset > 0)
+ dtemp = -(peer->delay - sys_mindly) / 2;
+ else
+ dtemp = (peer->delay - sys_mindly) / 2;
+ fp_offset += dtemp;
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "local_clock: size %d mindly %.6f huffpuff %.6f\n",
+ sys_hufflen, sys_mindly, dtemp);
+#endif
+ }
+
+ /*
+ * Clock state machine transition function which defines how the
+ * system reacts to large phase and frequency excursion. There
+ * are two main regimes: when the offset exceeds the step
+ * threshold (128 ms) and when it does not. Under certain
+ * conditions updates are suspended until the stepout theshold
+ * (900 s) is exceeded. See the documentation on how these
+ * thresholds interact with commands and command line options.
+ *
+ * Note the kernel is disabled if step is disabled or greater
+ * than 0.5 s or in ntpdate mode.
+ */
+ osys_poll = sys_poll;
+ if (sys_poll < peer->minpoll)
+ sys_poll = peer->minpoll;
+ if (sys_poll > peer->maxpoll)
+ sys_poll = peer->maxpoll;
+ mu = current_time - clock_epoch;
+ clock_frequency = drift_comp;
+ rval = 1;
+ if (fabs(fp_offset) > clock_max && clock_max > 0) {
+ switch (state) {
+
+ /*
+ * In SYNC state we ignore the first outlyer and switch
+ * to SPIK state.
+ */
+ case EVNT_SYNC:
+ snprintf(tbuf, sizeof(tbuf), "%+.6f s",
+ fp_offset);
+ report_event(EVNT_SPIK, NULL, tbuf);
+ state = EVNT_SPIK;
+ return (0);
+
+ /*
+ * In FREQ state we ignore outlyers and inlyers. At the
+ * first outlyer after the stepout threshold, compute
+ * the apparent frequency correction and step the phase.
+ */
+ case EVNT_FREQ:
+ if (mu < clock_minstep)
+ return (0);
+
+ clock_frequency = direct_freq(fp_offset);
+
+ /* fall through to EVNT_SPIK */
+
+ /*
+ * In SPIK state we ignore succeeding outlyers until
+ * either an inlyer is found or the stepout threshold is
+ * exceeded.
+ */
+ case EVNT_SPIK:
+ if (mu < clock_minstep)
+ return (0);
+
+ /* fall through to default */
+
+ /*
+ * We get here by default in NSET and FSET states and
+ * from above in FREQ or SPIK states.
+ *
+ * In NSET state an initial frequency correction is not
+ * available, usually because the frequency file has not
+ * yet been written. Since the time is outside the step
+ * threshold, the clock is stepped. The frequency will
+ * be set directly following the stepout interval.
+ *
+ * In FSET state the initial frequency has been set from
+ * the frequency file. Since the time is outside the
+ * step threshold, the clock is stepped immediately,
+ * rather than after the stepout interval. Guys get
+ * nervous if it takes 15 minutes to set the clock for
+ * the first time.
+ *
+ * In FREQ and SPIK states the stepout threshold has
+ * expired and the phase is still above the step
+ * threshold. Note that a single spike greater than the
+ * step threshold is always suppressed, even with a
+ * long time constant.
+ */
+ default:
+ snprintf(tbuf, sizeof(tbuf), "%+.6f s",
+ fp_offset);
+ report_event(EVNT_CLOCKRESET, NULL, tbuf);
+ step_systime(fp_offset);
+ reinit_timer();
+ tc_counter = 0;
+ clock_jitter = LOGTOD(sys_precision);
+ rval = 2;
+ if (state == EVNT_NSET) {
+ rstclock(EVNT_FREQ, 0);
+ return (rval);
+ }
+ break;
+ }
+ rstclock(EVNT_SYNC, 0);
+ } else {
+
+ /*
+ * The offset is less than the step threshold. Calculate
+ * the jitter as the exponentially weighted offset
+ * differences.
+ */
+ etemp = SQUARE(clock_jitter);
+ dtemp = SQUARE(max(fabs(fp_offset - last_offset),
+ LOGTOD(sys_precision)));
+ clock_jitter = SQRT(etemp + (dtemp - etemp) /
+ CLOCK_AVG);
+ switch (state) {
+
+ /*
+ * In NSET state this is the first update received and
+ * the frequency has not been initialized. Adjust the
+ * phase, but do not adjust the frequency until after
+ * the stepout threshold.
+ */
+ case EVNT_NSET:
+ adj_systime(fp_offset);
+ rstclock(EVNT_FREQ, fp_offset);
+ break;
+
+ /*
+ * In FREQ state ignore updates until the stepout
+ * threshold. After that, compute the new frequency, but
+ * do not adjust the frequency until the holdoff counter
+ * decrements to zero.
+ */
+ case EVNT_FREQ:
+ if (mu < clock_minstep)
+ return (0);
+
+ clock_frequency = direct_freq(fp_offset);
+ /* fall through */
+
+ /*
+ * We get here by default in FSET, SPIK and SYNC states.
+ * Here compute the frequency update due to PLL and FLL
+ * contributions. Note, we avoid frequency discipline at
+ * startup until the initial transient has subsided.
+ */
+ default:
+ allow_panic = FALSE;
+ if (freq_cnt == 0) {
+
+ /*
+ * The FLL and PLL frequency gain constants
+ * depend on the time constant and Allan
+ * intercept. The PLL is always used, but
+ * becomes ineffective above the Allan intercept
+ * where the FLL becomes effective.
+ */
+ if (sys_poll >= allan_xpt)
+ clock_frequency += (fp_offset -
+ clock_offset) / max(ULOGTOD(sys_poll),
+ mu) * CLOCK_FLL;
+
+ /*
+ * The PLL frequency gain (numerator) depends on
+ * the minimum of the update interval and Allan
+ * intercept. This reduces the PLL gain when the
+ * FLL becomes effective.
+ */
+ etemp = min(ULOGTOD(allan_xpt), mu);
+ dtemp = 4 * CLOCK_PLL * ULOGTOD(sys_poll);
+ clock_frequency += fp_offset * etemp / (dtemp *
+ dtemp);
+ }
+ rstclock(EVNT_SYNC, fp_offset);
+ if (fabs(fp_offset) < CLOCK_FLOOR)
+ freq_cnt = 0;
+ break;
+ }
+ }
+
+#ifdef KERNEL_PLL
+ /*
+ * This code segment works when clock adjustments are made using
+ * precision time kernel support and the ntp_adjtime() system
+ * call. This support is available in Solaris 2.6 and later,
+ * Digital Unix 4.0 and later, FreeBSD, Linux and specially
+ * modified kernels for HP-UX 9 and Ultrix 4. In the case of the
+ * DECstation 5000/240 and Alpha AXP, additional kernel
+ * modifications provide a true microsecond clock and nanosecond
+ * clock, respectively.
+ *
+ * Important note: The kernel discipline is used only if the
+ * step threshold is less than 0.5 s, as anything higher can
+ * lead to overflow problems. This might occur if some misguided
+ * lad set the step threshold to something ridiculous.
+ */
+ if (pll_control && kern_enable && freq_cnt == 0) {
+
+ /*
+ * We initialize the structure for the ntp_adjtime()
+ * system call. We have to convert everything to
+ * microseconds or nanoseconds first. Do not update the
+ * system variables if the ext_enable flag is set. In
+ * this case, the external clock driver will update the
+ * variables, which will be read later by the local
+ * clock driver. Afterwards, remember the time and
+ * frequency offsets for jitter and stability values and
+ * to update the frequency file.
+ */
+ ZERO(ntv);
+ if (ext_enable) {
+ ntv.modes = MOD_STATUS;
+ } else {
+#ifdef STA_NANO
+ ntv.modes = MOD_BITS | MOD_NANO;
+#else /* STA_NANO */
+ ntv.modes = MOD_BITS;
+#endif /* STA_NANO */
+ if (clock_offset < 0)
+ dtemp = -.5;
+ else
+ dtemp = .5;
+#ifdef STA_NANO
+ ntv.offset = (int32)(clock_offset * 1e9 +
+ dtemp);
+ ntv.constant = sys_poll;
+#else /* STA_NANO */
+ ntv.offset = (int32)(clock_offset * 1e6 +
+ dtemp);
+ ntv.constant = sys_poll - 4;
+#endif /* STA_NANO */
+ if (ntv.constant < 0)
+ ntv.constant = 0;
+
+ ntv.esterror = (u_int32)(clock_jitter * 1e6);
+ ntv.maxerror = (u_int32)((sys_rootdelay / 2 +
+ sys_rootdisp) * 1e6);
+ ntv.status = STA_PLL;
+
+ /*
+ * Enable/disable the PPS if requested.
+ */
+ if (hardpps_enable) {
+ if (!(pll_status & STA_PPSTIME))
+ report_event(EVNT_KERN,
+ NULL, "PPS enabled");
+ ntv.status |= STA_PPSTIME | STA_PPSFREQ;
+ } else {
+ if (pll_status & STA_PPSTIME)
+ report_event(EVNT_KERN,
+ NULL, "PPS disabled");
+ ntv.status &= ~(STA_PPSTIME |
+ STA_PPSFREQ);
+ }
+ if (sys_leap == LEAP_ADDSECOND)
+ ntv.status |= STA_INS;
+ else if (sys_leap == LEAP_DELSECOND)
+ ntv.status |= STA_DEL;
+ }
+
+ /*
+ * Pass the stuff to the kernel. If it squeals, turn off
+ * the pps. In any case, fetch the kernel offset,
+ * frequency and jitter.
+ */
+ if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) {
+ ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, hardpps_enable, 0, __LINE__ - 1);
+ }
+ pll_status = ntv.status;
+#ifdef STA_NANO
+ clock_offset = ntv.offset / 1e9;
+#else /* STA_NANO */
+ clock_offset = ntv.offset / 1e6;
+#endif /* STA_NANO */
+ clock_frequency = FREQTOD(ntv.freq);
+
+ /*
+ * If the kernel PPS is lit, monitor its performance.
+ */
+ if (ntv.status & STA_PPSTIME) {
+#ifdef STA_NANO
+ clock_jitter = ntv.jitter / 1e9;
+#else /* STA_NANO */
+ clock_jitter = ntv.jitter / 1e6;
+#endif /* STA_NANO */
+ }
+
+#if defined(STA_NANO) && NTP_API == 4
+ /*
+ * If the TAI changes, update the kernel TAI.
+ */
+ if (loop_tai != sys_tai) {
+ loop_tai = sys_tai;
+ ntv.modes = MOD_TAI;
+ ntv.constant = sys_tai;
+ if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) {
+ ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 1, __LINE__ - 1);
+ }
+ }
+#endif /* STA_NANO */
+ }
+#endif /* KERNEL_PLL */
+
+ /*
+ * Clamp the frequency within the tolerance range and calculate
+ * the frequency difference since the last update.
+ */
+ if (fabs(clock_frequency) > NTP_MAXFREQ)
+ msyslog(LOG_NOTICE,
+ "frequency error %.0f PPM exceeds tolerance %.0f PPM",
+ clock_frequency * 1e6, NTP_MAXFREQ * 1e6);
+ dtemp = SQUARE(clock_frequency - drift_comp);
+ if (clock_frequency > NTP_MAXFREQ)
+ drift_comp = NTP_MAXFREQ;
+ else if (clock_frequency < -NTP_MAXFREQ)
+ drift_comp = -NTP_MAXFREQ;
+ else
+ drift_comp = clock_frequency;
+
+ /*
+ * Calculate the wander as the exponentially weighted RMS
+ * frequency differences. Record the change for the frequency
+ * file update.
+ */
+ etemp = SQUARE(clock_stability);
+ clock_stability = SQRT(etemp + (dtemp - etemp) / CLOCK_AVG);
+
+ /*
+ * Here we adjust the time constant by comparing the current
+ * offset with the clock jitter. If the offset is less than the
+ * clock jitter times a constant, then the averaging interval is
+ * increased, otherwise it is decreased. A bit of hysteresis
+ * helps calm the dance. Works best using burst mode. Don't
+ * fiddle with the poll during the startup clamp period.
+ */
+ if (freq_cnt > 0) {
+ tc_counter = 0;
+ } else if (fabs(clock_offset) < CLOCK_PGATE * clock_jitter) {
+ tc_counter += sys_poll;
+ if (tc_counter > CLOCK_LIMIT) {
+ tc_counter = CLOCK_LIMIT;
+ if (sys_poll < peer->maxpoll) {
+ tc_counter = 0;
+ sys_poll++;
+ }
+ }
+ } else {
+ tc_counter -= sys_poll << 1;
+ if (tc_counter < -CLOCK_LIMIT) {
+ tc_counter = -CLOCK_LIMIT;
+ if (sys_poll > peer->minpoll) {
+ tc_counter = 0;
+ sys_poll--;
+ }
+ }
+ }
+
+ /*
+ * If the time constant has changed, update the poll variables.
+ */
+ if (osys_poll != sys_poll)
+ poll_update(peer, sys_poll);
+
+ /*
+ * Yibbidy, yibbbidy, yibbidy; that'h all folks.
+ */
+ record_loop_stats(clock_offset, drift_comp, clock_jitter,
+ clock_stability, sys_poll);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "local_clock: offset %.9f jit %.9f freq %.3f stab %.3f poll %d\n",
+ clock_offset, clock_jitter, drift_comp * 1e6,
+ clock_stability * 1e6, sys_poll);
+#endif /* DEBUG */
+ return (rval);
+#endif /* LOCKCLOCK */
+}
+
+
+/*
+ * adj_host_clock - Called once every second to update the local clock.
+ *
+ * LOCKCLOCK: The only thing this routine does is increment the
+ * sys_rootdisp variable.
+ */
+void
+adj_host_clock(
+ void
+ )
+{
+ double offset_adj;
+ double freq_adj;
+
+ /*
+ * Update the dispersion since the last update. In contrast to
+ * NTPv3, NTPv4 does not declare unsynchronized after one day,
+ * since the dispersion check serves this function. Also,
+ * since the poll interval can exceed one day, the old test
+ * would be counterproductive. During the startup clamp period, the
+ * time constant is clamped at 2.
+ */
+ sys_rootdisp += clock_phi;
+#ifndef LOCKCLOCK
+ if (!ntp_enable || mode_ntpdate)
+ return;
+ /*
+ * Determine the phase adjustment. The gain factor (denominator)
+ * increases with poll interval, so is dominated by the FLL
+ * above the Allan intercept. Note the reduced time constant at
+ * startup.
+ */
+ if (state != EVNT_SYNC) {
+ offset_adj = 0.;
+ } else if (freq_cnt > 0) {
+ offset_adj = clock_offset / (CLOCK_PLL * ULOGTOD(1));
+ freq_cnt--;
+#ifdef KERNEL_PLL
+ } else if (pll_control && kern_enable) {
+ offset_adj = 0.;
+#endif /* KERNEL_PLL */
+ } else {
+ offset_adj = clock_offset / (CLOCK_PLL * ULOGTOD(sys_poll));
+ }
+
+ /*
+ * If the kernel discipline is enabled the frequency correction
+ * drift_comp has already been engaged via ntp_adjtime() in
+ * set_freq(). Otherwise it is a component of the adj_systime()
+ * offset.
+ */
+#ifdef KERNEL_PLL
+ if (pll_control && kern_enable)
+ freq_adj = 0.;
+ else
+#endif /* KERNEL_PLL */
+ freq_adj = drift_comp;
+
+ /* Bound absolute value of total adjustment to NTP_MAXFREQ. */
+ if (offset_adj + freq_adj > NTP_MAXFREQ)
+ offset_adj = NTP_MAXFREQ - freq_adj;
+ else if (offset_adj + freq_adj < -NTP_MAXFREQ)
+ offset_adj = -NTP_MAXFREQ - freq_adj;
+
+ clock_offset -= offset_adj;
+ /*
+ * Windows port adj_systime() must be called each second,
+ * even if the argument is zero, to ease emulation of
+ * adjtime() using Windows' slew API which controls the rate
+ * but does not automatically stop slewing when an offset
+ * has decayed to zero.
+ */
+ adj_systime(offset_adj + freq_adj);
+#endif /* LOCKCLOCK */
+}
+
+
+/*
+ * Clock state machine. Enter new state and set state variables.
+ */
+static void
+rstclock(
+ int trans, /* new state */
+ double offset /* new offset */
+ )
+{
+#ifdef DEBUG
+ if (debug > 1)
+ printf("local_clock: mu %lu state %d poll %d count %d\n",
+ current_time - clock_epoch, trans, sys_poll,
+ tc_counter);
+#endif
+ if (trans != state && trans != EVNT_FSET)
+ report_event(trans, NULL, NULL);
+ state = trans;
+ last_offset = clock_offset = offset;
+ clock_epoch = current_time;
+}
+
+
+/*
+ * calc_freq - calculate frequency directly
+ *
+ * This is very carefully done. When the offset is first computed at the
+ * first update, a residual frequency component results. Subsequently,
+ * updates are suppresed until the end of the measurement interval while
+ * the offset is amortized. At the end of the interval the frequency is
+ * calculated from the current offset, residual offset, length of the
+ * interval and residual frequency component. At the same time the
+ * frequenchy file is armed for update at the next hourly stats.
+ */
+static double
+direct_freq(
+ double fp_offset
+ )
+{
+ set_freq(fp_offset / (current_time - clock_epoch));
+
+ return drift_comp;
+}
+
+
+/*
+ * set_freq - set clock frequency correction
+ *
+ * Used to step the frequency correction at startup, possibly again once
+ * the frequency is measured (that is, transitioning from EVNT_NSET to
+ * EVNT_FSET), and finally to switch between daemon and kernel loop
+ * discipline at runtime.
+ *
+ * When the kernel loop discipline is available but the daemon loop is
+ * in use, the kernel frequency correction is disabled (set to 0) to
+ * ensure drift_comp is applied by only one of the loops.
+ */
+static void
+set_freq(
+ double freq /* frequency update */
+ )
+{
+ const char * loop_desc;
+ int ntp_adj_ret;
+
+ drift_comp = freq;
+ loop_desc = "ntpd";
+#ifdef KERNEL_PLL
+ if (pll_control) {
+ ZERO(ntv);
+ ntv.modes = MOD_FREQUENCY;
+ if (kern_enable) {
+ loop_desc = "kernel";
+ ntv.freq = DTOFREQ(drift_comp);
+ }
+ if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) {
+ ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 0, __LINE__ - 1);
+ }
+ }
+#endif /* KERNEL_PLL */
+ mprintf_event(EVNT_FSET, NULL, "%s %.3f PPM", loop_desc,
+ drift_comp * 1e6);
+}
+
+
+#ifdef KERNEL_PLL
+static void
+start_kern_loop(void)
+{
+ static int atexit_done;
+ int ntp_adj_ret;
+
+ pll_control = TRUE;
+ ZERO(ntv);
+ ntv.modes = MOD_BITS;
+ ntv.status = STA_PLL;
+ ntv.maxerror = MAXDISPERSE;
+ ntv.esterror = MAXDISPERSE;
+ ntv.constant = sys_poll; /* why is it that here constant is unconditionally set to sys_poll, whereas elsewhere is is modified depending on nanosecond vs. microsecond kernel? */
+#ifdef SIGSYS
+ /*
+ * Use sigsetjmp() to save state and then call ntp_adjtime(); if
+ * it fails, then pll_trap() will set pll_control FALSE before
+ * returning control using siglogjmp().
+ */
+ newsigsys.sa_handler = pll_trap;
+ newsigsys.sa_flags = 0;
+ if (sigaction(SIGSYS, &newsigsys, &sigsys)) {
+ msyslog(LOG_ERR, "sigaction() trap SIGSYS: %m");
+ pll_control = FALSE;
+ } else {
+ if (sigsetjmp(env, 1) == 0) {
+ if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) {
+ ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 0, __LINE__ - 1);
+ }
+ }
+ if (sigaction(SIGSYS, &sigsys, NULL)) {
+ msyslog(LOG_ERR,
+ "sigaction() restore SIGSYS: %m");
+ pll_control = FALSE;
+ }
+ }
+#else /* SIGSYS */
+ if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) {
+ ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 0, __LINE__ - 1);
+ }
+#endif /* SIGSYS */
+
+ /*
+ * Save the result status and light up an external clock
+ * if available.
+ */
+ pll_status = ntv.status;
+ if (pll_control) {
+ if (!atexit_done) {
+ atexit_done = TRUE;
+ atexit(&stop_kern_loop);
+ }
+#ifdef STA_NANO
+ if (pll_status & STA_CLK)
+ ext_enable = TRUE;
+#endif /* STA_NANO */
+ report_event(EVNT_KERN, NULL,
+ "kernel time sync enabled");
+ }
+}
+#endif /* KERNEL_PLL */
+
+
+#ifdef KERNEL_PLL
+static void
+stop_kern_loop(void)
+{
+ if (pll_control && kern_enable)
+ report_event(EVNT_KERN, NULL,
+ "kernel time sync disabled");
+}
+#endif /* KERNEL_PLL */
+
+
+/*
+ * select_loop() - choose kernel or daemon loop discipline.
+ */
+void
+select_loop(
+ int use_kern_loop
+ )
+{
+ if (kern_enable == use_kern_loop)
+ return;
+#ifdef KERNEL_PLL
+ if (pll_control && !use_kern_loop)
+ stop_kern_loop();
+#endif
+ kern_enable = use_kern_loop;
+#ifdef KERNEL_PLL
+ if (pll_control && use_kern_loop)
+ start_kern_loop();
+#endif
+ /*
+ * If this loop selection change occurs after initial startup,
+ * call set_freq() to switch the frequency compensation to or
+ * from the kernel loop.
+ */
+#ifdef KERNEL_PLL
+ if (pll_control && loop_started)
+ set_freq(drift_comp);
+#endif
+}
+
+
+/*
+ * huff-n'-puff filter
+ */
+void
+huffpuff(void)
+{
+ int i;
+
+ if (sys_huffpuff == NULL)
+ return;
+
+ sys_huffptr = (sys_huffptr + 1) % sys_hufflen;
+ sys_huffpuff[sys_huffptr] = 1e9;
+ sys_mindly = 1e9;
+ for (i = 0; i < sys_hufflen; i++) {
+ if (sys_huffpuff[i] < sys_mindly)
+ sys_mindly = sys_huffpuff[i];
+ }
+}
+
+
+/*
+ * loop_config - configure the loop filter
+ *
+ * LOCKCLOCK: The LOOP_DRIFTINIT and LOOP_DRIFTCOMP cases are no-ops.
+ */
+void
+loop_config(
+ int item,
+ double freq
+ )
+{
+ int i;
+ double ftemp;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("loop_config: item %d freq %f\n", item, freq);
+#endif
+ switch (item) {
+
+ /*
+ * We first assume the kernel supports the ntp_adjtime()
+ * syscall. If that syscall works, initialize the kernel time
+ * variables. Otherwise, continue leaving no harm behind.
+ */
+ case LOOP_DRIFTINIT:
+#ifndef LOCKCLOCK
+#ifdef KERNEL_PLL
+ if (mode_ntpdate)
+ break;
+
+ start_kern_loop();
+#endif /* KERNEL_PLL */
+
+ /*
+ * Initialize frequency if given; otherwise, begin frequency
+ * calibration phase.
+ */
+ ftemp = init_drift_comp / 1e6;
+ if (ftemp > NTP_MAXFREQ)
+ ftemp = NTP_MAXFREQ;
+ else if (ftemp < -NTP_MAXFREQ)
+ ftemp = -NTP_MAXFREQ;
+ set_freq(ftemp);
+ if (freq_set)
+ rstclock(EVNT_FSET, 0);
+ else
+ rstclock(EVNT_NSET, 0);
+ loop_started = TRUE;
+#endif /* LOCKCLOCK */
+ break;
+
+ case LOOP_KERN_CLEAR:
+ break;
+
+ /*
+ * Tinker command variables for Ulrich Windl. Very dangerous.
+ */
+ case LOOP_ALLAN: /* Allan intercept (log2) (allan) */
+ allan_xpt = (u_char)freq;
+ break;
+
+ case LOOP_CODEC: /* audio codec frequency (codec) */
+ clock_codec = freq / 1e6;
+ break;
+
+ case LOOP_PHI: /* dispersion threshold (dispersion) */
+ clock_phi = freq / 1e6;
+ break;
+
+ case LOOP_FREQ: /* initial frequency (freq) */
+ init_drift_comp = freq;
+ freq_set++;
+ break;
+
+ case LOOP_HUFFPUFF: /* huff-n'-puff length (huffpuff) */
+ if (freq < HUFFPUFF)
+ freq = HUFFPUFF;
+ sys_hufflen = (int)(freq / HUFFPUFF);
+ sys_huffpuff = emalloc(sizeof(sys_huffpuff[0]) *
+ sys_hufflen);
+ for (i = 0; i < sys_hufflen; i++)
+ sys_huffpuff[i] = 1e9;
+ sys_mindly = 1e9;
+ break;
+
+ case LOOP_PANIC: /* panic threshold (panic) */
+ clock_panic = freq;
+ break;
+
+ case LOOP_MAX: /* step threshold (step) */
+ clock_max = freq;
+ if (clock_max == 0 || clock_max > 0.5)
+ select_loop(FALSE);
+ break;
+
+ case LOOP_MINSTEP: /* stepout threshold (stepout) */
+ if (freq < CLOCK_MINSTEP)
+ clock_minstep = CLOCK_MINSTEP;
+ else
+ clock_minstep = freq;
+ break;
+
+ case LOOP_TICK: /* tick increment (tick) */
+ set_sys_tick_precision(freq);
+ break;
+
+ case LOOP_LEAP: /* not used, fall through */
+ default:
+ msyslog(LOG_NOTICE,
+ "loop_config: unsupported option %d", item);
+ }
+}
+
+
+#if defined(KERNEL_PLL) && defined(SIGSYS)
+/*
+ * _trap - trap processor for undefined syscalls
+ *
+ * This nugget is called by the kernel when the SYS_ntp_adjtime()
+ * syscall bombs because the silly thing has not been implemented in
+ * the kernel. In this case the phase-lock loop is emulated by
+ * the stock adjtime() syscall and a lot of indelicate abuse.
+ */
+static RETSIGTYPE
+pll_trap(
+ int arg
+ )
+{
+ pll_control = FALSE;
+ siglongjmp(env, 1);
+}
+#endif /* KERNEL_PLL && SIGSYS */
diff --git a/ntpd/ntp_monitor.c b/ntpd/ntp_monitor.c
new file mode 100644
index 0000000..1214e1d
--- /dev/null
+++ b/ntpd/ntp_monitor.c
@@ -0,0 +1,496 @@
+/*
+ * ntp_monitor - monitor ntpd statistics
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_if.h"
+#include "ntp_lists.h"
+#include "ntp_stdlib.h"
+#include <ntp_random.h>
+
+#include <stdio.h>
+#include <signal.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+/*
+ * Record statistics based on source address, mode and version. The
+ * receive procedure calls us with the incoming rbufp before it does
+ * anything else. While at it, implement rate controls for inbound
+ * traffic.
+ *
+ * Each entry is doubly linked into two lists, a hash table and a most-
+ * recently-used (MRU) list. When a packet arrives it is looked up in
+ * the hash table. If found, the statistics are updated and the entry
+ * relinked at the head of the MRU list. If not found, a new entry is
+ * allocated, initialized and linked into both the hash table and at the
+ * head of the MRU list.
+ *
+ * Memory is usually allocated by grabbing a big chunk of new memory and
+ * cutting it up into littler pieces. The exception to this when we hit
+ * the memory limit. Then we free memory by grabbing entries off the
+ * tail for the MRU list, unlinking from the hash table, and
+ * reinitializing.
+ *
+ * INC_MONLIST is the default allocation granularity in entries.
+ * INIT_MONLIST is the default initial allocation in entries.
+ */
+#ifdef MONMEMINC /* old name */
+# define INC_MONLIST MONMEMINC
+#elif !defined(INC_MONLIST)
+# define INC_MONLIST (4 * 1024 / sizeof(mon_entry))
+#endif
+#ifndef INIT_MONLIST
+# define INIT_MONLIST (4 * 1024 / sizeof(mon_entry))
+#endif
+#ifndef MRU_MAXDEPTH_DEF
+# define MRU_MAXDEPTH_DEF (1024 * 1024 / sizeof(mon_entry))
+#endif
+
+/*
+ * Hashing stuff
+ */
+u_char mon_hash_bits;
+
+/*
+ * Pointers to the hash table and the MRU list. Memory for the hash
+ * table is allocated only if monitoring is enabled.
+ */
+mon_entry ** mon_hash; /* MRU hash table */
+mon_entry mon_mru_list; /* mru listhead */
+
+/*
+ * List of free structures structures, and counters of in-use and total
+ * structures. The free structures are linked with the hash_next field.
+ */
+static mon_entry *mon_free; /* free list or null if none */
+ u_int mru_alloc; /* mru list + free list count */
+ u_int mru_entries; /* mru list count */
+ u_int mru_peakentries; /* highest mru_entries seen */
+ u_int mru_initalloc = INIT_MONLIST;/* entries to preallocate */
+ u_int mru_incalloc = INC_MONLIST;/* allocation batch factor */
+static u_int mon_mem_increments; /* times called malloc() */
+
+/*
+ * Parameters of the RES_LIMITED restriction option. We define headway
+ * as the idle time between packets. A packet is discarded if the
+ * headway is less than the minimum, as well as if the average headway
+ * is less than eight times the increment.
+ */
+int ntp_minpkt = NTP_MINPKT; /* minimum (log 2 s) */
+u_char ntp_minpoll = NTP_MINPOLL; /* increment (log 2 s) */
+
+/*
+ * Initialization state. We may be monitoring, we may not. If
+ * we aren't, we may not even have allocated any memory yet.
+ */
+ u_int mon_enabled; /* enable switch */
+ u_int mru_mindepth = 600; /* preempt above this */
+ int mru_maxage = 64; /* for entries older than */
+ u_int mru_maxdepth = /* MRU count hard limit */
+ MRU_MAXDEPTH_DEF;
+ int mon_age = 3000; /* preemption limit */
+
+static void mon_getmoremem(void);
+static void remove_from_hash(mon_entry *);
+static inline void mon_free_entry(mon_entry *);
+static inline void mon_reclaim_entry(mon_entry *);
+
+
+/*
+ * init_mon - initialize monitoring global data
+ */
+void
+init_mon(void)
+{
+ /*
+ * Don't do much of anything here. We don't allocate memory
+ * until mon_start().
+ */
+ mon_enabled = MON_OFF;
+ INIT_DLIST(mon_mru_list, mru);
+}
+
+
+/*
+ * remove_from_hash - removes an entry from the address hash table and
+ * decrements mru_entries.
+ */
+static void
+remove_from_hash(
+ mon_entry *mon
+ )
+{
+ u_int hash;
+ mon_entry *punlinked;
+
+ mru_entries--;
+ hash = MON_HASH(&mon->rmtadr);
+ UNLINK_SLIST(punlinked, mon_hash[hash], mon, hash_next,
+ mon_entry);
+ NTP_ENSURE(punlinked == mon);
+}
+
+
+static inline void
+mon_free_entry(
+ mon_entry *m
+ )
+{
+ ZERO(*m);
+ LINK_SLIST(mon_free, m, hash_next);
+}
+
+
+/*
+ * mon_reclaim_entry - Remove an entry from the MRU list and from the
+ * hash array, then zero-initialize it. Indirectly
+ * decrements mru_entries.
+
+ * The entry is prepared to be reused. Before return, in
+ * remove_from_hash(), mru_entries is decremented. It is the caller's
+ * responsibility to increment it again.
+ */
+static inline void
+mon_reclaim_entry(
+ mon_entry *m
+ )
+{
+ DEBUG_INSIST(NULL != m);
+
+ UNLINK_DLIST(m, mru);
+ remove_from_hash(m);
+ ZERO(*m);
+}
+
+
+/*
+ * mon_getmoremem - get more memory and put it on the free list
+ */
+static void
+mon_getmoremem(void)
+{
+ mon_entry *chunk;
+ u_int entries;
+
+ entries = (0 == mon_mem_increments)
+ ? mru_initalloc
+ : mru_incalloc;
+
+ if (entries) {
+ chunk = emalloc(entries * sizeof(*chunk));
+ mru_alloc += entries;
+ for (chunk += entries; entries; entries--)
+ mon_free_entry(--chunk);
+
+ mon_mem_increments++;
+ }
+}
+
+
+/*
+ * mon_start - start up the monitoring software
+ */
+void
+mon_start(
+ int mode
+ )
+{
+ size_t octets;
+ u_int min_hash_slots;
+
+ if (MON_OFF == mode) /* MON_OFF is 0 */
+ return;
+ if (mon_enabled) {
+ mon_enabled |= mode;
+ return;
+ }
+ if (0 == mon_mem_increments)
+ mon_getmoremem();
+ /*
+ * Select the MRU hash table size to limit the average count
+ * per bucket at capacity (mru_maxdepth) to 8, if possible
+ * given our hash is limited to 16 bits.
+ */
+ min_hash_slots = (mru_maxdepth / 8) + 1;
+ mon_hash_bits = 0;
+ while (min_hash_slots >>= 1)
+ mon_hash_bits++;
+ mon_hash_bits = max(4, mon_hash_bits);
+ mon_hash_bits = min(16, mon_hash_bits);
+ octets = sizeof(*mon_hash) * MON_HASH_SIZE;
+ mon_hash = erealloc_zero(mon_hash, octets, 0);
+
+ mon_enabled = mode;
+}
+
+
+/*
+ * mon_stop - stop the monitoring software
+ */
+void
+mon_stop(
+ int mode
+ )
+{
+ mon_entry *mon;
+
+ if (MON_OFF == mon_enabled)
+ return;
+ if ((mon_enabled & mode) == 0 || mode == MON_OFF)
+ return;
+
+ mon_enabled &= ~mode;
+ if (mon_enabled != MON_OFF)
+ return;
+
+ /*
+ * Move everything on the MRU list to the free list quickly,
+ * without bothering to remove each from either the MRU list or
+ * the hash table.
+ */
+ ITER_DLIST_BEGIN(mon_mru_list, mon, mru, mon_entry)
+ mon_free_entry(mon);
+ ITER_DLIST_END()
+
+ /* empty the MRU list and hash table. */
+ mru_entries = 0;
+ INIT_DLIST(mon_mru_list, mru);
+ zero_mem(mon_hash, sizeof(*mon_hash) * MON_HASH_SIZE);
+}
+
+
+/*
+ * mon_clearinterface -- remove mru entries referring to a local address
+ * which is going away.
+ */
+void
+mon_clearinterface(
+ endpt *lcladr
+ )
+{
+ mon_entry *mon;
+
+ /* iterate mon over mon_mru_list */
+ ITER_DLIST_BEGIN(mon_mru_list, mon, mru, mon_entry)
+ if (mon->lcladr == lcladr) {
+ /* remove from mru list */
+ UNLINK_DLIST(mon, mru);
+ /* remove from hash list, adjust mru_entries */
+ remove_from_hash(mon);
+ /* put on free list */
+ mon_free_entry(mon);
+ }
+ ITER_DLIST_END()
+}
+
+
+/*
+ * ntp_monitor - record stats about this packet
+ *
+ * Returns supplied restriction flags, with RES_LIMITED and RES_KOD
+ * cleared unless the packet should not be responded to normally
+ * (RES_LIMITED) and possibly should trigger a KoD response (RES_KOD).
+ * The returned flags are saved in the MRU entry, so that it reflects
+ * whether the last packet from that source triggered rate limiting,
+ * and if so, possible KoD response. This implies you can not tell
+ * whether a given address is eligible for rate limiting/KoD from the
+ * monlist restrict bits, only whether or not the last packet triggered
+ * such responses. ntpdc -c reslist lets you see whether RES_LIMITED
+ * or RES_KOD is lit for a particular address before ntp_monitor()'s
+ * typical dousing.
+ */
+u_short
+ntp_monitor(
+ struct recvbuf *rbufp,
+ u_short flags
+ )
+{
+ l_fp interval_fp;
+ struct pkt * pkt;
+ mon_entry * mon;
+ mon_entry * oldest;
+ int oldest_age;
+ u_int hash;
+ u_short restrict_mask;
+ u_char mode;
+ u_char version;
+ int interval;
+ int head; /* headway increment */
+ int leak; /* new headway */
+ int limit; /* average threshold */
+
+ if (mon_enabled == MON_OFF)
+ return ~(RES_LIMITED | RES_KOD) & flags;
+
+ pkt = &rbufp->recv_pkt;
+ hash = MON_HASH(&rbufp->recv_srcadr);
+ mode = PKT_MODE(pkt->li_vn_mode);
+ version = PKT_VERSION(pkt->li_vn_mode);
+ mon = mon_hash[hash];
+
+ /*
+ * We keep track of all traffic for a given IP in one entry,
+ * otherwise cron'ed ntpdate or similar evades RES_LIMITED.
+ */
+
+ for (; mon != NULL; mon = mon->hash_next)
+ if (SOCK_EQ(&mon->rmtadr, &rbufp->recv_srcadr))
+ break;
+
+ if (mon != NULL) {
+ interval_fp = rbufp->recv_time;
+ L_SUB(&interval_fp, &mon->last);
+ /* add one-half second to round up */
+ L_ADDUF(&interval_fp, 0x80000000);
+ interval = interval_fp.l_i;
+ mon->last = rbufp->recv_time;
+ NSRCPORT(&mon->rmtadr) = NSRCPORT(&rbufp->recv_srcadr);
+ mon->count++;
+ restrict_mask = flags;
+ mon->vn_mode = VN_MODE(version, mode);
+
+ /* Shuffle to the head of the MRU list. */
+ UNLINK_DLIST(mon, mru);
+ LINK_DLIST(mon_mru_list, mon, mru);
+
+ /*
+ * At this point the most recent arrival is first in the
+ * MRU list. Decrease the counter by the headway, but
+ * not less than zero.
+ */
+ mon->leak -= interval;
+ mon->leak = max(0, mon->leak);
+ head = 1 << ntp_minpoll;
+ leak = mon->leak + head;
+ limit = NTP_SHIFT * head;
+
+ DPRINTF(2, ("MRU: interval %d headway %d limit %d\n",
+ interval, leak, limit));
+
+ /*
+ * If the minimum and average thresholds are not
+ * exceeded, douse the RES_LIMITED and RES_KOD bits and
+ * increase the counter by the headway increment. Note
+ * that we give a 1-s grace for the minimum threshold
+ * and a 2-s grace for the headway increment. If one or
+ * both thresholds are exceeded and the old counter is
+ * less than the average threshold, set the counter to
+ * the average threshold plus the increment and leave
+ * the RES_LIMITED and RES_KOD bits lit. Otherwise,
+ * leave the counter alone and douse the RES_KOD bit.
+ * This rate-limits the KoDs to no less than the average
+ * headway.
+ */
+ if (interval + 1 >= ntp_minpkt && leak < limit) {
+ mon->leak = leak - 2;
+ restrict_mask &= ~(RES_LIMITED | RES_KOD);
+ } else if (mon->leak < limit)
+ mon->leak = limit + head;
+ else
+ restrict_mask &= ~RES_KOD;
+
+ mon->flags = restrict_mask;
+
+ return mon->flags;
+ }
+
+ /*
+ * If we got here, this is the first we've heard of this
+ * guy. Get him some memory, either from the free list
+ * or from the tail of the MRU list.
+ *
+ * The following ntp.conf "mru" knobs come into play determining
+ * the depth (or count) of the MRU list:
+ * - mru_mindepth ("mru mindepth") is a floor beneath which
+ * entries are kept without regard to their age. The
+ * default is 600 which matches the longtime implementation
+ * limit on the total number of entries.
+ * - mru_maxage ("mru maxage") is a ceiling on the age in
+ * seconds of entries. Entries older than this are
+ * reclaimed once mon_mindepth is exceeded. 64s default.
+ * Note that entries older than this can easily survive
+ * as they are reclaimed only as needed.
+ * - mru_maxdepth ("mru maxdepth") is a hard limit on the
+ * number of entries.
+ * - "mru maxmem" sets mru_maxdepth to the number of entries
+ * which fit in the given number of kilobytes. The default is
+ * 1024, or 1 megabyte.
+ * - mru_initalloc ("mru initalloc" sets the count of the
+ * initial allocation of MRU entries.
+ * - "mru initmem" sets mru_initalloc in units of kilobytes.
+ * The default is 4.
+ * - mru_incalloc ("mru incalloc" sets the number of entries to
+ * allocate on-demand each time the free list is empty.
+ * - "mru incmem" sets mru_incalloc in units of kilobytes.
+ * The default is 4.
+ * Whichever of "mru maxmem" or "mru maxdepth" occurs last in
+ * ntp.conf controls. Similarly for "mru initalloc" and "mru
+ * initmem", and for "mru incalloc" and "mru incmem".
+ */
+ if (mru_entries < mru_mindepth) {
+ if (NULL == mon_free)
+ mon_getmoremem();
+ UNLINK_HEAD_SLIST(mon, mon_free, hash_next);
+ } else {
+ oldest = TAIL_DLIST(mon_mru_list, mru);
+ oldest_age = 0; /* silence uninit warning */
+ if (oldest != NULL) {
+ interval_fp = rbufp->recv_time;
+ L_SUB(&interval_fp, &oldest->last);
+ /* add one-half second to round up */
+ L_ADDUF(&interval_fp, 0x80000000);
+ oldest_age = interval_fp.l_i;
+ }
+ /* note -1 is legal for mru_maxage (disables) */
+ if (oldest != NULL && mru_maxage < oldest_age) {
+ mon_reclaim_entry(oldest);
+ mon = oldest;
+ } else if (mon_free != NULL || mru_alloc <
+ mru_maxdepth) {
+ if (NULL == mon_free)
+ mon_getmoremem();
+ UNLINK_HEAD_SLIST(mon, mon_free, hash_next);
+ /* Preempt from the MRU list if old enough. */
+ } else if (ntp_random() / (2. * FRAC) >
+ (double)oldest_age / mon_age) {
+ return ~(RES_LIMITED | RES_KOD) & flags;
+ } else {
+ mon_reclaim_entry(oldest);
+ mon = oldest;
+ }
+ }
+
+ /*
+ * Got one, initialize it
+ */
+ mru_entries++;
+ mru_peakentries = max(mru_peakentries, mru_entries);
+ mon->last = rbufp->recv_time;
+ mon->first = mon->last;
+ mon->count = 1;
+ mon->flags = ~(RES_LIMITED | RES_KOD) & flags;
+ mon->leak = 0;
+ memcpy(&mon->rmtadr, &rbufp->recv_srcadr, sizeof(mon->rmtadr));
+ mon->vn_mode = VN_MODE(version, mode);
+ mon->lcladr = rbufp->dstadr;
+ mon->cast_flags = (u_char)(((rbufp->dstadr->flags &
+ INT_MCASTOPEN) && rbufp->fd == mon->lcladr->fd) ? MDF_MCAST
+ : rbufp->fd == mon->lcladr->bfd ? MDF_BCAST : MDF_UCAST);
+
+ /*
+ * Drop him into front of the hash table. Also put him on top of
+ * the MRU list.
+ */
+ LINK_SLIST(mon_hash[hash], mon, hash_next);
+ LINK_DLIST(mon_mru_list, mon, mru);
+
+ return mon->flags;
+}
+
+
diff --git a/ntpd/ntp_parser.c b/ntpd/ntp_parser.c
new file mode 100644
index 0000000..b3f4e4f
--- /dev/null
+++ b/ntpd/ntp_parser.c
@@ -0,0 +1,3612 @@
+/* A Bison parser, made by GNU Bison 3.0.2. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "3.0.2"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 14 "ntp_parser.y" /* yacc.c:339 */
+
+ #ifdef HAVE_CONFIG_H
+ # include <config.h>
+ #endif
+
+ #include "ntp.h"
+ #include "ntpd.h"
+ #include "ntp_machine.h"
+ #include "ntp_stdlib.h"
+ #include "ntp_filegen.h"
+ #include "ntp_scanner.h"
+ #include "ntp_config.h"
+ #include "ntp_crypto.h"
+
+ #include "ntpsim.h" /* HMS: Do we really want this all the time? */
+ /* SK: It might be a good idea to always
+ include the simulator code. That way
+ someone can use the same configuration file
+ for both the simulator and the daemon
+ */
+
+ #define YYMALLOC emalloc
+ #define YYFREE free
+ #define YYERROR_VERBOSE
+ #define YYMAXDEPTH 1000 /* stop the madness sooner */
+ void yyerror(struct FILE_INFO *ip_file, const char *msg);
+
+ #ifdef SIM
+ # define ONLY_SIM(a) (a)
+ #else
+ # define ONLY_SIM(a) NULL
+ #endif
+
+#line 100 "../../ntpd/ntp_parser.c" /* yacc.c:339 */
+
+# ifndef YY_NULLPTR
+# if defined __cplusplus && 201103L <= __cplusplus
+# define YY_NULLPTR nullptr
+# else
+# define YY_NULLPTR 0
+# endif
+# endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* In a future release of Bison, this section will be replaced
+ by #include "y.tab.h". */
+#ifndef YY_YY_Y_TAB_H_INCLUDED
+# define YY_YY_Y_TAB_H_INCLUDED
+/* Debug traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 1
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+/* Token type. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ enum yytokentype
+ {
+ T_Abbrev = 258,
+ T_Age = 259,
+ T_All = 260,
+ T_Allan = 261,
+ T_Allpeers = 262,
+ T_Auth = 263,
+ T_Autokey = 264,
+ T_Automax = 265,
+ T_Average = 266,
+ T_Bclient = 267,
+ T_Beacon = 268,
+ T_Broadcast = 269,
+ T_Broadcastclient = 270,
+ T_Broadcastdelay = 271,
+ T_Burst = 272,
+ T_Calibrate = 273,
+ T_Ceiling = 274,
+ T_Clockstats = 275,
+ T_Cohort = 276,
+ T_ControlKey = 277,
+ T_Crypto = 278,
+ T_Cryptostats = 279,
+ T_Ctl = 280,
+ T_Day = 281,
+ T_Default = 282,
+ T_Digest = 283,
+ T_Disable = 284,
+ T_Discard = 285,
+ T_Dispersion = 286,
+ T_Double = 287,
+ T_Driftfile = 288,
+ T_Drop = 289,
+ T_Ellipsis = 290,
+ T_Enable = 291,
+ T_End = 292,
+ T_False = 293,
+ T_File = 294,
+ T_Filegen = 295,
+ T_Filenum = 296,
+ T_Flag1 = 297,
+ T_Flag2 = 298,
+ T_Flag3 = 299,
+ T_Flag4 = 300,
+ T_Flake = 301,
+ T_Floor = 302,
+ T_Freq = 303,
+ T_Fudge = 304,
+ T_Host = 305,
+ T_Huffpuff = 306,
+ T_Iburst = 307,
+ T_Ident = 308,
+ T_Ignore = 309,
+ T_Incalloc = 310,
+ T_Incmem = 311,
+ T_Initalloc = 312,
+ T_Initmem = 313,
+ T_Includefile = 314,
+ T_Integer = 315,
+ T_Interface = 316,
+ T_Intrange = 317,
+ T_Io = 318,
+ T_Ipv4 = 319,
+ T_Ipv4_flag = 320,
+ T_Ipv6 = 321,
+ T_Ipv6_flag = 322,
+ T_Kernel = 323,
+ T_Key = 324,
+ T_Keys = 325,
+ T_Keysdir = 326,
+ T_Kod = 327,
+ T_Mssntp = 328,
+ T_Leapfile = 329,
+ T_Limited = 330,
+ T_Link = 331,
+ T_Listen = 332,
+ T_Logconfig = 333,
+ T_Logfile = 334,
+ T_Loopstats = 335,
+ T_Lowpriotrap = 336,
+ T_Manycastclient = 337,
+ T_Manycastserver = 338,
+ T_Mask = 339,
+ T_Maxage = 340,
+ T_Maxclock = 341,
+ T_Maxdepth = 342,
+ T_Maxdist = 343,
+ T_Maxmem = 344,
+ T_Maxpoll = 345,
+ T_Mem = 346,
+ T_Memlock = 347,
+ T_Minclock = 348,
+ T_Mindepth = 349,
+ T_Mindist = 350,
+ T_Minimum = 351,
+ T_Minpoll = 352,
+ T_Minsane = 353,
+ T_Mode = 354,
+ T_Mode7 = 355,
+ T_Monitor = 356,
+ T_Month = 357,
+ T_Mru = 358,
+ T_Multicastclient = 359,
+ T_Nic = 360,
+ T_Nolink = 361,
+ T_Nomodify = 362,
+ T_Nomrulist = 363,
+ T_None = 364,
+ T_Nonvolatile = 365,
+ T_Nopeer = 366,
+ T_Noquery = 367,
+ T_Noselect = 368,
+ T_Noserve = 369,
+ T_Notrap = 370,
+ T_Notrust = 371,
+ T_Ntp = 372,
+ T_Ntpport = 373,
+ T_NtpSignDsocket = 374,
+ T_Orphan = 375,
+ T_Orphanwait = 376,
+ T_Panic = 377,
+ T_Peer = 378,
+ T_Peerstats = 379,
+ T_Phone = 380,
+ T_Pid = 381,
+ T_Pidfile = 382,
+ T_Pool = 383,
+ T_Port = 384,
+ T_Preempt = 385,
+ T_Prefer = 386,
+ T_Protostats = 387,
+ T_Pw = 388,
+ T_Randfile = 389,
+ T_Rawstats = 390,
+ T_Refid = 391,
+ T_Requestkey = 392,
+ T_Reset = 393,
+ T_Restrict = 394,
+ T_Revoke = 395,
+ T_Rlimit = 396,
+ T_Saveconfigdir = 397,
+ T_Server = 398,
+ T_Setvar = 399,
+ T_Source = 400,
+ T_Stacksize = 401,
+ T_Statistics = 402,
+ T_Stats = 403,
+ T_Statsdir = 404,
+ T_Step = 405,
+ T_Stepout = 406,
+ T_Stratum = 407,
+ T_String = 408,
+ T_Sys = 409,
+ T_Sysstats = 410,
+ T_Tick = 411,
+ T_Time1 = 412,
+ T_Time2 = 413,
+ T_Timer = 414,
+ T_Timingstats = 415,
+ T_Tinker = 416,
+ T_Tos = 417,
+ T_Trap = 418,
+ T_True = 419,
+ T_Trustedkey = 420,
+ T_Ttl = 421,
+ T_Type = 422,
+ T_U_int = 423,
+ T_Unconfig = 424,
+ T_Unpeer = 425,
+ T_Version = 426,
+ T_WanderThreshold = 427,
+ T_Week = 428,
+ T_Wildcard = 429,
+ T_Xleave = 430,
+ T_Year = 431,
+ T_Flag = 432,
+ T_EOC = 433,
+ T_Simulate = 434,
+ T_Beep_Delay = 435,
+ T_Sim_Duration = 436,
+ T_Server_Offset = 437,
+ T_Duration = 438,
+ T_Freq_Offset = 439,
+ T_Wander = 440,
+ T_Jitter = 441,
+ T_Prop_Delay = 442,
+ T_Proc_Delay = 443
+ };
+#endif
+/* Tokens. */
+#define T_Abbrev 258
+#define T_Age 259
+#define T_All 260
+#define T_Allan 261
+#define T_Allpeers 262
+#define T_Auth 263
+#define T_Autokey 264
+#define T_Automax 265
+#define T_Average 266
+#define T_Bclient 267
+#define T_Beacon 268
+#define T_Broadcast 269
+#define T_Broadcastclient 270
+#define T_Broadcastdelay 271
+#define T_Burst 272
+#define T_Calibrate 273
+#define T_Ceiling 274
+#define T_Clockstats 275
+#define T_Cohort 276
+#define T_ControlKey 277
+#define T_Crypto 278
+#define T_Cryptostats 279
+#define T_Ctl 280
+#define T_Day 281
+#define T_Default 282
+#define T_Digest 283
+#define T_Disable 284
+#define T_Discard 285
+#define T_Dispersion 286
+#define T_Double 287
+#define T_Driftfile 288
+#define T_Drop 289
+#define T_Ellipsis 290
+#define T_Enable 291
+#define T_End 292
+#define T_False 293
+#define T_File 294
+#define T_Filegen 295
+#define T_Filenum 296
+#define T_Flag1 297
+#define T_Flag2 298
+#define T_Flag3 299
+#define T_Flag4 300
+#define T_Flake 301
+#define T_Floor 302
+#define T_Freq 303
+#define T_Fudge 304
+#define T_Host 305
+#define T_Huffpuff 306
+#define T_Iburst 307
+#define T_Ident 308
+#define T_Ignore 309
+#define T_Incalloc 310
+#define T_Incmem 311
+#define T_Initalloc 312
+#define T_Initmem 313
+#define T_Includefile 314
+#define T_Integer 315
+#define T_Interface 316
+#define T_Intrange 317
+#define T_Io 318
+#define T_Ipv4 319
+#define T_Ipv4_flag 320
+#define T_Ipv6 321
+#define T_Ipv6_flag 322
+#define T_Kernel 323
+#define T_Key 324
+#define T_Keys 325
+#define T_Keysdir 326
+#define T_Kod 327
+#define T_Mssntp 328
+#define T_Leapfile 329
+#define T_Limited 330
+#define T_Link 331
+#define T_Listen 332
+#define T_Logconfig 333
+#define T_Logfile 334
+#define T_Loopstats 335
+#define T_Lowpriotrap 336
+#define T_Manycastclient 337
+#define T_Manycastserver 338
+#define T_Mask 339
+#define T_Maxage 340
+#define T_Maxclock 341
+#define T_Maxdepth 342
+#define T_Maxdist 343
+#define T_Maxmem 344
+#define T_Maxpoll 345
+#define T_Mem 346
+#define T_Memlock 347
+#define T_Minclock 348
+#define T_Mindepth 349
+#define T_Mindist 350
+#define T_Minimum 351
+#define T_Minpoll 352
+#define T_Minsane 353
+#define T_Mode 354
+#define T_Mode7 355
+#define T_Monitor 356
+#define T_Month 357
+#define T_Mru 358
+#define T_Multicastclient 359
+#define T_Nic 360
+#define T_Nolink 361
+#define T_Nomodify 362
+#define T_Nomrulist 363
+#define T_None 364
+#define T_Nonvolatile 365
+#define T_Nopeer 366
+#define T_Noquery 367
+#define T_Noselect 368
+#define T_Noserve 369
+#define T_Notrap 370
+#define T_Notrust 371
+#define T_Ntp 372
+#define T_Ntpport 373
+#define T_NtpSignDsocket 374
+#define T_Orphan 375
+#define T_Orphanwait 376
+#define T_Panic 377
+#define T_Peer 378
+#define T_Peerstats 379
+#define T_Phone 380
+#define T_Pid 381
+#define T_Pidfile 382
+#define T_Pool 383
+#define T_Port 384
+#define T_Preempt 385
+#define T_Prefer 386
+#define T_Protostats 387
+#define T_Pw 388
+#define T_Randfile 389
+#define T_Rawstats 390
+#define T_Refid 391
+#define T_Requestkey 392
+#define T_Reset 393
+#define T_Restrict 394
+#define T_Revoke 395
+#define T_Rlimit 396
+#define T_Saveconfigdir 397
+#define T_Server 398
+#define T_Setvar 399
+#define T_Source 400
+#define T_Stacksize 401
+#define T_Statistics 402
+#define T_Stats 403
+#define T_Statsdir 404
+#define T_Step 405
+#define T_Stepout 406
+#define T_Stratum 407
+#define T_String 408
+#define T_Sys 409
+#define T_Sysstats 410
+#define T_Tick 411
+#define T_Time1 412
+#define T_Time2 413
+#define T_Timer 414
+#define T_Timingstats 415
+#define T_Tinker 416
+#define T_Tos 417
+#define T_Trap 418
+#define T_True 419
+#define T_Trustedkey 420
+#define T_Ttl 421
+#define T_Type 422
+#define T_U_int 423
+#define T_Unconfig 424
+#define T_Unpeer 425
+#define T_Version 426
+#define T_WanderThreshold 427
+#define T_Week 428
+#define T_Wildcard 429
+#define T_Xleave 430
+#define T_Year 431
+#define T_Flag 432
+#define T_EOC 433
+#define T_Simulate 434
+#define T_Beep_Delay 435
+#define T_Sim_Duration 436
+#define T_Server_Offset 437
+#define T_Duration 438
+#define T_Freq_Offset 439
+#define T_Wander 440
+#define T_Jitter 441
+#define T_Prop_Delay 442
+#define T_Proc_Delay 443
+
+/* Value type. */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE YYSTYPE;
+union YYSTYPE
+{
+#line 54 "ntp_parser.y" /* yacc.c:355 */
+
+ char * String;
+ double Double;
+ int Integer;
+ unsigned U_int;
+ gen_fifo * Generic_fifo;
+ attr_val * Attr_val;
+ attr_val_fifo * Attr_val_fifo;
+ int_fifo * Int_fifo;
+ string_fifo * String_fifo;
+ address_node * Address_node;
+ address_fifo * Address_fifo;
+ setvar_node * Set_var;
+ server_info * Sim_server;
+ server_info_fifo * Sim_server_fifo;
+ script_info * Sim_script;
+ script_info_fifo * Sim_script_fifo;
+
+#line 535 "../../ntpd/ntp_parser.c" /* yacc.c:355 */
+};
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+extern YYSTYPE yylval;
+
+int yyparse (struct FILE_INFO *ip_file);
+
+#endif /* !YY_YY_Y_TAB_H_INCLUDED */
+
+/* Copy the second part of user declarations. */
+
+#line 550 "../../ntpd/ntp_parser.c" /* yacc.c:358 */
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#else
+typedef signed char yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(Msgid) Msgid
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE
+# if (defined __GNUC__ \
+ && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \
+ || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C
+# define YY_ATTRIBUTE(Spec) __attribute__(Spec)
+# else
+# define YY_ATTRIBUTE(Spec) /* empty */
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_PURE
+# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__))
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
+#endif
+
+#if !defined _Noreturn \
+ && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)
+# if defined _MSC_VER && 1200 <= _MSC_VER
+# define _Noreturn __declspec (noreturn)
+# else
+# define _Noreturn YY_ATTRIBUTE ((__noreturn__))
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+/* Suppress an incorrect diagnostic about yylval being uninitialized. */
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\
+ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+ _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+ /* Use EXIT_SUCCESS as a witness for stdlib.h. */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's 'empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(Dst, Src, Count) \
+ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+# else
+# define YYCOPY(Dst, Src, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (Dst)[yyi] = (Src)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 203
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 653
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 194
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 104
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 307
+/* YYNSTATES -- Number of states. */
+#define YYNSTATES 411
+
+/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
+ by yylex, with out-of-bounds checking. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 443
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
+ as returned by yylex, without out-of-bounds checking. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 190, 191, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 189, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 192, 2, 193, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
+ 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
+ 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174,
+ 175, 176, 177, 178, 179, 180, 181, 182, 183, 184,
+ 185, 186, 187, 188
+};
+
+#if YYDEBUG
+ /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 363, 363, 367, 368, 369, 383, 384, 385, 386,
+ 387, 388, 389, 390, 391, 392, 393, 394, 395, 396,
+ 404, 414, 415, 416, 417, 418, 422, 423, 428, 433,
+ 435, 441, 442, 450, 451, 452, 456, 461, 462, 463,
+ 464, 465, 466, 467, 468, 472, 474, 479, 480, 481,
+ 482, 483, 484, 488, 493, 502, 512, 513, 523, 525,
+ 527, 538, 545, 547, 552, 554, 556, 558, 560, 569,
+ 575, 576, 584, 586, 598, 599, 600, 601, 602, 611,
+ 616, 621, 629, 631, 633, 638, 639, 640, 641, 642,
+ 643, 647, 648, 649, 650, 659, 661, 670, 680, 685,
+ 693, 694, 695, 696, 697, 698, 699, 700, 705, 706,
+ 714, 724, 733, 748, 753, 754, 758, 759, 763, 764,
+ 765, 766, 767, 768, 769, 778, 782, 786, 794, 802,
+ 810, 825, 840, 853, 854, 862, 863, 864, 865, 866,
+ 867, 868, 869, 870, 871, 872, 873, 874, 875, 876,
+ 880, 885, 893, 898, 899, 900, 904, 909, 917, 922,
+ 923, 924, 925, 926, 927, 928, 929, 937, 947, 952,
+ 960, 962, 964, 966, 968, 973, 974, 978, 979, 980,
+ 981, 989, 994, 999, 1007, 1012, 1013, 1014, 1023, 1025,
+ 1030, 1035, 1043, 1045, 1062, 1063, 1064, 1065, 1066, 1067,
+ 1071, 1072, 1080, 1085, 1090, 1098, 1103, 1104, 1105, 1106,
+ 1107, 1108, 1109, 1110, 1119, 1120, 1121, 1128, 1135, 1151,
+ 1170, 1175, 1177, 1179, 1181, 1183, 1190, 1195, 1196, 1197,
+ 1201, 1202, 1203, 1207, 1208, 1212, 1219, 1229, 1238, 1243,
+ 1245, 1250, 1251, 1259, 1261, 1269, 1274, 1282, 1307, 1314,
+ 1324, 1325, 1329, 1330, 1331, 1332, 1336, 1337, 1338, 1342,
+ 1347, 1352, 1360, 1361, 1362, 1363, 1364, 1365, 1366, 1376,
+ 1381, 1389, 1394, 1402, 1404, 1408, 1413, 1418, 1426, 1431,
+ 1439, 1448, 1449, 1453, 1454, 1463, 1481, 1485, 1490, 1498,
+ 1503, 1504, 1508, 1513, 1521, 1526, 1531, 1536, 1541, 1549,
+ 1554, 1559, 1567, 1572, 1573, 1574, 1575, 1576
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 1
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "T_Abbrev", "T_Age", "T_All", "T_Allan",
+ "T_Allpeers", "T_Auth", "T_Autokey", "T_Automax", "T_Average",
+ "T_Bclient", "T_Beacon", "T_Broadcast", "T_Broadcastclient",
+ "T_Broadcastdelay", "T_Burst", "T_Calibrate", "T_Ceiling",
+ "T_Clockstats", "T_Cohort", "T_ControlKey", "T_Crypto", "T_Cryptostats",
+ "T_Ctl", "T_Day", "T_Default", "T_Digest", "T_Disable", "T_Discard",
+ "T_Dispersion", "T_Double", "T_Driftfile", "T_Drop", "T_Ellipsis",
+ "T_Enable", "T_End", "T_False", "T_File", "T_Filegen", "T_Filenum",
+ "T_Flag1", "T_Flag2", "T_Flag3", "T_Flag4", "T_Flake", "T_Floor",
+ "T_Freq", "T_Fudge", "T_Host", "T_Huffpuff", "T_Iburst", "T_Ident",
+ "T_Ignore", "T_Incalloc", "T_Incmem", "T_Initalloc", "T_Initmem",
+ "T_Includefile", "T_Integer", "T_Interface", "T_Intrange", "T_Io",
+ "T_Ipv4", "T_Ipv4_flag", "T_Ipv6", "T_Ipv6_flag", "T_Kernel", "T_Key",
+ "T_Keys", "T_Keysdir", "T_Kod", "T_Mssntp", "T_Leapfile", "T_Limited",
+ "T_Link", "T_Listen", "T_Logconfig", "T_Logfile", "T_Loopstats",
+ "T_Lowpriotrap", "T_Manycastclient", "T_Manycastserver", "T_Mask",
+ "T_Maxage", "T_Maxclock", "T_Maxdepth", "T_Maxdist", "T_Maxmem",
+ "T_Maxpoll", "T_Mem", "T_Memlock", "T_Minclock", "T_Mindepth",
+ "T_Mindist", "T_Minimum", "T_Minpoll", "T_Minsane", "T_Mode", "T_Mode7",
+ "T_Monitor", "T_Month", "T_Mru", "T_Multicastclient", "T_Nic",
+ "T_Nolink", "T_Nomodify", "T_Nomrulist", "T_None", "T_Nonvolatile",
+ "T_Nopeer", "T_Noquery", "T_Noselect", "T_Noserve", "T_Notrap",
+ "T_Notrust", "T_Ntp", "T_Ntpport", "T_NtpSignDsocket", "T_Orphan",
+ "T_Orphanwait", "T_Panic", "T_Peer", "T_Peerstats", "T_Phone", "T_Pid",
+ "T_Pidfile", "T_Pool", "T_Port", "T_Preempt", "T_Prefer", "T_Protostats",
+ "T_Pw", "T_Randfile", "T_Rawstats", "T_Refid", "T_Requestkey", "T_Reset",
+ "T_Restrict", "T_Revoke", "T_Rlimit", "T_Saveconfigdir", "T_Server",
+ "T_Setvar", "T_Source", "T_Stacksize", "T_Statistics", "T_Stats",
+ "T_Statsdir", "T_Step", "T_Stepout", "T_Stratum", "T_String", "T_Sys",
+ "T_Sysstats", "T_Tick", "T_Time1", "T_Time2", "T_Timer", "T_Timingstats",
+ "T_Tinker", "T_Tos", "T_Trap", "T_True", "T_Trustedkey", "T_Ttl",
+ "T_Type", "T_U_int", "T_Unconfig", "T_Unpeer", "T_Version",
+ "T_WanderThreshold", "T_Week", "T_Wildcard", "T_Xleave", "T_Year",
+ "T_Flag", "T_EOC", "T_Simulate", "T_Beep_Delay", "T_Sim_Duration",
+ "T_Server_Offset", "T_Duration", "T_Freq_Offset", "T_Wander", "T_Jitter",
+ "T_Prop_Delay", "T_Proc_Delay", "'='", "'('", "')'", "'{'", "'}'",
+ "$accept", "configuration", "command_list", "command", "server_command",
+ "client_type", "address", "ip_address", "address_fam", "option_list",
+ "option", "option_flag", "option_flag_keyword", "option_int",
+ "option_int_keyword", "option_str", "option_str_keyword",
+ "unpeer_command", "unpeer_keyword", "other_mode_command",
+ "authentication_command", "crypto_command_list", "crypto_command",
+ "crypto_str_keyword", "orphan_mode_command", "tos_option_list",
+ "tos_option", "tos_option_int_keyword", "tos_option_dbl_keyword",
+ "monitoring_command", "stats_list", "stat", "filegen_option_list",
+ "filegen_option", "link_nolink", "enable_disable", "filegen_type",
+ "access_control_command", "ac_flag_list", "access_control_flag",
+ "discard_option_list", "discard_option", "discard_option_keyword",
+ "mru_option_list", "mru_option", "mru_option_keyword", "fudge_command",
+ "fudge_factor_list", "fudge_factor", "fudge_factor_dbl_keyword",
+ "fudge_factor_bool_keyword", "rlimit_command", "rlimit_option_list",
+ "rlimit_option", "rlimit_option_keyword", "system_option_command",
+ "system_option_list", "system_option", "system_option_flag_keyword",
+ "system_option_local_flag_keyword", "tinker_command",
+ "tinker_option_list", "tinker_option", "tinker_option_keyword",
+ "miscellaneous_command", "misc_cmd_dbl_keyword", "misc_cmd_str_keyword",
+ "misc_cmd_str_lcl_keyword", "drift_parm", "variable_assign",
+ "t_default_or_zero", "trap_option_list", "trap_option",
+ "log_config_list", "log_config_command", "interface_command",
+ "interface_nic", "nic_rule_class", "nic_rule_action", "reset_command",
+ "counter_set_list", "counter_set_keyword", "integer_list",
+ "integer_list_range", "integer_list_range_elt", "integer_range",
+ "string_list", "address_list", "boolean", "number", "simulate_command",
+ "sim_conf_start", "sim_init_statement_list", "sim_init_statement",
+ "sim_init_keyword", "sim_server_list", "sim_server", "sim_server_offset",
+ "sim_server_name", "sim_act_list", "sim_act", "sim_act_stmt_list",
+ "sim_act_stmt", "sim_act_keyword", YY_NULLPTR
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[NUM] -- (External) token number corresponding to the
+ (internal) symbol number NUM (which must be that of a token). */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 334,
+ 335, 336, 337, 338, 339, 340, 341, 342, 343, 344,
+ 345, 346, 347, 348, 349, 350, 351, 352, 353, 354,
+ 355, 356, 357, 358, 359, 360, 361, 362, 363, 364,
+ 365, 366, 367, 368, 369, 370, 371, 372, 373, 374,
+ 375, 376, 377, 378, 379, 380, 381, 382, 383, 384,
+ 385, 386, 387, 388, 389, 390, 391, 392, 393, 394,
+ 395, 396, 397, 398, 399, 400, 401, 402, 403, 404,
+ 405, 406, 407, 408, 409, 410, 411, 412, 413, 414,
+ 415, 416, 417, 418, 419, 420, 421, 422, 423, 424,
+ 425, 426, 427, 428, 429, 430, 431, 432, 433, 434,
+ 435, 436, 437, 438, 439, 440, 441, 442, 443, 61,
+ 40, 41, 123, 125
+};
+# endif
+
+#define YYPACT_NINF -178
+
+#define yypact_value_is_default(Yystate) \
+ (!!((Yystate) == (-178)))
+
+#define YYTABLE_NINF -7
+
+#define yytable_value_is_error(Yytable_value) \
+ 0
+
+ /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+static const yytype_int16 yypact[] =
+{
+ 26, -153, -30, -178, -178, -178, -28, -178, 166, 18,
+ -109, 166, -178, 200, -47, -178, -102, -178, -96, -93,
+ -178, -89, -178, -178, -47, 330, -47, -178, -178, -85,
+ -178, -76, -178, -178, 20, -2, 45, 22, -22, -178,
+ -178, -67, 200, -65, -178, 107, 520, -63, -53, 35,
+ -178, -178, -178, 46, 203, -94, -178, -47, -178, -47,
+ -178, -178, -178, -178, -178, -178, -178, -178, -178, -178,
+ -21, -55, -54, -178, 4, -178, -178, -77, -178, -178,
+ -178, 158, -178, -178, -178, -178, -178, -178, -178, -178,
+ 166, -178, -178, -178, -178, -178, -178, 18, -178, 47,
+ 84, -178, 166, -178, -178, -178, -178, -178, -178, -178,
+ -178, -178, -178, -178, -178, 49, -178, -33, 361, -178,
+ -178, -178, -89, -178, -178, -47, -178, -178, -178, -178,
+ -178, -178, -178, -178, 330, -178, 58, -47, -178, -178,
+ -31, -178, -178, -178, -178, -178, -178, -178, -178, -2,
+ -178, -178, 94, 98, -178, -178, 43, -178, -178, -178,
+ -178, -22, -178, 68, -57, -178, 200, -178, -178, -178,
+ -178, -178, -178, -178, -178, -178, -178, 107, -178, -21,
+ -178, -178, -25, -178, -178, -178, -178, -178, -178, -178,
+ -178, 520, -178, 75, -21, -178, -178, 86, -53, -178,
+ -178, -178, 87, -178, -19, -178, -178, -178, -178, -178,
+ -178, -178, -178, -178, -178, -178, 3, -107, -178, -178,
+ -178, -178, -178, 88, -178, 7, -178, -178, -178, -178,
+ -5, 8, -178, -178, -178, -178, 23, 111, -178, -178,
+ 49, -178, -21, -25, -178, -178, -178, -178, -178, -178,
+ -178, -178, 482, -178, -178, 482, 482, -63, -178, -178,
+ 28, -178, -178, -178, -178, -178, -178, -178, -178, -178,
+ -178, -46, 144, -178, -178, -178, 416, -178, -178, -178,
+ -178, -178, -178, -178, -178, -127, 5, 10, -178, -178,
+ -178, -178, 40, -178, -178, 24, -178, -178, -178, -178,
+ -178, -178, -178, -178, -178, -178, -178, -178, -178, -178,
+ -178, -178, -178, -178, -178, -178, -178, -178, -178, -178,
+ -178, 482, 482, -178, 167, -63, 142, -178, 143, -178,
+ -178, -178, -178, -178, -178, -178, -178, -178, -178, -178,
+ -178, -178, -178, -178, -178, -178, -178, -178, -51, -178,
+ 57, 27, 34, -117, -178, 29, -178, -21, -178, -178,
+ -178, -178, -178, -178, -178, -178, -178, 482, -178, -178,
+ -178, -178, 32, -178, -178, -178, -47, -178, -178, -178,
+ 33, -178, -178, -178, 38, 52, -21, 39, -146, -178,
+ 59, -21, -178, -178, -178, 50, -44, -178, -178, -178,
+ -178, -178, 60, 63, 41, -178, 71, -178, -21, -178,
+ -178
+};
+
+ /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE does not specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint16 yydefact[] =
+{
+ 0, 0, 0, 24, 58, 227, 0, 70, 0, 0,
+ 237, 0, 220, 0, 0, 230, 0, 250, 0, 0,
+ 231, 0, 233, 25, 0, 0, 0, 251, 228, 0,
+ 23, 0, 232, 22, 0, 0, 0, 0, 0, 234,
+ 21, 0, 0, 0, 229, 0, 0, 0, 0, 0,
+ 56, 57, 286, 0, 2, 0, 7, 0, 8, 0,
+ 9, 10, 13, 11, 12, 14, 15, 16, 17, 18,
+ 0, 0, 0, 214, 0, 215, 19, 0, 5, 61,
+ 62, 63, 194, 195, 196, 197, 200, 198, 199, 201,
+ 189, 191, 192, 193, 153, 154, 155, 125, 151, 0,
+ 235, 221, 188, 100, 101, 102, 103, 107, 104, 105,
+ 106, 108, 29, 30, 28, 0, 26, 0, 6, 64,
+ 65, 247, 222, 246, 279, 59, 159, 160, 161, 162,
+ 163, 164, 165, 166, 126, 157, 0, 60, 69, 277,
+ 223, 66, 262, 263, 264, 265, 266, 267, 268, 259,
+ 261, 133, 29, 30, 133, 133, 26, 67, 187, 185,
+ 186, 181, 183, 0, 0, 224, 95, 99, 96, 206,
+ 207, 208, 209, 210, 211, 212, 213, 202, 204, 0,
+ 90, 85, 0, 86, 94, 92, 93, 91, 89, 87,
+ 88, 79, 81, 0, 0, 241, 273, 0, 68, 272,
+ 274, 270, 226, 1, 0, 4, 31, 55, 284, 283,
+ 216, 217, 218, 258, 257, 256, 0, 0, 78, 74,
+ 75, 76, 77, 0, 71, 0, 190, 150, 152, 236,
+ 97, 0, 177, 178, 179, 180, 0, 0, 175, 176,
+ 167, 169, 0, 0, 27, 219, 245, 278, 156, 158,
+ 276, 260, 129, 133, 133, 132, 127, 0, 182, 184,
+ 0, 98, 203, 205, 282, 280, 281, 84, 80, 82,
+ 83, 225, 0, 271, 269, 3, 20, 252, 253, 254,
+ 249, 255, 248, 290, 291, 0, 0, 0, 73, 72,
+ 117, 116, 0, 114, 115, 0, 109, 112, 113, 173,
+ 174, 172, 168, 170, 171, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 134, 130, 131, 133, 240, 0, 0, 242, 0, 37,
+ 38, 39, 54, 47, 49, 48, 51, 40, 41, 42,
+ 43, 50, 52, 44, 32, 33, 36, 34, 0, 35,
+ 0, 0, 0, 0, 293, 0, 288, 0, 110, 124,
+ 120, 122, 118, 119, 121, 123, 111, 128, 239, 238,
+ 244, 243, 0, 45, 46, 53, 0, 287, 285, 292,
+ 0, 289, 275, 296, 0, 0, 0, 0, 0, 298,
+ 0, 0, 294, 297, 295, 0, 0, 303, 304, 305,
+ 306, 307, 0, 0, 0, 299, 0, 301, 0, 300,
+ 302
+};
+
+ /* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -178, -178, -178, -40, -178, -178, -14, -35, -178, -178,
+ -178, -178, -178, -178, -178, -178, -178, -178, -178, -178,
+ -178, -178, -178, -178, -178, -178, 64, -178, -178, -178,
+ -178, -32, -178, -178, -178, -178, -178, -178, -151, -178,
+ -178, 141, -178, -178, 116, -178, -178, -178, 11, -178,
+ -178, -178, -178, 93, -178, -178, 248, -69, -178, -178,
+ -178, -178, 83, -178, -178, -178, -178, -178, -178, -178,
+ -178, -178, -178, -178, 139, -178, -178, -178, -178, -178,
+ -178, 119, -178, -178, 67, -178, -178, 243, 36, -177,
+ -178, -178, -178, -15, -178, -178, -82, -178, -178, -178,
+ -116, -178, -126, -178
+};
+
+ /* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 53, 54, 55, 56, 57, 124, 116, 117, 276,
+ 344, 345, 346, 347, 348, 349, 350, 58, 59, 60,
+ 61, 81, 224, 225, 62, 191, 192, 193, 194, 63,
+ 166, 111, 230, 296, 297, 298, 366, 64, 252, 320,
+ 97, 98, 99, 134, 135, 136, 65, 240, 241, 242,
+ 243, 66, 161, 162, 163, 67, 90, 91, 92, 93,
+ 68, 177, 178, 179, 69, 70, 71, 72, 101, 165,
+ 369, 271, 327, 122, 123, 73, 74, 282, 216, 75,
+ 149, 150, 202, 198, 199, 200, 140, 125, 267, 210,
+ 76, 77, 285, 286, 287, 353, 354, 385, 355, 388,
+ 389, 402, 403, 404
+};
+
+ /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule whose
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+static const yytype_int16 yytable[] =
+{
+ 115, 156, 263, 255, 256, 142, 143, 196, 277, 373,
+ 167, 208, 195, 264, 204, 325, 351, 270, 112, 158,
+ 113, 226, 155, 144, 290, 78, 351, 1, 359, 94,
+ 79, 291, 80, 226, 292, 265, 2, 387, 213, 209,
+ 3, 4, 5, 206, 100, 207, 203, 392, 6, 7,
+ 360, 118, 231, 283, 284, 8, 9, 119, 214, 10,
+ 120, 145, 11, 12, 121, 303, 13, 278, 138, 279,
+ 159, 293, 151, 283, 284, 14, 378, 139, 245, 15,
+ 141, 215, 157, 326, 205, 16, 164, 17, 168, 146,
+ 114, 232, 233, 234, 235, 201, 18, 19, 211, 212,
+ 20, 294, 321, 322, 21, 22, 114, 228, 23, 24,
+ 152, 247, 153, 169, 95, 217, 229, 374, 249, 96,
+ 244, 253, 250, 247, 160, 254, 361, 257, 259, 25,
+ 26, 27, 260, 362, 261, 269, 28, 197, 170, 266,
+ 397, 398, 399, 400, 401, 29, 272, 274, 288, 30,
+ 363, 31, 147, 32, 33, 171, 280, 148, 172, 275,
+ 289, 299, 295, 34, 35, 36, 37, 38, 39, 40,
+ 41, 301, 367, 42, 82, 43, 300, 281, 83, 328,
+ 381, 324, 44, 356, 84, 236, 218, 45, 46, 47,
+ 154, 48, 49, 358, 368, 50, 51, 364, 114, 357,
+ 365, 237, 371, 372, -6, 52, 238, 239, 219, 390,
+ 375, 220, 377, 2, 395, 384, 376, 3, 4, 5,
+ 103, 380, 323, 382, 104, 6, 7, 386, 391, 173,
+ 408, 410, 8, 9, 85, 387, 10, 394, 227, 11,
+ 12, 407, 396, 13, 397, 398, 399, 400, 401, 409,
+ 248, 302, 14, 405, 258, 268, 15, 174, 175, 102,
+ 262, 246, 16, 176, 17, 273, 86, 87, 251, 137,
+ 352, 379, 393, 18, 19, 0, 406, 20, 0, 304,
+ 105, 21, 22, 88, 0, 23, 24, 0, 0, 0,
+ 370, 221, 222, 0, 0, 0, 0, 0, 223, 0,
+ 0, 0, 0, 0, 0, 0, 25, 26, 27, 0,
+ 0, 0, 0, 28, 89, 0, 0, 0, 0, 0,
+ 0, 0, 29, 0, 106, 0, 30, 0, 31, 0,
+ 32, 33, 107, 0, 0, 108, 0, 0, 0, 0,
+ 34, 35, 36, 37, 38, 39, 40, 41, 0, 0,
+ 42, 0, 43, 0, 0, 109, 0, 0, 0, 44,
+ 110, 0, 383, 0, 45, 46, 47, 0, 48, 49,
+ 0, 2, 50, 51, 0, 3, 4, 5, 0, 0,
+ 0, -6, 52, 6, 7, 126, 127, 128, 129, 0,
+ 8, 9, 0, 0, 10, 0, 0, 11, 12, 0,
+ 0, 13, 0, 0, 0, 0, 0, 0, 0, 0,
+ 14, 0, 0, 0, 15, 130, 0, 131, 0, 132,
+ 16, 0, 17, 0, 133, 329, 0, 0, 0, 0,
+ 0, 18, 19, 330, 0, 20, 0, 0, 0, 21,
+ 22, 0, 0, 23, 24, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 25, 26, 27, 0, 331, 332,
+ 0, 28, 0, 0, 0, 0, 0, 0, 0, 0,
+ 29, 0, 0, 0, 30, 333, 31, 0, 32, 33,
+ 0, 0, 0, 0, 0, 0, 0, 0, 34, 35,
+ 36, 37, 38, 39, 40, 41, 334, 0, 42, 0,
+ 43, 0, 0, 335, 0, 336, 0, 44, 0, 0,
+ 0, 0, 45, 46, 47, 0, 48, 49, 305, 337,
+ 50, 51, 0, 180, 0, 0, 306, 0, 0, 181,
+ 52, 182, 0, 0, 0, 0, 338, 339, 0, 0,
+ 0, 0, 0, 0, 307, 308, 0, 309, 0, 0,
+ 0, 0, 0, 310, 0, 0, 0, 183, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 340, 0, 341, 0, 0, 0, 0, 342, 0, 311,
+ 312, 343, 0, 313, 314, 0, 315, 316, 317, 0,
+ 318, 0, 0, 0, 0, 0, 184, 0, 185, 0,
+ 0, 0, 0, 186, 0, 187, 0, 0, 188, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 189, 190, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 319
+};
+
+static const yytype_int16 yycheck[] =
+{
+ 14, 36, 179, 154, 155, 7, 8, 60, 5, 60,
+ 42, 32, 47, 38, 54, 61, 143, 194, 65, 41,
+ 67, 90, 36, 25, 29, 178, 143, 1, 4, 11,
+ 60, 36, 60, 102, 39, 60, 10, 183, 34, 60,
+ 14, 15, 16, 57, 153, 59, 0, 193, 22, 23,
+ 26, 153, 3, 180, 181, 29, 30, 153, 54, 33,
+ 153, 63, 36, 37, 153, 242, 40, 64, 153, 66,
+ 92, 76, 27, 180, 181, 49, 193, 153, 118, 53,
+ 60, 77, 60, 129, 178, 59, 153, 61, 153, 91,
+ 153, 42, 43, 44, 45, 60, 70, 71, 153, 153,
+ 74, 106, 253, 254, 78, 79, 153, 60, 82, 83,
+ 65, 125, 67, 6, 96, 192, 32, 168, 60, 101,
+ 153, 27, 153, 137, 146, 27, 102, 84, 60, 103,
+ 104, 105, 189, 109, 166, 60, 110, 190, 31, 164,
+ 184, 185, 186, 187, 188, 119, 60, 60, 60, 123,
+ 126, 125, 154, 127, 128, 48, 153, 159, 51, 178,
+ 153, 153, 167, 137, 138, 139, 140, 141, 142, 143,
+ 144, 60, 323, 147, 8, 149, 153, 174, 12, 35,
+ 357, 153, 156, 178, 18, 136, 28, 161, 162, 163,
+ 145, 165, 166, 153, 27, 169, 170, 173, 153, 189,
+ 176, 152, 60, 60, 178, 179, 157, 158, 50, 386,
+ 153, 53, 178, 10, 391, 182, 189, 14, 15, 16,
+ 20, 192, 257, 191, 24, 22, 23, 189, 189, 122,
+ 189, 408, 29, 30, 68, 183, 33, 178, 97, 36,
+ 37, 178, 192, 40, 184, 185, 186, 187, 188, 178,
+ 134, 240, 49, 193, 161, 191, 53, 150, 151, 11,
+ 177, 122, 59, 156, 61, 198, 100, 101, 149, 26,
+ 285, 353, 388, 70, 71, -1, 402, 74, -1, 243,
+ 80, 78, 79, 117, -1, 82, 83, -1, -1, -1,
+ 325, 133, 134, -1, -1, -1, -1, -1, 140, -1,
+ -1, -1, -1, -1, -1, -1, 103, 104, 105, -1,
+ -1, -1, -1, 110, 148, -1, -1, -1, -1, -1,
+ -1, -1, 119, -1, 124, -1, 123, -1, 125, -1,
+ 127, 128, 132, -1, -1, 135, -1, -1, -1, -1,
+ 137, 138, 139, 140, 141, 142, 143, 144, -1, -1,
+ 147, -1, 149, -1, -1, 155, -1, -1, -1, 156,
+ 160, -1, 376, -1, 161, 162, 163, -1, 165, 166,
+ -1, 10, 169, 170, -1, 14, 15, 16, -1, -1,
+ -1, 178, 179, 22, 23, 55, 56, 57, 58, -1,
+ 29, 30, -1, -1, 33, -1, -1, 36, 37, -1,
+ -1, 40, -1, -1, -1, -1, -1, -1, -1, -1,
+ 49, -1, -1, -1, 53, 85, -1, 87, -1, 89,
+ 59, -1, 61, -1, 94, 9, -1, -1, -1, -1,
+ -1, 70, 71, 17, -1, 74, -1, -1, -1, 78,
+ 79, -1, -1, 82, 83, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 103, 104, 105, -1, 52, 53,
+ -1, 110, -1, -1, -1, -1, -1, -1, -1, -1,
+ 119, -1, -1, -1, 123, 69, 125, -1, 127, 128,
+ -1, -1, -1, -1, -1, -1, -1, -1, 137, 138,
+ 139, 140, 141, 142, 143, 144, 90, -1, 147, -1,
+ 149, -1, -1, 97, -1, 99, -1, 156, -1, -1,
+ -1, -1, 161, 162, 163, -1, 165, 166, 46, 113,
+ 169, 170, -1, 13, -1, -1, 54, -1, -1, 19,
+ 179, 21, -1, -1, -1, -1, 130, 131, -1, -1,
+ -1, -1, -1, -1, 72, 73, -1, 75, -1, -1,
+ -1, -1, -1, 81, -1, -1, -1, 47, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 164, -1, 166, -1, -1, -1, -1, 171, -1, 107,
+ 108, 175, -1, 111, 112, -1, 114, 115, 116, -1,
+ 118, -1, -1, -1, -1, -1, 86, -1, 88, -1,
+ -1, -1, -1, 93, -1, 95, -1, -1, 98, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 120, 121, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 171
+};
+
+ /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint16 yystos[] =
+{
+ 0, 1, 10, 14, 15, 16, 22, 23, 29, 30,
+ 33, 36, 37, 40, 49, 53, 59, 61, 70, 71,
+ 74, 78, 79, 82, 83, 103, 104, 105, 110, 119,
+ 123, 125, 127, 128, 137, 138, 139, 140, 141, 142,
+ 143, 144, 147, 149, 156, 161, 162, 163, 165, 166,
+ 169, 170, 179, 195, 196, 197, 198, 199, 211, 212,
+ 213, 214, 218, 223, 231, 240, 245, 249, 254, 258,
+ 259, 260, 261, 269, 270, 273, 284, 285, 178, 60,
+ 60, 215, 8, 12, 18, 68, 100, 101, 117, 148,
+ 250, 251, 252, 253, 11, 96, 101, 234, 235, 236,
+ 153, 262, 250, 20, 24, 80, 124, 132, 135, 155,
+ 160, 225, 65, 67, 153, 200, 201, 202, 153, 153,
+ 153, 153, 267, 268, 200, 281, 55, 56, 57, 58,
+ 85, 87, 89, 94, 237, 238, 239, 281, 153, 153,
+ 280, 60, 7, 8, 25, 63, 91, 154, 159, 274,
+ 275, 27, 65, 67, 145, 200, 201, 60, 41, 92,
+ 146, 246, 247, 248, 153, 263, 224, 225, 153, 6,
+ 31, 48, 51, 122, 150, 151, 156, 255, 256, 257,
+ 13, 19, 21, 47, 86, 88, 93, 95, 98, 120,
+ 121, 219, 220, 221, 222, 201, 60, 190, 277, 278,
+ 279, 60, 276, 0, 197, 178, 200, 200, 32, 60,
+ 283, 153, 153, 34, 54, 77, 272, 192, 28, 50,
+ 53, 133, 134, 140, 216, 217, 251, 235, 60, 32,
+ 226, 3, 42, 43, 44, 45, 136, 152, 157, 158,
+ 241, 242, 243, 244, 153, 197, 268, 200, 238, 60,
+ 153, 275, 232, 27, 27, 232, 232, 84, 247, 60,
+ 189, 225, 256, 283, 38, 60, 164, 282, 220, 60,
+ 283, 265, 60, 278, 60, 178, 203, 5, 64, 66,
+ 153, 174, 271, 180, 181, 286, 287, 288, 60, 153,
+ 29, 36, 39, 76, 106, 167, 227, 228, 229, 153,
+ 153, 60, 242, 283, 282, 46, 54, 72, 73, 75,
+ 81, 107, 108, 111, 112, 114, 115, 116, 118, 171,
+ 233, 232, 232, 201, 153, 61, 129, 266, 35, 9,
+ 17, 52, 53, 69, 90, 97, 99, 113, 130, 131,
+ 164, 166, 171, 175, 204, 205, 206, 207, 208, 209,
+ 210, 143, 287, 289, 290, 292, 178, 189, 153, 4,
+ 26, 102, 109, 126, 173, 176, 230, 232, 27, 264,
+ 201, 60, 60, 60, 168, 153, 189, 178, 193, 290,
+ 192, 283, 191, 200, 182, 291, 189, 183, 293, 294,
+ 283, 189, 193, 294, 178, 283, 192, 184, 185, 186,
+ 187, 188, 295, 296, 297, 193, 296, 178, 189, 178,
+ 283
+};
+
+ /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint16 yyr1[] =
+{
+ 0, 194, 195, 196, 196, 196, 197, 197, 197, 197,
+ 197, 197, 197, 197, 197, 197, 197, 197, 197, 197,
+ 198, 199, 199, 199, 199, 199, 200, 200, 201, 202,
+ 202, 203, 203, 204, 204, 204, 205, 206, 206, 206,
+ 206, 206, 206, 206, 206, 207, 207, 208, 208, 208,
+ 208, 208, 208, 209, 210, 211, 212, 212, 213, 213,
+ 213, 214, 214, 214, 214, 214, 214, 214, 214, 214,
+ 215, 215, 216, 216, 217, 217, 217, 217, 217, 218,
+ 219, 219, 220, 220, 220, 221, 221, 221, 221, 221,
+ 221, 222, 222, 222, 222, 223, 223, 223, 224, 224,
+ 225, 225, 225, 225, 225, 225, 225, 225, 226, 226,
+ 227, 227, 227, 227, 228, 228, 229, 229, 230, 230,
+ 230, 230, 230, 230, 230, 231, 231, 231, 231, 231,
+ 231, 231, 231, 232, 232, 233, 233, 233, 233, 233,
+ 233, 233, 233, 233, 233, 233, 233, 233, 233, 233,
+ 234, 234, 235, 236, 236, 236, 237, 237, 238, 239,
+ 239, 239, 239, 239, 239, 239, 239, 240, 241, 241,
+ 242, 242, 242, 242, 242, 243, 243, 244, 244, 244,
+ 244, 245, 246, 246, 247, 248, 248, 248, 249, 249,
+ 250, 250, 251, 251, 252, 252, 252, 252, 252, 252,
+ 253, 253, 254, 255, 255, 256, 257, 257, 257, 257,
+ 257, 257, 257, 257, 258, 258, 258, 258, 258, 258,
+ 258, 258, 258, 258, 258, 258, 258, 259, 259, 259,
+ 260, 260, 260, 261, 261, 262, 262, 262, 263, 264,
+ 264, 265, 265, 266, 266, 267, 267, 268, 269, 269,
+ 270, 270, 271, 271, 271, 271, 272, 272, 272, 273,
+ 274, 274, 275, 275, 275, 275, 275, 275, 275, 276,
+ 276, 277, 277, 278, 278, 279, 280, 280, 281, 281,
+ 282, 282, 282, 283, 283, 284, 285, 286, 286, 287,
+ 288, 288, 289, 289, 290, 291, 292, 293, 293, 294,
+ 295, 295, 296, 297, 297, 297, 297, 297
+};
+
+ /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 1, 3, 2, 2, 0, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 3, 1, 1, 1, 1, 1, 1, 2, 1, 1,
+ 1, 0, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 2, 2, 1, 1, 1,
+ 1, 1, 1, 2, 1, 2, 1, 1, 1, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 0, 2, 2, 2, 1, 1, 1, 1, 1, 2,
+ 2, 1, 2, 2, 2, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 2, 2, 3, 2, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2,
+ 2, 2, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 2, 2, 3, 5, 3,
+ 4, 4, 3, 0, 2, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 1, 2, 1, 1, 1, 2, 1, 2, 1,
+ 1, 1, 1, 1, 1, 1, 1, 3, 2, 1,
+ 2, 2, 2, 2, 2, 1, 1, 1, 1, 1,
+ 1, 2, 2, 1, 2, 1, 1, 1, 2, 2,
+ 2, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 2, 2, 1, 2, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 2, 2, 2, 3,
+ 1, 2, 2, 2, 2, 3, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 2, 0, 4, 1,
+ 0, 0, 2, 2, 2, 2, 1, 1, 3, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
+ 2, 1, 1, 1, 1, 1, 1, 1, 1, 2,
+ 1, 2, 1, 1, 1, 5, 2, 1, 2, 1,
+ 1, 1, 1, 1, 1, 5, 1, 3, 2, 3,
+ 1, 1, 2, 1, 5, 4, 3, 2, 1, 6,
+ 3, 2, 3, 1, 1, 1, 1, 1
+};
+
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (yylen); \
+ yystate = *yyssp; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (ip_file, YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (0)
+
+/* Error token number */
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+
+/* This macro is provided for backward compatibility. */
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, ip_file); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+
+/*----------------------------------------.
+| Print this symbol's value on YYOUTPUT. |
+`----------------------------------------*/
+
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, struct FILE_INFO *ip_file)
+{
+ FILE *yyo = yyoutput;
+ YYUSE (yyo);
+ YYUSE (ip_file);
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+ YYUSE (yytype);
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, struct FILE_INFO *ip_file)
+{
+ YYFPRINTF (yyoutput, "%s %s (",
+ yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, ip_file);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+static void
+yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, struct FILE_INFO *ip_file)
+{
+ unsigned long int yylno = yyrline[yyrule];
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr,
+ yystos[yyssp[yyi + 1 - yynrhs]],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ , ip_file);
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyssp, yyvsp, Rule, ip_file); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+static YYSIZE_T
+yystrlen (const char *yystr)
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = YY_NULLPTR;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ {
+ YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ {
+ YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, struct FILE_INFO *ip_file)
+{
+ YYUSE (yyvaluep);
+ YYUSE (ip_file);
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ YYUSE (yytype);
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+
+
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+int
+yyparse (struct FILE_INFO *ip_file)
+{
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ 'yyss': related to states.
+ 'yyvs': related to semantic values.
+
+ Refer to the stacks through separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken = 0;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yyssp = yyss = yyssa;
+ yyvsp = yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = yylex (ip_file);
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ *++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ '$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 5:
+#line 370 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ /* I will need to incorporate much more fine grained
+ * error messages. The following should suffice for
+ * the time being.
+ */
+ msyslog(LOG_ERR,
+ "syntax error in %s line %d, column %d",
+ ip_file->fname,
+ ip_file->err_line_no,
+ ip_file->err_col_no);
+ }
+#line 2093 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 20:
+#line 405 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ peer_node *my_node;
+
+ my_node = create_peer_node((yyvsp[-2].Integer), (yyvsp[-1].Address_node), (yyvsp[0].Attr_val_fifo));
+ APPEND_G_FIFO(cfgt.peers, my_node);
+ }
+#line 2104 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 27:
+#line 424 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Address_node) = create_address_node((yyvsp[0].String), (yyvsp[-1].Integer)); }
+#line 2110 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 28:
+#line 429 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Address_node) = create_address_node((yyvsp[0].String), AF_UNSPEC); }
+#line 2116 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 29:
+#line 434 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Integer) = AF_INET; }
+#line 2122 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 30:
+#line 436 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Integer) = AF_INET6; }
+#line 2128 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 31:
+#line 441 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val_fifo) = NULL; }
+#line 2134 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 32:
+#line 443 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-1].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2143 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 36:
+#line 457 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_ival(T_Flag, (yyvsp[0].Integer)); }
+#line 2149 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 45:
+#line 473 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_ival((yyvsp[-1].Integer), (yyvsp[0].Integer)); }
+#line 2155 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 46:
+#line 475 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_uval((yyvsp[-1].Integer), (yyvsp[0].Integer)); }
+#line 2161 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 53:
+#line 489 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_sval((yyvsp[-1].Integer), (yyvsp[0].String)); }
+#line 2167 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 55:
+#line 503 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ unpeer_node *my_node;
+
+ my_node = create_unpeer_node((yyvsp[0].Address_node));
+ if (my_node)
+ APPEND_G_FIFO(cfgt.unpeers, my_node);
+ }
+#line 2179 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 58:
+#line 524 "ntp_parser.y" /* yacc.c:1646 */
+ { cfgt.broadcastclient = 1; }
+#line 2185 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 59:
+#line 526 "ntp_parser.y" /* yacc.c:1646 */
+ { CONCAT_G_FIFOS(cfgt.manycastserver, (yyvsp[0].Address_fifo)); }
+#line 2191 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 60:
+#line 528 "ntp_parser.y" /* yacc.c:1646 */
+ { CONCAT_G_FIFOS(cfgt.multicastclient, (yyvsp[0].Address_fifo)); }
+#line 2197 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 61:
+#line 539 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ attr_val *atrv;
+
+ atrv = create_attr_ival((yyvsp[-1].Integer), (yyvsp[0].Integer));
+ APPEND_G_FIFO(cfgt.vars, atrv);
+ }
+#line 2208 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 62:
+#line 546 "ntp_parser.y" /* yacc.c:1646 */
+ { cfgt.auth.control_key = (yyvsp[0].Integer); }
+#line 2214 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 63:
+#line 548 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ cfgt.auth.cryptosw++;
+ CONCAT_G_FIFOS(cfgt.auth.crypto_cmd_list, (yyvsp[0].Attr_val_fifo));
+ }
+#line 2223 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 64:
+#line 553 "ntp_parser.y" /* yacc.c:1646 */
+ { cfgt.auth.keys = (yyvsp[0].String); }
+#line 2229 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 65:
+#line 555 "ntp_parser.y" /* yacc.c:1646 */
+ { cfgt.auth.keysdir = (yyvsp[0].String); }
+#line 2235 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 66:
+#line 557 "ntp_parser.y" /* yacc.c:1646 */
+ { cfgt.auth.request_key = (yyvsp[0].Integer); }
+#line 2241 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 67:
+#line 559 "ntp_parser.y" /* yacc.c:1646 */
+ { cfgt.auth.revoke = (yyvsp[0].Integer); }
+#line 2247 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 68:
+#line 561 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ cfgt.auth.trusted_key_list = (yyvsp[0].Attr_val_fifo);
+
+ // if (!cfgt.auth.trusted_key_list)
+ // cfgt.auth.trusted_key_list = $2;
+ // else
+ // LINK_SLIST(cfgt.auth.trusted_key_list, $2, link);
+ }
+#line 2260 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 69:
+#line 570 "ntp_parser.y" /* yacc.c:1646 */
+ { cfgt.auth.ntp_signd_socket = (yyvsp[0].String); }
+#line 2266 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 70:
+#line 575 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val_fifo) = NULL; }
+#line 2272 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 71:
+#line 577 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-1].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2281 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 72:
+#line 585 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_sval((yyvsp[-1].Integer), (yyvsp[0].String)); }
+#line 2287 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 73:
+#line 587 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val) = NULL;
+ cfgt.auth.revoke = (yyvsp[0].Integer);
+ msyslog(LOG_WARNING,
+ "'crypto revoke %d' is deprecated, "
+ "please use 'revoke %d' instead.",
+ cfgt.auth.revoke, cfgt.auth.revoke);
+ }
+#line 2300 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 79:
+#line 612 "ntp_parser.y" /* yacc.c:1646 */
+ { CONCAT_G_FIFOS(cfgt.orphan_cmds, (yyvsp[0].Attr_val_fifo)); }
+#line 2306 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 80:
+#line 617 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-1].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2315 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 81:
+#line 622 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2324 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 82:
+#line 630 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_dval((yyvsp[-1].Integer), (double)(yyvsp[0].Integer)); }
+#line 2330 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 83:
+#line 632 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_dval((yyvsp[-1].Integer), (yyvsp[0].Double)); }
+#line 2336 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 84:
+#line 634 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_dval((yyvsp[-1].Integer), (double)(yyvsp[0].Integer)); }
+#line 2342 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 95:
+#line 660 "ntp_parser.y" /* yacc.c:1646 */
+ { CONCAT_G_FIFOS(cfgt.stats_list, (yyvsp[0].Int_fifo)); }
+#line 2348 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 96:
+#line 662 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ if (input_from_file) {
+ cfgt.stats_dir = (yyvsp[0].String);
+ } else {
+ YYFREE((yyvsp[0].String));
+ yyerror(ip_file, "statsdir remote configuration ignored");
+ }
+ }
+#line 2361 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 97:
+#line 671 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ filegen_node *fgn;
+
+ fgn = create_filegen_node((yyvsp[-1].Integer), (yyvsp[0].Attr_val_fifo));
+ APPEND_G_FIFO(cfgt.filegen_opts, fgn);
+ }
+#line 2372 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 98:
+#line 681 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Int_fifo) = (yyvsp[-1].Int_fifo);
+ APPEND_G_FIFO((yyval.Int_fifo), create_int_node((yyvsp[0].Integer)));
+ }
+#line 2381 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 99:
+#line 686 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Int_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Int_fifo), create_int_node((yyvsp[0].Integer)));
+ }
+#line 2390 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 108:
+#line 705 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val_fifo) = NULL; }
+#line 2396 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 109:
+#line 707 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-1].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2405 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 110:
+#line 715 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ if (input_from_file) {
+ (yyval.Attr_val) = create_attr_sval((yyvsp[-1].Integer), (yyvsp[0].String));
+ } else {
+ (yyval.Attr_val) = NULL;
+ YYFREE((yyvsp[0].String));
+ yyerror(ip_file, "filegen file remote config ignored");
+ }
+ }
+#line 2419 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 111:
+#line 725 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ if (input_from_file) {
+ (yyval.Attr_val) = create_attr_ival((yyvsp[-1].Integer), (yyvsp[0].Integer));
+ } else {
+ (yyval.Attr_val) = NULL;
+ yyerror(ip_file, "filegen type remote config ignored");
+ }
+ }
+#line 2432 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 112:
+#line 734 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ const char *err;
+
+ if (input_from_file) {
+ (yyval.Attr_val) = create_attr_ival(T_Flag, (yyvsp[0].Integer));
+ } else {
+ (yyval.Attr_val) = NULL;
+ if (T_Link == (yyvsp[0].Integer))
+ err = "filegen link remote config ignored";
+ else
+ err = "filegen nolink remote config ignored";
+ yyerror(ip_file, err);
+ }
+ }
+#line 2451 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 113:
+#line 749 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_ival(T_Flag, (yyvsp[0].Integer)); }
+#line 2457 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 125:
+#line 779 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ CONCAT_G_FIFOS(cfgt.discard_opts, (yyvsp[0].Attr_val_fifo));
+ }
+#line 2465 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 126:
+#line 783 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ CONCAT_G_FIFOS(cfgt.mru_opts, (yyvsp[0].Attr_val_fifo));
+ }
+#line 2473 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 127:
+#line 787 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ restrict_node *rn;
+
+ rn = create_restrict_node((yyvsp[-1].Address_node), NULL, (yyvsp[0].Int_fifo),
+ ip_file->line_no);
+ APPEND_G_FIFO(cfgt.restrict_opts, rn);
+ }
+#line 2485 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 128:
+#line 795 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ restrict_node *rn;
+
+ rn = create_restrict_node((yyvsp[-3].Address_node), (yyvsp[-1].Address_node), (yyvsp[0].Int_fifo),
+ ip_file->line_no);
+ APPEND_G_FIFO(cfgt.restrict_opts, rn);
+ }
+#line 2497 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 129:
+#line 803 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ restrict_node *rn;
+
+ rn = create_restrict_node(NULL, NULL, (yyvsp[0].Int_fifo),
+ ip_file->line_no);
+ APPEND_G_FIFO(cfgt.restrict_opts, rn);
+ }
+#line 2509 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 130:
+#line 811 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ restrict_node *rn;
+
+ rn = create_restrict_node(
+ create_address_node(
+ estrdup("0.0.0.0"),
+ AF_INET),
+ create_address_node(
+ estrdup("0.0.0.0"),
+ AF_INET),
+ (yyvsp[0].Int_fifo),
+ ip_file->line_no);
+ APPEND_G_FIFO(cfgt.restrict_opts, rn);
+ }
+#line 2528 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 131:
+#line 826 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ restrict_node *rn;
+
+ rn = create_restrict_node(
+ create_address_node(
+ estrdup("::"),
+ AF_INET6),
+ create_address_node(
+ estrdup("::"),
+ AF_INET6),
+ (yyvsp[0].Int_fifo),
+ ip_file->line_no);
+ APPEND_G_FIFO(cfgt.restrict_opts, rn);
+ }
+#line 2547 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 132:
+#line 841 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ restrict_node * rn;
+
+ APPEND_G_FIFO((yyvsp[0].Int_fifo), create_int_node((yyvsp[-1].Integer)));
+ rn = create_restrict_node(
+ NULL, NULL, (yyvsp[0].Int_fifo), ip_file->line_no);
+ APPEND_G_FIFO(cfgt.restrict_opts, rn);
+ }
+#line 2560 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 133:
+#line 853 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Int_fifo) = NULL; }
+#line 2566 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 134:
+#line 855 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Int_fifo) = (yyvsp[-1].Int_fifo);
+ APPEND_G_FIFO((yyval.Int_fifo), create_int_node((yyvsp[0].Integer)));
+ }
+#line 2575 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 150:
+#line 881 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-1].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2584 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 151:
+#line 886 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2593 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 152:
+#line 894 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_ival((yyvsp[-1].Integer), (yyvsp[0].Integer)); }
+#line 2599 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 156:
+#line 905 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-1].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2608 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 157:
+#line 910 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2617 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 158:
+#line 918 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_ival((yyvsp[-1].Integer), (yyvsp[0].Integer)); }
+#line 2623 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 167:
+#line 938 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ addr_opts_node *aon;
+
+ aon = create_addr_opts_node((yyvsp[-1].Address_node), (yyvsp[0].Attr_val_fifo));
+ APPEND_G_FIFO(cfgt.fudge, aon);
+ }
+#line 2634 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 168:
+#line 948 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-1].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2643 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 169:
+#line 953 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2652 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 170:
+#line 961 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_dval((yyvsp[-1].Integer), (yyvsp[0].Double)); }
+#line 2658 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 171:
+#line 963 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_ival((yyvsp[-1].Integer), (yyvsp[0].Integer)); }
+#line 2664 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 172:
+#line 965 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_ival((yyvsp[-1].Integer), (yyvsp[0].Integer)); }
+#line 2670 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 173:
+#line 967 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_sval((yyvsp[-1].Integer), (yyvsp[0].String)); }
+#line 2676 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 174:
+#line 969 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_sval((yyvsp[-1].Integer), (yyvsp[0].String)); }
+#line 2682 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 181:
+#line 990 "ntp_parser.y" /* yacc.c:1646 */
+ { CONCAT_G_FIFOS(cfgt.rlimit, (yyvsp[0].Attr_val_fifo)); }
+#line 2688 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 182:
+#line 995 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-1].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2697 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 183:
+#line 1000 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2706 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 184:
+#line 1008 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_ival((yyvsp[-1].Integer), (yyvsp[0].Integer)); }
+#line 2712 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 188:
+#line 1024 "ntp_parser.y" /* yacc.c:1646 */
+ { CONCAT_G_FIFOS(cfgt.enable_opts, (yyvsp[0].Attr_val_fifo)); }
+#line 2718 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 189:
+#line 1026 "ntp_parser.y" /* yacc.c:1646 */
+ { CONCAT_G_FIFOS(cfgt.disable_opts, (yyvsp[0].Attr_val_fifo)); }
+#line 2724 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 190:
+#line 1031 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-1].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2733 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 191:
+#line 1036 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2742 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 192:
+#line 1044 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_ival(T_Flag, (yyvsp[0].Integer)); }
+#line 2748 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 193:
+#line 1046 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ if (input_from_file) {
+ (yyval.Attr_val) = create_attr_ival(T_Flag, (yyvsp[0].Integer));
+ } else {
+ char err_str[128];
+
+ (yyval.Attr_val) = NULL;
+ snprintf(err_str, sizeof(err_str),
+ "enable/disable %s remote configuration ignored",
+ keyword((yyvsp[0].Integer)));
+ yyerror(ip_file, err_str);
+ }
+ }
+#line 2766 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 202:
+#line 1081 "ntp_parser.y" /* yacc.c:1646 */
+ { CONCAT_G_FIFOS(cfgt.tinker, (yyvsp[0].Attr_val_fifo)); }
+#line 2772 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 203:
+#line 1086 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-1].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2781 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 204:
+#line 1091 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2790 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 205:
+#line 1099 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_dval((yyvsp[-1].Integer), (yyvsp[0].Double)); }
+#line 2796 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 216:
+#line 1122 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ attr_val *av;
+
+ av = create_attr_dval((yyvsp[-1].Integer), (yyvsp[0].Double));
+ APPEND_G_FIFO(cfgt.vars, av);
+ }
+#line 2807 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 217:
+#line 1129 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ attr_val *av;
+
+ av = create_attr_sval((yyvsp[-1].Integer), (yyvsp[0].String));
+ APPEND_G_FIFO(cfgt.vars, av);
+ }
+#line 2818 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 218:
+#line 1136 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ char error_text[64];
+ attr_val *av;
+
+ if (input_from_file) {
+ av = create_attr_sval((yyvsp[-1].Integer), (yyvsp[0].String));
+ APPEND_G_FIFO(cfgt.vars, av);
+ } else {
+ YYFREE((yyvsp[0].String));
+ snprintf(error_text, sizeof(error_text),
+ "%s remote config ignored",
+ keyword((yyvsp[-1].Integer)));
+ yyerror(ip_file, error_text);
+ }
+ }
+#line 2838 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 219:
+#line 1152 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ if (!input_from_file) {
+ yyerror(ip_file, "remote includefile ignored");
+ break;
+ }
+ if (curr_include_level >= MAXINCLUDELEVEL) {
+ fprintf(stderr, "getconfig: Maximum include file level exceeded.\n");
+ msyslog(LOG_ERR, "getconfig: Maximum include file level exceeded.");
+ } else {
+ fp[curr_include_level + 1] = F_OPEN(FindConfig((yyvsp[-1].String)), "r");
+ if (fp[curr_include_level + 1] == NULL) {
+ fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig((yyvsp[-1].String)));
+ msyslog(LOG_ERR, "getconfig: Couldn't open <%s>", FindConfig((yyvsp[-1].String)));
+ } else {
+ ip_file = fp[++curr_include_level];
+ }
+ }
+ }
+#line 2861 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 220:
+#line 1171 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ while (curr_include_level != -1)
+ FCLOSE(fp[curr_include_level--]);
+ }
+#line 2870 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 221:
+#line 1176 "ntp_parser.y" /* yacc.c:1646 */
+ { /* see drift_parm below for actions */ }
+#line 2876 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 222:
+#line 1178 "ntp_parser.y" /* yacc.c:1646 */
+ { CONCAT_G_FIFOS(cfgt.logconfig, (yyvsp[0].Attr_val_fifo)); }
+#line 2882 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 223:
+#line 1180 "ntp_parser.y" /* yacc.c:1646 */
+ { CONCAT_G_FIFOS(cfgt.phone, (yyvsp[0].String_fifo)); }
+#line 2888 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 224:
+#line 1182 "ntp_parser.y" /* yacc.c:1646 */
+ { APPEND_G_FIFO(cfgt.setvar, (yyvsp[0].Set_var)); }
+#line 2894 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 225:
+#line 1184 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ addr_opts_node *aon;
+
+ aon = create_addr_opts_node((yyvsp[-1].Address_node), (yyvsp[0].Attr_val_fifo));
+ APPEND_G_FIFO(cfgt.trap, aon);
+ }
+#line 2905 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 226:
+#line 1191 "ntp_parser.y" /* yacc.c:1646 */
+ { CONCAT_G_FIFOS(cfgt.ttl, (yyvsp[0].Attr_val_fifo)); }
+#line 2911 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 235:
+#line 1213 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ attr_val *av;
+
+ av = create_attr_sval(T_Driftfile, (yyvsp[0].String));
+ APPEND_G_FIFO(cfgt.vars, av);
+ }
+#line 2922 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 236:
+#line 1220 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ attr_val *av;
+
+ av = create_attr_sval(T_Driftfile, (yyvsp[-1].String));
+ APPEND_G_FIFO(cfgt.vars, av);
+ av = create_attr_dval(T_WanderThreshold, (yyvsp[0].Double));
+ APPEND_G_FIFO(cfgt.vars, av);
+ }
+#line 2935 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 237:
+#line 1229 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ attr_val *av;
+
+ av = create_attr_sval(T_Driftfile, "");
+ APPEND_G_FIFO(cfgt.vars, av);
+ }
+#line 2946 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 238:
+#line 1239 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Set_var) = create_setvar_node((yyvsp[-3].String), (yyvsp[-1].String), (yyvsp[0].Integer)); }
+#line 2952 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 240:
+#line 1245 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Integer) = 0; }
+#line 2958 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 241:
+#line 1250 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val_fifo) = NULL; }
+#line 2964 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 242:
+#line 1252 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-1].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2973 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 243:
+#line 1260 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_ival((yyvsp[-1].Integer), (yyvsp[0].Integer)); }
+#line 2979 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 244:
+#line 1262 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val) = create_attr_sval((yyvsp[-1].Integer), estrdup((yyvsp[0].Address_node)->address));
+ destroy_address_node((yyvsp[0].Address_node));
+ }
+#line 2988 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 245:
+#line 1270 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-1].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 2997 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 246:
+#line 1275 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 3006 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 247:
+#line 1283 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ char prefix;
+ char * type;
+
+ switch ((yyvsp[0].String)[0]) {
+
+ case '+':
+ case '-':
+ case '=':
+ prefix = (yyvsp[0].String)[0];
+ type = (yyvsp[0].String) + 1;
+ break;
+
+ default:
+ prefix = '=';
+ type = (yyvsp[0].String);
+ }
+
+ (yyval.Attr_val) = create_attr_sval(prefix, estrdup(type));
+ YYFREE((yyvsp[0].String));
+ }
+#line 3032 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 248:
+#line 1308 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ nic_rule_node *nrn;
+
+ nrn = create_nic_rule_node((yyvsp[0].Integer), NULL, (yyvsp[-1].Integer));
+ APPEND_G_FIFO(cfgt.nic_rules, nrn);
+ }
+#line 3043 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 249:
+#line 1315 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ nic_rule_node *nrn;
+
+ nrn = create_nic_rule_node(0, (yyvsp[0].String), (yyvsp[-1].Integer));
+ APPEND_G_FIFO(cfgt.nic_rules, nrn);
+ }
+#line 3054 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 259:
+#line 1343 "ntp_parser.y" /* yacc.c:1646 */
+ { CONCAT_G_FIFOS(cfgt.reset_counters, (yyvsp[0].Int_fifo)); }
+#line 3060 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 260:
+#line 1348 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Int_fifo) = (yyvsp[-1].Int_fifo);
+ APPEND_G_FIFO((yyval.Int_fifo), create_int_node((yyvsp[0].Integer)));
+ }
+#line 3069 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 261:
+#line 1353 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Int_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Int_fifo), create_int_node((yyvsp[0].Integer)));
+ }
+#line 3078 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 269:
+#line 1377 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-1].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), create_int_node((yyvsp[0].Integer)));
+ }
+#line 3087 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 270:
+#line 1382 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Attr_val_fifo), create_int_node((yyvsp[0].Integer)));
+ }
+#line 3096 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 271:
+#line 1390 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-1].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 3105 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 272:
+#line 1395 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[0].Attr_val));
+ }
+#line 3114 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 273:
+#line 1403 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_ival('i', (yyvsp[0].Integer)); }
+#line 3120 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 275:
+#line 1409 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_rangeval('-', (yyvsp[-3].Integer), (yyvsp[-1].Integer)); }
+#line 3126 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 276:
+#line 1414 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.String_fifo) = (yyvsp[-1].String_fifo);
+ APPEND_G_FIFO((yyval.String_fifo), create_string_node((yyvsp[0].String)));
+ }
+#line 3135 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 277:
+#line 1419 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.String_fifo) = NULL;
+ APPEND_G_FIFO((yyval.String_fifo), create_string_node((yyvsp[0].String)));
+ }
+#line 3144 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 278:
+#line 1427 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Address_fifo) = (yyvsp[-1].Address_fifo);
+ APPEND_G_FIFO((yyval.Address_fifo), (yyvsp[0].Address_node));
+ }
+#line 3153 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 279:
+#line 1432 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Address_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Address_fifo), (yyvsp[0].Address_node));
+ }
+#line 3162 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 280:
+#line 1440 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ if ((yyvsp[0].Integer) != 0 && (yyvsp[0].Integer) != 1) {
+ yyerror(ip_file, "Integer value is not boolean (0 or 1). Assuming 1");
+ (yyval.Integer) = 1;
+ } else {
+ (yyval.Integer) = (yyvsp[0].Integer);
+ }
+ }
+#line 3175 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 281:
+#line 1448 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Integer) = 1; }
+#line 3181 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 282:
+#line 1449 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Integer) = 0; }
+#line 3187 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 283:
+#line 1453 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Double) = (double)(yyvsp[0].Integer); }
+#line 3193 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 285:
+#line 1464 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ sim_node *sn;
+
+ sn = create_sim_node((yyvsp[-2].Attr_val_fifo), (yyvsp[-1].Sim_server_fifo));
+ APPEND_G_FIFO(cfgt.sim_details, sn);
+
+ /* Revert from ; to \n for end-of-command */
+ old_config_style = 1;
+ }
+#line 3207 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 286:
+#line 1481 "ntp_parser.y" /* yacc.c:1646 */
+ { old_config_style = 0; }
+#line 3213 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 287:
+#line 1486 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-2].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[-1].Attr_val));
+ }
+#line 3222 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 288:
+#line 1491 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[-1].Attr_val));
+ }
+#line 3231 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 289:
+#line 1499 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_dval((yyvsp[-2].Integer), (yyvsp[0].Double)); }
+#line 3237 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 292:
+#line 1509 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Sim_server_fifo) = (yyvsp[-1].Sim_server_fifo);
+ APPEND_G_FIFO((yyval.Sim_server_fifo), (yyvsp[0].Sim_server));
+ }
+#line 3246 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 293:
+#line 1514 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Sim_server_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Sim_server_fifo), (yyvsp[0].Sim_server));
+ }
+#line 3255 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 294:
+#line 1522 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Sim_server) = ONLY_SIM(create_sim_server((yyvsp[-4].Address_node), (yyvsp[-2].Double), (yyvsp[-1].Sim_script_fifo))); }
+#line 3261 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 295:
+#line 1527 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Double) = (yyvsp[-1].Double); }
+#line 3267 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 296:
+#line 1532 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Address_node) = (yyvsp[0].Address_node); }
+#line 3273 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 297:
+#line 1537 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Sim_script_fifo) = (yyvsp[-1].Sim_script_fifo);
+ APPEND_G_FIFO((yyval.Sim_script_fifo), (yyvsp[0].Sim_script));
+ }
+#line 3282 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 298:
+#line 1542 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Sim_script_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Sim_script_fifo), (yyvsp[0].Sim_script));
+ }
+#line 3291 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 299:
+#line 1550 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Sim_script) = ONLY_SIM(create_sim_script_info((yyvsp[-3].Double), (yyvsp[-1].Attr_val_fifo))); }
+#line 3297 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 300:
+#line 1555 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = (yyvsp[-2].Attr_val_fifo);
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[-1].Attr_val));
+ }
+#line 3306 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 301:
+#line 1560 "ntp_parser.y" /* yacc.c:1646 */
+ {
+ (yyval.Attr_val_fifo) = NULL;
+ APPEND_G_FIFO((yyval.Attr_val_fifo), (yyvsp[-1].Attr_val));
+ }
+#line 3315 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+ case 302:
+#line 1568 "ntp_parser.y" /* yacc.c:1646 */
+ { (yyval.Attr_val) = create_attr_dval((yyvsp[-2].Integer), (yyvsp[0].Double)); }
+#line 3321 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ break;
+
+
+#line 3325 "../../ntpd/ntp_parser.c" /* yacc.c:1646 */
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now 'shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*--------------------------------------.
+| yyerrlab -- here on detecting error. |
+`--------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (ip_file, YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (ip_file, yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, ip_file);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, ip_file);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ *++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (ip_file, YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, ip_file);
+ }
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, ip_file);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ return yyresult;
+}
+#line 1579 "ntp_parser.y" /* yacc.c:1906 */
+
+
+void
+yyerror(
+ struct FILE_INFO *ip_file,
+ const char *msg
+ )
+{
+ int retval;
+
+ ip_file->err_line_no = ip_file->prev_token_line_no;
+ ip_file->err_col_no = ip_file->prev_token_col_no;
+
+ msyslog(LOG_ERR,
+ "line %d column %d %s",
+ ip_file->err_line_no,
+ ip_file->err_col_no,
+ msg);
+ if (!input_from_file) {
+ /* Save the error message in the correct buffer */
+ retval = snprintf(remote_config.err_msg + remote_config.err_pos,
+ MAXLINE - remote_config.err_pos,
+ "column %d %s",
+ ip_file->err_col_no, msg);
+
+ /* Increment the value of err_pos */
+ if (retval > 0)
+ remote_config.err_pos += retval;
+
+ /* Increment the number of errors */
+ ++remote_config.no_errors;
+ }
+}
+
+
+/*
+ * token_name - convert T_ token integers to text
+ * example: token_name(T_Server) returns "T_Server"
+ */
+const char *
+token_name(
+ int token
+ )
+{
+ return yytname[YYTRANSLATE(token)];
+}
+
+
+/* Initial Testing function -- ignore */
+#if 0
+int main(int argc, char *argv[])
+{
+ ip_file = FOPEN(argv[1], "r");
+ if (!ip_file)
+ fprintf(stderr, "ERROR!! Could not open file: %s\n", argv[1]);
+ yyparse();
+ return 0;
+}
+#endif
+
diff --git a/ntpd/ntp_parser.h b/ntpd/ntp_parser.h
new file mode 100644
index 0000000..c830157
--- /dev/null
+++ b/ntpd/ntp_parser.h
@@ -0,0 +1,459 @@
+/* A Bison parser, made by GNU Bison 3.0.2. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+#ifndef YY_YY_______NTPD_NTP_PARSER_H_INCLUDED
+# define YY_YY_______NTPD_NTP_PARSER_H_INCLUDED
+/* Debug traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 1
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+/* Token type. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ enum yytokentype
+ {
+ T_Abbrev = 258,
+ T_Age = 259,
+ T_All = 260,
+ T_Allan = 261,
+ T_Allpeers = 262,
+ T_Auth = 263,
+ T_Autokey = 264,
+ T_Automax = 265,
+ T_Average = 266,
+ T_Bclient = 267,
+ T_Beacon = 268,
+ T_Broadcast = 269,
+ T_Broadcastclient = 270,
+ T_Broadcastdelay = 271,
+ T_Burst = 272,
+ T_Calibrate = 273,
+ T_Ceiling = 274,
+ T_Clockstats = 275,
+ T_Cohort = 276,
+ T_ControlKey = 277,
+ T_Crypto = 278,
+ T_Cryptostats = 279,
+ T_Ctl = 280,
+ T_Day = 281,
+ T_Default = 282,
+ T_Digest = 283,
+ T_Disable = 284,
+ T_Discard = 285,
+ T_Dispersion = 286,
+ T_Double = 287,
+ T_Driftfile = 288,
+ T_Drop = 289,
+ T_Ellipsis = 290,
+ T_Enable = 291,
+ T_End = 292,
+ T_False = 293,
+ T_File = 294,
+ T_Filegen = 295,
+ T_Filenum = 296,
+ T_Flag1 = 297,
+ T_Flag2 = 298,
+ T_Flag3 = 299,
+ T_Flag4 = 300,
+ T_Flake = 301,
+ T_Floor = 302,
+ T_Freq = 303,
+ T_Fudge = 304,
+ T_Host = 305,
+ T_Huffpuff = 306,
+ T_Iburst = 307,
+ T_Ident = 308,
+ T_Ignore = 309,
+ T_Incalloc = 310,
+ T_Incmem = 311,
+ T_Initalloc = 312,
+ T_Initmem = 313,
+ T_Includefile = 314,
+ T_Integer = 315,
+ T_Interface = 316,
+ T_Intrange = 317,
+ T_Io = 318,
+ T_Ipv4 = 319,
+ T_Ipv4_flag = 320,
+ T_Ipv6 = 321,
+ T_Ipv6_flag = 322,
+ T_Kernel = 323,
+ T_Key = 324,
+ T_Keys = 325,
+ T_Keysdir = 326,
+ T_Kod = 327,
+ T_Mssntp = 328,
+ T_Leapfile = 329,
+ T_Limited = 330,
+ T_Link = 331,
+ T_Listen = 332,
+ T_Logconfig = 333,
+ T_Logfile = 334,
+ T_Loopstats = 335,
+ T_Lowpriotrap = 336,
+ T_Manycastclient = 337,
+ T_Manycastserver = 338,
+ T_Mask = 339,
+ T_Maxage = 340,
+ T_Maxclock = 341,
+ T_Maxdepth = 342,
+ T_Maxdist = 343,
+ T_Maxmem = 344,
+ T_Maxpoll = 345,
+ T_Mem = 346,
+ T_Memlock = 347,
+ T_Minclock = 348,
+ T_Mindepth = 349,
+ T_Mindist = 350,
+ T_Minimum = 351,
+ T_Minpoll = 352,
+ T_Minsane = 353,
+ T_Mode = 354,
+ T_Mode7 = 355,
+ T_Monitor = 356,
+ T_Month = 357,
+ T_Mru = 358,
+ T_Multicastclient = 359,
+ T_Nic = 360,
+ T_Nolink = 361,
+ T_Nomodify = 362,
+ T_Nomrulist = 363,
+ T_None = 364,
+ T_Nonvolatile = 365,
+ T_Nopeer = 366,
+ T_Noquery = 367,
+ T_Noselect = 368,
+ T_Noserve = 369,
+ T_Notrap = 370,
+ T_Notrust = 371,
+ T_Ntp = 372,
+ T_Ntpport = 373,
+ T_NtpSignDsocket = 374,
+ T_Orphan = 375,
+ T_Orphanwait = 376,
+ T_Panic = 377,
+ T_Peer = 378,
+ T_Peerstats = 379,
+ T_Phone = 380,
+ T_Pid = 381,
+ T_Pidfile = 382,
+ T_Pool = 383,
+ T_Port = 384,
+ T_Preempt = 385,
+ T_Prefer = 386,
+ T_Protostats = 387,
+ T_Pw = 388,
+ T_Randfile = 389,
+ T_Rawstats = 390,
+ T_Refid = 391,
+ T_Requestkey = 392,
+ T_Reset = 393,
+ T_Restrict = 394,
+ T_Revoke = 395,
+ T_Rlimit = 396,
+ T_Saveconfigdir = 397,
+ T_Server = 398,
+ T_Setvar = 399,
+ T_Source = 400,
+ T_Stacksize = 401,
+ T_Statistics = 402,
+ T_Stats = 403,
+ T_Statsdir = 404,
+ T_Step = 405,
+ T_Stepout = 406,
+ T_Stratum = 407,
+ T_String = 408,
+ T_Sys = 409,
+ T_Sysstats = 410,
+ T_Tick = 411,
+ T_Time1 = 412,
+ T_Time2 = 413,
+ T_Timer = 414,
+ T_Timingstats = 415,
+ T_Tinker = 416,
+ T_Tos = 417,
+ T_Trap = 418,
+ T_True = 419,
+ T_Trustedkey = 420,
+ T_Ttl = 421,
+ T_Type = 422,
+ T_U_int = 423,
+ T_Unconfig = 424,
+ T_Unpeer = 425,
+ T_Version = 426,
+ T_WanderThreshold = 427,
+ T_Week = 428,
+ T_Wildcard = 429,
+ T_Xleave = 430,
+ T_Year = 431,
+ T_Flag = 432,
+ T_EOC = 433,
+ T_Simulate = 434,
+ T_Beep_Delay = 435,
+ T_Sim_Duration = 436,
+ T_Server_Offset = 437,
+ T_Duration = 438,
+ T_Freq_Offset = 439,
+ T_Wander = 440,
+ T_Jitter = 441,
+ T_Prop_Delay = 442,
+ T_Proc_Delay = 443
+ };
+#endif
+/* Tokens. */
+#define T_Abbrev 258
+#define T_Age 259
+#define T_All 260
+#define T_Allan 261
+#define T_Allpeers 262
+#define T_Auth 263
+#define T_Autokey 264
+#define T_Automax 265
+#define T_Average 266
+#define T_Bclient 267
+#define T_Beacon 268
+#define T_Broadcast 269
+#define T_Broadcastclient 270
+#define T_Broadcastdelay 271
+#define T_Burst 272
+#define T_Calibrate 273
+#define T_Ceiling 274
+#define T_Clockstats 275
+#define T_Cohort 276
+#define T_ControlKey 277
+#define T_Crypto 278
+#define T_Cryptostats 279
+#define T_Ctl 280
+#define T_Day 281
+#define T_Default 282
+#define T_Digest 283
+#define T_Disable 284
+#define T_Discard 285
+#define T_Dispersion 286
+#define T_Double 287
+#define T_Driftfile 288
+#define T_Drop 289
+#define T_Ellipsis 290
+#define T_Enable 291
+#define T_End 292
+#define T_False 293
+#define T_File 294
+#define T_Filegen 295
+#define T_Filenum 296
+#define T_Flag1 297
+#define T_Flag2 298
+#define T_Flag3 299
+#define T_Flag4 300
+#define T_Flake 301
+#define T_Floor 302
+#define T_Freq 303
+#define T_Fudge 304
+#define T_Host 305
+#define T_Huffpuff 306
+#define T_Iburst 307
+#define T_Ident 308
+#define T_Ignore 309
+#define T_Incalloc 310
+#define T_Incmem 311
+#define T_Initalloc 312
+#define T_Initmem 313
+#define T_Includefile 314
+#define T_Integer 315
+#define T_Interface 316
+#define T_Intrange 317
+#define T_Io 318
+#define T_Ipv4 319
+#define T_Ipv4_flag 320
+#define T_Ipv6 321
+#define T_Ipv6_flag 322
+#define T_Kernel 323
+#define T_Key 324
+#define T_Keys 325
+#define T_Keysdir 326
+#define T_Kod 327
+#define T_Mssntp 328
+#define T_Leapfile 329
+#define T_Limited 330
+#define T_Link 331
+#define T_Listen 332
+#define T_Logconfig 333
+#define T_Logfile 334
+#define T_Loopstats 335
+#define T_Lowpriotrap 336
+#define T_Manycastclient 337
+#define T_Manycastserver 338
+#define T_Mask 339
+#define T_Maxage 340
+#define T_Maxclock 341
+#define T_Maxdepth 342
+#define T_Maxdist 343
+#define T_Maxmem 344
+#define T_Maxpoll 345
+#define T_Mem 346
+#define T_Memlock 347
+#define T_Minclock 348
+#define T_Mindepth 349
+#define T_Mindist 350
+#define T_Minimum 351
+#define T_Minpoll 352
+#define T_Minsane 353
+#define T_Mode 354
+#define T_Mode7 355
+#define T_Monitor 356
+#define T_Month 357
+#define T_Mru 358
+#define T_Multicastclient 359
+#define T_Nic 360
+#define T_Nolink 361
+#define T_Nomodify 362
+#define T_Nomrulist 363
+#define T_None 364
+#define T_Nonvolatile 365
+#define T_Nopeer 366
+#define T_Noquery 367
+#define T_Noselect 368
+#define T_Noserve 369
+#define T_Notrap 370
+#define T_Notrust 371
+#define T_Ntp 372
+#define T_Ntpport 373
+#define T_NtpSignDsocket 374
+#define T_Orphan 375
+#define T_Orphanwait 376
+#define T_Panic 377
+#define T_Peer 378
+#define T_Peerstats 379
+#define T_Phone 380
+#define T_Pid 381
+#define T_Pidfile 382
+#define T_Pool 383
+#define T_Port 384
+#define T_Preempt 385
+#define T_Prefer 386
+#define T_Protostats 387
+#define T_Pw 388
+#define T_Randfile 389
+#define T_Rawstats 390
+#define T_Refid 391
+#define T_Requestkey 392
+#define T_Reset 393
+#define T_Restrict 394
+#define T_Revoke 395
+#define T_Rlimit 396
+#define T_Saveconfigdir 397
+#define T_Server 398
+#define T_Setvar 399
+#define T_Source 400
+#define T_Stacksize 401
+#define T_Statistics 402
+#define T_Stats 403
+#define T_Statsdir 404
+#define T_Step 405
+#define T_Stepout 406
+#define T_Stratum 407
+#define T_String 408
+#define T_Sys 409
+#define T_Sysstats 410
+#define T_Tick 411
+#define T_Time1 412
+#define T_Time2 413
+#define T_Timer 414
+#define T_Timingstats 415
+#define T_Tinker 416
+#define T_Tos 417
+#define T_Trap 418
+#define T_True 419
+#define T_Trustedkey 420
+#define T_Ttl 421
+#define T_Type 422
+#define T_U_int 423
+#define T_Unconfig 424
+#define T_Unpeer 425
+#define T_Version 426
+#define T_WanderThreshold 427
+#define T_Week 428
+#define T_Wildcard 429
+#define T_Xleave 430
+#define T_Year 431
+#define T_Flag 432
+#define T_EOC 433
+#define T_Simulate 434
+#define T_Beep_Delay 435
+#define T_Sim_Duration 436
+#define T_Server_Offset 437
+#define T_Duration 438
+#define T_Freq_Offset 439
+#define T_Wander 440
+#define T_Jitter 441
+#define T_Prop_Delay 442
+#define T_Proc_Delay 443
+
+/* Value type. */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE YYSTYPE;
+union YYSTYPE
+{
+#line 54 "ntp_parser.y" /* yacc.c:1909 */
+
+ char * String;
+ double Double;
+ int Integer;
+ unsigned U_int;
+ gen_fifo * Generic_fifo;
+ attr_val * Attr_val;
+ attr_val_fifo * Attr_val_fifo;
+ int_fifo * Int_fifo;
+ string_fifo * String_fifo;
+ address_node * Address_node;
+ address_fifo * Address_fifo;
+ setvar_node * Set_var;
+ server_info * Sim_server;
+ server_info_fifo * Sim_server_fifo;
+ script_info * Sim_script;
+ script_info_fifo * Sim_script_fifo;
+
+#line 449 "../../ntpd/ntp_parser.h" /* yacc.c:1909 */
+};
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+extern YYSTYPE yylval;
+
+int yyparse (struct FILE_INFO *ip_file);
+
+#endif /* !YY_YY_______NTPD_NTP_PARSER_H_INCLUDED */
diff --git a/ntpd/ntp_parser.y b/ntpd/ntp_parser.y
new file mode 100644
index 0000000..e75bffe
--- /dev/null
+++ b/ntpd/ntp_parser.y
@@ -0,0 +1,1638 @@
+/* ntp_parser.y
+ *
+ * The parser for the NTP configuration file.
+ *
+ * Written By: Sachin Kamboj
+ * University of Delaware
+ * Newark, DE 19711
+ * Copyright (c) 2006
+ */
+
+%parse-param {struct FILE_INFO *ip_file}
+%lex-param {struct FILE_INFO *ip_file}
+
+%{
+ #ifdef HAVE_CONFIG_H
+ # include <config.h>
+ #endif
+
+ #include "ntp.h"
+ #include "ntpd.h"
+ #include "ntp_machine.h"
+ #include "ntp_stdlib.h"
+ #include "ntp_filegen.h"
+ #include "ntp_scanner.h"
+ #include "ntp_config.h"
+ #include "ntp_crypto.h"
+
+ #include "ntpsim.h" /* HMS: Do we really want this all the time? */
+ /* SK: It might be a good idea to always
+ include the simulator code. That way
+ someone can use the same configuration file
+ for both the simulator and the daemon
+ */
+
+ #define YYMALLOC emalloc
+ #define YYFREE free
+ #define YYERROR_VERBOSE
+ #define YYMAXDEPTH 1000 /* stop the madness sooner */
+ void yyerror(struct FILE_INFO *ip_file, const char *msg);
+
+ #ifdef SIM
+ # define ONLY_SIM(a) (a)
+ #else
+ # define ONLY_SIM(a) NULL
+ #endif
+%}
+
+/*
+ * Enable generation of token names array even without YYDEBUG.
+ * We access via token_name() defined below.
+ */
+%token-table
+
+%union {
+ char * String;
+ double Double;
+ int Integer;
+ unsigned U_int;
+ gen_fifo * Generic_fifo;
+ attr_val * Attr_val;
+ attr_val_fifo * Attr_val_fifo;
+ int_fifo * Int_fifo;
+ string_fifo * String_fifo;
+ address_node * Address_node;
+ address_fifo * Address_fifo;
+ setvar_node * Set_var;
+ server_info * Sim_server;
+ server_info_fifo * Sim_server_fifo;
+ script_info * Sim_script;
+ script_info_fifo * Sim_script_fifo;
+}
+
+/* TERMINALS (do not appear left of colon) */
+%token <Integer> T_Abbrev
+%token <Integer> T_Age
+%token <Integer> T_All
+%token <Integer> T_Allan
+%token <Integer> T_Allpeers
+%token <Integer> T_Auth
+%token <Integer> T_Autokey
+%token <Integer> T_Automax
+%token <Integer> T_Average
+%token <Integer> T_Bclient
+%token <Integer> T_Beacon
+%token <Integer> T_Broadcast
+%token <Integer> T_Broadcastclient
+%token <Integer> T_Broadcastdelay
+%token <Integer> T_Burst
+%token <Integer> T_Calibrate
+%token <Integer> T_Ceiling
+%token <Integer> T_Clockstats
+%token <Integer> T_Cohort
+%token <Integer> T_ControlKey
+%token <Integer> T_Crypto
+%token <Integer> T_Cryptostats
+%token <Integer> T_Ctl
+%token <Integer> T_Day
+%token <Integer> T_Default
+%token <Integer> T_Digest
+%token <Integer> T_Disable
+%token <Integer> T_Discard
+%token <Integer> T_Dispersion
+%token <Double> T_Double /* not a token */
+%token <Integer> T_Driftfile
+%token <Integer> T_Drop
+%token <Integer> T_Ellipsis /* "..." not "ellipsis" */
+%token <Integer> T_Enable
+%token <Integer> T_End
+%token <Integer> T_False
+%token <Integer> T_File
+%token <Integer> T_Filegen
+%token <Integer> T_Filenum
+%token <Integer> T_Flag1
+%token <Integer> T_Flag2
+%token <Integer> T_Flag3
+%token <Integer> T_Flag4
+%token <Integer> T_Flake
+%token <Integer> T_Floor
+%token <Integer> T_Freq
+%token <Integer> T_Fudge
+%token <Integer> T_Host
+%token <Integer> T_Huffpuff
+%token <Integer> T_Iburst
+%token <Integer> T_Ident
+%token <Integer> T_Ignore
+%token <Integer> T_Incalloc
+%token <Integer> T_Incmem
+%token <Integer> T_Initalloc
+%token <Integer> T_Initmem
+%token <Integer> T_Includefile
+%token <Integer> T_Integer /* not a token */
+%token <Integer> T_Interface
+%token <Integer> T_Intrange /* not a token */
+%token <Integer> T_Io
+%token <Integer> T_Ipv4
+%token <Integer> T_Ipv4_flag
+%token <Integer> T_Ipv6
+%token <Integer> T_Ipv6_flag
+%token <Integer> T_Kernel
+%token <Integer> T_Key
+%token <Integer> T_Keys
+%token <Integer> T_Keysdir
+%token <Integer> T_Kod
+%token <Integer> T_Mssntp
+%token <Integer> T_Leapfile
+%token <Integer> T_Limited
+%token <Integer> T_Link
+%token <Integer> T_Listen
+%token <Integer> T_Logconfig
+%token <Integer> T_Logfile
+%token <Integer> T_Loopstats
+%token <Integer> T_Lowpriotrap
+%token <Integer> T_Manycastclient
+%token <Integer> T_Manycastserver
+%token <Integer> T_Mask
+%token <Integer> T_Maxage
+%token <Integer> T_Maxclock
+%token <Integer> T_Maxdepth
+%token <Integer> T_Maxdist
+%token <Integer> T_Maxmem
+%token <Integer> T_Maxpoll
+%token <Integer> T_Mem
+%token <Integer> T_Memlock
+%token <Integer> T_Minclock
+%token <Integer> T_Mindepth
+%token <Integer> T_Mindist
+%token <Integer> T_Minimum
+%token <Integer> T_Minpoll
+%token <Integer> T_Minsane
+%token <Integer> T_Mode
+%token <Integer> T_Mode7
+%token <Integer> T_Monitor
+%token <Integer> T_Month
+%token <Integer> T_Mru
+%token <Integer> T_Multicastclient
+%token <Integer> T_Nic
+%token <Integer> T_Nolink
+%token <Integer> T_Nomodify
+%token <Integer> T_Nomrulist
+%token <Integer> T_None
+%token <Integer> T_Nonvolatile
+%token <Integer> T_Nopeer
+%token <Integer> T_Noquery
+%token <Integer> T_Noselect
+%token <Integer> T_Noserve
+%token <Integer> T_Notrap
+%token <Integer> T_Notrust
+%token <Integer> T_Ntp
+%token <Integer> T_Ntpport
+%token <Integer> T_NtpSignDsocket
+%token <Integer> T_Orphan
+%token <Integer> T_Orphanwait
+%token <Integer> T_Panic
+%token <Integer> T_Peer
+%token <Integer> T_Peerstats
+%token <Integer> T_Phone
+%token <Integer> T_Pid
+%token <Integer> T_Pidfile
+%token <Integer> T_Pool
+%token <Integer> T_Port
+%token <Integer> T_Preempt
+%token <Integer> T_Prefer
+%token <Integer> T_Protostats
+%token <Integer> T_Pw
+%token <Integer> T_Randfile
+%token <Integer> T_Rawstats
+%token <Integer> T_Refid
+%token <Integer> T_Requestkey
+%token <Integer> T_Reset
+%token <Integer> T_Restrict
+%token <Integer> T_Revoke
+%token <Integer> T_Rlimit
+%token <Integer> T_Saveconfigdir
+%token <Integer> T_Server
+%token <Integer> T_Setvar
+%token <Integer> T_Source
+%token <Integer> T_Stacksize
+%token <Integer> T_Statistics
+%token <Integer> T_Stats
+%token <Integer> T_Statsdir
+%token <Integer> T_Step
+%token <Integer> T_Stepout
+%token <Integer> T_Stratum
+%token <String> T_String /* not a token */
+%token <Integer> T_Sys
+%token <Integer> T_Sysstats
+%token <Integer> T_Tick
+%token <Integer> T_Time1
+%token <Integer> T_Time2
+%token <Integer> T_Timer
+%token <Integer> T_Timingstats
+%token <Integer> T_Tinker
+%token <Integer> T_Tos
+%token <Integer> T_Trap
+%token <Integer> T_True
+%token <Integer> T_Trustedkey
+%token <Integer> T_Ttl
+%token <Integer> T_Type
+%token <Integer> T_U_int /* Not a token */
+%token <Integer> T_Unconfig
+%token <Integer> T_Unpeer
+%token <Integer> T_Version
+%token <Integer> T_WanderThreshold /* Not a token */
+%token <Integer> T_Week
+%token <Integer> T_Wildcard
+%token <Integer> T_Xleave
+%token <Integer> T_Year
+%token <Integer> T_Flag /* Not a token */
+%token <Integer> T_EOC
+
+
+/* NTP Simulator Tokens */
+%token <Integer> T_Simulate
+%token <Integer> T_Beep_Delay
+%token <Integer> T_Sim_Duration
+%token <Integer> T_Server_Offset
+%token <Integer> T_Duration
+%token <Integer> T_Freq_Offset
+%token <Integer> T_Wander
+%token <Integer> T_Jitter
+%token <Integer> T_Prop_Delay
+%token <Integer> T_Proc_Delay
+
+
+
+/*** NON-TERMINALS ***/
+%type <Integer> access_control_flag
+%type <Int_fifo> ac_flag_list
+%type <Address_node> address
+%type <Integer> address_fam
+%type <Address_fifo> address_list
+%type <Integer> boolean
+%type <Integer> client_type
+%type <Integer> counter_set_keyword
+%type <Int_fifo> counter_set_list
+%type <Attr_val> crypto_command
+%type <Attr_val_fifo> crypto_command_list
+%type <Integer> crypto_str_keyword
+%type <Attr_val> discard_option
+%type <Integer> discard_option_keyword
+%type <Attr_val_fifo> discard_option_list
+%type <Integer> enable_disable
+%type <Attr_val> filegen_option
+%type <Attr_val_fifo> filegen_option_list
+%type <Integer> filegen_type
+%type <Attr_val> fudge_factor
+%type <Integer> fudge_factor_bool_keyword
+%type <Integer> fudge_factor_dbl_keyword
+%type <Attr_val_fifo> fudge_factor_list
+%type <Attr_val_fifo> integer_list
+%type <Attr_val_fifo> integer_list_range
+%type <Attr_val> integer_list_range_elt
+%type <Attr_val> integer_range
+%type <Integer> nic_rule_action
+%type <Integer> interface_command
+%type <Integer> interface_nic
+%type <Address_node> ip_address
+%type <Integer> link_nolink
+%type <Attr_val> log_config_command
+%type <Attr_val_fifo> log_config_list
+%type <Integer> misc_cmd_dbl_keyword
+%type <Integer> misc_cmd_str_keyword
+%type <Integer> misc_cmd_str_lcl_keyword
+%type <Attr_val> mru_option
+%type <Integer> mru_option_keyword
+%type <Attr_val_fifo> mru_option_list
+%type <Integer> nic_rule_class
+%type <Double> number
+%type <Attr_val> option
+%type <Attr_val> option_flag
+%type <Integer> option_flag_keyword
+%type <Attr_val_fifo> option_list
+%type <Attr_val> option_int
+%type <Integer> option_int_keyword
+%type <Attr_val> option_str
+%type <Integer> option_str_keyword
+%type <Integer> reset_command
+%type <Integer> rlimit_option_keyword
+%type <Attr_val> rlimit_option
+%type <Attr_val_fifo> rlimit_option_list
+%type <Integer> stat
+%type <Int_fifo> stats_list
+%type <String_fifo> string_list
+%type <Attr_val> system_option
+%type <Integer> system_option_flag_keyword
+%type <Integer> system_option_local_flag_keyword
+%type <Attr_val_fifo> system_option_list
+%type <Integer> t_default_or_zero
+%type <Integer> tinker_option_keyword
+%type <Attr_val> tinker_option
+%type <Attr_val_fifo> tinker_option_list
+%type <Attr_val> tos_option
+%type <Integer> tos_option_dbl_keyword
+%type <Integer> tos_option_int_keyword
+%type <Attr_val_fifo> tos_option_list
+%type <Attr_val> trap_option
+%type <Attr_val_fifo> trap_option_list
+%type <Integer> unpeer_keyword
+%type <Set_var> variable_assign
+
+/* NTP Simulator non-terminals */
+%type <Attr_val> sim_init_statement
+%type <Attr_val_fifo> sim_init_statement_list
+%type <Integer> sim_init_keyword
+%type <Sim_server_fifo> sim_server_list
+%type <Sim_server> sim_server
+%type <Double> sim_server_offset
+%type <Address_node> sim_server_name
+%type <Sim_script> sim_act
+%type <Sim_script_fifo> sim_act_list
+%type <Integer> sim_act_keyword
+%type <Attr_val_fifo> sim_act_stmt_list
+%type <Attr_val> sim_act_stmt
+
+%%
+
+/* ntp.conf
+ * Configuration File Grammar
+ * --------------------------
+ */
+
+configuration
+ : command_list
+ ;
+
+command_list
+ : command_list command T_EOC
+ | command T_EOC
+ | error T_EOC
+ {
+ /* I will need to incorporate much more fine grained
+ * error messages. The following should suffice for
+ * the time being.
+ */
+ msyslog(LOG_ERR,
+ "syntax error in %s line %d, column %d",
+ ip_file->fname,
+ ip_file->err_line_no,
+ ip_file->err_col_no);
+ }
+ ;
+
+command : /* NULL STATEMENT */
+ | server_command
+ | unpeer_command
+ | other_mode_command
+ | authentication_command
+ | monitoring_command
+ | access_control_command
+ | orphan_mode_command
+ | fudge_command
+ | rlimit_command
+ | system_option_command
+ | tinker_command
+ | miscellaneous_command
+ | simulate_command
+ ;
+
+/* Server Commands
+ * ---------------
+ */
+
+server_command
+ : client_type address option_list
+ {
+ peer_node *my_node;
+
+ my_node = create_peer_node($1, $2, $3);
+ APPEND_G_FIFO(cfgt.peers, my_node);
+ }
+ ;
+
+client_type
+ : T_Server
+ | T_Pool
+ | T_Peer
+ | T_Broadcast
+ | T_Manycastclient
+ ;
+
+address
+ : ip_address
+ | address_fam T_String
+ { $$ = create_address_node($2, $1); }
+ ;
+
+ip_address
+ : T_String
+ { $$ = create_address_node($1, AF_UNSPEC); }
+ ;
+
+address_fam
+ : T_Ipv4_flag
+ { $$ = AF_INET; }
+ | T_Ipv6_flag
+ { $$ = AF_INET6; }
+ ;
+
+option_list
+ : /* empty list */
+ { $$ = NULL; }
+ | option_list option
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ ;
+
+option
+ : option_flag
+ | option_int
+ | option_str
+ ;
+
+option_flag
+ : option_flag_keyword
+ { $$ = create_attr_ival(T_Flag, $1); }
+ ;
+
+option_flag_keyword
+ : T_Autokey
+ | T_Burst
+ | T_Iburst
+ | T_Noselect
+ | T_Preempt
+ | T_Prefer
+ | T_True
+ | T_Xleave
+ ;
+
+option_int
+ : option_int_keyword T_Integer
+ { $$ = create_attr_ival($1, $2); }
+ | option_int_keyword T_U_int
+ { $$ = create_attr_uval($1, $2); }
+ ;
+
+option_int_keyword
+ : T_Key
+ | T_Minpoll
+ | T_Maxpoll
+ | T_Ttl
+ | T_Mode
+ | T_Version
+ ;
+
+option_str
+ : option_str_keyword T_String
+ { $$ = create_attr_sval($1, $2); }
+ ;
+
+option_str_keyword
+ : T_Ident
+ ;
+
+
+/* unpeer commands
+ * ---------------
+ */
+
+unpeer_command
+ : unpeer_keyword address
+ {
+ unpeer_node *my_node;
+
+ my_node = create_unpeer_node($2);
+ if (my_node)
+ APPEND_G_FIFO(cfgt.unpeers, my_node);
+ }
+ ;
+unpeer_keyword
+ : T_Unconfig
+ | T_Unpeer
+ ;
+
+
+/* Other Modes
+ * (broadcastclient manycastserver multicastclient)
+ * ------------------------------------------------
+ */
+
+other_mode_command
+ : T_Broadcastclient
+ { cfgt.broadcastclient = 1; }
+ | T_Manycastserver address_list
+ { CONCAT_G_FIFOS(cfgt.manycastserver, $2); }
+ | T_Multicastclient address_list
+ { CONCAT_G_FIFOS(cfgt.multicastclient, $2); }
+ ;
+
+
+
+/* Authentication Commands
+ * -----------------------
+ */
+
+authentication_command
+ : T_Automax T_Integer
+ {
+ attr_val *atrv;
+
+ atrv = create_attr_ival($1, $2);
+ APPEND_G_FIFO(cfgt.vars, atrv);
+ }
+ | T_ControlKey T_Integer
+ { cfgt.auth.control_key = $2; }
+ | T_Crypto crypto_command_list
+ {
+ cfgt.auth.cryptosw++;
+ CONCAT_G_FIFOS(cfgt.auth.crypto_cmd_list, $2);
+ }
+ | T_Keys T_String
+ { cfgt.auth.keys = $2; }
+ | T_Keysdir T_String
+ { cfgt.auth.keysdir = $2; }
+ | T_Requestkey T_Integer
+ { cfgt.auth.request_key = $2; }
+ | T_Revoke T_Integer
+ { cfgt.auth.revoke = $2; }
+ | T_Trustedkey integer_list_range
+ {
+ cfgt.auth.trusted_key_list = $2;
+
+ // if (!cfgt.auth.trusted_key_list)
+ // cfgt.auth.trusted_key_list = $2;
+ // else
+ // LINK_SLIST(cfgt.auth.trusted_key_list, $2, link);
+ }
+ | T_NtpSignDsocket T_String
+ { cfgt.auth.ntp_signd_socket = $2; }
+ ;
+
+crypto_command_list
+ : /* empty list */
+ { $$ = NULL; }
+ | crypto_command_list crypto_command
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ ;
+
+crypto_command
+ : crypto_str_keyword T_String
+ { $$ = create_attr_sval($1, $2); }
+ | T_Revoke T_Integer
+ {
+ $$ = NULL;
+ cfgt.auth.revoke = $2;
+ msyslog(LOG_WARNING,
+ "'crypto revoke %d' is deprecated, "
+ "please use 'revoke %d' instead.",
+ cfgt.auth.revoke, cfgt.auth.revoke);
+ }
+ ;
+
+crypto_str_keyword
+ : T_Host
+ | T_Ident
+ | T_Pw
+ | T_Randfile
+ | T_Digest
+ ;
+
+
+/* Orphan Mode Commands
+ * --------------------
+ */
+
+orphan_mode_command
+ : T_Tos tos_option_list
+ { CONCAT_G_FIFOS(cfgt.orphan_cmds, $2); }
+ ;
+
+tos_option_list
+ : tos_option_list tos_option
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ | tos_option
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, $1);
+ }
+ ;
+
+tos_option
+ : tos_option_int_keyword T_Integer
+ { $$ = create_attr_dval($1, (double)$2); }
+ | tos_option_dbl_keyword number
+ { $$ = create_attr_dval($1, $2); }
+ | T_Cohort boolean
+ { $$ = create_attr_dval($1, (double)$2); }
+ ;
+
+tos_option_int_keyword
+ : T_Ceiling
+ | T_Floor
+ | T_Orphan
+ | T_Orphanwait
+ | T_Minsane
+ | T_Beacon
+ ;
+
+tos_option_dbl_keyword
+ : T_Mindist
+ | T_Maxdist
+ | T_Minclock
+ | T_Maxclock
+ ;
+
+
+/* Monitoring Commands
+ * -------------------
+ */
+
+monitoring_command
+ : T_Statistics stats_list
+ { CONCAT_G_FIFOS(cfgt.stats_list, $2); }
+ | T_Statsdir T_String
+ {
+ if (input_from_file) {
+ cfgt.stats_dir = $2;
+ } else {
+ YYFREE($2);
+ yyerror(ip_file, "statsdir remote configuration ignored");
+ }
+ }
+ | T_Filegen stat filegen_option_list
+ {
+ filegen_node *fgn;
+
+ fgn = create_filegen_node($2, $3);
+ APPEND_G_FIFO(cfgt.filegen_opts, fgn);
+ }
+ ;
+
+stats_list
+ : stats_list stat
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, create_int_node($2));
+ }
+ | stat
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, create_int_node($1));
+ }
+ ;
+
+stat
+ : T_Clockstats
+ | T_Cryptostats
+ | T_Loopstats
+ | T_Peerstats
+ | T_Rawstats
+ | T_Sysstats
+ | T_Timingstats
+ | T_Protostats
+ ;
+
+filegen_option_list
+ : /* empty list */
+ { $$ = NULL; }
+ | filegen_option_list filegen_option
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ ;
+
+filegen_option
+ : T_File T_String
+ {
+ if (input_from_file) {
+ $$ = create_attr_sval($1, $2);
+ } else {
+ $$ = NULL;
+ YYFREE($2);
+ yyerror(ip_file, "filegen file remote config ignored");
+ }
+ }
+ | T_Type filegen_type
+ {
+ if (input_from_file) {
+ $$ = create_attr_ival($1, $2);
+ } else {
+ $$ = NULL;
+ yyerror(ip_file, "filegen type remote config ignored");
+ }
+ }
+ | link_nolink
+ {
+ const char *err;
+
+ if (input_from_file) {
+ $$ = create_attr_ival(T_Flag, $1);
+ } else {
+ $$ = NULL;
+ if (T_Link == $1)
+ err = "filegen link remote config ignored";
+ else
+ err = "filegen nolink remote config ignored";
+ yyerror(ip_file, err);
+ }
+ }
+ | enable_disable
+ { $$ = create_attr_ival(T_Flag, $1); }
+ ;
+
+link_nolink
+ : T_Link
+ | T_Nolink
+ ;
+
+enable_disable
+ : T_Enable
+ | T_Disable
+ ;
+
+filegen_type
+ : T_None
+ | T_Pid
+ | T_Day
+ | T_Week
+ | T_Month
+ | T_Year
+ | T_Age
+ ;
+
+
+/* Access Control Commands
+ * -----------------------
+ */
+
+access_control_command
+ : T_Discard discard_option_list
+ {
+ CONCAT_G_FIFOS(cfgt.discard_opts, $2);
+ }
+ | T_Mru mru_option_list
+ {
+ CONCAT_G_FIFOS(cfgt.mru_opts, $2);
+ }
+ | T_Restrict address ac_flag_list
+ {
+ restrict_node *rn;
+
+ rn = create_restrict_node($2, NULL, $3,
+ ip_file->line_no);
+ APPEND_G_FIFO(cfgt.restrict_opts, rn);
+ }
+ | T_Restrict ip_address T_Mask ip_address ac_flag_list
+ {
+ restrict_node *rn;
+
+ rn = create_restrict_node($2, $4, $5,
+ ip_file->line_no);
+ APPEND_G_FIFO(cfgt.restrict_opts, rn);
+ }
+ | T_Restrict T_Default ac_flag_list
+ {
+ restrict_node *rn;
+
+ rn = create_restrict_node(NULL, NULL, $3,
+ ip_file->line_no);
+ APPEND_G_FIFO(cfgt.restrict_opts, rn);
+ }
+ | T_Restrict T_Ipv4_flag T_Default ac_flag_list
+ {
+ restrict_node *rn;
+
+ rn = create_restrict_node(
+ create_address_node(
+ estrdup("0.0.0.0"),
+ AF_INET),
+ create_address_node(
+ estrdup("0.0.0.0"),
+ AF_INET),
+ $4,
+ ip_file->line_no);
+ APPEND_G_FIFO(cfgt.restrict_opts, rn);
+ }
+ | T_Restrict T_Ipv6_flag T_Default ac_flag_list
+ {
+ restrict_node *rn;
+
+ rn = create_restrict_node(
+ create_address_node(
+ estrdup("::"),
+ AF_INET6),
+ create_address_node(
+ estrdup("::"),
+ AF_INET6),
+ $4,
+ ip_file->line_no);
+ APPEND_G_FIFO(cfgt.restrict_opts, rn);
+ }
+ | T_Restrict T_Source ac_flag_list
+ {
+ restrict_node * rn;
+
+ APPEND_G_FIFO($3, create_int_node($2));
+ rn = create_restrict_node(
+ NULL, NULL, $3, ip_file->line_no);
+ APPEND_G_FIFO(cfgt.restrict_opts, rn);
+ }
+ ;
+
+ac_flag_list
+ : /* empty list is allowed */
+ { $$ = NULL; }
+ | ac_flag_list access_control_flag
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, create_int_node($2));
+ }
+ ;
+
+access_control_flag
+ : T_Flake
+ | T_Ignore
+ | T_Kod
+ | T_Mssntp
+ | T_Limited
+ | T_Lowpriotrap
+ | T_Nomodify
+ | T_Nomrulist
+ | T_Nopeer
+ | T_Noquery
+ | T_Noserve
+ | T_Notrap
+ | T_Notrust
+ | T_Ntpport
+ | T_Version
+ ;
+
+discard_option_list
+ : discard_option_list discard_option
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ | discard_option
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, $1);
+ }
+ ;
+
+discard_option
+ : discard_option_keyword T_Integer
+ { $$ = create_attr_ival($1, $2); }
+ ;
+
+discard_option_keyword
+ : T_Average
+ | T_Minimum
+ | T_Monitor
+ ;
+
+mru_option_list
+ : mru_option_list mru_option
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ | mru_option
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, $1);
+ }
+ ;
+
+mru_option
+ : mru_option_keyword T_Integer
+ { $$ = create_attr_ival($1, $2); }
+ ;
+
+mru_option_keyword
+ : T_Incalloc
+ | T_Incmem
+ | T_Initalloc
+ | T_Initmem
+ | T_Maxage
+ | T_Maxdepth
+ | T_Maxmem
+ | T_Mindepth
+ ;
+
+/* Fudge Commands
+ * --------------
+ */
+
+fudge_command
+ : T_Fudge address fudge_factor_list
+ {
+ addr_opts_node *aon;
+
+ aon = create_addr_opts_node($2, $3);
+ APPEND_G_FIFO(cfgt.fudge, aon);
+ }
+ ;
+
+fudge_factor_list
+ : fudge_factor_list fudge_factor
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ | fudge_factor
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, $1);
+ }
+ ;
+
+fudge_factor
+ : fudge_factor_dbl_keyword number
+ { $$ = create_attr_dval($1, $2); }
+ | fudge_factor_bool_keyword boolean
+ { $$ = create_attr_ival($1, $2); }
+ | T_Stratum T_Integer
+ { $$ = create_attr_ival($1, $2); }
+ | T_Abbrev T_String
+ { $$ = create_attr_sval($1, $2); }
+ | T_Refid T_String
+ { $$ = create_attr_sval($1, $2); }
+ ;
+
+fudge_factor_dbl_keyword
+ : T_Time1
+ | T_Time2
+ ;
+
+fudge_factor_bool_keyword
+ : T_Flag1
+ | T_Flag2
+ | T_Flag3
+ | T_Flag4
+ ;
+
+/* rlimit Commands
+ * ---------------
+ */
+
+rlimit_command
+ : T_Rlimit rlimit_option_list
+ { CONCAT_G_FIFOS(cfgt.rlimit, $2); }
+ ;
+
+rlimit_option_list
+ : rlimit_option_list rlimit_option
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ | rlimit_option
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, $1);
+ }
+ ;
+
+rlimit_option
+ : rlimit_option_keyword T_Integer
+ { $$ = create_attr_ival($1, $2); }
+ ;
+
+rlimit_option_keyword
+ : T_Memlock
+ | T_Stacksize
+ | T_Filenum
+ ;
+
+
+/* Command for System Options
+ * --------------------------
+ */
+
+system_option_command
+ : T_Enable system_option_list
+ { CONCAT_G_FIFOS(cfgt.enable_opts, $2); }
+ | T_Disable system_option_list
+ { CONCAT_G_FIFOS(cfgt.disable_opts, $2); }
+ ;
+
+system_option_list
+ : system_option_list system_option
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ | system_option
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, $1);
+ }
+ ;
+
+system_option
+ : system_option_flag_keyword
+ { $$ = create_attr_ival(T_Flag, $1); }
+ | system_option_local_flag_keyword
+ {
+ if (input_from_file) {
+ $$ = create_attr_ival(T_Flag, $1);
+ } else {
+ char err_str[128];
+
+ $$ = NULL;
+ snprintf(err_str, sizeof(err_str),
+ "enable/disable %s remote configuration ignored",
+ keyword($1));
+ yyerror(ip_file, err_str);
+ }
+ }
+ ;
+
+system_option_flag_keyword
+ : T_Auth
+ | T_Bclient
+ | T_Calibrate
+ | T_Kernel
+ | T_Monitor
+ | T_Ntp
+ ;
+
+system_option_local_flag_keyword
+ : T_Mode7
+ | T_Stats
+ ;
+
+/* Tinker Commands
+ * ---------------
+ */
+
+tinker_command
+ : T_Tinker tinker_option_list
+ { CONCAT_G_FIFOS(cfgt.tinker, $2); }
+ ;
+
+tinker_option_list
+ : tinker_option_list tinker_option
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ | tinker_option
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, $1);
+ }
+ ;
+
+tinker_option
+ : tinker_option_keyword number
+ { $$ = create_attr_dval($1, $2); }
+ ;
+
+tinker_option_keyword
+ : T_Allan
+ | T_Dispersion
+ | T_Freq
+ | T_Huffpuff
+ | T_Panic
+ | T_Step
+ | T_Stepout
+ | T_Tick
+ ;
+
+
+/* Miscellaneous Commands
+ * ----------------------
+ */
+
+miscellaneous_command
+ : interface_command
+ | reset_command
+ | misc_cmd_dbl_keyword number
+ {
+ attr_val *av;
+
+ av = create_attr_dval($1, $2);
+ APPEND_G_FIFO(cfgt.vars, av);
+ }
+ | misc_cmd_str_keyword T_String
+ {
+ attr_val *av;
+
+ av = create_attr_sval($1, $2);
+ APPEND_G_FIFO(cfgt.vars, av);
+ }
+ | misc_cmd_str_lcl_keyword T_String
+ {
+ char error_text[64];
+ attr_val *av;
+
+ if (input_from_file) {
+ av = create_attr_sval($1, $2);
+ APPEND_G_FIFO(cfgt.vars, av);
+ } else {
+ YYFREE($2);
+ snprintf(error_text, sizeof(error_text),
+ "%s remote config ignored",
+ keyword($1));
+ yyerror(ip_file, error_text);
+ }
+ }
+ | T_Includefile T_String command
+ {
+ if (!input_from_file) {
+ yyerror(ip_file, "remote includefile ignored");
+ break;
+ }
+ if (curr_include_level >= MAXINCLUDELEVEL) {
+ fprintf(stderr, "getconfig: Maximum include file level exceeded.\n");
+ msyslog(LOG_ERR, "getconfig: Maximum include file level exceeded.");
+ } else {
+ fp[curr_include_level + 1] = F_OPEN(FindConfig($2), "r");
+ if (fp[curr_include_level + 1] == NULL) {
+ fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig($2));
+ msyslog(LOG_ERR, "getconfig: Couldn't open <%s>", FindConfig($2));
+ } else {
+ ip_file = fp[++curr_include_level];
+ }
+ }
+ }
+ | T_End
+ {
+ while (curr_include_level != -1)
+ FCLOSE(fp[curr_include_level--]);
+ }
+ | T_Driftfile drift_parm
+ { /* see drift_parm below for actions */ }
+ | T_Logconfig log_config_list
+ { CONCAT_G_FIFOS(cfgt.logconfig, $2); }
+ | T_Phone string_list
+ { CONCAT_G_FIFOS(cfgt.phone, $2); }
+ | T_Setvar variable_assign
+ { APPEND_G_FIFO(cfgt.setvar, $2); }
+ | T_Trap ip_address trap_option_list
+ {
+ addr_opts_node *aon;
+
+ aon = create_addr_opts_node($2, $3);
+ APPEND_G_FIFO(cfgt.trap, aon);
+ }
+ | T_Ttl integer_list
+ { CONCAT_G_FIFOS(cfgt.ttl, $2); }
+ ;
+
+misc_cmd_dbl_keyword
+ : T_Broadcastdelay
+ | T_Nonvolatile
+ | T_Tick
+ ;
+
+misc_cmd_str_keyword
+ : T_Ident
+ | T_Leapfile
+ | T_Pidfile
+ ;
+
+misc_cmd_str_lcl_keyword
+ : T_Logfile
+ | T_Saveconfigdir
+ ;
+
+drift_parm
+ : T_String
+ {
+ attr_val *av;
+
+ av = create_attr_sval(T_Driftfile, $1);
+ APPEND_G_FIFO(cfgt.vars, av);
+ }
+ | T_String T_Double
+ {
+ attr_val *av;
+
+ av = create_attr_sval(T_Driftfile, $1);
+ APPEND_G_FIFO(cfgt.vars, av);
+ av = create_attr_dval(T_WanderThreshold, $2);
+ APPEND_G_FIFO(cfgt.vars, av);
+ }
+ | /* Null driftfile, indicated by empty string "" */
+ {
+ attr_val *av;
+
+ av = create_attr_sval(T_Driftfile, "");
+ APPEND_G_FIFO(cfgt.vars, av);
+ }
+ ;
+
+variable_assign
+ : T_String '=' T_String t_default_or_zero
+ { $$ = create_setvar_node($1, $3, $4); }
+ ;
+
+t_default_or_zero
+ : T_Default
+ | /* empty, no "default" modifier */
+ { $$ = 0; }
+ ;
+
+trap_option_list
+ : /* empty list */
+ { $$ = NULL; }
+ | trap_option_list trap_option
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ ;
+
+trap_option
+ : T_Port T_Integer
+ { $$ = create_attr_ival($1, $2); }
+ | T_Interface ip_address
+ {
+ $$ = create_attr_sval($1, estrdup($2->address));
+ destroy_address_node($2);
+ }
+ ;
+
+log_config_list
+ : log_config_list log_config_command
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ | log_config_command
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, $1);
+ }
+ ;
+
+log_config_command
+ : T_String
+ {
+ char prefix;
+ char * type;
+
+ switch ($1[0]) {
+
+ case '+':
+ case '-':
+ case '=':
+ prefix = $1[0];
+ type = $1 + 1;
+ break;
+
+ default:
+ prefix = '=';
+ type = $1;
+ }
+
+ $$ = create_attr_sval(prefix, estrdup(type));
+ YYFREE($1);
+ }
+ ;
+
+interface_command
+ : interface_nic nic_rule_action nic_rule_class
+ {
+ nic_rule_node *nrn;
+
+ nrn = create_nic_rule_node($3, NULL, $2);
+ APPEND_G_FIFO(cfgt.nic_rules, nrn);
+ }
+ | interface_nic nic_rule_action T_String
+ {
+ nic_rule_node *nrn;
+
+ nrn = create_nic_rule_node(0, $3, $2);
+ APPEND_G_FIFO(cfgt.nic_rules, nrn);
+ }
+ ;
+
+interface_nic
+ : T_Interface
+ | T_Nic
+ ;
+
+nic_rule_class
+ : T_All
+ | T_Ipv4
+ | T_Ipv6
+ | T_Wildcard
+ ;
+
+nic_rule_action
+ : T_Listen
+ | T_Ignore
+ | T_Drop
+ ;
+
+reset_command
+ : T_Reset counter_set_list
+ { CONCAT_G_FIFOS(cfgt.reset_counters, $2); }
+ ;
+
+counter_set_list
+ : counter_set_list counter_set_keyword
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, create_int_node($2));
+ }
+ | counter_set_keyword
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, create_int_node($1));
+ }
+ ;
+
+counter_set_keyword
+ : T_Allpeers
+ | T_Auth
+ | T_Ctl
+ | T_Io
+ | T_Mem
+ | T_Sys
+ | T_Timer
+ ;
+
+
+
+/* Miscellaneous Rules
+ * -------------------
+ */
+
+integer_list
+ : integer_list T_Integer
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, create_int_node($2));
+ }
+ | T_Integer
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, create_int_node($1));
+ }
+ ;
+
+integer_list_range
+ : integer_list_range integer_list_range_elt
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ | integer_list_range_elt
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, $1);
+ }
+ ;
+
+integer_list_range_elt
+ : T_Integer
+ { $$ = create_attr_ival('i', $1); }
+ | integer_range
+ ;
+
+integer_range
+ : '(' T_Integer T_Ellipsis T_Integer ')'
+ { $$ = create_attr_rangeval('-', $2, $4); }
+ ;
+
+string_list
+ : string_list T_String
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, create_string_node($2));
+ }
+ | T_String
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, create_string_node($1));
+ }
+ ;
+
+address_list
+ : address_list address
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ | address
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, $1);
+ }
+ ;
+
+boolean
+ : T_Integer
+ {
+ if ($1 != 0 && $1 != 1) {
+ yyerror(ip_file, "Integer value is not boolean (0 or 1). Assuming 1");
+ $$ = 1;
+ } else {
+ $$ = $1;
+ }
+ }
+ | T_True { $$ = 1; }
+ | T_False { $$ = 0; }
+ ;
+
+number
+ : T_Integer { $$ = (double)$1; }
+ | T_Double
+ ;
+
+
+/* Simulator Configuration Commands
+ * --------------------------------
+ */
+
+simulate_command
+ : sim_conf_start '{' sim_init_statement_list sim_server_list '}'
+ {
+ sim_node *sn;
+
+ sn = create_sim_node($3, $4);
+ APPEND_G_FIFO(cfgt.sim_details, sn);
+
+ /* Revert from ; to \n for end-of-command */
+ old_config_style = 1;
+ }
+ ;
+
+/* The following is a terrible hack to get the configuration file to
+ * treat newlines as whitespace characters within the simulation.
+ * This is needed because newlines are significant in the rest of the
+ * configuration file.
+ */
+sim_conf_start
+ : T_Simulate { old_config_style = 0; }
+ ;
+
+sim_init_statement_list
+ : sim_init_statement_list sim_init_statement T_EOC
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ | sim_init_statement T_EOC
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, $1);
+ }
+ ;
+
+sim_init_statement
+ : sim_init_keyword '=' number
+ { $$ = create_attr_dval($1, $3); }
+ ;
+
+sim_init_keyword
+ : T_Beep_Delay
+ | T_Sim_Duration
+ ;
+
+sim_server_list
+ : sim_server_list sim_server
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ | sim_server
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, $1);
+ }
+ ;
+
+sim_server
+ : sim_server_name '{' sim_server_offset sim_act_list '}'
+ { $$ = ONLY_SIM(create_sim_server($1, $3, $4)); }
+ ;
+
+sim_server_offset
+ : T_Server_Offset '=' number T_EOC
+ { $$ = $3; }
+ ;
+
+sim_server_name
+ : T_Server '=' address
+ { $$ = $3; }
+ ;
+
+sim_act_list
+ : sim_act_list sim_act
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ | sim_act
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, $1);
+ }
+ ;
+
+sim_act
+ : T_Duration '=' number '{' sim_act_stmt_list '}'
+ { $$ = ONLY_SIM(create_sim_script_info($3, $5)); }
+ ;
+
+sim_act_stmt_list
+ : sim_act_stmt_list sim_act_stmt T_EOC
+ {
+ $$ = $1;
+ APPEND_G_FIFO($$, $2);
+ }
+ | sim_act_stmt T_EOC
+ {
+ $$ = NULL;
+ APPEND_G_FIFO($$, $1);
+ }
+ ;
+
+sim_act_stmt
+ : sim_act_keyword '=' number
+ { $$ = create_attr_dval($1, $3); }
+ ;
+
+sim_act_keyword
+ : T_Freq_Offset
+ | T_Wander
+ | T_Jitter
+ | T_Prop_Delay
+ | T_Proc_Delay
+ ;
+
+%%
+
+void
+yyerror(
+ struct FILE_INFO *ip_file,
+ const char *msg
+ )
+{
+ int retval;
+
+ ip_file->err_line_no = ip_file->prev_token_line_no;
+ ip_file->err_col_no = ip_file->prev_token_col_no;
+
+ msyslog(LOG_ERR,
+ "line %d column %d %s",
+ ip_file->err_line_no,
+ ip_file->err_col_no,
+ msg);
+ if (!input_from_file) {
+ /* Save the error message in the correct buffer */
+ retval = snprintf(remote_config.err_msg + remote_config.err_pos,
+ MAXLINE - remote_config.err_pos,
+ "column %d %s",
+ ip_file->err_col_no, msg);
+
+ /* Increment the value of err_pos */
+ if (retval > 0)
+ remote_config.err_pos += retval;
+
+ /* Increment the number of errors */
+ ++remote_config.no_errors;
+ }
+}
+
+
+/*
+ * token_name - convert T_ token integers to text
+ * example: token_name(T_Server) returns "T_Server"
+ */
+const char *
+token_name(
+ int token
+ )
+{
+ return yytname[YYTRANSLATE(token)];
+}
+
+
+/* Initial Testing function -- ignore */
+#if 0
+int main(int argc, char *argv[])
+{
+ ip_file = FOPEN(argv[1], "r");
+ if (!ip_file)
+ fprintf(stderr, "ERROR!! Could not open file: %s\n", argv[1]);
+ yyparse();
+ return 0;
+}
+#endif
+
diff --git a/ntpd/ntp_peer.c b/ntpd/ntp_peer.c
new file mode 100644
index 0000000..495ee30
--- /dev/null
+++ b/ntpd/ntp_peer.c
@@ -0,0 +1,1038 @@
+/*
+ * ntp_peer.c - management of data maintained for peer associations
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_lists.h"
+#include "ntp_stdlib.h"
+#include "ntp_control.h"
+#include <ntp_random.h>
+
+/*
+ * Table of valid association combinations
+ * ---------------------------------------
+ *
+ * packet->mode
+ * peer->mode | UNSPEC ACTIVE PASSIVE CLIENT SERVER BCAST
+ * ---------- | ---------------------------------------------
+ * NO_PEER | e 1 0 1 1 1
+ * ACTIVE | e 1 1 0 0 0
+ * PASSIVE | e 1 e 0 0 0
+ * CLIENT | e 0 0 0 1 0
+ * SERVER | e 0 0 0 0 0
+ * BCAST | e 0 0 0 0 0
+ * BCLIENT | e 0 0 0 e 1
+ *
+ * One point to note here: a packet in BCAST mode can potentially match
+ * a peer in CLIENT mode, but we that is a special case and we check for
+ * that early in the decision process. This avoids having to keep track
+ * of what kind of associations are possible etc... We actually
+ * circumvent that problem by requiring that the first b(m)roadcast
+ * received after the change back to BCLIENT mode sets the clock.
+ */
+#define AM_MODES 7 /* number of rows and columns */
+#define NO_PEER 0 /* action when no peer is found */
+
+int AM[AM_MODES][AM_MODES] = {
+/* packet->mode */
+/* peer { UNSPEC, ACTIVE, PASSIVE, CLIENT, SERVER, BCAST } */
+/* mode */
+/*NONE*/{ AM_ERR, AM_NEWPASS, AM_NOMATCH, AM_FXMIT, AM_MANYCAST, AM_NEWBCL},
+
+/*A*/ { AM_ERR, AM_PROCPKT, AM_PROCPKT, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*P*/ { AM_ERR, AM_PROCPKT, AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*C*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT, AM_NOMATCH},
+
+/*S*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*BCST*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*BCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT},
+};
+
+#define MATCH_ASSOC(x, y) AM[(x)][(y)]
+
+/*
+ * These routines manage the allocation of memory to peer structures
+ * and the maintenance of three data structures involving all peers:
+ *
+ * - peer_list is a single list with all peers, suitable for scanning
+ * operations over all peers.
+ * - peer_adr_hash is an array of lists indexed by hashed peer address.
+ * - peer_aid_hash is an array of lists indexed by hashed associd.
+ *
+ * They also maintain a free list of peer structures, peer_free.
+ *
+ * The three main entry points are findpeer(), which looks for matching
+ * peer structures in the peer list, newpeer(), which allocates a new
+ * peer structure and adds it to the list, and unpeer(), which
+ * demobilizes the association and deallocates the structure.
+ */
+/*
+ * Peer hash tables
+ */
+struct peer *peer_hash[NTP_HASH_SIZE]; /* peer hash table */
+int peer_hash_count[NTP_HASH_SIZE]; /* peers in each bucket */
+struct peer *assoc_hash[NTP_HASH_SIZE]; /* association ID hash table */
+int assoc_hash_count[NTP_HASH_SIZE];/* peers in each bucket */
+struct peer *peer_list; /* peer structures list */
+static struct peer *peer_free; /* peer structures free list */
+int peer_free_count; /* count of free structures */
+
+/*
+ * Association ID. We initialize this value randomly, then assign a new
+ * value every time an association is mobilized.
+ */
+static associd_t current_association_ID; /* association ID */
+
+/*
+ * Memory allocation watermarks.
+ */
+#define INIT_PEER_ALLOC 8 /* static preallocation */
+#define INC_PEER_ALLOC 4 /* add N more when empty */
+
+/*
+ * Miscellaneous statistic counters which may be queried.
+ */
+u_long peer_timereset; /* time stat counters zeroed */
+u_long findpeer_calls; /* calls to findpeer */
+u_long assocpeer_calls; /* calls to findpeerbyassoc */
+u_long peer_allocations; /* allocations from free list */
+u_long peer_demobilizations; /* structs freed to free list */
+int total_peer_structs; /* peer structs */
+int peer_associations; /* mobilized associations */
+int peer_preempt; /* preemptable associations */
+static struct peer init_peer_alloc[INIT_PEER_ALLOC]; /* init alloc */
+
+static struct peer * findexistingpeer_name(const char *, u_short,
+ struct peer *, int);
+static struct peer * findexistingpeer_addr(sockaddr_u *,
+ struct peer *, int,
+ u_char);
+static void free_peer(struct peer *, int);
+static void getmorepeermem(void);
+static int score(struct peer *);
+
+
+/*
+ * init_peer - initialize peer data structures and counters
+ *
+ * N.B. We use the random number routine in here. It had better be
+ * initialized prior to getting here.
+ */
+void
+init_peer(void)
+{
+ int i;
+
+ /*
+ * Initialize peer free list from static allocation.
+ */
+ for (i = COUNTOF(init_peer_alloc) - 1; i >= 0; i--)
+ LINK_SLIST(peer_free, &init_peer_alloc[i], p_link);
+ total_peer_structs = COUNTOF(init_peer_alloc);
+ peer_free_count = COUNTOF(init_peer_alloc);
+
+ /*
+ * Initialize our first association ID
+ */
+ do
+ current_association_ID = ntp_random() & ASSOCID_MAX;
+ while (!current_association_ID);
+}
+
+
+/*
+ * getmorepeermem - add more peer structures to the free list
+ */
+static void
+getmorepeermem(void)
+{
+ int i;
+ struct peer *peers;
+
+ peers = emalloc_zero(INC_PEER_ALLOC * sizeof(*peers));
+
+ for (i = INC_PEER_ALLOC - 1; i >= 0; i--)
+ LINK_SLIST(peer_free, &peers[i], p_link);
+
+ total_peer_structs += INC_PEER_ALLOC;
+ peer_free_count += INC_PEER_ALLOC;
+}
+
+
+static struct peer *
+findexistingpeer_name(
+ const char * hostname,
+ u_short hname_fam,
+ struct peer * start_peer,
+ int mode
+ )
+{
+ struct peer *p;
+
+ if (NULL == start_peer)
+ p = peer_list;
+ else
+ p = start_peer->p_link;
+ for (; p != NULL; p = p->p_link)
+ if (p->hostname != NULL
+ && (-1 == mode || p->hmode == mode)
+ && (AF_UNSPEC == hname_fam
+ || AF_UNSPEC == AF(&p->srcadr)
+ || hname_fam == AF(&p->srcadr))
+ && !strcasecmp(p->hostname, hostname))
+ break;
+ return p;
+}
+
+
+static
+struct peer *
+findexistingpeer_addr(
+ sockaddr_u * addr,
+ struct peer * start_peer,
+ int mode,
+ u_char cast_flags
+ )
+{
+ struct peer *peer;
+
+ DPRINTF(2, ("findexistingpeer_addr(%s, %s, %d, 0x%x)\n",
+ sptoa(addr),
+ (start_peer)
+ ? sptoa(&start_peer->srcadr)
+ : "NULL",
+ mode, (u_int)cast_flags));
+
+ /*
+ * start_peer is included so we can locate instances of the
+ * same peer through different interfaces in the hash table.
+ * Without MDF_BCLNT, a match requires the same mode and remote
+ * address. MDF_BCLNT associations start out as MODE_CLIENT
+ * if broadcastdelay is not specified, and switch to
+ * MODE_BCLIENT after estimating the one-way delay. Duplicate
+ * associations are expanded in definition to match any other
+ * MDF_BCLNT with the same srcadr (remote, unicast address).
+ */
+ if (NULL == start_peer)
+ peer = peer_hash[NTP_HASH_ADDR(addr)];
+ else
+ peer = start_peer->adr_link;
+
+ while (peer != NULL) {
+ DPRINTF(3, ("%s %s %d %d 0x%x 0x%x ", sptoa(addr),
+ sptoa(&peer->srcadr), mode, peer->hmode,
+ (u_int)cast_flags, (u_int)peer->cast_flags));
+ if ((-1 == mode || peer->hmode == mode ||
+ ((MDF_BCLNT & peer->cast_flags) &&
+ (MDF_BCLNT & cast_flags))) &&
+ ADDR_PORT_EQ(addr, &peer->srcadr)) {
+ DPRINTF(3, ("found.\n"));
+ break;
+ }
+ DPRINTF(3, ("\n"));
+ peer = peer->adr_link;
+ }
+
+ return peer;
+}
+
+
+/*
+ * findexistingpeer - search by address and return a pointer to a peer.
+ */
+struct peer *
+findexistingpeer(
+ sockaddr_u * addr,
+ const char * hostname,
+ struct peer * start_peer,
+ int mode,
+ u_char cast_flags
+ )
+{
+ if (hostname != NULL)
+ return findexistingpeer_name(hostname, AF(addr),
+ start_peer, mode);
+ else
+ return findexistingpeer_addr(addr, start_peer, mode,
+ cast_flags);
+}
+
+
+/*
+ * findpeer - find and return a peer match for a received datagram in
+ * the peer_hash table.
+ */
+struct peer *
+findpeer(
+ struct recvbuf *rbufp,
+ int pkt_mode,
+ int * action
+ )
+{
+ struct peer * p;
+ sockaddr_u * srcadr;
+ u_int hash;
+ struct pkt * pkt;
+ l_fp pkt_org;
+
+ findpeer_calls++;
+ srcadr = &rbufp->recv_srcadr;
+ hash = NTP_HASH_ADDR(srcadr);
+ for (p = peer_hash[hash]; p != NULL; p = p->adr_link) {
+ if (ADDR_PORT_EQ(srcadr, &p->srcadr)) {
+
+ /*
+ * if the association matching rules determine
+ * that this is not a valid combination, then
+ * look for the next valid peer association.
+ */
+ *action = MATCH_ASSOC(p->hmode, pkt_mode);
+
+ /*
+ * A response to our manycastclient solicitation
+ * might be misassociated with an ephemeral peer
+ * already spun for the server. If the packet's
+ * org timestamp doesn't match the peer's, check
+ * if it matches the ACST prototype peer's. If
+ * so it is a redundant solicitation response,
+ * return AM_ERR to discard it. [Bug 1762]
+ */
+ if (MODE_SERVER == pkt_mode &&
+ AM_PROCPKT == *action) {
+ pkt = &rbufp->recv_pkt;
+ NTOHL_FP(&pkt->org, &pkt_org);
+ if (!L_ISEQU(&p->aorg, &pkt_org) &&
+ findmanycastpeer(rbufp))
+ *action = AM_ERR;
+ }
+
+ /*
+ * if an error was returned, exit back right
+ * here.
+ */
+ if (*action == AM_ERR)
+ return NULL;
+
+ /*
+ * if a match is found, we stop our search.
+ */
+ if (*action != AM_NOMATCH)
+ break;
+ }
+ }
+
+ /*
+ * If no matching association is found
+ */
+ if (NULL == p) {
+ *action = MATCH_ASSOC(NO_PEER, pkt_mode);
+ } else if (p->dstadr != rbufp->dstadr) {
+ set_peerdstadr(p, rbufp->dstadr);
+ if (p->dstadr == rbufp->dstadr) {
+ DPRINTF(1, ("Changed %s local address to match response\n",
+ stoa(&p->srcadr)));
+ return findpeer(rbufp, pkt_mode, action);
+ }
+ }
+ return p;
+}
+
+/*
+ * findpeerbyassoc - find and return a peer using his association ID
+ */
+struct peer *
+findpeerbyassoc(
+ associd_t assoc
+ )
+{
+ struct peer *p;
+ u_int hash;
+
+ assocpeer_calls++;
+ hash = assoc & NTP_HASH_MASK;
+ for (p = assoc_hash[hash]; p != NULL; p = p->aid_link)
+ if (assoc == p->associd)
+ break;
+ return p;
+}
+
+
+/*
+ * clear_all - flush all time values for all associations
+ */
+void
+clear_all(void)
+{
+ struct peer *p;
+
+ /*
+ * This routine is called when the clock is stepped, and so all
+ * previously saved time values are untrusted.
+ */
+ for (p = peer_list; p != NULL; p = p->p_link)
+ if (!(MDF_TXONLY_MASK & p->cast_flags))
+ peer_clear(p, "STEP");
+
+ DPRINTF(1, ("clear_all: at %lu\n", current_time));
+}
+
+
+/*
+ * score_all() - determine if an association can be demobilized
+ */
+int
+score_all(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct peer *speer;
+ int temp, tamp;
+ int x;
+
+ /*
+ * This routine finds the minimum score for all preemptible
+ * associations and returns > 0 if the association can be
+ * demobilized.
+ */
+ tamp = score(peer);
+ temp = 100;
+ for (speer = peer_list; speer != NULL; speer = speer->p_link)
+ if (speer->flags & FLAG_PREEMPT) {
+ x = score(speer);
+ if (x < temp)
+ temp = x;
+ }
+ DPRINTF(1, ("score_all: at %lu score %d min %d\n",
+ current_time, tamp, temp));
+
+ if (tamp != temp)
+ temp = 0;
+
+ return temp;
+}
+
+
+/*
+ * score() - calculate preemption score
+ */
+static int
+score(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ int temp;
+
+ /*
+ * This routine calculates the premption score from the peer
+ * error bits and status. Increasing values are more cherished.
+ */
+ temp = 0;
+ if (!(peer->flash & TEST10))
+ temp++; /* 1 good synch and stratum */
+ if (!(peer->flash & TEST13))
+ temp++; /* 2 reachable */
+ if (!(peer->flash & TEST12))
+ temp++; /* 3 no loop */
+ if (!(peer->flash & TEST11))
+ temp++; /* 4 good distance */
+ if (peer->status >= CTL_PST_SEL_SELCAND)
+ temp++; /* 5 in the hunt */
+ if (peer->status != CTL_PST_SEL_EXCESS)
+ temp++; /* 6 not spare tire */
+ return (temp); /* selection status */
+}
+
+
+/*
+ * free_peer - internal routine to free memory referred to by a struct
+ * peer and return it to the peer free list. If unlink is
+ * nonzero, unlink from the various lists.
+ */
+static void
+free_peer(
+ struct peer * p,
+ int unlink_peer
+ )
+{
+ struct peer * unlinked;
+ int hash;
+
+ if (unlink_peer) {
+ hash = NTP_HASH_ADDR(&p->srcadr);
+ peer_hash_count[hash]--;
+
+ UNLINK_SLIST(unlinked, peer_hash[hash], p, adr_link,
+ struct peer);
+ if (NULL == unlinked) {
+ peer_hash_count[hash]++;
+ msyslog(LOG_ERR, "peer %s not in address table!",
+ stoa(&p->srcadr));
+ }
+
+ /*
+ * Remove him from the association hash as well.
+ */
+ hash = p->associd & NTP_HASH_MASK;
+ assoc_hash_count[hash]--;
+
+ UNLINK_SLIST(unlinked, assoc_hash[hash], p, aid_link,
+ struct peer);
+ if (NULL == unlinked) {
+ assoc_hash_count[hash]++;
+ msyslog(LOG_ERR,
+ "peer %s not in association ID table!",
+ stoa(&p->srcadr));
+ }
+
+ /* Remove him from the overall list. */
+ UNLINK_SLIST(unlinked, peer_list, p, p_link,
+ struct peer);
+ if (NULL == unlinked)
+ msyslog(LOG_ERR, "%s not in peer list!",
+ stoa(&p->srcadr));
+ }
+
+ if (p->hostname != NULL)
+ free(p->hostname);
+
+ if (p->ident != NULL)
+ free(p->ident);
+
+ if (p->addrs != NULL)
+ free(p->addrs); /* from copy_addrinfo_list() */
+
+ /* Add his corporeal form to peer free list */
+ ZERO(*p);
+ LINK_SLIST(peer_free, p, p_link);
+ peer_free_count++;
+}
+
+
+/*
+ * unpeer - remove peer structure from hash table and free structure
+ */
+void
+unpeer(
+ struct peer *peer
+ )
+{
+ mprintf_event(PEVNT_DEMOBIL, peer, "assoc %u", peer->associd);
+ restrict_source(&peer->srcadr, 1, 0);
+ set_peerdstadr(peer, NULL);
+ peer_demobilizations++;
+ peer_associations--;
+ if (FLAG_PREEMPT & peer->flags)
+ peer_preempt--;
+#ifdef REFCLOCK
+ /*
+ * If this peer is actually a clock, shut it down first
+ */
+ if (FLAG_REFCLOCK & peer->flags)
+ refclock_unpeer(peer);
+#endif
+
+ free_peer(peer, TRUE);
+}
+
+
+/*
+ * peer_config - configure a new association
+ */
+struct peer *
+peer_config(
+ sockaddr_u * srcadr,
+ const char * hostname,
+ endpt * dstadr,
+ u_char hmode,
+ u_char version,
+ u_char minpoll,
+ u_char maxpoll,
+ u_int flags,
+ u_int32 ttl,
+ keyid_t key,
+ const char * ident /* autokey group */
+ )
+{
+ u_char cast_flags;
+
+ /*
+ * We do a dirty little jig to figure the cast flags. This is
+ * probably not the best place to do this, at least until the
+ * configure code is rebuilt. Note only one flag can be set.
+ */
+ switch (hmode) {
+ case MODE_BROADCAST:
+ if (IS_MCAST(srcadr))
+ cast_flags = MDF_MCAST;
+ else
+ cast_flags = MDF_BCAST;
+ break;
+
+ case MODE_CLIENT:
+ if (hostname != NULL && SOCK_UNSPEC(srcadr))
+ cast_flags = MDF_POOL;
+ else if (IS_MCAST(srcadr))
+ cast_flags = MDF_ACAST;
+ else
+ cast_flags = MDF_UCAST;
+ break;
+
+ default:
+ cast_flags = MDF_UCAST;
+ }
+
+ /*
+ * Mobilize the association and initialize its variables. If
+ * emulating ntpdate, force iburst. For pool and manycastclient
+ * strip FLAG_PREEMPT as the prototype associations are not
+ * themselves preemptible, though the resulting associations
+ * are.
+ */
+ flags |= FLAG_CONFIG;
+ if (mode_ntpdate)
+ flags |= FLAG_IBURST;
+ if ((MDF_ACAST | MDF_POOL) & cast_flags)
+ flags &= ~FLAG_PREEMPT;
+ return newpeer(srcadr, hostname, dstadr, hmode, version,
+ minpoll, maxpoll, flags, cast_flags, ttl, key, ident);
+}
+
+/*
+ * setup peer dstadr field keeping it in sync with the interface
+ * structures
+ */
+void
+set_peerdstadr(
+ struct peer * p,
+ endpt * dstadr
+ )
+{
+ struct peer * unlinked;
+
+ if (p->dstadr == dstadr)
+ return;
+
+ /*
+ * Don't accept updates to a separate multicast receive-only
+ * endpt while a BCLNT peer is running its unicast protocol.
+ */
+ if (dstadr != NULL && (FLAG_BC_VOL & p->flags) &&
+ (INT_MCASTIF & dstadr->flags) && MODE_CLIENT == p->hmode) {
+ return;
+ }
+ if (p->dstadr != NULL) {
+ p->dstadr->peercnt--;
+ UNLINK_SLIST(unlinked, p->dstadr->peers, p, ilink,
+ struct peer);
+ msyslog(LOG_INFO, "%s local addr %s -> %s",
+ stoa(&p->srcadr), latoa(p->dstadr),
+ latoa(dstadr));
+ }
+ p->dstadr = dstadr;
+ if (dstadr != NULL) {
+ LINK_SLIST(dstadr->peers, p, ilink);
+ dstadr->peercnt++;
+ }
+}
+
+/*
+ * attempt to re-rebind interface if necessary
+ */
+static void
+peer_refresh_interface(
+ struct peer *p
+ )
+{
+ endpt * niface;
+ endpt * piface;
+
+ niface = select_peerinterface(p, &p->srcadr, NULL);
+
+ DPRINTF(4, (
+ "peer_refresh_interface: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %u key %08x: new interface: ",
+ p->dstadr == NULL ? "<null>" :
+ stoa(&p->dstadr->sin), stoa(&p->srcadr), p->hmode,
+ p->version, p->minpoll, p->maxpoll, p->flags, p->cast_flags,
+ p->ttl, p->keyid));
+ if (niface != NULL) {
+ DPRINTF(4, (
+ "fd=%d, bfd=%d, name=%.16s, flags=0x%x, ifindex=%u, sin=%s",
+ niface->fd, niface->bfd, niface->name,
+ niface->flags, niface->ifindex,
+ stoa(&niface->sin)));
+ if (niface->flags & INT_BROADCAST)
+ DPRINTF(4, (", bcast=%s",
+ stoa(&niface->bcast)));
+ DPRINTF(4, (", mask=%s\n", stoa(&niface->mask)));
+ } else {
+ DPRINTF(4, ("<NONE>\n"));
+ }
+
+ piface = p->dstadr;
+ set_peerdstadr(p, niface);
+ if (p->dstadr != NULL) {
+ /*
+ * clear crypto if we change the local address
+ */
+ if (p->dstadr != piface && !(MDF_ACAST & p->cast_flags)
+ && MODE_BROADCAST != p->pmode)
+ peer_clear(p, "XFAC");
+
+ /*
+ * Broadcast needs the socket enabled for broadcast
+ */
+ if (MDF_BCAST & p->cast_flags)
+ enable_broadcast(p->dstadr, &p->srcadr);
+
+ /*
+ * Multicast needs the socket interface enabled for
+ * multicast
+ */
+ if (MDF_MCAST & p->cast_flags)
+ enable_multicast_if(p->dstadr, &p->srcadr);
+ }
+}
+
+
+/*
+ * refresh_all_peerinterfaces - see that all interface bindings are up
+ * to date
+ */
+void
+refresh_all_peerinterfaces(void)
+{
+ struct peer *p;
+
+ /*
+ * this is called when the interface list has changed
+ * give all peers a chance to find a better interface
+ */
+ for (p = peer_list; p != NULL; p = p->p_link)
+ peer_refresh_interface(p);
+}
+
+
+/*
+ * newpeer - initialize a new peer association
+ */
+struct peer *
+newpeer(
+ sockaddr_u * srcadr,
+ const char * hostname,
+ endpt * dstadr,
+ u_char hmode,
+ u_char version,
+ u_char minpoll,
+ u_char maxpoll,
+ u_int flags,
+ u_char cast_flags,
+ u_int32 ttl,
+ keyid_t key,
+ const char * ident
+ )
+{
+ struct peer * peer;
+ u_int hash;
+
+#ifdef AUTOKEY
+ /*
+ * If Autokey is requested but not configured, complain loudly.
+ */
+ if (!crypto_flags) {
+ if (key > NTP_MAXKEY) {
+ return (NULL);
+
+ } else if (flags & FLAG_SKEY) {
+ msyslog(LOG_ERR, "Autokey not configured");
+ return (NULL);
+ }
+ }
+#endif /* AUTOKEY */
+
+ /*
+ * For now only pool associations have a hostname.
+ */
+ NTP_INSIST(NULL == hostname || (MDF_POOL & cast_flags));
+
+ /*
+ * First search from the beginning for an association with given
+ * remote address and mode. If an interface is given, search
+ * from there to find the association which matches that
+ * destination. If the given interface is "any", track down the
+ * actual interface, because that's what gets put into the peer
+ * structure.
+ */
+ if (dstadr != NULL) {
+ peer = findexistingpeer(srcadr, hostname, NULL, hmode,
+ cast_flags);
+ while (peer != NULL) {
+ if (peer->dstadr == dstadr ||
+ ((MDF_BCLNT & cast_flags) &&
+ (MDF_BCLNT & peer->cast_flags)))
+ break;
+
+ if (dstadr == ANY_INTERFACE_CHOOSE(srcadr) &&
+ peer->dstadr == findinterface(srcadr))
+ break;
+
+ peer = findexistingpeer(srcadr, hostname, peer,
+ hmode, cast_flags);
+ }
+ } else {
+ /* no endpt address given */
+ peer = findexistingpeer(srcadr, hostname, NULL, hmode,
+ cast_flags);
+ }
+
+ /*
+ * If a peer is found, this would be a duplicate and we don't
+ * allow that. This avoids duplicate ephemeral (broadcast/
+ * multicast) and preemptible (manycast and pool) client
+ * associations.
+ */
+ if (peer != NULL) {
+ DPRINTF(2, ("newpeer(%s) found existing association\n",
+ (hostname)
+ ? hostname
+ : stoa(srcadr)));
+ return NULL;
+ }
+
+ /*
+ * Allocate a new peer structure. Some dirt here, since some of
+ * the initialization requires knowlege of our system state.
+ */
+ if (peer_free_count == 0)
+ getmorepeermem();
+ UNLINK_HEAD_SLIST(peer, peer_free, p_link);
+ peer_free_count--;
+ peer_associations++;
+ if (FLAG_PREEMPT & flags)
+ peer_preempt++;
+
+ /*
+ * Assign an association ID and increment the system variable.
+ */
+ peer->associd = current_association_ID;
+ if (++current_association_ID == 0)
+ ++current_association_ID;
+
+ peer->srcadr = *srcadr;
+ if (hostname != NULL)
+ peer->hostname = estrdup(hostname);
+ peer->hmode = hmode;
+ peer->version = version;
+ peer->flags = flags;
+ peer->cast_flags = cast_flags;
+ set_peerdstadr(peer,
+ select_peerinterface(peer, srcadr, dstadr));
+
+ /*
+ * It is an error to set minpoll less than NTP_MINPOLL or to
+ * set maxpoll greater than NTP_MAXPOLL. However, minpoll is
+ * clamped not greater than NTP_MAXPOLL and maxpoll is clamped
+ * not less than NTP_MINPOLL without complaint. Finally,
+ * minpoll is clamped not greater than maxpoll.
+ */
+ if (minpoll == 0)
+ peer->minpoll = NTP_MINDPOLL;
+ else
+ peer->minpoll = min(minpoll, NTP_MAXPOLL);
+ if (maxpoll == 0)
+ peer->maxpoll = NTP_MAXDPOLL;
+ else
+ peer->maxpoll = max(maxpoll, NTP_MINPOLL);
+ if (peer->minpoll > peer->maxpoll)
+ peer->minpoll = peer->maxpoll;
+
+ if (peer->dstadr != NULL)
+ DPRINTF(3, ("newpeer(%s): using fd %d and our addr %s\n",
+ stoa(srcadr), peer->dstadr->fd,
+ stoa(&peer->dstadr->sin)));
+ else
+ DPRINTF(3, ("newpeer(%s): local interface currently not bound\n",
+ stoa(srcadr)));
+
+ /*
+ * Broadcast needs the socket enabled for broadcast
+ */
+ if ((MDF_BCAST & cast_flags) && peer->dstadr != NULL)
+ enable_broadcast(peer->dstadr, srcadr);
+
+ /*
+ * Multicast needs the socket interface enabled for multicast
+ */
+ if ((MDF_MCAST & cast_flags) && peer->dstadr != NULL)
+ enable_multicast_if(peer->dstadr, srcadr);
+
+#ifdef AUTOKEY
+ if (key > NTP_MAXKEY)
+ peer->flags |= FLAG_SKEY;
+#endif /* AUTOKEY */
+ peer->ttl = ttl;
+ peer->keyid = key;
+ if (ident != NULL)
+ peer->ident = estrdup(ident);
+ peer->precision = sys_precision;
+ peer->hpoll = peer->minpoll;
+ if (cast_flags & MDF_ACAST)
+ peer_clear(peer, "ACST");
+ else if (cast_flags & MDF_POOL)
+ peer_clear(peer, "POOL");
+ else if (cast_flags & MDF_MCAST)
+ peer_clear(peer, "MCST");
+ else if (cast_flags & MDF_BCAST)
+ peer_clear(peer, "BCST");
+ else
+ peer_clear(peer, "INIT");
+ if (mode_ntpdate)
+ peer_ntpdate++;
+
+ /*
+ * Note time on statistics timers.
+ */
+ peer->timereset = current_time;
+ peer->timereachable = current_time;
+ peer->timereceived = current_time;
+
+ if (ISREFCLOCKADR(&peer->srcadr)) {
+#ifdef REFCLOCK
+ /*
+ * We let the reference clock support do clock
+ * dependent initialization. This includes setting
+ * the peer timer, since the clock may have requirements
+ * for this.
+ */
+ if (maxpoll == 0)
+ peer->maxpoll = peer->minpoll;
+ if (!refclock_newpeer(peer)) {
+ /*
+ * Dump it, something screwed up
+ */
+ set_peerdstadr(peer, NULL);
+ free_peer(peer, 0);
+ return NULL;
+ }
+#else /* REFCLOCK */
+ msyslog(LOG_ERR, "refclock %s isn't supported. ntpd was compiled without refclock support.",
+ stoa(&peer->srcadr));
+ set_peerdstadr(peer, NULL);
+ free_peer(peer, 0);
+ return NULL;
+#endif /* REFCLOCK */
+ }
+
+ /*
+ * Put the new peer in the hash tables.
+ */
+ hash = NTP_HASH_ADDR(&peer->srcadr);
+ LINK_SLIST(peer_hash[hash], peer, adr_link);
+ peer_hash_count[hash]++;
+ hash = peer->associd & NTP_HASH_MASK;
+ LINK_SLIST(assoc_hash[hash], peer, aid_link);
+ assoc_hash_count[hash]++;
+ LINK_SLIST(peer_list, peer, p_link);
+
+ restrict_source(&peer->srcadr, 0, 0);
+ mprintf_event(PEVNT_MOBIL, peer, "assoc %d", peer->associd);
+ DPRINTF(1, ("newpeer: %s->%s mode %u vers %u poll %u %u flags 0x%x 0x%x ttl %u key %08x\n",
+ latoa(peer->dstadr), stoa(&peer->srcadr), peer->hmode,
+ peer->version, peer->minpoll, peer->maxpoll, peer->flags,
+ peer->cast_flags, peer->ttl, peer->keyid));
+ return peer;
+}
+
+
+/*
+ * peer_clr_stats - clear peer module statistics counters
+ */
+void
+peer_clr_stats(void)
+{
+ findpeer_calls = 0;
+ assocpeer_calls = 0;
+ peer_allocations = 0;
+ peer_demobilizations = 0;
+ peer_timereset = current_time;
+}
+
+
+/*
+ * peer_reset - reset statistics counters
+ */
+void
+peer_reset(
+ struct peer *peer
+ )
+{
+ if (peer == NULL)
+ return;
+
+ peer->timereset = current_time;
+ peer->sent = 0;
+ peer->received = 0;
+ peer->processed = 0;
+ peer->badauth = 0;
+ peer->bogusorg = 0;
+ peer->oldpkt = 0;
+ peer->seldisptoolarge = 0;
+ peer->selbroken = 0;
+}
+
+
+/*
+ * peer_all_reset - reset all peer statistics counters
+ */
+void
+peer_all_reset(void)
+{
+ struct peer *peer;
+
+ for (peer = peer_list; peer != NULL; peer = peer->p_link)
+ peer_reset(peer);
+}
+
+
+/*
+ * findmanycastpeer - find and return a manycastclient or pool
+ * association matching a received response.
+ */
+struct peer *
+findmanycastpeer(
+ struct recvbuf *rbufp /* receive buffer pointer */
+ )
+{
+ struct peer *peer;
+ struct pkt *pkt;
+ l_fp p_org;
+
+ /*
+ * This routine is called upon arrival of a server-mode response
+ * to a manycastclient multicast solicitation, or to a pool
+ * server unicast solicitation. Search the peer list for a
+ * manycastclient association where the last transmit timestamp
+ * matches the response packet's originate timestamp. There can
+ * be multiple manycastclient associations, or multiple pool
+ * solicitation assocations, so this assumes the transmit
+ * timestamps are unique for such.
+ */
+ pkt = &rbufp->recv_pkt;
+ for (peer = peer_list; peer != NULL; peer = peer->p_link)
+ if (MDF_SOLICIT_MASK & peer->cast_flags) {
+ NTOHL_FP(&pkt->org, &p_org);
+ if (L_ISEQU(&p_org, &peer->aorg))
+ break;
+ }
+
+ return peer;
+}
diff --git a/ntpd/ntp_prio_q.c b/ntpd/ntp_prio_q.c
new file mode 100644
index 0000000..703673b
--- /dev/null
+++ b/ntpd/ntp_prio_q.c
@@ -0,0 +1,238 @@
+/* ntp_prio_q.c
+ *
+ * This file contains the priority queue implementation used by the
+ * discrete event simulator.
+ *
+ * Written By: Sachin Kamboj
+ * University of Delaware
+ * Newark, DE 19711
+ * Copyright (c) 2006
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ntp_stdlib.h>
+#include <ntp_prio_q.h>
+
+/* Priority Queue
+ * --------------
+ * Define a priority queue in which the relative priority of the elements
+ * is determined by a function 'get_order' which is supplied to the
+ * priority_queue
+ */
+queue *debug_create_priority_queue(
+ q_order_func get_order
+#ifdef _CRTDBG_MAP_ALLOC
+ , const char * sourcefile
+ , int line_num
+#endif
+ )
+{
+ queue *my_queue;
+
+#ifndef _CRTDBG_MAP_ALLOC
+ my_queue = emalloc(sizeof(queue));
+#else
+ /* preserve original callsite __FILE__ and __LINE__ for leak report */
+ my_queue = debug_erealloc(NULL, sizeof(queue), sourcefile, line_num);
+#endif
+ my_queue->get_order = get_order;
+ my_queue->front = NULL;
+ my_queue->no_of_elements = 0;
+
+ return my_queue;
+}
+
+
+/* Define a function to "destroy" a priority queue, freeing-up
+ * all the allocated resources in the process
+ */
+
+void destroy_queue(
+ queue *my_queue
+ )
+{
+ node *temp = NULL;
+
+ /* Empty out the queue elements if they are not already empty */
+ while (my_queue->front != NULL) {
+ temp = my_queue->front;
+ my_queue->front = my_queue->front->node_next;
+ free(temp);
+ }
+
+ /* Now free the queue */
+ free(my_queue);
+}
+
+
+/* Define a function to allocate memory for one element
+ * of the queue. The allocated memory consists of size
+ * bytes plus the number of bytes needed for bookkeeping
+ */
+
+void *debug_get_node(
+ size_t size
+#ifdef _CRTDBG_MAP_ALLOC
+ , const char * sourcefile
+ , int line_num
+#endif
+ )
+{
+ node *new_node;
+
+#ifndef _CRTDBG_MAP_ALLOC
+ new_node = emalloc(sizeof(*new_node) + size);
+#else
+ new_node = debug_erealloc(NULL, sizeof(*new_node) + size,
+ sourcefile, line_num);
+#endif
+ new_node->node_next = NULL;
+
+ return new_node + 1;
+}
+
+/* Define a function to free the allocated memory for a queue node */
+void free_node(
+ void *my_node
+ )
+{
+ node *old_node = my_node;
+
+ free(old_node - 1);
+}
+
+
+void *
+next_node(
+ void *pv
+ )
+{
+ node *pn;
+
+ pn = pv;
+ pn--;
+
+ if (pn->node_next == NULL)
+ return NULL;
+
+ return pn->node_next + 1;
+}
+
+
+/* Define a function to check if the queue is empty. */
+int empty(
+ queue *my_queue
+ )
+{
+ return (!my_queue || !my_queue->front);
+}
+
+
+void *
+queue_head(
+ queue *q
+ )
+{
+ if (NULL == q || NULL == q->front)
+ return NULL;
+
+ return q->front + 1;
+}
+
+
+/* Define a function to add an element to the priority queue.
+ * The element is added according to its priority -
+ * relative priority is given by the get_order function
+ */
+queue *enqueue(
+ queue * my_queue,
+ void * my_node
+ )
+{
+ node *new_node = (node *)my_node - 1;
+ node *i = NULL;
+ node *j = my_queue->front;
+
+ while (j != NULL &&
+ (*my_queue->get_order)(new_node + 1, j + 1) > 0) {
+ i = j;
+ j = j->node_next;
+ }
+
+ if (i == NULL) { /* Insert at beginning of the queue */
+ new_node->node_next = my_queue->front;
+ my_queue->front = new_node;
+ } else { /* Insert Elsewhere, including the end */
+ new_node->node_next = i->node_next;
+ i->node_next = new_node;
+ }
+
+ ++my_queue->no_of_elements;
+ return my_queue;
+}
+
+
+/* Define a function to dequeue the first element from the priority
+ * queue and return it
+ */
+void *dequeue(
+ queue *my_queue
+ )
+{
+ node *my_node = my_queue->front;
+
+ if (my_node != NULL) {
+ my_queue->front = my_node->node_next;
+ --my_queue->no_of_elements;
+ return my_node + 1;
+ } else
+ return NULL;
+}
+
+
+/* Define a function that returns the number of elements in the
+ * priority queue
+ */
+int get_no_of_elements(
+ queue *my_queue
+ )
+{
+ return my_queue->no_of_elements;
+}
+
+
+/* Define a function to append a queue onto another.
+ * Note: there is a faster way (O(1) as opposed to O(n))
+ * to do this for simple (FIFO) queues, but we can't rely on
+ * that for priority queues. (Given the current representation)
+ *
+ * I don't anticipate this to be a problem. If it does turn
+ * out to be a bottleneck, I will consider replacing the
+ * current implementation with a binomial or fibonacci heap.
+ */
+void append_queue(
+ queue *q1,
+ queue *q2
+ )
+{
+ while (!empty(q2))
+ enqueue(q1, dequeue(q2));
+ destroy_queue(q2);
+}
+
+
+/* FIFO Queue
+ * ----------
+ * Use the priority queue to create a traditional FIFO queue.
+ * The only extra function needed is the create_queue
+ */
+
+/* C is not Lisp and does not allow anonymous lambda functions :-(.
+ * So define a get_fifo_order function here
+ */
+int get_fifo_order(const void *el1, const void *el2)
+{
+ return 1;
+}
diff --git a/ntpd/ntp_proto.c b/ntpd/ntp_proto.c
new file mode 100644
index 0000000..2f7543f
--- /dev/null
+++ b/ntpd/ntp_proto.c
@@ -0,0 +1,4168 @@
+/*
+ * ntp_proto.c - NTP version 4 protocol machinery
+ *
+ * ATTENTION: Get approval from Dave Mills on all changes to this file!
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+#include "ntp_control.h"
+#include "ntp_string.h"
+#include "ntp_leapsec.h"
+
+#include <stdio.h>
+#ifdef HAVE_LIBSCF_H
+#include <libscf.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/*
+ * This macro defines the authentication state. If x is 1 authentication
+ * is required; othewise it is optional.
+ */
+#define AUTH(x, y) ((x) ? (y) == AUTH_OK : (y) == AUTH_OK || \
+ (y) == AUTH_NONE)
+
+#define AUTH_NONE 0 /* authentication not required */
+#define AUTH_OK 1 /* authentication OK */
+#define AUTH_ERROR 2 /* authentication error */
+#define AUTH_CRYPTO 3 /* crypto_NAK */
+
+/*
+ * traffic shaping parameters
+ */
+#define NTP_IBURST 6 /* packets in iburst */
+#define RESP_DELAY 1 /* refclock burst delay (s) */
+
+/*
+ * pool soliciting restriction duration (s)
+ */
+#define POOL_SOLICIT_WINDOW 8
+
+/*
+ * peer_select groups statistics for a peer used by clock_select() and
+ * clock_cluster().
+ */
+typedef struct peer_select_tag {
+ struct peer * peer;
+ double synch; /* sync distance */
+ double error; /* jitter */
+ double seljit; /* selection jitter */
+} peer_select;
+
+/*
+ * System variables are declared here. Unless specified otherwise, all
+ * times are in seconds.
+ */
+u_char sys_leap; /* system leap indicator */
+u_char sys_stratum; /* system stratum */
+s_char sys_precision; /* local clock precision (log2 s) */
+double sys_rootdelay; /* roundtrip delay to primary source */
+double sys_rootdisp; /* dispersion to primary source */
+u_int32 sys_refid; /* reference id (network byte order) */
+l_fp sys_reftime; /* last update time */
+struct peer *sys_peer; /* current peer */
+
+/*
+ * Rate controls. Leaky buckets are used to throttle the packet
+ * transmission rates in order to protect busy servers such as at NIST
+ * and USNO. There is a counter for each association and another for KoD
+ * packets. The association counter decrements each second, but not
+ * below zero. Each time a packet is sent the counter is incremented by
+ * a configurable value representing the average interval between
+ * packets. A packet is delayed as long as the counter is greater than
+ * zero. Note this does not affect the time value computations.
+ */
+/*
+ * Nonspecified system state variables
+ */
+int sys_bclient; /* broadcast client enable */
+double sys_bdelay; /* broadcast client default delay */
+int sys_authenticate; /* requre authentication for config */
+l_fp sys_authdelay; /* authentication delay */
+double sys_offset; /* current local clock offset */
+double sys_mindisp = MINDISPERSE; /* minimum distance (s) */
+double sys_maxdist = MAXDISTANCE; /* selection threshold */
+double sys_jitter; /* system jitter */
+u_long sys_epoch; /* last clock update time */
+static double sys_clockhop; /* clockhop threshold */
+static int leap_vote_ins; /* leap consensus for insert */
+static int leap_vote_del; /* leap consensus for delete */
+keyid_t sys_private; /* private value for session seed */
+int sys_manycastserver; /* respond to manycast client pkts */
+int ntp_mode7; /* respond to ntpdc (mode7) */
+int peer_ntpdate; /* active peers in ntpdate mode */
+int sys_survivors; /* truest of the truechimers */
+char *sys_ident = NULL; /* identity scheme */
+
+/*
+ * TOS and multicast mapping stuff
+ */
+int sys_floor = 0; /* cluster stratum floor */
+int sys_ceiling = STRATUM_UNSPEC - 1; /* cluster stratum ceiling */
+int sys_minsane = 1; /* minimum candidates */
+int sys_minclock = NTP_MINCLOCK; /* minimum candidates */
+int sys_maxclock = NTP_MAXCLOCK; /* maximum candidates */
+int sys_cohort = 0; /* cohort switch */
+int sys_orphan = STRATUM_UNSPEC + 1; /* orphan stratum */
+int sys_orphwait = NTP_ORPHWAIT; /* orphan wait */
+int sys_beacon = BEACON; /* manycast beacon interval */
+int sys_ttlmax; /* max ttl mapping vector index */
+u_char sys_ttl[MAX_TTL]; /* ttl mapping vector */
+
+/*
+ * Statistics counters - first the good, then the bad
+ */
+u_long sys_stattime; /* elapsed time */
+u_long sys_received; /* packets received */
+u_long sys_processed; /* packets for this host */
+u_long sys_newversion; /* current version */
+u_long sys_oldversion; /* old version */
+u_long sys_restricted; /* access denied */
+u_long sys_badlength; /* bad length or format */
+u_long sys_badauth; /* bad authentication */
+u_long sys_declined; /* declined */
+u_long sys_limitrejected; /* rate exceeded */
+u_long sys_kodsent; /* KoD sent */
+
+static double root_distance (struct peer *);
+static void clock_combine (peer_select *, int, int);
+static void peer_xmit (struct peer *);
+static void fast_xmit (struct recvbuf *, int, keyid_t, int);
+static void pool_xmit (struct peer *);
+static void clock_update (struct peer *);
+static void measure_precision(void);
+static double measure_tick_fuzz(void);
+static int local_refid (struct peer *);
+static int peer_unfit (struct peer *);
+#ifdef AUTOKEY
+static int group_test (char *, char *);
+#endif /* AUTOKEY */
+#ifdef WORKER
+void pool_name_resolved (int, int, void *, const char *,
+ const char *, const struct addrinfo *,
+ const struct addrinfo *);
+#endif /* WORKER */
+
+
+/*
+ * transmit - transmit procedure called by poll timeout
+ */
+void
+transmit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ u_char hpoll;
+
+ /*
+ * The polling state machine. There are two kinds of machines,
+ * those that never expect a reply (broadcast and manycast
+ * server modes) and those that do (all other modes). The dance
+ * is intricate...
+ */
+ hpoll = peer->hpoll;
+
+ /*
+ * In broadcast mode the poll interval is never changed from
+ * minpoll.
+ */
+ if (peer->cast_flags & (MDF_BCAST | MDF_MCAST)) {
+ peer->outdate = current_time;
+ if (sys_leap != LEAP_NOTINSYNC)
+ peer_xmit(peer);
+ poll_update(peer, hpoll);
+ return;
+ }
+
+ /*
+ * In manycast mode we start with unity ttl. The ttl is
+ * increased by one for each poll until either sys_maxclock
+ * servers have been found or the maximum ttl is reached. When
+ * sys_maxclock servers are found we stop polling until one or
+ * more servers have timed out or until less than sys_minclock
+ * associations turn up. In this case additional better servers
+ * are dragged in and preempt the existing ones. Once every
+ * sys_beacon seconds we are to transmit unconditionally, but
+ * this code is not quite right -- peer->unreach counts polls
+ * and is being compared with sys_beacon, so the beacons happen
+ * every sys_beacon polls.
+ */
+ if (peer->cast_flags & MDF_ACAST) {
+ peer->outdate = current_time;
+ if (peer->unreach > sys_beacon) {
+ peer->unreach = 0;
+ peer->ttl = 0;
+ peer_xmit(peer);
+ } else if (sys_survivors < sys_minclock ||
+ peer_associations < sys_maxclock) {
+ if (peer->ttl < (u_int32)sys_ttlmax)
+ peer->ttl++;
+ peer_xmit(peer);
+ }
+ peer->unreach++;
+ poll_update(peer, hpoll);
+ return;
+ }
+
+ /*
+ * Pool associations transmit unicast solicitations when there
+ * are less than a hard limit of 2 * sys_maxclock associations,
+ * and either less than sys_minclock survivors or less than
+ * sys_maxclock associations. The hard limit prevents unbounded
+ * growth in associations if the system clock or network quality
+ * result in survivor count dipping below sys_minclock often.
+ * This was observed testing with pool, where sys_maxclock == 12
+ * resulted in 60 associations without the hard limit. A
+ * similar hard limit on manycastclient ephemeral associations
+ * may be appropriate.
+ */
+ if (peer->cast_flags & MDF_POOL) {
+ peer->outdate = current_time;
+ if ((peer_associations <= 2 * sys_maxclock) &&
+ (peer_associations < sys_maxclock ||
+ sys_survivors < sys_minclock))
+ pool_xmit(peer);
+ poll_update(peer, hpoll);
+ return;
+ }
+
+ /*
+ * In unicast modes the dance is much more intricate. It is
+ * designed to back off whenever possible to minimize network
+ * traffic.
+ */
+ if (peer->burst == 0) {
+ u_char oreach;
+
+ /*
+ * Update the reachability status. If not heard for
+ * three consecutive polls, stuff infinity in the clock
+ * filter.
+ */
+ oreach = peer->reach;
+ peer->outdate = current_time;
+ peer->unreach++;
+ peer->reach <<= 1;
+ if (!peer->reach) {
+
+ /*
+ * Here the peer is unreachable. If it was
+ * previously reachable raise a trap. Send a
+ * burst if enabled.
+ */
+ clock_filter(peer, 0., 0., MAXDISPERSE);
+ if (oreach) {
+ peer_unfit(peer);
+ report_event(PEVNT_UNREACH, peer, NULL);
+ }
+ if ((peer->flags & FLAG_IBURST) &&
+ peer->retry == 0)
+ peer->retry = NTP_RETRY;
+ } else {
+
+ /*
+ * Here the peer is reachable. Send a burst if
+ * enabled and the peer is fit. Reset unreach
+ * for persistent and ephemeral associations.
+ * Unreach is also reset for survivors in
+ * clock_select().
+ */
+ hpoll = sys_poll;
+ if (!(peer->flags & FLAG_PREEMPT))
+ peer->unreach = 0;
+ if ((peer->flags & FLAG_BURST) && peer->retry ==
+ 0 && !peer_unfit(peer))
+ peer->retry = NTP_RETRY;
+ }
+
+ /*
+ * Watch for timeout. If ephemeral, toss the rascal;
+ * otherwise, bump the poll interval. Note the
+ * poll_update() routine will clamp it to maxpoll.
+ * If preemptible and we have more peers than maxclock,
+ * and this peer has the minimum score of preemptibles,
+ * demobilize.
+ */
+ if (peer->unreach >= NTP_UNREACH) {
+ hpoll++;
+ /* ephemeral: no FLAG_CONFIG nor FLAG_PREEMPT */
+ if (!(peer->flags & (FLAG_CONFIG | FLAG_PREEMPT))) {
+ report_event(PEVNT_RESTART, peer, "timeout");
+ peer_clear(peer, "TIME");
+ unpeer(peer);
+ return;
+ }
+ if ((peer->flags & FLAG_PREEMPT) &&
+ (peer_associations > sys_maxclock) &&
+ score_all(peer)) {
+ report_event(PEVNT_RESTART, peer, "timeout");
+ peer_clear(peer, "TIME");
+ unpeer(peer);
+ return;
+ }
+ }
+ } else {
+ peer->burst--;
+ if (peer->burst == 0) {
+
+ /*
+ * If ntpdate mode and the clock has not been
+ * set and all peers have completed the burst,
+ * we declare a successful failure.
+ */
+ if (mode_ntpdate) {
+ peer_ntpdate--;
+ if (peer_ntpdate == 0) {
+ msyslog(LOG_NOTICE,
+ "ntpd: no servers found");
+ if (!msyslog_term)
+ printf(
+ "ntpd: no servers found\n");
+ exit (0);
+ }
+ }
+ }
+ }
+ if (peer->retry > 0)
+ peer->retry--;
+
+ /*
+ * Do not transmit if in broadcast client mode.
+ */
+ if (peer->hmode != MODE_BCLIENT)
+ peer_xmit(peer);
+ poll_update(peer, hpoll);
+}
+
+
+/*
+ * receive - receive procedure called for each packet received
+ */
+void
+receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct peer *peer; /* peer structure pointer */
+ register struct pkt *pkt; /* receive packet pointer */
+ u_char hisversion; /* packet version */
+ u_char hisleap; /* packet leap indicator */
+ u_char hismode; /* packet mode */
+ u_char hisstratum; /* packet stratum */
+ u_short restrict_mask; /* restrict bits */
+ int has_mac; /* length of MAC field */
+ int authlen; /* offset of MAC field */
+ int is_authentic = 0; /* cryptosum ok */
+ int retcode = AM_NOMATCH; /* match code */
+ keyid_t skeyid = 0; /* key IDs */
+ u_int32 opcode = 0; /* extension field opcode */
+ sockaddr_u *dstadr_sin; /* active runway */
+ struct peer *peer2; /* aux peer structure pointer */
+ endpt * match_ep; /* newpeer() local address */
+ l_fp p_org; /* origin timestamp */
+ l_fp p_rec; /* receive timestamp */
+ l_fp p_xmt; /* transmit timestamp */
+#ifdef AUTOKEY
+ char hostname[NTP_MAXSTRLEN + 1];
+ char *groupname = NULL;
+ struct autokey *ap; /* autokey structure pointer */
+ int rval; /* cookie snatcher */
+ keyid_t pkeyid = 0, tkeyid = 0; /* key IDs */
+#endif /* AUTOKEY */
+#ifdef HAVE_NTP_SIGND
+ static unsigned char zero_key[16];
+#endif /* HAVE_NTP_SIGND */
+
+ /*
+ * Monitor the packet and get restrictions. Note that the packet
+ * length for control and private mode packets must be checked
+ * by the service routines. Some restrictions have to be handled
+ * later in order to generate a kiss-o'-death packet.
+ */
+ /*
+ * Bogus port check is before anything, since it probably
+ * reveals a clogging attack.
+ */
+ sys_received++;
+ if (0 == SRCPORT(&rbufp->recv_srcadr)) {
+ sys_badlength++;
+ return; /* bogus port */
+ }
+ restrict_mask = restrictions(&rbufp->recv_srcadr);
+ DPRINTF(2, ("receive: at %ld %s<-%s flags %x restrict %03x\n",
+ current_time, stoa(&rbufp->dstadr->sin),
+ stoa(&rbufp->recv_srcadr),
+ rbufp->dstadr->flags, restrict_mask));
+ pkt = &rbufp->recv_pkt;
+ hisversion = PKT_VERSION(pkt->li_vn_mode);
+ hisleap = PKT_LEAP(pkt->li_vn_mode);
+ hismode = (int)PKT_MODE(pkt->li_vn_mode);
+ hisstratum = PKT_TO_STRATUM(pkt->stratum);
+ if (restrict_mask & RES_IGNORE) {
+ sys_restricted++;
+ return; /* ignore everything */
+ }
+ if (hismode == MODE_PRIVATE) {
+ if (!ntp_mode7 || (restrict_mask & RES_NOQUERY)) {
+ sys_restricted++;
+ return; /* no query private */
+ }
+ process_private(rbufp, ((restrict_mask &
+ RES_NOMODIFY) == 0));
+ return;
+ }
+ if (hismode == MODE_CONTROL) {
+ if (restrict_mask & RES_NOQUERY) {
+ sys_restricted++;
+ return; /* no query control */
+ }
+ process_control(rbufp, restrict_mask);
+ return;
+ }
+ if (restrict_mask & RES_DONTSERVE) {
+ sys_restricted++;
+ return; /* no time serve */
+ }
+
+ /*
+ * This is for testing. If restricted drop ten percent of
+ * surviving packets.
+ */
+ if (restrict_mask & RES_FLAKE) {
+ if ((double)ntp_random() / 0x7fffffff < .1) {
+ sys_restricted++;
+ return; /* no flakeway */
+ }
+ }
+
+ /*
+ * Version check must be after the query packets, since they
+ * intentionally use an early version.
+ */
+ if (hisversion == NTP_VERSION) {
+ sys_newversion++; /* new version */
+ } else if (!(restrict_mask & RES_VERSION) && hisversion >=
+ NTP_OLDVERSION) {
+ sys_oldversion++; /* previous version */
+ } else {
+ sys_badlength++;
+ return; /* old version */
+ }
+
+ /*
+ * Figure out his mode and validate the packet. This has some
+ * legacy raunch that probably should be removed. In very early
+ * NTP versions mode 0 was equivalent to what later versions
+ * would interpret as client mode.
+ */
+ if (hismode == MODE_UNSPEC) {
+ if (hisversion == NTP_OLDVERSION) {
+ hismode = MODE_CLIENT;
+ } else {
+ sys_badlength++;
+ return; /* invalid mode */
+ }
+ }
+
+ /*
+ * Parse the extension field if present. We figure out whether
+ * an extension field is present by measuring the MAC size. If
+ * the number of words following the packet header is 0, no MAC
+ * is present and the packet is not authenticated. If 1, the
+ * packet is a crypto-NAK; if 3, the packet is authenticated
+ * with DES; if 5, the packet is authenticated with MD5; if 6,
+ * the packet is authenticated with SHA. If 2 or * 4, the packet
+ * is a runt and discarded forthwith. If greater than 6, an
+ * extension field is present, so we subtract the length of the
+ * field and go around again.
+ */
+ authlen = LEN_PKT_NOMAC;
+ has_mac = rbufp->recv_length - authlen;
+ while (has_mac != 0) {
+ u_int32 len;
+#ifdef AUTOKEY
+ u_int32 hostlen;
+ struct exten *ep;
+#endif /*AUTOKEY */
+
+ if (has_mac % 4 != 0 || has_mac < MIN_MAC_LEN) {
+ sys_badlength++;
+ return; /* bad length */
+ }
+ if (has_mac <= MAX_MAC_LEN) {
+ skeyid = ntohl(((u_int32 *)pkt)[authlen / 4]);
+ break;
+
+ } else {
+ opcode = ntohl(((u_int32 *)pkt)[authlen / 4]);
+ len = opcode & 0xffff;
+ if (len % 4 != 0 || len < 4 || (int)len +
+ authlen > rbufp->recv_length) {
+ sys_badlength++;
+ return; /* bad length */
+ }
+#ifdef AUTOKEY
+ /*
+ * Extract calling group name for later. If
+ * sys_groupname is non-NULL, there must be
+ * a group name provided to elicit a response.
+ */
+ if ((opcode & 0x3fff0000) == CRYPTO_ASSOC &&
+ sys_groupname != NULL) {
+ ep = (struct exten *)&((u_int32 *)pkt)[authlen / 4];
+ hostlen = ntohl(ep->vallen);
+ if (hostlen >= sizeof(hostname) ||
+ hostlen > len -
+ offsetof(struct exten, pkt)) {
+ sys_badlength++;
+ return; /* bad length */
+ }
+ memcpy(hostname, &ep->pkt, hostlen);
+ hostname[hostlen] = '\0';
+ groupname = strchr(hostname, '@');
+ if (groupname == NULL) {
+ sys_declined++;
+ return;
+ }
+ groupname++;
+ }
+#endif /* AUTOKEY */
+ authlen += len;
+ has_mac -= len;
+ }
+ }
+
+ /*
+ * If authentication required, a MAC must be present.
+ */
+ if (restrict_mask & RES_DONTTRUST && has_mac == 0) {
+ sys_restricted++;
+ return; /* access denied */
+ }
+
+ /*
+ * Update the MRU list and finger the cloggers. It can be a
+ * little expensive, so turn it off for production use.
+ * RES_LIMITED and RES_KOD will be cleared in the returned
+ * restrict_mask unless one or both actions are warranted.
+ */
+ restrict_mask = ntp_monitor(rbufp, restrict_mask);
+ if (restrict_mask & RES_LIMITED) {
+ sys_limitrejected++;
+ if (!(restrict_mask & RES_KOD) || MODE_BROADCAST ==
+ hismode || MODE_SERVER == hismode) {
+ if (MODE_SERVER == hismode)
+ DPRINTF(1, ("Possibly self-induced rate limiting of MODE_SERVER from %s\n",
+ stoa(&rbufp->recv_srcadr)));
+ return; /* rate exceeded */
+ }
+ if (hismode == MODE_CLIENT)
+ fast_xmit(rbufp, MODE_SERVER, skeyid,
+ restrict_mask);
+ else
+ fast_xmit(rbufp, MODE_ACTIVE, skeyid,
+ restrict_mask);
+ return; /* rate exceeded */
+ }
+ restrict_mask &= ~RES_KOD;
+
+ /*
+ * We have tossed out as many buggy packets as possible early in
+ * the game to reduce the exposure to a clogging attack. Now we
+ * have to burn some cycles to find the association and
+ * authenticate the packet if required. Note that we burn only
+ * digest cycles, again to reduce exposure. There may be no
+ * matching association and that's okay.
+ *
+ * More on the autokey mambo. Normally the local interface is
+ * found when the association was mobilized with respect to a
+ * designated remote address. We assume packets arriving from
+ * the remote address arrive via this interface and the local
+ * address used to construct the autokey is the unicast address
+ * of the interface. However, if the sender is a broadcaster,
+ * the interface broadcast address is used instead.
+ * Notwithstanding this technobabble, if the sender is a
+ * multicaster, the broadcast address is null, so we use the
+ * unicast address anyway. Don't ask.
+ */
+ peer = findpeer(rbufp, hismode, &retcode);
+ dstadr_sin = &rbufp->dstadr->sin;
+ NTOHL_FP(&pkt->org, &p_org);
+ NTOHL_FP(&pkt->rec, &p_rec);
+ NTOHL_FP(&pkt->xmt, &p_xmt);
+
+ /*
+ * Authentication is conditioned by three switches:
+ *
+ * NOPEER (RES_NOPEER) do not mobilize an association unless
+ * authenticated
+ * NOTRUST (RES_DONTTRUST) do not allow access unless
+ * authenticated (implies NOPEER)
+ * enable (sys_authenticate) master NOPEER switch, by default
+ * on
+ *
+ * The NOPEER and NOTRUST can be specified on a per-client basis
+ * using the restrict command. The enable switch if on implies
+ * NOPEER for all clients. There are four outcomes:
+ *
+ * NONE The packet has no MAC.
+ * OK the packet has a MAC and authentication succeeds
+ * ERROR the packet has a MAC and authentication fails
+ * CRYPTO crypto-NAK. The MAC has four octets only.
+ *
+ * Note: The AUTH(x, y) macro is used to filter outcomes. If x
+ * is zero, acceptable outcomes of y are NONE and OK. If x is
+ * one, the only acceptable outcome of y is OK.
+ */
+
+ if (has_mac == 0) {
+ restrict_mask &= ~RES_MSSNTP;
+ is_authentic = AUTH_NONE; /* not required */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "receive: at %ld %s<-%s mode %d len %d\n",
+ current_time, stoa(dstadr_sin),
+ stoa(&rbufp->recv_srcadr), hismode,
+ authlen);
+#endif
+ } else if (has_mac == 4) {
+ restrict_mask &= ~RES_MSSNTP;
+ is_authentic = AUTH_CRYPTO; /* crypto-NAK */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "receive: at %ld %s<-%s mode %d keyid %08x len %d auth %d\n",
+ current_time, stoa(dstadr_sin),
+ stoa(&rbufp->recv_srcadr), hismode, skeyid,
+ authlen + has_mac, is_authentic);
+#endif
+
+#ifdef HAVE_NTP_SIGND
+ /*
+ * If the signature is 20 bytes long, the last 16 of
+ * which are zero, then this is a Microsoft client
+ * wanting AD-style authentication of the server's
+ * reply.
+ *
+ * This is described in Microsoft's WSPP docs, in MS-SNTP:
+ * http://msdn.microsoft.com/en-us/library/cc212930.aspx
+ */
+ } else if (has_mac == MAX_MD5_LEN && (restrict_mask & RES_MSSNTP) &&
+ (retcode == AM_FXMIT || retcode == AM_NEWPASS) &&
+ (memcmp(zero_key, (char *)pkt + authlen + 4, MAX_MD5_LEN - 4) ==
+ 0)) {
+ is_authentic = AUTH_NONE;
+#endif /* HAVE_NTP_SIGND */
+
+ } else {
+ restrict_mask &= ~RES_MSSNTP;
+#ifdef AUTOKEY
+ /*
+ * For autokey modes, generate the session key
+ * and install in the key cache. Use the socket
+ * broadcast or unicast address as appropriate.
+ */
+ if (crypto_flags && skeyid > NTP_MAXKEY) {
+
+ /*
+ * More on the autokey dance (AKD). A cookie is
+ * constructed from public and private values.
+ * For broadcast packets, the cookie is public
+ * (zero). For packets that match no
+ * association, the cookie is hashed from the
+ * addresses and private value. For server
+ * packets, the cookie was previously obtained
+ * from the server. For symmetric modes, the
+ * cookie was previously constructed using an
+ * agreement protocol; however, should PKI be
+ * unavailable, we construct a fake agreement as
+ * the EXOR of the peer and host cookies.
+ *
+ * hismode ephemeral persistent
+ * =======================================
+ * active 0 cookie#
+ * passive 0% cookie#
+ * client sys cookie 0%
+ * server 0% sys cookie
+ * broadcast 0 0
+ *
+ * # if unsync, 0
+ * % can't happen
+ */
+ if (has_mac < MAX_MD5_LEN) {
+ sys_badauth++;
+ return;
+ }
+ if (hismode == MODE_BROADCAST) {
+
+ /*
+ * For broadcaster, use the interface
+ * broadcast address when available;
+ * otherwise, use the unicast address
+ * found when the association was
+ * mobilized. However, if this is from
+ * the wildcard interface, game over.
+ */
+ if (crypto_flags && rbufp->dstadr ==
+ ANY_INTERFACE_CHOOSE(&rbufp->recv_srcadr)) {
+ sys_restricted++;
+ return; /* no wildcard */
+ }
+ pkeyid = 0;
+ if (!SOCK_UNSPEC(&rbufp->dstadr->bcast))
+ dstadr_sin =
+ &rbufp->dstadr->bcast;
+ } else if (peer == NULL) {
+ pkeyid = session_key(
+ &rbufp->recv_srcadr, dstadr_sin, 0,
+ sys_private, 0);
+ } else {
+ pkeyid = peer->pcookie;
+ }
+
+ /*
+ * The session key includes both the public
+ * values and cookie. In case of an extension
+ * field, the cookie used for authentication
+ * purposes is zero. Note the hash is saved for
+ * use later in the autokey mambo.
+ */
+ if (authlen > LEN_PKT_NOMAC && pkeyid != 0) {
+ session_key(&rbufp->recv_srcadr,
+ dstadr_sin, skeyid, 0, 2);
+ tkeyid = session_key(
+ &rbufp->recv_srcadr, dstadr_sin,
+ skeyid, pkeyid, 0);
+ } else {
+ tkeyid = session_key(
+ &rbufp->recv_srcadr, dstadr_sin,
+ skeyid, pkeyid, 2);
+ }
+
+ }
+#endif /* AUTOKEY */
+
+ /*
+ * Compute the cryptosum. Note a clogging attack may
+ * succeed in bloating the key cache. If an autokey,
+ * purge it immediately, since we won't be needing it
+ * again. If the packet is authentic, it can mobilize an
+ * association. Note that there is no key zero.
+ */
+ if (!authdecrypt(skeyid, (u_int32 *)pkt, authlen,
+ has_mac))
+ is_authentic = AUTH_ERROR;
+ else
+ is_authentic = AUTH_OK;
+#ifdef AUTOKEY
+ if (crypto_flags && skeyid > NTP_MAXKEY)
+ authtrust(skeyid, 0);
+#endif /* AUTOKEY */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "receive: at %ld %s<-%s mode %d keyid %08x len %d auth %d\n",
+ current_time, stoa(dstadr_sin),
+ stoa(&rbufp->recv_srcadr), hismode, skeyid,
+ authlen + has_mac, is_authentic);
+#endif
+ }
+
+ /*
+ * The association matching rules are implemented by a set of
+ * routines and an association table. A packet matching an
+ * association is processed by the peer process for that
+ * association. If there are no errors, an ephemeral association
+ * is mobilized: a broadcast packet mobilizes a broadcast client
+ * aassociation; a manycast server packet mobilizes a manycast
+ * client association; a symmetric active packet mobilizes a
+ * symmetric passive association.
+ */
+ switch (retcode) {
+
+ /*
+ * This is a client mode packet not matching any association. If
+ * an ordinary client, simply toss a server mode packet back
+ * over the fence. If a manycast client, we have to work a
+ * little harder.
+ */
+ case AM_FXMIT:
+
+ /*
+ * If authentication OK, send a server reply; otherwise,
+ * send a crypto-NAK.
+ */
+ if (!(rbufp->dstadr->flags & INT_MCASTOPEN)) {
+ if (AUTH(restrict_mask & RES_DONTTRUST,
+ is_authentic)) {
+ fast_xmit(rbufp, MODE_SERVER, skeyid,
+ restrict_mask);
+ } else if (is_authentic == AUTH_ERROR) {
+ fast_xmit(rbufp, MODE_SERVER, 0,
+ restrict_mask);
+ sys_badauth++;
+ } else {
+ sys_restricted++;
+ }
+ return; /* hooray */
+ }
+
+ /*
+ * This must be manycast. Do not respond if not
+ * configured as a manycast server.
+ */
+ if (!sys_manycastserver) {
+ sys_restricted++;
+ return; /* not enabled */
+ }
+
+#ifdef AUTOKEY
+ /*
+ * Do not respond if not the same group.
+ */
+ if (group_test(groupname, NULL)) {
+ sys_declined++;
+ return;
+ }
+#endif /* AUTOKEY */
+
+ /*
+ * Do not respond if we are not synchronized or our
+ * stratum is greater than the manycaster or the
+ * manycaster has already synchronized to us.
+ */
+ if (sys_leap == LEAP_NOTINSYNC || sys_stratum >=
+ hisstratum || (!sys_cohort && sys_stratum ==
+ hisstratum + 1) || rbufp->dstadr->addr_refid ==
+ pkt->refid) {
+ sys_declined++;
+ return; /* no help */
+ }
+
+ /*
+ * Respond only if authentication succeeds. Don't do a
+ * crypto-NAK, as that would not be useful.
+ */
+ if (AUTH(restrict_mask & RES_DONTTRUST, is_authentic))
+ fast_xmit(rbufp, MODE_SERVER, skeyid,
+ restrict_mask);
+ return; /* hooray */
+
+ /*
+ * This is a server mode packet returned in response to a client
+ * mode packet sent to a multicast group address (for
+ * manycastclient) or to a unicast address (for pool). The
+ * origin timestamp is a good nonce to reliably associate the
+ * reply with what was sent. If there is no match, that's
+ * curious and could be an intruder attempting to clog, so we
+ * just ignore it.
+ *
+ * If the packet is authentic and the manycastclient or pool
+ * association is found, we mobilize a client association and
+ * copy pertinent variables from the manycastclient or pool
+ * association to the new client association. If not, just
+ * ignore the packet.
+ *
+ * There is an implosion hazard at the manycast client, since
+ * the manycast servers send the server packet immediately. If
+ * the guy is already here, don't fire up a duplicate.
+ */
+ case AM_MANYCAST:
+
+#ifdef AUTOKEY
+ /*
+ * Do not respond if not the same group.
+ */
+ if (group_test(groupname, NULL)) {
+ sys_declined++;
+ return;
+ }
+#endif /* AUTOKEY */
+ if ((peer2 = findmanycastpeer(rbufp)) == NULL) {
+ sys_restricted++;
+ return; /* not enabled */
+ }
+ if (!AUTH((!(peer2->cast_flags & MDF_POOL) &&
+ sys_authenticate) | (restrict_mask & (RES_NOPEER |
+ RES_DONTTRUST)), is_authentic)) {
+ sys_restricted++;
+ return; /* access denied */
+ }
+
+ /*
+ * Do not respond if unsynchronized or stratum is below
+ * the floor or at or above the ceiling.
+ */
+ if (hisleap == LEAP_NOTINSYNC || hisstratum <
+ sys_floor || hisstratum >= sys_ceiling) {
+ sys_declined++;
+ return; /* no help */
+ }
+ peer = newpeer(&rbufp->recv_srcadr, NULL, rbufp->dstadr,
+ MODE_CLIENT, hisversion, peer2->minpoll,
+ peer2->maxpoll, FLAG_PREEMPT |
+ (FLAG_IBURST & peer2->flags), MDF_UCAST |
+ MDF_UCLNT, 0, skeyid, sys_ident);
+ if (NULL == peer) {
+ sys_declined++;
+ return; /* ignore duplicate */
+ }
+
+ /*
+ * After each ephemeral pool association is spun,
+ * accelerate the next poll for the pool solicitor so
+ * the pool will fill promptly.
+ */
+ if (peer2->cast_flags & MDF_POOL)
+ peer2->nextdate = current_time + 1;
+
+ /*
+ * Further processing of the solicitation response would
+ * simply detect its origin timestamp as bogus for the
+ * brand-new association (it matches the prototype
+ * association) and tinker with peer->nextdate delaying
+ * first sync.
+ */
+ return; /* solicitation response handled */
+
+ /*
+ * This is the first packet received from a broadcast server. If
+ * the packet is authentic and we are enabled as broadcast
+ * client, mobilize a broadcast client association. We don't
+ * kiss any frogs here.
+ */
+ case AM_NEWBCL:
+
+#ifdef AUTOKEY
+ /*
+ * Do not respond if not the same group.
+ */
+ if (group_test(groupname, sys_ident)) {
+ sys_declined++;
+ return;
+ }
+#endif /* AUTOKEY */
+ if (sys_bclient == 0) {
+ sys_restricted++;
+ return; /* not enabled */
+ }
+ if (!AUTH(sys_authenticate | (restrict_mask &
+ (RES_NOPEER | RES_DONTTRUST)), is_authentic)) {
+ sys_restricted++;
+ return; /* access denied */
+ }
+
+ /*
+ * Do not respond if unsynchronized or stratum is below
+ * the floor or at or above the ceiling.
+ */
+ if (hisleap == LEAP_NOTINSYNC || hisstratum <
+ sys_floor || hisstratum >= sys_ceiling) {
+ sys_declined++;
+ return; /* no help */
+ }
+
+#ifdef AUTOKEY
+ /*
+ * Do not respond if Autokey and the opcode is not a
+ * CRYPTO_ASSOC response with association ID.
+ */
+ if (crypto_flags && skeyid > NTP_MAXKEY && (opcode &
+ 0xffff0000) != (CRYPTO_ASSOC | CRYPTO_RESP)) {
+ sys_declined++;
+ return; /* protocol error */
+ }
+#endif /* AUTOKEY */
+
+ /*
+ * Broadcasts received via a multicast address may
+ * arrive after a unicast volley has begun
+ * with the same remote address. newpeer() will not
+ * find duplicate associations on other local endpoints
+ * if a non-NULL endpoint is supplied. multicastclient
+ * ephemeral associations are unique across all local
+ * endpoints.
+ */
+ if (!(INT_MCASTOPEN & rbufp->dstadr->flags))
+ match_ep = rbufp->dstadr;
+ else
+ match_ep = NULL;
+
+ /*
+ * Determine whether to execute the initial volley.
+ */
+ if (sys_bdelay != 0) {
+#ifdef AUTOKEY
+ /*
+ * If a two-way exchange is not possible,
+ * neither is Autokey.
+ */
+ if (crypto_flags && skeyid > NTP_MAXKEY) {
+ sys_restricted++;
+ return; /* no autokey */
+ }
+#endif /* AUTOKEY */
+
+ /*
+ * Do not execute the volley. Start out in
+ * broadcast client mode.
+ */
+ peer = newpeer(&rbufp->recv_srcadr, NULL,
+ match_ep, MODE_BCLIENT, hisversion,
+ pkt->ppoll, pkt->ppoll, FLAG_PREEMPT,
+ MDF_BCLNT, 0, skeyid, sys_ident);
+ if (NULL == peer) {
+ sys_restricted++;
+ return; /* ignore duplicate */
+
+ } else {
+ peer->delay = sys_bdelay;
+ }
+ break;
+ }
+
+ /*
+ * Execute the initial volley in order to calibrate the
+ * propagation delay and run the Autokey protocol.
+ *
+ * Note that the minpoll is taken from the broadcast
+ * packet, normally 6 (64 s) and that the poll interval
+ * is fixed at this value.
+ */
+ peer = newpeer(&rbufp->recv_srcadr, NULL, match_ep,
+ MODE_CLIENT, hisversion, pkt->ppoll, pkt->ppoll,
+ FLAG_BC_VOL | FLAG_IBURST | FLAG_PREEMPT, MDF_BCLNT,
+ 0, skeyid, sys_ident);
+ if (NULL == peer) {
+ sys_restricted++;
+ return; /* ignore duplicate */
+ }
+#ifdef AUTOKEY
+ if (skeyid > NTP_MAXKEY)
+ crypto_recv(peer, rbufp);
+#endif /* AUTOKEY */
+
+ return; /* hooray */
+
+ /*
+ * This is the first packet received from a symmetric active
+ * peer. If the packet is authentic and the first he sent,
+ * mobilize a passive association. If not, kiss the frog.
+ */
+ case AM_NEWPASS:
+
+#ifdef AUTOKEY
+ /*
+ * Do not respond if not the same group.
+ */
+ if (group_test(groupname, sys_ident)) {
+ sys_declined++;
+ return;
+ }
+#endif /* AUTOKEY */
+ if (!AUTH(sys_authenticate | (restrict_mask &
+ (RES_NOPEER | RES_DONTTRUST)), is_authentic)) {
+
+ /*
+ * If authenticated but cannot mobilize an
+ * association, send a symmetric passive
+ * response without mobilizing an association.
+ * This is for drat broken Windows clients. See
+ * Microsoft KB 875424 for preferred workaround.
+ */
+ if (AUTH(restrict_mask & RES_DONTTRUST,
+ is_authentic)) {
+ fast_xmit(rbufp, MODE_PASSIVE, skeyid,
+ restrict_mask);
+ return; /* hooray */
+ }
+ if (is_authentic == AUTH_ERROR) {
+ fast_xmit(rbufp, MODE_ACTIVE, 0,
+ restrict_mask);
+ sys_restricted++;
+ }
+ }
+
+ /*
+ * Do not respond if synchronized and if stratum is
+ * below the floor or at or above the ceiling. Note,
+ * this allows an unsynchronized peer to synchronize to
+ * us. It would be very strange if he did and then was
+ * nipped, but that could only happen if we were
+ * operating at the top end of the range. It also means
+ * we will spin an ephemeral association in response to
+ * MODE_ACTIVE KoDs, which will time out eventually.
+ */
+ if (hisleap != LEAP_NOTINSYNC && (hisstratum <
+ sys_floor || hisstratum >= sys_ceiling)) {
+ sys_declined++;
+ return; /* no help */
+ }
+
+ /*
+ * The message is correctly authenticated and allowed.
+ * Mobilize a symmetric passive association.
+ */
+ if ((peer = newpeer(&rbufp->recv_srcadr, NULL,
+ rbufp->dstadr, MODE_PASSIVE, hisversion, pkt->ppoll,
+ NTP_MAXDPOLL, 0, MDF_UCAST, 0, skeyid,
+ sys_ident)) == NULL) {
+ sys_declined++;
+ return; /* ignore duplicate */
+ }
+ break;
+
+
+ /*
+ * Process regular packet. Nothing special.
+ */
+ case AM_PROCPKT:
+
+#ifdef AUTOKEY
+ /*
+ * Do not respond if not the same group.
+ */
+ if (group_test(groupname, peer->ident)) {
+ sys_declined++;
+ return;
+ }
+#endif /* AUTOKEY */
+ break;
+
+ /*
+ * A passive packet matches a passive association. This is
+ * usually the result of reconfiguring a client on the fly. As
+ * this association might be legitimate and this packet an
+ * attempt to deny service, just ignore it.
+ */
+ case AM_ERR:
+ sys_declined++;
+ return;
+
+ /*
+ * For everything else there is the bit bucket.
+ */
+ default:
+ sys_declined++;
+ return;
+ }
+
+#ifdef AUTOKEY
+ /*
+ * If the association is configured for Autokey, the packet must
+ * have a public key ID; if not, the packet must have a
+ * symmetric key ID.
+ */
+ if (is_authentic != AUTH_CRYPTO && (((peer->flags &
+ FLAG_SKEY) && skeyid <= NTP_MAXKEY) || (!(peer->flags &
+ FLAG_SKEY) && skeyid > NTP_MAXKEY))) {
+ sys_badauth++;
+ return;
+ }
+#endif /* AUTOKEY */
+ peer->received++;
+ peer->flash &= ~PKT_TEST_MASK;
+ if (peer->flags & FLAG_XBOGUS) {
+ peer->flags &= ~FLAG_XBOGUS;
+ peer->flash |= TEST3;
+ }
+
+ /*
+ * Next comes a rigorous schedule of timestamp checking. If the
+ * transmit timestamp is zero, the server has not initialized in
+ * interleaved modes or is horribly broken.
+ */
+ if (L_ISZERO(&p_xmt)) {
+ peer->flash |= TEST3; /* unsynch */
+
+ /*
+ * If the transmit timestamp duplicates a previous one, the
+ * packet is a replay. This prevents the bad guys from replaying
+ * the most recent packet, authenticated or not.
+ */
+ } else if (L_ISEQU(&peer->xmt, &p_xmt)) {
+ peer->flash |= TEST1; /* duplicate */
+ peer->oldpkt++;
+ return;
+
+ /*
+ * If this is a broadcast mode packet, skip further checking. If
+ * an initial volley, bail out now and let the client do its
+ * stuff. If the origin timestamp is nonzero, this is an
+ * interleaved broadcast. so restart the protocol.
+ */
+ } else if (hismode == MODE_BROADCAST) {
+ if (!L_ISZERO(&p_org) && !(peer->flags & FLAG_XB)) {
+ peer->flags |= FLAG_XB;
+ peer->aorg = p_xmt;
+ peer->borg = rbufp->recv_time;
+ report_event(PEVNT_XLEAVE, peer, NULL);
+ return;
+ }
+
+ /*
+ * Check for bogus packet in basic mode. If found, switch to
+ * interleaved mode and resynchronize, but only after confirming
+ * the packet is not bogus in symmetric interleaved mode.
+ */
+ } else if (peer->flip == 0) {
+ if (!L_ISEQU(&p_org, &peer->aorg)) {
+ peer->bogusorg++;
+ peer->flash |= TEST2; /* bogus */
+ if (!L_ISZERO(&peer->dst) && L_ISEQU(&p_org,
+ &peer->dst)) {
+ peer->flip = 1;
+ report_event(PEVNT_XLEAVE, peer, NULL);
+ }
+ } else {
+ L_CLR(&peer->aorg);
+ }
+
+ /*
+ * Check for valid nonzero timestamp fields.
+ */
+ } else if (L_ISZERO(&p_org) || L_ISZERO(&p_rec) ||
+ L_ISZERO(&peer->dst)) {
+ peer->flash |= TEST3; /* unsynch */
+
+ /*
+ * Check for bogus packet in interleaved symmetric mode. This
+ * can happen if a packet is lost, duplicated or crossed. If
+ * found, flip and resynchronize.
+ */
+ } else if (!L_ISZERO(&peer->dst) && !L_ISEQU(&p_org,
+ &peer->dst)) {
+ peer->bogusorg++;
+ peer->flags |= FLAG_XBOGUS;
+ peer->flash |= TEST2; /* bogus */
+ }
+
+ /*
+ * Update the state variables.
+ */
+ if (peer->flip == 0) {
+ if (hismode != MODE_BROADCAST)
+ peer->rec = p_xmt;
+ peer->dst = rbufp->recv_time;
+ }
+ peer->xmt = p_xmt;
+
+ /*
+ * If this is a crypto_NAK, the server cannot authenticate a
+ * client packet. The server might have just changed keys. Clear
+ * the association and restart the protocol.
+ */
+ if (is_authentic == AUTH_CRYPTO) {
+ report_event(PEVNT_AUTH, peer, "crypto_NAK");
+ peer->flash |= TEST5; /* bad auth */
+ peer->badauth++;
+ if (peer->flags & FLAG_PREEMPT) {
+ unpeer(peer);
+ return;
+ }
+#ifdef AUTOKEY
+ if (peer->crypto)
+ peer_clear(peer, "AUTH");
+#endif /* AUTOKEY */
+ return;
+
+ /*
+ * If the digest fails, the client cannot authenticate a server
+ * reply to a client packet previously sent. The loopback check
+ * is designed to avoid a bait-and-switch attack, which was
+ * possible in past versions. If symmetric modes, return a
+ * crypto-NAK. The peer should restart the protocol.
+ */
+ } else if (!AUTH(has_mac || (restrict_mask & RES_DONTTRUST),
+ is_authentic)) {
+ report_event(PEVNT_AUTH, peer, "digest");
+ peer->flash |= TEST5; /* bad auth */
+ peer->badauth++;
+ if (hismode == MODE_ACTIVE || hismode == MODE_PASSIVE)
+ fast_xmit(rbufp, MODE_ACTIVE, 0, restrict_mask);
+ if (peer->flags & FLAG_PREEMPT) {
+ unpeer(peer);
+ return;
+ }
+#ifdef AUTOKEY
+ if (peer->crypto)
+ peer_clear(peer, "AUTH");
+#endif /* AUTOKEY */
+ return;
+ }
+
+ /*
+ * Set the peer ppoll to the maximum of the packet ppoll and the
+ * peer minpoll. If a kiss-o'-death, set the peer minpoll to
+ * this maximum and advance the headway to give the sender some
+ * headroom. Very intricate.
+ */
+ peer->ppoll = max(peer->minpoll, pkt->ppoll);
+ if (hismode == MODE_SERVER && hisleap == LEAP_NOTINSYNC &&
+ hisstratum == STRATUM_UNSPEC && memcmp(&pkt->refid,
+ "RATE", 4) == 0) {
+ peer->selbroken++;
+ report_event(PEVNT_RATE, peer, NULL);
+ if (pkt->ppoll > peer->minpoll)
+ peer->minpoll = peer->ppoll;
+ peer->burst = peer->retry = 0;
+ peer->throttle = (NTP_SHIFT + 1) * (1 << peer->minpoll);
+ poll_update(peer, pkt->ppoll);
+ return; /* kiss-o'-death */
+ }
+
+ /*
+ * That was hard and I am sweaty, but the packet is squeaky
+ * clean. Get on with real work.
+ */
+ peer->timereceived = current_time;
+ if (is_authentic == AUTH_OK)
+ peer->flags |= FLAG_AUTHENTIC;
+ else
+ peer->flags &= ~FLAG_AUTHENTIC;
+
+#ifdef AUTOKEY
+ /*
+ * More autokey dance. The rules of the cha-cha are as follows:
+ *
+ * 1. If there is no key or the key is not auto, do nothing.
+ *
+ * 2. If this packet is in response to the one just previously
+ * sent or from a broadcast server, do the extension fields.
+ * Otherwise, assume bogosity and bail out.
+ *
+ * 3. If an extension field contains a verified signature, it is
+ * self-authenticated and we sit the dance.
+ *
+ * 4. If this is a server reply, check only to see that the
+ * transmitted key ID matches the received key ID.
+ *
+ * 5. Check to see that one or more hashes of the current key ID
+ * matches the previous key ID or ultimate original key ID
+ * obtained from the broadcaster or symmetric peer. If no
+ * match, sit the dance and call for new autokey values.
+ *
+ * In case of crypto error, fire the orchestra, stop dancing and
+ * restart the protocol.
+ */
+ if (peer->flags & FLAG_SKEY) {
+ /*
+ * Decrement remaining autokey hashes. This isn't
+ * perfect if a packet is lost, but results in no harm.
+ */
+ ap = (struct autokey *)peer->recval.ptr;
+ if (ap != NULL) {
+ if (ap->seq > 0)
+ ap->seq--;
+ }
+ peer->flash |= TEST8;
+ rval = crypto_recv(peer, rbufp);
+ if (rval == XEVNT_OK) {
+ peer->unreach = 0;
+ } else {
+ if (rval == XEVNT_ERR) {
+ report_event(PEVNT_RESTART, peer,
+ "crypto error");
+ peer_clear(peer, "CRYP");
+ peer->flash |= TEST9; /* bad crypt */
+ if (peer->flags & FLAG_PREEMPT)
+ unpeer(peer);
+ }
+ return;
+ }
+
+ /*
+ * If server mode, verify the receive key ID matches
+ * the transmit key ID.
+ */
+ if (hismode == MODE_SERVER) {
+ if (skeyid == peer->keyid)
+ peer->flash &= ~TEST8;
+
+ /*
+ * If an extension field is present, verify only that it
+ * has been correctly signed. We don't need a sequence
+ * check here, but the sequence continues.
+ */
+ } else if (!(peer->flash & TEST8)) {
+ peer->pkeyid = skeyid;
+
+ /*
+ * Now the fun part. Here, skeyid is the current ID in
+ * the packet, pkeyid is the ID in the last packet and
+ * tkeyid is the hash of skeyid. If the autokey values
+ * have not been received, this is an automatic error.
+ * If so, check that the tkeyid matches pkeyid. If not,
+ * hash tkeyid and try again. If the number of hashes
+ * exceeds the number remaining in the sequence, declare
+ * a successful failure and refresh the autokey values.
+ */
+ } else if (ap != NULL) {
+ int i;
+
+ for (i = 0; ; i++) {
+ if (tkeyid == peer->pkeyid ||
+ tkeyid == ap->key) {
+ peer->flash &= ~TEST8;
+ peer->pkeyid = skeyid;
+ ap->seq -= i;
+ break;
+ }
+ if (i > ap->seq) {
+ peer->crypto &=
+ ~CRYPTO_FLAG_AUTO;
+ break;
+ }
+ tkeyid = session_key(
+ &rbufp->recv_srcadr, dstadr_sin,
+ tkeyid, pkeyid, 0);
+ }
+ if (peer->flash & TEST8)
+ report_event(PEVNT_AUTH, peer, "keylist");
+ }
+ if (!(peer->crypto & CRYPTO_FLAG_PROV)) /* test 9 */
+ peer->flash |= TEST8; /* bad autokey */
+
+ /*
+ * The maximum lifetime of the protocol is about one
+ * week before restarting the Autokey protocol to
+ * refresh certificates and leapseconds values.
+ */
+ if (current_time > peer->refresh) {
+ report_event(PEVNT_RESTART, peer,
+ "crypto refresh");
+ peer_clear(peer, "TIME");
+ return;
+ }
+ }
+#endif /* AUTOKEY */
+
+ /*
+ * The dance is complete and the flash bits have been lit. Toss
+ * the packet over the fence for processing, which may light up
+ * more flashers.
+ */
+ process_packet(peer, pkt, rbufp->recv_length);
+
+ /*
+ * In interleaved mode update the state variables. Also adjust the
+ * transmit phase to avoid crossover.
+ */
+ if (peer->flip != 0) {
+ peer->rec = p_rec;
+ peer->dst = rbufp->recv_time;
+ if (peer->nextdate - current_time < (1U << min(peer->ppoll,
+ peer->hpoll)) / 2)
+ peer->nextdate++;
+ else
+ peer->nextdate--;
+ }
+}
+
+
+/*
+ * process_packet - Packet Procedure, a la Section 3.4.4 of the
+ * specification. Or almost, at least. If we're in here we have a
+ * reasonable expectation that we will be having a long term
+ * relationship with this host.
+ */
+void
+process_packet(
+ register struct peer *peer,
+ register struct pkt *pkt,
+ u_int len
+ )
+{
+ double t34, t21;
+ double p_offset, p_del, p_disp;
+ l_fp p_rec, p_xmt, p_org, p_reftime, ci;
+ u_char pmode, pleap, pversion, pstratum;
+ char statstr[NTP_MAXSTRLEN];
+#ifdef ASSYM
+ int itemp;
+ double etemp, ftemp, td;
+#endif /* ASSYM */
+
+ sys_processed++;
+ peer->processed++;
+ p_del = FPTOD(NTOHS_FP(pkt->rootdelay));
+ p_offset = 0;
+ p_disp = FPTOD(NTOHS_FP(pkt->rootdisp));
+ NTOHL_FP(&pkt->reftime, &p_reftime);
+ NTOHL_FP(&pkt->org, &p_org);
+ NTOHL_FP(&pkt->rec, &p_rec);
+ NTOHL_FP(&pkt->xmt, &p_xmt);
+ pmode = PKT_MODE(pkt->li_vn_mode);
+ pleap = PKT_LEAP(pkt->li_vn_mode);
+ pversion = PKT_VERSION(pkt->li_vn_mode);
+ pstratum = PKT_TO_STRATUM(pkt->stratum);
+
+ /*
+ * Capture the header values in the client/peer association..
+ */
+ record_raw_stats(&peer->srcadr, peer->dstadr ?
+ &peer->dstadr->sin : NULL,
+ &p_org, &p_rec, &p_xmt, &peer->dst,
+ pleap, pversion, pmode, pstratum, pkt->ppoll, pkt->precision,
+ p_del, p_disp, pkt->refid);
+ peer->leap = pleap;
+ peer->stratum = min(pstratum, STRATUM_UNSPEC);
+ peer->pmode = pmode;
+ peer->precision = pkt->precision;
+ peer->rootdelay = p_del;
+ peer->rootdisp = p_disp;
+ peer->refid = pkt->refid; /* network byte order */
+ peer->reftime = p_reftime;
+
+ /*
+ * First, if either burst mode is armed, enable the burst.
+ * Compute the headway for the next packet and delay if
+ * necessary to avoid exceeding the threshold.
+ */
+ if (peer->retry > 0) {
+ peer->retry = 0;
+ if (peer->reach)
+ peer->burst = min(1 << (peer->hpoll -
+ peer->minpoll), NTP_SHIFT) - 1;
+ else
+ peer->burst = NTP_IBURST - 1;
+ if (peer->burst > 0)
+ peer->nextdate = current_time;
+ }
+ poll_update(peer, peer->hpoll);
+
+ /*
+ * Verify the server is synchronized; that is, the leap bits,
+ * stratum and root distance are valid.
+ */
+ if (pleap == LEAP_NOTINSYNC || /* test 6 */
+ pstratum < sys_floor || pstratum >= sys_ceiling)
+ peer->flash |= TEST6; /* bad synch or strat */
+ if (p_del / 2 + p_disp >= MAXDISPERSE) /* test 7 */
+ peer->flash |= TEST7; /* bad header */
+
+ /*
+ * If any tests fail at this point, the packet is discarded.
+ * Note that some flashers may have already been set in the
+ * receive() routine.
+ */
+ if (peer->flash & PKT_TEST_MASK) {
+ peer->seldisptoolarge++;
+#ifdef DEBUG
+ if (debug)
+ printf("packet: flash header %04x\n",
+ peer->flash);
+#endif
+ return;
+ }
+
+ /*
+ * If the peer was previously unreachable, raise a trap. In any
+ * case, mark it reachable.
+ */
+ if (!peer->reach) {
+ report_event(PEVNT_REACH, peer, NULL);
+ peer->timereachable = current_time;
+ }
+ peer->reach |= 1;
+
+ /*
+ * For a client/server association, calculate the clock offset,
+ * roundtrip delay and dispersion. The equations are reordered
+ * from the spec for more efficient use of temporaries. For a
+ * broadcast association, offset the last measurement by the
+ * computed delay during the client/server volley. Note the
+ * computation of dispersion includes the system precision plus
+ * that due to the frequency error since the origin time.
+ *
+ * It is very important to respect the hazards of overflow. The
+ * only permitted operation on raw timestamps is subtraction,
+ * where the result is a signed quantity spanning from 68 years
+ * in the past to 68 years in the future. To avoid loss of
+ * precision, these calculations are done using 64-bit integer
+ * arithmetic. However, the offset and delay calculations are
+ * sums and differences of these first-order differences, which
+ * if done using 64-bit integer arithmetic, would be valid over
+ * only half that span. Since the typical first-order
+ * differences are usually very small, they are converted to 64-
+ * bit doubles and all remaining calculations done in floating-
+ * double arithmetic. This preserves the accuracy while
+ * retaining the 68-year span.
+ *
+ * There are three interleaving schemes, basic, interleaved
+ * symmetric and interleaved broadcast. The timestamps are
+ * idioscyncratically different. See the onwire briefing/white
+ * paper at www.eecis.udel.edu/~mills for details.
+ *
+ * Interleaved symmetric mode
+ * t1 = peer->aorg/borg, t2 = peer->rec, t3 = p_xmt,
+ * t4 = peer->dst
+ */
+ if (peer->flip != 0) {
+ ci = p_xmt; /* t3 - t4 */
+ L_SUB(&ci, &peer->dst);
+ LFPTOD(&ci, t34);
+ ci = p_rec; /* t2 - t1 */
+ if (peer->flip > 0)
+ L_SUB(&ci, &peer->borg);
+ else
+ L_SUB(&ci, &peer->aorg);
+ LFPTOD(&ci, t21);
+ p_del = t21 - t34;
+ p_offset = (t21 + t34) / 2.;
+ if (p_del < 0 || p_del > 1.) {
+ snprintf(statstr, sizeof(statstr),
+ "t21 %.6f t34 %.6f", t21, t34);
+ report_event(PEVNT_XERR, peer, statstr);
+ return;
+ }
+
+ /*
+ * Broadcast modes
+ */
+ } else if (peer->pmode == MODE_BROADCAST) {
+
+ /*
+ * Interleaved broadcast mode. Use interleaved timestamps.
+ * t1 = peer->borg, t2 = p_org, t3 = p_org, t4 = aorg
+ */
+ if (peer->flags & FLAG_XB) {
+ ci = p_org; /* delay */
+ L_SUB(&ci, &peer->aorg);
+ LFPTOD(&ci, t34);
+ ci = p_org; /* t2 - t1 */
+ L_SUB(&ci, &peer->borg);
+ LFPTOD(&ci, t21);
+ peer->aorg = p_xmt;
+ peer->borg = peer->dst;
+ if (t34 < 0 || t34 > 1.) {
+ snprintf(statstr, sizeof(statstr),
+ "offset %.6f delay %.6f", t21, t34);
+ report_event(PEVNT_XERR, peer, statstr);
+ return;
+ }
+ p_offset = t21;
+ peer->xleave = t34;
+
+ /*
+ * Basic broadcast - use direct timestamps.
+ * t3 = p_xmt, t4 = peer->dst
+ */
+ } else {
+ ci = p_xmt; /* t3 - t4 */
+ L_SUB(&ci, &peer->dst);
+ LFPTOD(&ci, t34);
+ p_offset = t34;
+ }
+
+ /*
+ * When calibration is complete and the clock is
+ * synchronized, the bias is calculated as the difference
+ * between the unicast timestamp and the broadcast
+ * timestamp. This works for both basic and interleaved
+ * modes.
+ */
+ if (FLAG_BC_VOL & peer->flags) {
+ peer->flags &= ~FLAG_BC_VOL;
+ peer->delay = fabs(peer->offset - p_offset) * 2;
+ }
+ p_del = peer->delay;
+ p_offset += p_del / 2;
+
+
+ /*
+ * Basic mode, otherwise known as the old fashioned way.
+ *
+ * t1 = p_org, t2 = p_rec, t3 = p_xmt, t4 = peer->dst
+ */
+ } else {
+ ci = p_xmt; /* t3 - t4 */
+ L_SUB(&ci, &peer->dst);
+ LFPTOD(&ci, t34);
+ ci = p_rec; /* t2 - t1 */
+ L_SUB(&ci, &p_org);
+ LFPTOD(&ci, t21);
+ p_del = fabs(t21 - t34);
+ p_offset = (t21 + t34) / 2.;
+ }
+ p_del = max(p_del, LOGTOD(sys_precision));
+ p_disp = LOGTOD(sys_precision) + LOGTOD(peer->precision) +
+ clock_phi * p_del;
+
+#if ASSYM
+ /*
+ * This code calculates the outbound and inbound data rates by
+ * measuring the differences between timestamps at different
+ * packet lengths. This is helpful in cases of large asymmetric
+ * delays commonly experienced on deep space communication
+ * links.
+ */
+ if (peer->t21_last > 0 && peer->t34_bytes > 0) {
+ itemp = peer->t21_bytes - peer->t21_last;
+ if (itemp > 25) {
+ etemp = t21 - peer->t21;
+ if (fabs(etemp) > 1e-6) {
+ ftemp = itemp / etemp;
+ if (ftemp > 1000.)
+ peer->r21 = ftemp;
+ }
+ }
+ itemp = len - peer->t34_bytes;
+ if (itemp > 25) {
+ etemp = -t34 - peer->t34;
+ if (fabs(etemp) > 1e-6) {
+ ftemp = itemp / etemp;
+ if (ftemp > 1000.)
+ peer->r34 = ftemp;
+ }
+ }
+ }
+
+ /*
+ * The following section compensates for different data rates on
+ * the outbound (d21) and inbound (t34) directions. To do this,
+ * it finds t such that r21 * t - r34 * (d - t) = 0, where d is
+ * the roundtrip delay. Then it calculates the correction as a
+ * fraction of d.
+ */
+ peer->t21 = t21;
+ peer->t21_last = peer->t21_bytes;
+ peer->t34 = -t34;
+ peer->t34_bytes = len;
+#ifdef DEBUG
+ if (debug > 1)
+ printf("packet: t21 %.9lf %d t34 %.9lf %d\n", peer->t21,
+ peer->t21_bytes, peer->t34, peer->t34_bytes);
+#endif
+ if (peer->r21 > 0 && peer->r34 > 0 && p_del > 0) {
+ if (peer->pmode != MODE_BROADCAST)
+ td = (peer->r34 / (peer->r21 + peer->r34) -
+ .5) * p_del;
+ else
+ td = 0;
+
+ /*
+ * Unfortunately, in many cases the errors are
+ * unacceptable, so for the present the rates are not
+ * used. In future, we might find conditions where the
+ * calculations are useful, so this should be considered
+ * a work in progress.
+ */
+ t21 -= td;
+ t34 -= td;
+#ifdef DEBUG
+ if (debug > 1)
+ printf("packet: del %.6lf r21 %.1lf r34 %.1lf %.6lf\n",
+ p_del, peer->r21 / 1e3, peer->r34 / 1e3,
+ td);
+#endif
+ }
+#endif /* ASSYM */
+
+ /*
+ * That was awesome. Now hand off to the clock filter.
+ */
+ clock_filter(peer, p_offset + peer->bias, p_del, p_disp);
+
+ /*
+ * If we are in broadcast calibrate mode, return to broadcast
+ * client mode when the client is fit and the autokey dance is
+ * complete.
+ */
+ if ((FLAG_BC_VOL & peer->flags) && MODE_CLIENT == peer->hmode &&
+ !(TEST11 & peer_unfit(peer))) { /* distance exceeded */
+#ifdef AUTOKEY
+ if (peer->flags & FLAG_SKEY) {
+ if (!(~peer->crypto & CRYPTO_FLAG_ALL))
+ peer->hmode = MODE_BCLIENT;
+ } else {
+ peer->hmode = MODE_BCLIENT;
+ }
+#else /* !AUTOKEY follows */
+ peer->hmode = MODE_BCLIENT;
+#endif /* !AUTOKEY */
+ }
+}
+
+
+/*
+ * clock_update - Called at system process update intervals.
+ */
+static void
+clock_update(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ double dtemp;
+ l_fp now;
+#ifdef HAVE_LIBSCF_H
+ char *fmri;
+#endif /* HAVE_LIBSCF_H */
+
+ /*
+ * Update the system state variables. We do this very carefully,
+ * as the poll interval might need to be clamped differently.
+ */
+ sys_peer = peer;
+ sys_epoch = peer->epoch;
+ if (sys_poll < peer->minpoll)
+ sys_poll = peer->minpoll;
+ if (sys_poll > peer->maxpoll)
+ sys_poll = peer->maxpoll;
+ poll_update(peer, sys_poll);
+ sys_stratum = min(peer->stratum + 1, STRATUM_UNSPEC);
+ if (peer->stratum == STRATUM_REFCLOCK ||
+ peer->stratum == STRATUM_UNSPEC)
+ sys_refid = peer->refid;
+ else
+ sys_refid = addr2refid(&peer->srcadr);
+ /*
+ * Root Dispersion (E) is defined (in RFC 5905) as:
+ *
+ * E = p.epsilon_r + p.epsilon + p.psi + PHI*(s.t - p.t) + |THETA|
+ *
+ * where:
+ * p.epsilon_r is the PollProc's root dispersion
+ * p.epsilon is the PollProc's dispersion
+ * p.psi is the PollProc's jitter
+ * THETA is the combined offset
+ *
+ * NB: Think Hard about where these numbers come from and
+ * what they mean. When did peer->update happen? Has anything
+ * interesting happened since then? What values are the most
+ * defensible? Why?
+ *
+ * DLM thinks this equation is probably the best of all worse choices.
+ */
+ dtemp = peer->rootdisp
+ + peer->disp
+ + sys_jitter
+ + clock_phi * (current_time - peer->update)
+ + fabs(sys_offset);
+
+ if (dtemp > sys_mindisp)
+ sys_rootdisp = dtemp;
+ else
+ sys_rootdisp = sys_mindisp;
+ sys_rootdelay = peer->delay + peer->rootdelay;
+ sys_reftime = peer->dst;
+
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "clock_update: at %lu sample %lu associd %d\n",
+ current_time, peer->epoch, peer->associd);
+#endif
+
+ /*
+ * Comes now the moment of truth. Crank the clock discipline and
+ * see what comes out.
+ */
+ switch (local_clock(peer, sys_offset)) {
+
+ /*
+ * Clock exceeds panic threshold. Life as we know it ends.
+ */
+ case -1:
+#ifdef HAVE_LIBSCF_H
+ /*
+ * For Solaris enter the maintenance mode.
+ */
+ if ((fmri = getenv("SMF_FMRI")) != NULL) {
+ if (smf_maintain_instance(fmri, 0) < 0) {
+ printf("smf_maintain_instance: %s\n",
+ scf_strerror(scf_error()));
+ exit(1);
+ }
+ /*
+ * Sleep until SMF kills us.
+ */
+ for (;;)
+ pause();
+ }
+#endif /* HAVE_LIBSCF_H */
+ exit (-1);
+ /* not reached */
+
+ /*
+ * Clock was stepped. Flush all time values of all peers.
+ */
+ case 2:
+ clear_all();
+ sys_leap = LEAP_NOTINSYNC;
+ sys_stratum = STRATUM_UNSPEC;
+ memcpy(&sys_refid, "STEP", 4);
+ sys_rootdelay = 0;
+ sys_rootdisp = 0;
+ L_CLR(&sys_reftime);
+ sys_jitter = LOGTOD(sys_precision);
+ leapsec_reset_frame();
+ break;
+
+ /*
+ * Clock was slewed. Handle the leapsecond stuff.
+ */
+ case 1:
+
+ /*
+ * If this is the first time the clock is set, reset the
+ * leap bits. If crypto, the timer will goose the setup
+ * process.
+ */
+ if (sys_leap == LEAP_NOTINSYNC) {
+ sys_leap = LEAP_NOWARNING;
+#ifdef AUTOKEY
+ if (crypto_flags)
+ crypto_update();
+#endif /* AUTOKEY */
+ /*
+ * If our parent process is waiting for the
+ * first clock sync, send them home satisfied.
+ */
+#ifdef HAVE_WORKING_FORK
+ if (waitsync_fd_to_close != -1) {
+ close(waitsync_fd_to_close);
+ waitsync_fd_to_close = -1;
+ DPRINTF(1, ("notified parent --wait-sync is done\n"));
+ }
+#endif /* HAVE_WORKING_FORK */
+
+ }
+
+ /*
+ * If there is no leap second pending and the number of
+ * survivor leap bits is greater than half the number of
+ * survivors, try to schedule a leap for the end of the
+ * current month. (This only works if no leap second for
+ * that range is in the table, so doing this more than
+ * once is mostly harmless.)
+ */
+ if (leapsec == LSPROX_NOWARN) {
+ if (leap_vote_ins > leap_vote_del
+ && leap_vote_ins > sys_survivors / 2) {
+ get_systime(&now);
+ leapsec_add_dyn(TRUE, now.l_ui, NULL);
+ }
+ if (leap_vote_del > leap_vote_ins
+ && leap_vote_del > sys_survivors / 2) {
+ get_systime(&now);
+ leapsec_add_dyn(FALSE, now.l_ui, NULL);
+ }
+ }
+ break;
+
+ /*
+ * Popcorn spike or step threshold exceeded. Pretend it never
+ * happened.
+ */
+ default:
+ break;
+ }
+}
+
+
+/*
+ * poll_update - update peer poll interval
+ */
+void
+poll_update(
+ struct peer *peer, /* peer structure pointer */
+ u_char mpoll
+ )
+{
+ u_long next, utemp;
+ u_char hpoll;
+
+ /*
+ * This routine figures out when the next poll should be sent.
+ * That turns out to be wickedly complicated. One problem is
+ * that sometimes the time for the next poll is in the past when
+ * the poll interval is reduced. We watch out for races here
+ * between the receive process and the poll process.
+ *
+ * Clamp the poll interval between minpoll and maxpoll.
+ */
+ hpoll = max(min(peer->maxpoll, mpoll), peer->minpoll);
+
+#ifdef AUTOKEY
+ /*
+ * If during the crypto protocol the poll interval has changed,
+ * the lifetimes in the key list are probably bogus. Purge the
+ * the key list and regenerate it later.
+ */
+ if ((peer->flags & FLAG_SKEY) && hpoll != peer->hpoll)
+ key_expire(peer);
+#endif /* AUTOKEY */
+ peer->hpoll = hpoll;
+
+ /*
+ * There are three variables important for poll scheduling, the
+ * current time (current_time), next scheduled time (nextdate)
+ * and the earliest time (utemp). The earliest time is 2 s
+ * seconds, but could be more due to rate management. When
+ * sending in a burst, use the earliest time. When not in a
+ * burst but with a reply pending, send at the earliest time
+ * unless the next scheduled time has not advanced. This can
+ * only happen if multiple replies are pending in the same
+ * response interval. Otherwise, send at the later of the next
+ * scheduled time and the earliest time.
+ *
+ * Now we figure out if there is an override. If a burst is in
+ * progress and we get called from the receive process, just
+ * slink away. If called from the poll process, delay 1 s for a
+ * reference clock, otherwise 2 s.
+ */
+ utemp = current_time + max(peer->throttle - (NTP_SHIFT - 1) *
+ (1 << peer->minpoll), ntp_minpkt);
+ if (peer->burst > 0) {
+ if (peer->nextdate > current_time)
+ return;
+#ifdef REFCLOCK
+ else if (peer->flags & FLAG_REFCLOCK)
+ peer->nextdate = current_time + RESP_DELAY;
+#endif /* REFCLOCK */
+ else
+ peer->nextdate = utemp;
+
+#ifdef AUTOKEY
+ /*
+ * If a burst is not in progress and a crypto response message
+ * is pending, delay 2 s, but only if this is a new interval.
+ */
+ } else if (peer->cmmd != NULL) {
+ if (peer->nextdate > current_time) {
+ if (peer->nextdate + ntp_minpkt != utemp)
+ peer->nextdate = utemp;
+ } else {
+ peer->nextdate = utemp;
+ }
+#endif /* AUTOKEY */
+
+ /*
+ * The ordinary case. If a retry, use minpoll; if unreachable,
+ * use host poll; otherwise, use the minimum of host and peer
+ * polls; In other words, oversampling is okay but
+ * understampling is evil. Use the maximum of this value and the
+ * headway. If the average headway is greater than the headway
+ * threshold, increase the headway by the minimum interval.
+ */
+ } else {
+ if (peer->retry > 0)
+ hpoll = peer->minpoll;
+ else if (!(peer->reach))
+ hpoll = peer->hpoll;
+ else
+ hpoll = min(peer->ppoll, peer->hpoll);
+#ifdef REFCLOCK
+ if (peer->flags & FLAG_REFCLOCK)
+ next = 1 << hpoll;
+ else
+#endif /* REFCLOCK */
+ next = ((0x1000UL | (ntp_random() & 0x0ff)) <<
+ hpoll) >> 12;
+ next += peer->outdate;
+ if (next > utemp)
+ peer->nextdate = next;
+ else
+ peer->nextdate = utemp;
+ if (peer->throttle > (1 << peer->minpoll))
+ peer->nextdate += ntp_minpkt;
+ }
+ DPRINTF(2, ("poll_update: at %lu %s poll %d burst %d retry %d head %d early %lu next %lu\n",
+ current_time, ntoa(&peer->srcadr), peer->hpoll,
+ peer->burst, peer->retry, peer->throttle,
+ utemp - current_time, peer->nextdate -
+ current_time));
+}
+
+
+/*
+ * peer_clear - clear peer filter registers. See Section 3.4.8 of the
+ * spec.
+ */
+void
+peer_clear(
+ struct peer *peer, /* peer structure */
+ char *ident /* tally lights */
+ )
+{
+ u_char u;
+
+#ifdef AUTOKEY
+ /*
+ * If cryptographic credentials have been acquired, toss them to
+ * Valhalla. Note that autokeys are ephemeral, in that they are
+ * tossed immediately upon use. Therefore, the keylist can be
+ * purged anytime without needing to preserve random keys. Note
+ * that, if the peer is purged, the cryptographic variables are
+ * purged, too. This makes it much harder to sneak in some
+ * unauthenticated data in the clock filter.
+ */
+ key_expire(peer);
+ if (peer->iffval != NULL)
+ BN_free(peer->iffval);
+ value_free(&peer->cookval);
+ value_free(&peer->recval);
+ value_free(&peer->encrypt);
+ value_free(&peer->sndval);
+ if (peer->cmmd != NULL)
+ free(peer->cmmd);
+ if (peer->subject != NULL)
+ free(peer->subject);
+ if (peer->issuer != NULL)
+ free(peer->issuer);
+#endif /* AUTOKEY */
+
+ /*
+ * Clear all values, including the optional crypto values above.
+ */
+ memset(CLEAR_TO_ZERO(peer), 0, LEN_CLEAR_TO_ZERO(peer));
+ peer->ppoll = peer->maxpoll;
+ peer->hpoll = peer->minpoll;
+ peer->disp = MAXDISPERSE;
+ peer->flash = peer_unfit(peer);
+ peer->jitter = LOGTOD(sys_precision);
+
+ /*
+ * If interleave mode, initialize the alternate origin switch.
+ */
+ if (peer->flags & FLAG_XLEAVE)
+ peer->flip = 1;
+ for (u = 0; u < NTP_SHIFT; u++) {
+ peer->filter_order[u] = u;
+ peer->filter_disp[u] = MAXDISPERSE;
+ }
+#ifdef REFCLOCK
+ if (!(peer->flags & FLAG_REFCLOCK)) {
+#endif
+ peer->leap = LEAP_NOTINSYNC;
+ peer->stratum = STRATUM_UNSPEC;
+ memcpy(&peer->refid, ident, 4);
+#ifdef REFCLOCK
+ }
+#endif
+
+ /*
+ * During initialization use the association count to spread out
+ * the polls at one-second intervals. Passive associations'
+ * first poll is delayed by the "discard minimum" to avoid rate
+ * limiting. Other post-startup new or cleared associations
+ * randomize the first poll over the minimum poll interval to
+ * avoid implosion.
+ */
+ peer->nextdate = peer->update = peer->outdate = current_time;
+ if (initializing) {
+ peer->nextdate += peer_associations;
+ } else if (MODE_PASSIVE == peer->hmode) {
+ peer->nextdate += ntp_minpkt;
+ } else {
+ peer->nextdate += ntp_random() % peer->minpoll;
+ }
+#ifdef AUTOKEY
+ peer->refresh = current_time + (1 << NTP_REFRESH);
+#endif /* AUTOKEY */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "peer_clear: at %ld next %ld associd %d refid %s\n",
+ current_time, peer->nextdate, peer->associd,
+ ident);
+#endif
+}
+
+
+/*
+ * clock_filter - add incoming clock sample to filter register and run
+ * the filter procedure to find the best sample.
+ */
+void
+clock_filter(
+ struct peer *peer, /* peer structure pointer */
+ double sample_offset, /* clock offset */
+ double sample_delay, /* roundtrip delay */
+ double sample_disp /* dispersion */
+ )
+{
+ double dst[NTP_SHIFT]; /* distance vector */
+ int ord[NTP_SHIFT]; /* index vector */
+ int i, j, k, m;
+ double dtemp, etemp;
+ char tbuf[80];
+
+ /*
+ * A sample consists of the offset, delay, dispersion and epoch
+ * of arrival. The offset and delay are determined by the on-
+ * wire protocol. The dispersion grows from the last outbound
+ * packet to the arrival of this one increased by the sum of the
+ * peer precision and the system precision as required by the
+ * error budget. First, shift the new arrival into the shift
+ * register discarding the oldest one.
+ */
+ j = peer->filter_nextpt;
+ peer->filter_offset[j] = sample_offset;
+ peer->filter_delay[j] = sample_delay;
+ peer->filter_disp[j] = sample_disp;
+ peer->filter_epoch[j] = current_time;
+ j = (j + 1) % NTP_SHIFT;
+ peer->filter_nextpt = j;
+
+ /*
+ * Update dispersions since the last update and at the same
+ * time initialize the distance and index lists. Since samples
+ * become increasingly uncorrelated beyond the Allan intercept,
+ * only under exceptional cases will an older sample be used.
+ * Therefore, the distance list uses a compound metric. If the
+ * dispersion is greater than the maximum dispersion, clamp the
+ * distance at that value. If the time since the last update is
+ * less than the Allan intercept use the delay; otherwise, use
+ * the sum of the delay and dispersion.
+ */
+ dtemp = clock_phi * (current_time - peer->update);
+ peer->update = current_time;
+ for (i = NTP_SHIFT - 1; i >= 0; i--) {
+ if (i != 0)
+ peer->filter_disp[j] += dtemp;
+ if (peer->filter_disp[j] >= MAXDISPERSE) {
+ peer->filter_disp[j] = MAXDISPERSE;
+ dst[i] = MAXDISPERSE;
+ } else if (peer->update - peer->filter_epoch[j] >
+ (u_long)ULOGTOD(allan_xpt)) {
+ dst[i] = peer->filter_delay[j] +
+ peer->filter_disp[j];
+ } else {
+ dst[i] = peer->filter_delay[j];
+ }
+ ord[i] = j;
+ j = (j + 1) % NTP_SHIFT;
+ }
+
+ /*
+ * If the clock has stabilized, sort the samples by distance.
+ */
+ if (freq_cnt == 0) {
+ for (i = 1; i < NTP_SHIFT; i++) {
+ for (j = 0; j < i; j++) {
+ if (dst[j] > dst[i]) {
+ k = ord[j];
+ ord[j] = ord[i];
+ ord[i] = k;
+ etemp = dst[j];
+ dst[j] = dst[i];
+ dst[i] = etemp;
+ }
+ }
+ }
+ }
+
+ /*
+ * Copy the index list to the association structure so ntpq
+ * can see it later. Prune the distance list to leave only
+ * samples less than the maximum dispersion, which disfavors
+ * uncorrelated samples older than the Allan intercept. To
+ * further improve the jitter estimate, of the remainder leave
+ * only samples less than the maximum distance, but keep at
+ * least two samples for jitter calculation.
+ */
+ m = 0;
+ for (i = 0; i < NTP_SHIFT; i++) {
+ peer->filter_order[i] = (u_char) ord[i];
+ if (dst[i] >= MAXDISPERSE || (m >= 2 && dst[i] >=
+ sys_maxdist))
+ continue;
+ m++;
+ }
+
+ /*
+ * Compute the dispersion and jitter. The dispersion is weighted
+ * exponentially by NTP_FWEIGHT (0.5) so it is normalized close
+ * to 1.0. The jitter is the RMS differences relative to the
+ * lowest delay sample.
+ */
+ peer->disp = peer->jitter = 0;
+ k = ord[0];
+ for (i = NTP_SHIFT - 1; i >= 0; i--) {
+ j = ord[i];
+ peer->disp = NTP_FWEIGHT * (peer->disp +
+ peer->filter_disp[j]);
+ if (i < m)
+ peer->jitter += DIFF(peer->filter_offset[j],
+ peer->filter_offset[k]);
+ }
+
+ /*
+ * If no acceptable samples remain in the shift register,
+ * quietly tiptoe home leaving only the dispersion. Otherwise,
+ * save the offset, delay and jitter. Note the jitter must not
+ * be less than the precision.
+ */
+ if (m == 0) {
+ clock_select();
+ return;
+ }
+ etemp = fabs(peer->offset - peer->filter_offset[k]);
+ peer->offset = peer->filter_offset[k];
+ peer->delay = peer->filter_delay[k];
+ if (m > 1)
+ peer->jitter /= m - 1;
+ peer->jitter = max(SQRT(peer->jitter), LOGTOD(sys_precision));
+
+ /*
+ * If the the new sample and the current sample are both valid
+ * and the difference between their offsets exceeds CLOCK_SGATE
+ * (3) times the jitter and the interval between them is less
+ * than twice the host poll interval, consider the new sample
+ * a popcorn spike and ignore it.
+ */
+ if (peer->disp < sys_maxdist && peer->filter_disp[k] <
+ sys_maxdist && etemp > CLOCK_SGATE * peer->jitter &&
+ peer->filter_epoch[k] - peer->epoch < 2. *
+ ULOGTOD(peer->hpoll)) {
+ snprintf(tbuf, sizeof(tbuf), "%.6f s", etemp);
+ report_event(PEVNT_POPCORN, peer, tbuf);
+ return;
+ }
+
+ /*
+ * A new minimum sample is useful only if it is later than the
+ * last one used. In this design the maximum lifetime of any
+ * sample is not greater than eight times the poll interval, so
+ * the maximum interval between minimum samples is eight
+ * packets.
+ */
+ if (peer->filter_epoch[k] <= peer->epoch) {
+#if DEBUG
+ if (debug > 1)
+ printf("clock_filter: old sample %lu\n", current_time -
+ peer->filter_epoch[k]);
+#endif
+ return;
+ }
+ peer->epoch = peer->filter_epoch[k];
+
+ /*
+ * The mitigated sample statistics are saved for later
+ * processing. If not synchronized or not in a burst, tickle the
+ * clock select algorithm.
+ */
+ record_peer_stats(&peer->srcadr, ctlpeerstatus(peer),
+ peer->offset, peer->delay, peer->disp, peer->jitter);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "clock_filter: n %d off %.6f del %.6f dsp %.6f jit %.6f\n",
+ m, peer->offset, peer->delay, peer->disp,
+ peer->jitter);
+#endif
+ if (peer->burst == 0 || sys_leap == LEAP_NOTINSYNC)
+ clock_select();
+}
+
+
+/*
+ * clock_select - find the pick-of-the-litter clock
+ *
+ * LOCKCLOCK: (1) If the local clock is the prefer peer, it will always
+ * be enabled, even if declared falseticker, (2) only the prefer peer
+ * can be selected as the system peer, (3) if the external source is
+ * down, the system leap bits are set to 11 and the stratum set to
+ * infinity.
+ */
+void
+clock_select(void)
+{
+ struct peer *peer;
+ int i, j, k, n;
+ int nlist, nl2;
+ int allow, osurv;
+ int speer;
+ double d, e, f, g;
+ double high, low;
+ double speermet;
+ double orphmet = 2.0 * U_INT32_MAX; /* 2x is greater than */
+ struct endpoint endp;
+ struct peer *osys_peer;
+ struct peer *sys_prefer = NULL; /* prefer peer */
+ struct peer *typesystem = NULL;
+ struct peer *typeorphan = NULL;
+#ifdef REFCLOCK
+ struct peer *typeacts = NULL;
+ struct peer *typelocal = NULL;
+ struct peer *typepps = NULL;
+#endif /* REFCLOCK */
+ static struct endpoint *endpoint = NULL;
+ static int *indx = NULL;
+ static peer_select *peers = NULL;
+ static u_int endpoint_size = 0;
+ static u_int peers_size = 0;
+ static u_int indx_size = 0;
+ size_t octets;
+
+ /*
+ * Initialize and create endpoint, index and peer lists big
+ * enough to handle all associations.
+ */
+ osys_peer = sys_peer;
+ osurv = sys_survivors;
+ sys_survivors = 0;
+#ifdef LOCKCLOCK
+ sys_leap = LEAP_NOTINSYNC;
+ sys_stratum = STRATUM_UNSPEC;
+ memcpy(&sys_refid, "DOWN", 4);
+#endif /* LOCKCLOCK */
+
+ /*
+ * Allocate dynamic space depending on the number of
+ * associations.
+ */
+ nlist = 1;
+ for (peer = peer_list; peer != NULL; peer = peer->p_link)
+ nlist++;
+ endpoint_size = ALIGNED_SIZE(nlist * 2 * sizeof(*endpoint));
+ peers_size = ALIGNED_SIZE(nlist * sizeof(*peers));
+ indx_size = ALIGNED_SIZE(nlist * 2 * sizeof(*indx));
+ octets = endpoint_size + peers_size + indx_size;
+ endpoint = erealloc(endpoint, octets);
+ peers = INC_ALIGNED_PTR(endpoint, endpoint_size);
+ indx = INC_ALIGNED_PTR(peers, peers_size);
+
+ /*
+ * Initially, we populate the island with all the rifraff peers
+ * that happen to be lying around. Those with seriously
+ * defective clocks are immediately booted off the island. Then,
+ * the falsetickers are culled and put to sea. The truechimers
+ * remaining are subject to repeated rounds where the most
+ * unpopular at each round is kicked off. When the population
+ * has dwindled to sys_minclock, the survivors split a million
+ * bucks and collectively crank the chimes.
+ */
+ nlist = nl2 = 0; /* none yet */
+ for (peer = peer_list; peer != NULL; peer = peer->p_link) {
+ peer->new_status = CTL_PST_SEL_REJECT;
+
+ /*
+ * Leave the island immediately if the peer is
+ * unfit to synchronize.
+ */
+ if (peer_unfit(peer))
+ continue;
+
+ /*
+ * If this peer is an orphan parent, elect the
+ * one with the lowest metric defined as the
+ * IPv4 address or the first 64 bits of the
+ * hashed IPv6 address. To ensure convergence
+ * on the same selected orphan, consider as
+ * well that this system may have the lowest
+ * metric and be the orphan parent. If this
+ * system wins, sys_peer will be NULL to trigger
+ * orphan mode in timer().
+ */
+ if (peer->stratum == sys_orphan) {
+ u_int32 localmet;
+ u_int32 peermet;
+
+ if (peer->dstadr != NULL)
+ localmet = ntohl(peer->dstadr->addr_refid);
+ else
+ localmet = U_INT32_MAX;
+ peermet = ntohl(addr2refid(&peer->srcadr));
+ if (peermet < localmet && peermet < orphmet) {
+ typeorphan = peer;
+ orphmet = peermet;
+ }
+ continue;
+ }
+
+ /*
+ * If this peer could have the orphan parent
+ * as a synchronization ancestor, exclude it
+ * from selection to avoid forming a
+ * synchronization loop within the orphan mesh,
+ * triggering stratum climb to infinity
+ * instability. Peers at stratum higher than
+ * the orphan stratum could have the orphan
+ * parent in ancestry so are excluded.
+ * See http://bugs.ntp.org/2050
+ */
+ if (peer->stratum > sys_orphan)
+ continue;
+#ifdef REFCLOCK
+ /*
+ * The following are special cases. We deal
+ * with them later.
+ */
+ if (!(peer->flags & FLAG_PREFER)) {
+ switch (peer->refclktype) {
+ case REFCLK_LOCALCLOCK:
+ if (current_time > orphwait &&
+ typelocal == NULL)
+ typelocal = peer;
+ continue;
+
+ case REFCLK_ACTS:
+ if (current_time > orphwait &&
+ typeacts == NULL)
+ typeacts = peer;
+ continue;
+ }
+ }
+#endif /* REFCLOCK */
+
+ /*
+ * If we get this far, the peer can stay on the
+ * island, but does not yet have the immunity
+ * idol.
+ */
+ peer->new_status = CTL_PST_SEL_SANE;
+ f = root_distance(peer);
+ peers[nlist].peer = peer;
+ peers[nlist].error = peer->jitter;
+ peers[nlist].synch = f;
+ nlist++;
+
+ /*
+ * Insert each interval endpoint on the unsorted
+ * endpoint[] list.
+ */
+ e = peer->offset;
+ endpoint[nl2].type = -1; /* lower end */
+ endpoint[nl2].val = e - f;
+ nl2++;
+ endpoint[nl2].type = 1; /* upper end */
+ endpoint[nl2].val = e + f;
+ nl2++;
+ }
+ /*
+ * Construct sorted indx[] of endpoint[] indexes ordered by
+ * offset.
+ */
+ for (i = 0; i < nl2; i++)
+ indx[i] = i;
+ for (i = 0; i < nl2; i++) {
+ endp = endpoint[indx[i]];
+ e = endp.val;
+ k = i;
+ for (j = i + 1; j < nl2; j++) {
+ endp = endpoint[indx[j]];
+ if (endp.val < e) {
+ e = endp.val;
+ k = j;
+ }
+ }
+ if (k != i) {
+ j = indx[k];
+ indx[k] = indx[i];
+ indx[i] = j;
+ }
+ }
+ for (i = 0; i < nl2; i++)
+ DPRINTF(3, ("select: endpoint %2d %.6f\n",
+ endpoint[indx[i]].type, endpoint[indx[i]].val));
+
+ /*
+ * This is the actual algorithm that cleaves the truechimers
+ * from the falsetickers. The original algorithm was described
+ * in Keith Marzullo's dissertation, but has been modified for
+ * better accuracy.
+ *
+ * Briefly put, we first assume there are no falsetickers, then
+ * scan the candidate list first from the low end upwards and
+ * then from the high end downwards. The scans stop when the
+ * number of intersections equals the number of candidates less
+ * the number of falsetickers. If this doesn't happen for a
+ * given number of falsetickers, we bump the number of
+ * falsetickers and try again. If the number of falsetickers
+ * becomes equal to or greater than half the number of
+ * candidates, the Albanians have won the Byzantine wars and
+ * correct synchronization is not possible.
+ *
+ * Here, nlist is the number of candidates and allow is the
+ * number of falsetickers. Upon exit, the truechimers are the
+ * survivors with offsets not less than low and not greater than
+ * high. There may be none of them.
+ */
+ low = 1e9;
+ high = -1e9;
+ for (allow = 0; 2 * allow < nlist; allow++) {
+
+ /*
+ * Bound the interval (low, high) as the smallest
+ * interval containing points from the most sources.
+ */
+ n = 0;
+ for (i = 0; i < nl2; i++) {
+ low = endpoint[indx[i]].val;
+ n -= endpoint[indx[i]].type;
+ if (n >= nlist - allow)
+ break;
+ }
+ n = 0;
+ for (j = nl2 - 1; j >= 0; j--) {
+ high = endpoint[indx[j]].val;
+ n += endpoint[indx[j]].type;
+ if (n >= nlist - allow)
+ break;
+ }
+
+ /*
+ * If an interval containing truechimers is found, stop.
+ * If not, increase the number of falsetickers and go
+ * around again.
+ */
+ if (high > low)
+ break;
+ }
+
+ /*
+ * Clustering algorithm. Whittle candidate list of falsetickers,
+ * who leave the island immediately. The TRUE peer is always a
+ * truechimer. We must leave at least one peer to collect the
+ * million bucks.
+ *
+ * We assert the correct time is contained in the interval, but
+ * the best offset estimate for the interval might not be
+ * contained in the interval. For this purpose, a truechimer is
+ * defined as the midpoint of an interval that overlaps the
+ * intersection interval.
+ */
+ j = 0;
+ for (i = 0; i < nlist; i++) {
+ double h;
+
+ peer = peers[i].peer;
+ h = peers[i].synch;
+ if ((high <= low || peer->offset + h < low ||
+ peer->offset - h > high) && !(peer->flags & FLAG_TRUE))
+ continue;
+
+#ifdef REFCLOCK
+ /*
+ * Eligible PPS peers must survive the intersection
+ * algorithm. Use the first one found, but don't
+ * include any of them in the cluster population.
+ */
+ if (peer->flags & FLAG_PPS) {
+ if (typepps == NULL)
+ typepps = peer;
+ continue;
+ }
+#endif /* REFCLOCK */
+
+ if (j != i)
+ peers[j] = peers[i];
+ j++;
+ }
+ nlist = j;
+
+ /*
+ * If no survivors remain at this point, check if the modem
+ * driver, local driver or orphan parent in that order. If so,
+ * nominate the first one found as the only survivor.
+ * Otherwise, give up and leave the island to the rats.
+ */
+ if (nlist == 0) {
+ peers[0].error = 0;
+ peers[0].synch = sys_mindisp;
+#ifdef REFCLOCK
+ if (typeacts != NULL) {
+ peers[0].peer = typeacts;
+ nlist = 1;
+ } else if (typelocal != NULL) {
+ peers[0].peer = typelocal;
+ nlist = 1;
+ } else
+#endif /* REFCLOCK */
+ if (typeorphan != NULL) {
+ peers[0].peer = typeorphan;
+ nlist = 1;
+ }
+ }
+
+ /*
+ * Mark the candidates at this point as truechimers.
+ */
+ for (i = 0; i < nlist; i++) {
+ peers[i].peer->new_status = CTL_PST_SEL_SELCAND;
+ DPRINTF(2, ("select: survivor %s %f\n",
+ stoa(&peers[i].peer->srcadr), peers[i].synch));
+ }
+
+ /*
+ * Now, vote outlyers off the island by select jitter weighted
+ * by root distance. Continue voting as long as there are more
+ * than sys_minclock survivors and the select jitter of the peer
+ * with the worst metric is greater than the minimum peer
+ * jitter. Stop if we are about to discard a TRUE or PREFER
+ * peer, who of course have the immunity idol.
+ */
+ while (1) {
+ d = 1e9;
+ e = -1e9;
+ g = 0;
+ k = 0;
+ for (i = 0; i < nlist; i++) {
+ if (peers[i].error < d)
+ d = peers[i].error;
+ peers[i].seljit = 0;
+ if (nlist > 1) {
+ f = 0;
+ for (j = 0; j < nlist; j++)
+ f += DIFF(peers[j].peer->offset,
+ peers[i].peer->offset);
+ peers[i].seljit = SQRT(f / (nlist - 1));
+ }
+ if (peers[i].seljit * peers[i].synch > e) {
+ g = peers[i].seljit;
+ e = peers[i].seljit * peers[i].synch;
+ k = i;
+ }
+ }
+ g = max(g, LOGTOD(sys_precision));
+ if (nlist <= max(1, sys_minclock) || g <= d ||
+ ((FLAG_TRUE | FLAG_PREFER) & peers[k].peer->flags))
+ break;
+
+ DPRINTF(3, ("select: drop %s seljit %.6f jit %.6f\n",
+ ntoa(&peers[k].peer->srcadr), g, d));
+ if (nlist > sys_maxclock)
+ peers[k].peer->new_status = CTL_PST_SEL_EXCESS;
+ for (j = k + 1; j < nlist; j++)
+ peers[j - 1] = peers[j];
+ nlist--;
+ }
+
+ /*
+ * What remains is a list usually not greater than sys_minclock
+ * peers. Note that unsynchronized peers cannot survive this
+ * far. Count and mark these survivors.
+ *
+ * While at it, count the number of leap warning bits found.
+ * This will be used later to vote the system leap warning bit.
+ * If a leap warning bit is found on a reference clock, the vote
+ * is always won.
+ *
+ * Choose the system peer using a hybrid metric composed of the
+ * selection jitter scaled by the root distance augmented by
+ * stratum scaled by sys_mindisp (.001 by default). The goal of
+ * the small stratum factor is to avoid clockhop between a
+ * reference clock and a network peer which has a refclock and
+ * is using an older ntpd, which does not floor sys_rootdisp at
+ * sys_mindisp.
+ *
+ * In contrast, ntpd 4.2.6 and earlier used stratum primarily
+ * in selecting the system peer, using a weight of 1 second of
+ * additional root distance per stratum. This heavy bias is no
+ * longer appropriate, as the scaled root distance provides a
+ * more rational metric carrying the cumulative error budget.
+ */
+ e = 1e9;
+ speer = 0;
+ leap_vote_ins = 0;
+ leap_vote_del = 0;
+ for (i = 0; i < nlist; i++) {
+ peer = peers[i].peer;
+ peer->unreach = 0;
+ peer->new_status = CTL_PST_SEL_SYNCCAND;
+ sys_survivors++;
+ if (peer->leap == LEAP_ADDSECOND) {
+ if (peer->flags & FLAG_REFCLOCK)
+ leap_vote_ins = nlist;
+ else if (leap_vote_ins < nlist)
+ leap_vote_ins++;
+ }
+ if (peer->leap == LEAP_DELSECOND) {
+ if (peer->flags & FLAG_REFCLOCK)
+ leap_vote_del = nlist;
+ else if (leap_vote_del < nlist)
+ leap_vote_del++;
+ }
+ if (peer->flags & FLAG_PREFER)
+ sys_prefer = peer;
+ speermet = peers[i].seljit * peers[i].synch +
+ peer->stratum * sys_mindisp;
+ if (speermet < e) {
+ e = speermet;
+ speer = i;
+ }
+ }
+
+ /*
+ * Unless there are at least sys_misane survivors, leave the
+ * building dark. Otherwise, do a clockhop dance. Ordinarily,
+ * use the selected survivor speer. However, if the current
+ * system peer is not speer, stay with the current system peer
+ * as long as it doesn't get too old or too ugly.
+ */
+ if (nlist > 0 && nlist >= sys_minsane) {
+ double x;
+
+ typesystem = peers[speer].peer;
+ if (osys_peer == NULL || osys_peer == typesystem) {
+ sys_clockhop = 0;
+ } else if ((x = fabs(typesystem->offset -
+ osys_peer->offset)) < sys_mindisp) {
+ if (sys_clockhop == 0)
+ sys_clockhop = sys_mindisp;
+ else
+ sys_clockhop *= .5;
+ DPRINTF(1, ("select: clockhop %d %.6f %.6f\n",
+ j, x, sys_clockhop));
+ if (fabs(x) < sys_clockhop)
+ typesystem = osys_peer;
+ else
+ sys_clockhop = 0;
+ } else {
+ sys_clockhop = 0;
+ }
+ }
+
+ /*
+ * Mitigation rules of the game. We have the pick of the
+ * litter in typesystem if any survivors are left. If
+ * there is a prefer peer, use its offset and jitter.
+ * Otherwise, use the combined offset and jitter of all kitters.
+ */
+ if (typesystem != NULL) {
+ if (sys_prefer == NULL) {
+ typesystem->new_status = CTL_PST_SEL_SYSPEER;
+ clock_combine(peers, sys_survivors, speer);
+ } else {
+ typesystem = sys_prefer;
+ sys_clockhop = 0;
+ typesystem->new_status = CTL_PST_SEL_SYSPEER;
+ sys_offset = typesystem->offset;
+ sys_jitter = typesystem->jitter;
+ }
+ DPRINTF(1, ("select: combine offset %.9f jitter %.9f\n",
+ sys_offset, sys_jitter));
+ }
+#ifdef REFCLOCK
+ /*
+ * If a PPS driver is lit and the combined offset is less than
+ * 0.4 s, select the driver as the PPS peer and use its offset
+ * and jitter. However, if this is the atom driver, use it only
+ * if there is a prefer peer or there are no survivors and none
+ * are required.
+ */
+ if (typepps != NULL && fabs(sys_offset) < 0.4 &&
+ (typepps->refclktype != REFCLK_ATOM_PPS ||
+ (typepps->refclktype == REFCLK_ATOM_PPS && (sys_prefer !=
+ NULL || (typesystem == NULL && sys_minsane == 0))))) {
+ typesystem = typepps;
+ sys_clockhop = 0;
+ typesystem->new_status = CTL_PST_SEL_PPS;
+ sys_offset = typesystem->offset;
+ sys_jitter = typesystem->jitter;
+ DPRINTF(1, ("select: pps offset %.9f jitter %.9f\n",
+ sys_offset, sys_jitter));
+ }
+#endif /* REFCLOCK */
+
+ /*
+ * If there are no survivors at this point, there is no
+ * system peer. If so and this is an old update, keep the
+ * current statistics, but do not update the clock.
+ */
+ if (typesystem == NULL) {
+ if (osys_peer != NULL) {
+ if (sys_orphwait > 0)
+ orphwait = current_time + sys_orphwait;
+ report_event(EVNT_NOPEER, NULL, NULL);
+ }
+ sys_peer = NULL;
+ for (peer = peer_list; peer != NULL; peer = peer->p_link)
+ peer->status = peer->new_status;
+ return;
+ }
+
+ /*
+ * Do not use old data, as this may mess up the clock discipline
+ * stability.
+ */
+ if (typesystem->epoch <= sys_epoch)
+ return;
+
+ /*
+ * We have found the alpha male. Wind the clock.
+ */
+ if (osys_peer != typesystem)
+ report_event(PEVNT_NEWPEER, typesystem, NULL);
+ for (peer = peer_list; peer != NULL; peer = peer->p_link)
+ peer->status = peer->new_status;
+ clock_update(typesystem);
+}
+
+
+static void
+clock_combine(
+ peer_select * peers, /* survivor list */
+ int npeers, /* number of survivors */
+ int syspeer /* index of sys.peer */
+ )
+{
+ int i;
+ double x, y, z, w;
+
+ y = z = w = 0;
+ for (i = 0; i < npeers; i++) {
+ x = 1. / peers[i].synch;
+ y += x;
+ z += x * peers[i].peer->offset;
+ w += x * DIFF(peers[i].peer->offset,
+ peers[syspeer].peer->offset);
+ }
+ sys_offset = z / y;
+ sys_jitter = SQRT(w / y + SQUARE(peers[syspeer].seljit));
+}
+
+
+/*
+ * root_distance - compute synchronization distance from peer to root
+ */
+static double
+root_distance(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ double dtemp;
+
+ /*
+ * Root Distance (LAMBDA) is defined as:
+ * (delta + DELTA)/2 + epsilon + EPSILON + phi
+ *
+ * where:
+ * delta is the round-trip delay
+ * DELTA is the root delay
+ * epsilon is the remote server precision + local precision
+ * + (15 usec each second)
+ * EPSILON is the root dispersion
+ * phi is the peer jitter statistic
+ *
+ * NB: Think hard about why we are using these values, and what
+ * the alternatives are, and the various pros/cons.
+ *
+ * DLM thinks these are probably the best choices from any of the
+ * other worse choices.
+ */
+ dtemp = (peer->delay + peer->rootdelay) / 2
+ + LOGTOD(peer->precision)
+ + LOGTOD(sys_precision)
+ + clock_phi * (current_time - peer->update)
+ + peer->rootdisp
+ + peer->jitter;
+ /*
+ * Careful squeak here. The value returned must be greater than
+ * the minimum root dispersion in order to avoid clockhop with
+ * highly precise reference clocks. Note that the root distance
+ * cannot exceed the sys_maxdist, as this is the cutoff by the
+ * selection algorithm.
+ */
+ if (dtemp < sys_mindisp)
+ dtemp = sys_mindisp;
+ return (dtemp);
+}
+
+
+/*
+ * peer_xmit - send packet for persistent association.
+ */
+static void
+peer_xmit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct pkt xpkt; /* transmit packet */
+ int sendlen, authlen;
+ keyid_t xkeyid = 0; /* transmit key ID */
+ l_fp xmt_tx, xmt_ty;
+
+ if (!peer->dstadr) /* drop peers without interface */
+ return;
+
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, peer->version,
+ peer->hmode);
+ xpkt.stratum = STRATUM_TO_PKT(sys_stratum);
+ xpkt.ppoll = peer->hpoll;
+ xpkt.precision = sys_precision;
+ xpkt.refid = sys_refid;
+ xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay));
+ xpkt.rootdisp = HTONS_FP(DTOUFP(sys_rootdisp));
+ HTONL_FP(&sys_reftime, &xpkt.reftime);
+ HTONL_FP(&peer->rec, &xpkt.org);
+ HTONL_FP(&peer->dst, &xpkt.rec);
+
+ /*
+ * If the received packet contains a MAC, the transmitted packet
+ * is authenticated and contains a MAC. If not, the transmitted
+ * packet is not authenticated.
+ *
+ * It is most important when autokey is in use that the local
+ * interface IP address be known before the first packet is
+ * sent. Otherwise, it is not possible to compute a correct MAC
+ * the recipient will accept. Thus, the I/O semantics have to do
+ * a little more work. In particular, the wildcard interface
+ * might not be usable.
+ */
+ sendlen = LEN_PKT_NOMAC;
+#ifdef AUTOKEY
+ if (!(peer->flags & FLAG_SKEY) && peer->keyid == 0) {
+#else /* !AUTOKEY follows */
+ if (peer->keyid == 0) {
+#endif /* !AUTOKEY */
+
+ /*
+ * Transmit a-priori timestamps
+ */
+ get_systime(&xmt_tx);
+ if (peer->flip == 0) { /* basic mode */
+ peer->aorg = xmt_tx;
+ HTONL_FP(&xmt_tx, &xpkt.xmt);
+ } else { /* interleaved modes */
+ if (peer->hmode == MODE_BROADCAST) { /* bcst */
+ HTONL_FP(&xmt_tx, &xpkt.xmt);
+ if (peer->flip > 0)
+ HTONL_FP(&peer->borg,
+ &xpkt.org);
+ else
+ HTONL_FP(&peer->aorg,
+ &xpkt.org);
+ } else { /* symmetric */
+ if (peer->flip > 0)
+ HTONL_FP(&peer->borg,
+ &xpkt.xmt);
+ else
+ HTONL_FP(&peer->aorg,
+ &xpkt.xmt);
+ }
+ }
+ peer->t21_bytes = sendlen;
+ sendpkt(&peer->srcadr, peer->dstadr, sys_ttl[peer->ttl],
+ &xpkt, sendlen);
+ peer->sent++;
+ peer->throttle += (1 << peer->minpoll) - 2;
+
+ /*
+ * Capture a-posteriori timestamps
+ */
+ get_systime(&xmt_ty);
+ if (peer->flip != 0) { /* interleaved modes */
+ if (peer->flip > 0)
+ peer->aorg = xmt_ty;
+ else
+ peer->borg = xmt_ty;
+ peer->flip = -peer->flip;
+ }
+ L_SUB(&xmt_ty, &xmt_tx);
+ LFPTOD(&xmt_ty, peer->xleave);
+#ifdef DEBUG
+ if (debug)
+ printf("transmit: at %ld %s->%s mode %d len %d\n",
+ current_time, peer->dstadr ?
+ stoa(&peer->dstadr->sin) : "-",
+ stoa(&peer->srcadr), peer->hmode, sendlen);
+#endif
+ return;
+ }
+
+ /*
+ * Authentication is enabled, so the transmitted packet must be
+ * authenticated. If autokey is enabled, fuss with the various
+ * modes; otherwise, symmetric key cryptography is used.
+ */
+#ifdef AUTOKEY
+ if (peer->flags & FLAG_SKEY) {
+ struct exten *exten; /* extension field */
+
+ /*
+ * The Public Key Dance (PKD): Cryptographic credentials
+ * are contained in extension fields, each including a
+ * 4-octet length/code word followed by a 4-octet
+ * association ID and optional additional data. Optional
+ * data includes a 4-octet data length field followed by
+ * the data itself. Request messages are sent from a
+ * configured association; response messages can be sent
+ * from a configured association or can take the fast
+ * path without ever matching an association. Response
+ * messages have the same code as the request, but have
+ * a response bit and possibly an error bit set. In this
+ * implementation, a message may contain no more than
+ * one command and one or more responses.
+ *
+ * Cryptographic session keys include both a public and
+ * a private componet. Request and response messages
+ * using extension fields are always sent with the
+ * private component set to zero. Packets without
+ * extension fields indlude the private component when
+ * the session key is generated.
+ */
+ while (1) {
+
+ /*
+ * Allocate and initialize a keylist if not
+ * already done. Then, use the list in inverse
+ * order, discarding keys once used. Keep the
+ * latest key around until the next one, so
+ * clients can use client/server packets to
+ * compute propagation delay.
+ *
+ * Note that once a key is used from the list,
+ * it is retained in the key cache until the
+ * next key is used. This is to allow a client
+ * to retrieve the encrypted session key
+ * identifier to verify authenticity.
+ *
+ * If for some reason a key is no longer in the
+ * key cache, a birthday has happened or the key
+ * has expired, so the pseudo-random sequence is
+ * broken. In that case, purge the keylist and
+ * regenerate it.
+ */
+ if (peer->keynumber == 0)
+ make_keylist(peer, peer->dstadr);
+ else
+ peer->keynumber--;
+ xkeyid = peer->keylist[peer->keynumber];
+ if (authistrusted(xkeyid))
+ break;
+ else
+ key_expire(peer);
+ }
+ peer->keyid = xkeyid;
+ exten = NULL;
+ switch (peer->hmode) {
+
+ /*
+ * In broadcast server mode the autokey values are
+ * required by the broadcast clients. Push them when a
+ * new keylist is generated; otherwise, push the
+ * association message so the client can request them at
+ * other times.
+ */
+ case MODE_BROADCAST:
+ if (peer->flags & FLAG_ASSOC)
+ exten = crypto_args(peer, CRYPTO_AUTO |
+ CRYPTO_RESP, peer->associd, NULL);
+ else
+ exten = crypto_args(peer, CRYPTO_ASSOC |
+ CRYPTO_RESP, peer->associd, NULL);
+ break;
+
+ /*
+ * In symmetric modes the parameter, certificate,
+ * identity, cookie and autokey exchanges are
+ * required. The leapsecond exchange is optional. But, a
+ * peer will not believe the other peer until the other
+ * peer has synchronized, so the certificate exchange
+ * might loop until then. If a peer finds a broken
+ * autokey sequence, it uses the autokey exchange to
+ * retrieve the autokey values. In any case, if a new
+ * keylist is generated, the autokey values are pushed.
+ */
+ case MODE_ACTIVE:
+ case MODE_PASSIVE:
+
+ /*
+ * Parameter, certificate and identity.
+ */
+ if (!peer->crypto)
+ exten = crypto_args(peer, CRYPTO_ASSOC,
+ peer->associd, hostval.ptr);
+ else if (!(peer->crypto & CRYPTO_FLAG_CERT))
+ exten = crypto_args(peer, CRYPTO_CERT,
+ peer->associd, peer->issuer);
+ else if (!(peer->crypto & CRYPTO_FLAG_VRFY))
+ exten = crypto_args(peer,
+ crypto_ident(peer), peer->associd,
+ NULL);
+
+ /*
+ * Cookie and autokey. We request the cookie
+ * only when the this peer and the other peer
+ * are synchronized. But, this peer needs the
+ * autokey values when the cookie is zero. Any
+ * time we regenerate the key list, we offer the
+ * autokey values without being asked. If for
+ * some reason either peer finds a broken
+ * autokey sequence, the autokey exchange is
+ * used to retrieve the autokey values.
+ */
+ else if (sys_leap != LEAP_NOTINSYNC &&
+ peer->leap != LEAP_NOTINSYNC &&
+ !(peer->crypto & CRYPTO_FLAG_COOK))
+ exten = crypto_args(peer, CRYPTO_COOK,
+ peer->associd, NULL);
+ else if (!(peer->crypto & CRYPTO_FLAG_AUTO))
+ exten = crypto_args(peer, CRYPTO_AUTO,
+ peer->associd, NULL);
+ else if (peer->flags & FLAG_ASSOC &&
+ peer->crypto & CRYPTO_FLAG_SIGN)
+ exten = crypto_args(peer, CRYPTO_AUTO |
+ CRYPTO_RESP, peer->assoc, NULL);
+
+ /*
+ * Wait for clock sync, then sign the
+ * certificate and retrieve the leapsecond
+ * values.
+ */
+ else if (sys_leap == LEAP_NOTINSYNC)
+ break;
+
+ else if (!(peer->crypto & CRYPTO_FLAG_SIGN))
+ exten = crypto_args(peer, CRYPTO_SIGN,
+ peer->associd, hostval.ptr);
+ else if (!(peer->crypto & CRYPTO_FLAG_LEAP))
+ exten = crypto_args(peer, CRYPTO_LEAP,
+ peer->associd, NULL);
+ break;
+
+ /*
+ * In client mode the parameter, certificate, identity,
+ * cookie and sign exchanges are required. The
+ * leapsecond exchange is optional. If broadcast client
+ * mode the same exchanges are required, except that the
+ * autokey exchange is substitutes for the cookie
+ * exchange, since the cookie is always zero. If the
+ * broadcast client finds a broken autokey sequence, it
+ * uses the autokey exchange to retrieve the autokey
+ * values.
+ */
+ case MODE_CLIENT:
+
+ /*
+ * Parameter, certificate and identity.
+ */
+ if (!peer->crypto)
+ exten = crypto_args(peer, CRYPTO_ASSOC,
+ peer->associd, hostval.ptr);
+ else if (!(peer->crypto & CRYPTO_FLAG_CERT))
+ exten = crypto_args(peer, CRYPTO_CERT,
+ peer->associd, peer->issuer);
+ else if (!(peer->crypto & CRYPTO_FLAG_VRFY))
+ exten = crypto_args(peer,
+ crypto_ident(peer), peer->associd,
+ NULL);
+
+ /*
+ * Cookie and autokey. These are requests, but
+ * we use the peer association ID with autokey
+ * rather than our own.
+ */
+ else if (!(peer->crypto & CRYPTO_FLAG_COOK))
+ exten = crypto_args(peer, CRYPTO_COOK,
+ peer->associd, NULL);
+ else if (!(peer->crypto & CRYPTO_FLAG_AUTO))
+ exten = crypto_args(peer, CRYPTO_AUTO,
+ peer->assoc, NULL);
+
+ /*
+ * Wait for clock sync, then sign the
+ * certificate and retrieve the leapsecond
+ * values.
+ */
+ else if (sys_leap == LEAP_NOTINSYNC)
+ break;
+
+ else if (!(peer->crypto & CRYPTO_FLAG_SIGN))
+ exten = crypto_args(peer, CRYPTO_SIGN,
+ peer->associd, hostval.ptr);
+ else if (!(peer->crypto & CRYPTO_FLAG_LEAP))
+ exten = crypto_args(peer, CRYPTO_LEAP,
+ peer->associd, NULL);
+ break;
+ }
+
+ /*
+ * Add a queued extension field if present. This is
+ * always a request message, so the reply ID is already
+ * in the message. If an error occurs, the error bit is
+ * lit in the response.
+ */
+ if (peer->cmmd != NULL) {
+ u_int32 temp32;
+
+ temp32 = CRYPTO_RESP;
+ peer->cmmd->opcode |= htonl(temp32);
+ sendlen += crypto_xmit(peer, &xpkt, NULL,
+ sendlen, peer->cmmd, 0);
+ free(peer->cmmd);
+ peer->cmmd = NULL;
+ }
+
+ /*
+ * Add an extension field created above. All but the
+ * autokey response message are request messages.
+ */
+ if (exten != NULL) {
+ if (exten->opcode != 0)
+ sendlen += crypto_xmit(peer, &xpkt,
+ NULL, sendlen, exten, 0);
+ free(exten);
+ }
+
+ /*
+ * Calculate the next session key. Since extension
+ * fields are present, the cookie value is zero.
+ */
+ if (sendlen > LEN_PKT_NOMAC) {
+ session_key(&peer->dstadr->sin, &peer->srcadr,
+ xkeyid, 0, 2);
+ }
+ }
+#endif /* AUTOKEY */
+
+ /*
+ * Transmit a-priori timestamps
+ */
+ get_systime(&xmt_tx);
+ if (peer->flip == 0) { /* basic mode */
+ peer->aorg = xmt_tx;
+ HTONL_FP(&xmt_tx, &xpkt.xmt);
+ } else { /* interleaved modes */
+ if (peer->hmode == MODE_BROADCAST) { /* bcst */
+ HTONL_FP(&xmt_tx, &xpkt.xmt);
+ if (peer->flip > 0)
+ HTONL_FP(&peer->borg, &xpkt.org);
+ else
+ HTONL_FP(&peer->aorg, &xpkt.org);
+ } else { /* symmetric */
+ if (peer->flip > 0)
+ HTONL_FP(&peer->borg, &xpkt.xmt);
+ else
+ HTONL_FP(&peer->aorg, &xpkt.xmt);
+ }
+ }
+ xkeyid = peer->keyid;
+ authlen = authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen);
+ if (authlen == 0) {
+ report_event(PEVNT_AUTH, peer, "no key");
+ peer->flash |= TEST5; /* auth error */
+ peer->badauth++;
+ return;
+ }
+ sendlen += authlen;
+#ifdef AUTOKEY
+ if (xkeyid > NTP_MAXKEY)
+ authtrust(xkeyid, 0);
+#endif /* AUTOKEY */
+ if (sendlen > sizeof(xpkt)) {
+ msyslog(LOG_ERR, "proto: buffer overflow %u", sendlen);
+ exit (-1);
+ }
+ peer->t21_bytes = sendlen;
+ sendpkt(&peer->srcadr, peer->dstadr, sys_ttl[peer->ttl], &xpkt,
+ sendlen);
+ peer->sent++;
+ peer->throttle += (1 << peer->minpoll) - 2;
+
+ /*
+ * Capture a-posteriori timestamps
+ */
+ get_systime(&xmt_ty);
+ if (peer->flip != 0) { /* interleaved modes */
+ if (peer->flip > 0)
+ peer->aorg = xmt_ty;
+ else
+ peer->borg = xmt_ty;
+ peer->flip = -peer->flip;
+ }
+ L_SUB(&xmt_ty, &xmt_tx);
+ LFPTOD(&xmt_ty, peer->xleave);
+#ifdef AUTOKEY
+#ifdef DEBUG
+ if (debug)
+ printf("transmit: at %ld %s->%s mode %d keyid %08x len %d index %d\n",
+ current_time, latoa(peer->dstadr),
+ ntoa(&peer->srcadr), peer->hmode, xkeyid, sendlen,
+ peer->keynumber);
+#endif
+#else /* !AUTOKEY follows */
+#ifdef DEBUG
+ if (debug)
+ printf("transmit: at %ld %s->%s mode %d keyid %08x len %d\n",
+ current_time, peer->dstadr ?
+ ntoa(&peer->dstadr->sin) : "-",
+ ntoa(&peer->srcadr), peer->hmode, xkeyid, sendlen);
+#endif
+#endif /* !AUTOKEY */
+}
+
+
+/*
+ * fast_xmit - Send packet for nonpersistent association. Note that
+ * neither the source or destination can be a broadcast address.
+ */
+static void
+fast_xmit(
+ struct recvbuf *rbufp, /* receive packet pointer */
+ int xmode, /* receive mode */
+ keyid_t xkeyid, /* transmit key ID */
+ int flags /* restrict mask */
+ )
+{
+ struct pkt xpkt; /* transmit packet structure */
+ struct pkt *rpkt; /* receive packet structure */
+ l_fp xmt_tx, xmt_ty;
+ int sendlen;
+#ifdef AUTOKEY
+ u_int32 temp32;
+#endif
+
+ /*
+ * Initialize transmit packet header fields from the receive
+ * buffer provided. We leave the fields intact as received, but
+ * set the peer poll at the maximum of the receive peer poll and
+ * the system minimum poll (ntp_minpoll). This is for KoD rate
+ * control and not strictly specification compliant, but doesn't
+ * break anything.
+ *
+ * If the gazinta was from a multicast address, the gazoutta
+ * must go out another way.
+ */
+ rpkt = &rbufp->recv_pkt;
+ if (rbufp->dstadr->flags & INT_MCASTOPEN)
+ rbufp->dstadr = findinterface(&rbufp->recv_srcadr);
+
+ /*
+ * If this is a kiss-o'-death (KoD) packet, show leap
+ * unsynchronized, stratum zero, reference ID the four-character
+ * kiss code and system root delay. Note we don't reveal the
+ * local time, so these packets can't be used for
+ * synchronization.
+ */
+ if (flags & RES_KOD) {
+ sys_kodsent++;
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOTINSYNC,
+ PKT_VERSION(rpkt->li_vn_mode), xmode);
+ xpkt.stratum = STRATUM_PKT_UNSPEC;
+ xpkt.ppoll = max(rpkt->ppoll, ntp_minpoll);
+ xpkt.precision = rpkt->precision;
+ memcpy(&xpkt.refid, "RATE", 4);
+ xpkt.rootdelay = rpkt->rootdelay;
+ xpkt.rootdisp = rpkt->rootdisp;
+ xpkt.reftime = rpkt->reftime;
+ xpkt.org = rpkt->xmt;
+ xpkt.rec = rpkt->xmt;
+ xpkt.xmt = rpkt->xmt;
+
+ /*
+ * This is a normal packet. Use the system variables.
+ */
+ } else {
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap,
+ PKT_VERSION(rpkt->li_vn_mode), xmode);
+ xpkt.stratum = STRATUM_TO_PKT(sys_stratum);
+ xpkt.ppoll = max(rpkt->ppoll, ntp_minpoll);
+ xpkt.precision = sys_precision;
+ xpkt.refid = sys_refid;
+ xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay));
+ xpkt.rootdisp = HTONS_FP(DTOUFP(sys_rootdisp));
+ HTONL_FP(&sys_reftime, &xpkt.reftime);
+ xpkt.org = rpkt->xmt;
+ HTONL_FP(&rbufp->recv_time, &xpkt.rec);
+ get_systime(&xmt_tx);
+ HTONL_FP(&xmt_tx, &xpkt.xmt);
+ }
+
+#ifdef HAVE_NTP_SIGND
+ if (flags & RES_MSSNTP) {
+ send_via_ntp_signd(rbufp, xmode, xkeyid, flags, &xpkt);
+ return;
+ }
+#endif /* HAVE_NTP_SIGND */
+
+ /*
+ * If the received packet contains a MAC, the transmitted packet
+ * is authenticated and contains a MAC. If not, the transmitted
+ * packet is not authenticated.
+ */
+ sendlen = LEN_PKT_NOMAC;
+ if (rbufp->recv_length == sendlen) {
+ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, &xpkt,
+ sendlen);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "transmit: at %ld %s->%s mode %d len %d\n",
+ current_time, stoa(&rbufp->dstadr->sin),
+ stoa(&rbufp->recv_srcadr), xmode, sendlen);
+#endif
+ return;
+ }
+
+ /*
+ * The received packet contains a MAC, so the transmitted packet
+ * must be authenticated. For symmetric key cryptography, use
+ * the predefined and trusted symmetric keys to generate the
+ * cryptosum. For autokey cryptography, use the server private
+ * value to generate the cookie, which is unique for every
+ * source-destination-key ID combination.
+ */
+#ifdef AUTOKEY
+ if (xkeyid > NTP_MAXKEY) {
+ keyid_t cookie;
+
+ /*
+ * The only way to get here is a reply to a legitimate
+ * client request message, so the mode must be
+ * MODE_SERVER. If an extension field is present, there
+ * can be only one and that must be a command. Do what
+ * needs, but with private value of zero so the poor
+ * jerk can decode it. If no extension field is present,
+ * use the cookie to generate the session key.
+ */
+ cookie = session_key(&rbufp->recv_srcadr,
+ &rbufp->dstadr->sin, 0, sys_private, 0);
+ if (rbufp->recv_length > sendlen + (int)MAX_MAC_LEN) {
+ session_key(&rbufp->dstadr->sin,
+ &rbufp->recv_srcadr, xkeyid, 0, 2);
+ temp32 = CRYPTO_RESP;
+ rpkt->exten[0] |= htonl(temp32);
+ sendlen += crypto_xmit(NULL, &xpkt, rbufp,
+ sendlen, (struct exten *)rpkt->exten,
+ cookie);
+ } else {
+ session_key(&rbufp->dstadr->sin,
+ &rbufp->recv_srcadr, xkeyid, cookie, 2);
+ }
+ }
+#endif /* AUTOKEY */
+ get_systime(&xmt_tx);
+ sendlen += authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen);
+#ifdef AUTOKEY
+ if (xkeyid > NTP_MAXKEY)
+ authtrust(xkeyid, 0);
+#endif /* AUTOKEY */
+ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, &xpkt, sendlen);
+ get_systime(&xmt_ty);
+ L_SUB(&xmt_ty, &xmt_tx);
+ sys_authdelay = xmt_ty;
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "transmit: at %ld %s->%s mode %d keyid %08x len %d\n",
+ current_time, ntoa(&rbufp->dstadr->sin),
+ ntoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen);
+#endif
+}
+
+
+/*
+ * pool_xmit - resolve hostname or send unicast solicitation for pool.
+ */
+static void
+pool_xmit(
+ struct peer *pool /* pool solicitor association */
+ )
+{
+#ifdef WORKER
+ struct pkt xpkt; /* transmit packet structure */
+ struct addrinfo hints;
+ int rc;
+ struct interface * lcladr;
+ sockaddr_u * rmtadr;
+ int restrict_mask;
+ struct peer * p;
+ l_fp xmt_tx;
+
+ if (NULL == pool->ai) {
+ if (pool->addrs != NULL) {
+ /* free() is used with copy_addrinfo_list() */
+ free(pool->addrs);
+ pool->addrs = NULL;
+ }
+ ZERO(hints);
+ hints.ai_family = AF(&pool->srcadr);
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ /* ignore getaddrinfo_sometime() errors, we will retry */
+ rc = getaddrinfo_sometime(
+ pool->hostname,
+ "ntp",
+ &hints,
+ 0, /* no retry */
+ &pool_name_resolved,
+ (void *)(u_int)pool->associd);
+ if (!rc)
+ DPRINTF(1, ("pool DNS lookup %s started\n",
+ pool->hostname));
+ else
+ msyslog(LOG_ERR,
+ "unable to start pool DNS %s %m",
+ pool->hostname);
+ return;
+ }
+
+ do {
+ /* copy_addrinfo_list ai_addr points to a sockaddr_u */
+ rmtadr = (sockaddr_u *)(void *)pool->ai->ai_addr;
+ pool->ai = pool->ai->ai_next;
+ p = findexistingpeer(rmtadr, NULL, NULL, MODE_CLIENT, 0);
+ } while (p != NULL && pool->ai != NULL);
+ if (p != NULL)
+ return; /* out of addresses, re-query DNS next poll */
+ restrict_mask = restrictions(rmtadr);
+ if (RES_FLAGS & restrict_mask)
+ restrict_source(rmtadr, 0,
+ current_time + POOL_SOLICIT_WINDOW + 1);
+ lcladr = findinterface(rmtadr);
+ memset(&xpkt, 0, sizeof(xpkt));
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, pool->version,
+ MODE_CLIENT);
+ xpkt.stratum = STRATUM_TO_PKT(sys_stratum);
+ xpkt.ppoll = pool->hpoll;
+ xpkt.precision = sys_precision;
+ xpkt.refid = sys_refid;
+ xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay));
+ xpkt.rootdisp = HTONS_FP(DTOUFP(sys_rootdisp));
+ HTONL_FP(&sys_reftime, &xpkt.reftime);
+ get_systime(&xmt_tx);
+ pool->aorg = xmt_tx;
+ HTONL_FP(&xmt_tx, &xpkt.xmt);
+ sendpkt(rmtadr, lcladr, sys_ttl[pool->ttl], &xpkt,
+ LEN_PKT_NOMAC);
+ pool->sent++;
+ pool->throttle += (1 << pool->minpoll) - 2;
+#ifdef DEBUG
+ if (debug)
+ printf("transmit: at %ld %s->%s pool\n",
+ current_time, latoa(lcladr), stoa(rmtadr));
+#endif
+ msyslog(LOG_INFO, "Soliciting pool server %s", stoa(rmtadr));
+#endif /* WORKER */
+}
+
+
+#ifdef AUTOKEY
+ /*
+ * group_test - test if this is the same group
+ *
+ * host assoc return action
+ * none none 0 mobilize *
+ * none group 0 mobilize *
+ * group none 0 mobilize *
+ * group group 1 mobilize
+ * group different 1 ignore
+ * * ignore if notrust
+ */
+int group_test(
+ char *grp,
+ char *ident
+ )
+{
+ if (grp == NULL)
+ return (0);
+
+ if (strcmp(grp, sys_groupname) == 0)
+ return (0);
+
+ if (ident == NULL)
+ return (1);
+
+ if (strcmp(grp, ident) == 0)
+ return (0);
+
+ return (1);
+}
+#endif /* AUTOKEY */
+
+#ifdef WORKER
+void
+pool_name_resolved(
+ int rescode,
+ int gai_errno,
+ void * context,
+ const char * name,
+ const char * service,
+ const struct addrinfo * hints,
+ const struct addrinfo * res
+ )
+{
+ struct peer * pool; /* pool solicitor association */
+ associd_t assoc;
+
+ if (rescode) {
+ msyslog(LOG_ERR,
+ "error resolving pool %s: %s (%d)",
+ name, gai_strerror(rescode), rescode);
+ return;
+ }
+
+ assoc = (associd_t)(u_int)context;
+ pool = findpeerbyassoc(assoc);
+ if (NULL == pool) {
+ msyslog(LOG_ERR,
+ "Could not find assoc %u for pool DNS %s",
+ assoc, name);
+ return;
+ }
+ DPRINTF(1, ("pool DNS %s completed\n", name));
+ pool->addrs = copy_addrinfo_list(res);
+ pool->ai = pool->addrs;
+ pool_xmit(pool);
+
+}
+#endif /* WORKER */
+
+
+#ifdef AUTOKEY
+/*
+ * key_expire - purge the key list
+ */
+void
+key_expire(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ int i;
+
+ if (peer->keylist != NULL) {
+ for (i = 0; i <= peer->keynumber; i++)
+ authtrust(peer->keylist[i], 0);
+ free(peer->keylist);
+ peer->keylist = NULL;
+ }
+ value_free(&peer->sndval);
+ peer->keynumber = 0;
+ peer->flags &= ~FLAG_ASSOC;
+#ifdef DEBUG
+ if (debug)
+ printf("key_expire: at %lu associd %d\n", current_time,
+ peer->associd);
+#endif
+}
+#endif /* AUTOKEY */
+
+
+/*
+ * local_refid(peer) - check peer refid to avoid selecting peers
+ * currently synced to this ntpd.
+ */
+static int
+local_refid(
+ struct peer * p
+ )
+{
+ endpt * unicast_ep;
+
+ if (p->dstadr != NULL && !(INT_MCASTIF & p->dstadr->flags))
+ unicast_ep = p->dstadr;
+ else
+ unicast_ep = findinterface(&p->srcadr);
+
+ if (unicast_ep != NULL && p->refid == unicast_ep->addr_refid)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+/*
+ * Determine if the peer is unfit for synchronization
+ *
+ * A peer is unfit for synchronization if
+ * > TEST10 bad leap or stratum below floor or at or above ceiling
+ * > TEST11 root distance exceeded for remote peer
+ * > TEST12 a direct or indirect synchronization loop would form
+ * > TEST13 unreachable or noselect
+ */
+int /* FALSE if fit, TRUE if unfit */
+peer_unfit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ int rval = 0;
+
+ /*
+ * A stratum error occurs if (1) the server has never been
+ * synchronized, (2) the server stratum is below the floor or
+ * greater than or equal to the ceiling.
+ */
+ if (peer->leap == LEAP_NOTINSYNC || peer->stratum < sys_floor ||
+ peer->stratum >= sys_ceiling)
+ rval |= TEST10; /* bad synch or stratum */
+
+ /*
+ * A distance error for a remote peer occurs if the root
+ * distance is greater than or equal to the distance threshold
+ * plus the increment due to one host poll interval.
+ */
+ if (!(peer->flags & FLAG_REFCLOCK) && root_distance(peer) >=
+ sys_maxdist + clock_phi * ULOGTOD(peer->hpoll))
+ rval |= TEST11; /* distance exceeded */
+
+ /*
+ * A loop error occurs if the remote peer is synchronized to the
+ * local peer or if the remote peer is synchronized to the same
+ * server as the local peer but only if the remote peer is
+ * neither a reference clock nor an orphan.
+ */
+ if (peer->stratum > 1 && local_refid(peer))
+ rval |= TEST12; /* synchronization loop */
+
+ /*
+ * An unreachable error occurs if the server is unreachable or
+ * the noselect bit is set.
+ */
+ if (!peer->reach || (peer->flags & FLAG_NOSELECT))
+ rval |= TEST13; /* unreachable */
+
+ peer->flash &= ~PEER_TEST_MASK;
+ peer->flash |= rval;
+ return (rval);
+}
+
+
+/*
+ * Find the precision of this particular machine
+ */
+#define MINSTEP 20e-9 /* minimum clock increment (s) */
+#define MAXSTEP 1 /* maximum clock increment (s) */
+#define MINCHANGES 12 /* minimum number of step samples */
+#define MAXLOOPS ((int)(1. / MINSTEP)) /* avoid infinite loop */
+
+/*
+ * This routine measures the system precision defined as the minimum of
+ * a sequence of differences between successive readings of the system
+ * clock. However, if a difference is less than MINSTEP, the clock has
+ * been read more than once during a clock tick and the difference is
+ * ignored. We set MINSTEP greater than zero in case something happens
+ * like a cache miss, and to tolerate underlying system clocks which
+ * ensure each reading is strictly greater than prior readings while
+ * using an underlying stepping (not interpolated) clock.
+ *
+ * sys_tick and sys_precision represent the time to read the clock for
+ * systems with high-precision clocks, and the tick interval or step
+ * size for lower-precision stepping clocks.
+ *
+ * This routine also measures the time to read the clock on stepping
+ * system clocks by counting the number of readings between changes of
+ * the underlying clock. With either type of clock, the minimum time
+ * to read the clock is saved as sys_fuzz, and used to ensure the
+ * get_systime() readings always increase and are fuzzed below sys_fuzz.
+ */
+void
+measure_precision(void)
+{
+ /*
+ * With sys_fuzz set to zero, get_systime() fuzzing of low bits
+ * is effectively disabled. trunc_os_clock is FALSE to disable
+ * get_ostime() simulation of a low-precision system clock.
+ */
+ set_sys_fuzz(0.);
+ trunc_os_clock = FALSE;
+ measured_tick = measure_tick_fuzz();
+ set_sys_tick_precision(measured_tick);
+ msyslog(LOG_INFO, "proto: precision = %.3f usec (%d)",
+ sys_tick * 1e6, sys_precision);
+ if (sys_fuzz < sys_tick) {
+ msyslog(LOG_NOTICE, "proto: fuzz beneath %.3f usec",
+ sys_fuzz * 1e6);
+ }
+}
+
+
+/*
+ * measure_tick_fuzz()
+ *
+ * measures the minimum time to read the clock (stored in sys_fuzz)
+ * and returns the tick, the larger of the minimum increment observed
+ * between successive clock readings and the time to read the clock.
+ */
+double
+measure_tick_fuzz(void)
+{
+ l_fp minstep; /* MINSTEP as l_fp */
+ l_fp val; /* current seconds fraction */
+ l_fp last; /* last seconds fraction */
+ l_fp ldiff; /* val - last */
+ double tick; /* computed tick value */
+ double diff;
+ long repeats;
+ long max_repeats;
+ int changes;
+ int i; /* log2 precision */
+
+ tick = MAXSTEP;
+ max_repeats = 0;
+ repeats = 0;
+ changes = 0;
+ DTOLFP(MINSTEP, &minstep);
+ get_systime(&last);
+ for (i = 0; i < MAXLOOPS && changes < MINCHANGES; i++) {
+ get_systime(&val);
+ ldiff = val;
+ L_SUB(&ldiff, &last);
+ last = val;
+ if (L_ISGT(&ldiff, &minstep)) {
+ max_repeats = max(repeats, max_repeats);
+ repeats = 0;
+ changes++;
+ LFPTOD(&ldiff, diff);
+ tick = min(diff, tick);
+ } else {
+ repeats++;
+ }
+ }
+ if (changes < MINCHANGES) {
+ msyslog(LOG_ERR, "Fatal error: precision could not be measured (MINSTEP too large?)");
+ exit(1);
+ }
+
+ if (0 == max_repeats) {
+ set_sys_fuzz(tick);
+ } else {
+ set_sys_fuzz(tick / max_repeats);
+ }
+
+ return tick;
+}
+
+
+void
+set_sys_tick_precision(
+ double tick
+ )
+{
+ int i;
+
+ if (tick > 1.) {
+ msyslog(LOG_ERR,
+ "unsupported tick %.3f > 1s ignored", tick);
+ return;
+ }
+ if (tick < measured_tick) {
+ msyslog(LOG_ERR,
+ "proto: tick %.3f less than measured tick %.3f, ignored",
+ tick, measured_tick);
+ return;
+ } else if (tick > measured_tick) {
+ trunc_os_clock = TRUE;
+ msyslog(LOG_NOTICE,
+ "proto: truncating system clock to multiples of %.9f",
+ tick);
+ }
+ sys_tick = tick;
+
+ /*
+ * Find the nearest power of two.
+ */
+ for (i = 0; tick <= 1; i--)
+ tick *= 2;
+ if (tick - 1 > 1 - tick / 2)
+ i++;
+
+ sys_precision = (s_char)i;
+}
+
+
+/*
+ * init_proto - initialize the protocol module's data
+ */
+void
+init_proto(void)
+{
+ l_fp dummy;
+ int i;
+
+ /*
+ * Fill in the sys_* stuff. Default is don't listen to
+ * broadcasting, require authentication.
+ */
+ sys_leap = LEAP_NOTINSYNC;
+ sys_stratum = STRATUM_UNSPEC;
+ memcpy(&sys_refid, "INIT", 4);
+ sys_peer = NULL;
+ sys_rootdelay = 0;
+ sys_rootdisp = 0;
+ L_CLR(&sys_reftime);
+ sys_jitter = 0;
+ measure_precision();
+ get_systime(&dummy);
+ sys_survivors = 0;
+ sys_manycastserver = 0;
+ sys_bclient = 0;
+ sys_bdelay = 0;
+ sys_authenticate = 1;
+ sys_stattime = current_time;
+ orphwait = current_time + sys_orphwait;
+ proto_clr_stats();
+ for (i = 0; i < MAX_TTL; i++) {
+ sys_ttl[i] = (u_char)((i * 256) / MAX_TTL);
+ sys_ttlmax = i;
+ }
+ hardpps_enable = 0;
+ stats_control = 1;
+}
+
+
+/*
+ * proto_config - configure the protocol module
+ */
+void
+proto_config(
+ int item,
+ u_long value,
+ double dvalue,
+ sockaddr_u *svalue
+ )
+{
+ /*
+ * Figure out what he wants to change, then do it
+ */
+ DPRINTF(2, ("proto_config: code %d value %lu dvalue %lf\n",
+ item, value, dvalue));
+
+ switch (item) {
+
+ /*
+ * enable and disable commands - arguments are Boolean.
+ */
+ case PROTO_AUTHENTICATE: /* authentication (auth) */
+ sys_authenticate = value;
+ break;
+
+ case PROTO_BROADCLIENT: /* broadcast client (bclient) */
+ sys_bclient = (int)value;
+ if (sys_bclient == 0)
+ io_unsetbclient();
+ else
+ io_setbclient();
+ break;
+
+#ifdef REFCLOCK
+ case PROTO_CAL: /* refclock calibrate (calibrate) */
+ cal_enable = value;
+ break;
+#endif /* REFCLOCK */
+
+ case PROTO_KERNEL: /* kernel discipline (kernel) */
+ select_loop(value);
+ break;
+
+ case PROTO_MONITOR: /* monitoring (monitor) */
+ if (value)
+ mon_start(MON_ON);
+ else
+ mon_stop(MON_ON);
+ break;
+
+ case PROTO_NTP: /* NTP discipline (ntp) */
+ ntp_enable = value;
+ break;
+
+ case PROTO_MODE7: /* mode7 management (ntpdc) */
+ ntp_mode7 = value;
+ break;
+
+ case PROTO_PPS: /* PPS discipline (pps) */
+ hardpps_enable = value;
+ break;
+
+ case PROTO_FILEGEN: /* statistics (stats) */
+ stats_control = value;
+ break;
+
+ /*
+ * tos command - arguments are double, sometimes cast to int
+ */
+ case PROTO_BEACON: /* manycast beacon (beacon) */
+ sys_beacon = (int)dvalue;
+ break;
+
+ case PROTO_BROADDELAY: /* default broadcast delay (bdelay) */
+ sys_bdelay = dvalue;
+ break;
+
+ case PROTO_CEILING: /* stratum ceiling (ceiling) */
+ sys_ceiling = (int)dvalue;
+ break;
+
+ case PROTO_COHORT: /* cohort switch (cohort) */
+ sys_cohort = (int)dvalue;
+ break;
+
+ case PROTO_FLOOR: /* stratum floor (floor) */
+ sys_floor = (int)dvalue;
+ break;
+
+ case PROTO_MAXCLOCK: /* maximum candidates (maxclock) */
+ sys_maxclock = (int)dvalue;
+ break;
+
+ case PROTO_MAXDIST: /* select threshold (maxdist) */
+ sys_maxdist = dvalue;
+ break;
+
+ case PROTO_CALLDELAY: /* modem call delay (mdelay) */
+ break; /* NOT USED */
+
+ case PROTO_MINCLOCK: /* minimum candidates (minclock) */
+ sys_minclock = (int)dvalue;
+ break;
+
+ case PROTO_MINDISP: /* minimum distance (mindist) */
+ sys_mindisp = dvalue;
+ break;
+
+ case PROTO_MINSANE: /* minimum survivors (minsane) */
+ sys_minsane = (int)dvalue;
+ break;
+
+ case PROTO_ORPHAN: /* orphan stratum (orphan) */
+ sys_orphan = (int)dvalue;
+ break;
+
+ case PROTO_ORPHWAIT: /* orphan wait (orphwait) */
+ orphwait -= sys_orphwait;
+ sys_orphwait = (int)dvalue;
+ orphwait += sys_orphwait;
+ break;
+
+ /*
+ * Miscellaneous commands
+ */
+ case PROTO_MULTICAST_ADD: /* add group address */
+ if (svalue != NULL)
+ io_multicast_add(svalue);
+ sys_bclient = 1;
+ break;
+
+ case PROTO_MULTICAST_DEL: /* delete group address */
+ if (svalue != NULL)
+ io_multicast_del(svalue);
+ break;
+
+ default:
+ msyslog(LOG_NOTICE,
+ "proto: unsupported option %d", item);
+ }
+}
+
+
+/*
+ * proto_clr_stats - clear protocol stat counters
+ */
+void
+proto_clr_stats(void)
+{
+ sys_stattime = current_time;
+ sys_received = 0;
+ sys_processed = 0;
+ sys_newversion = 0;
+ sys_oldversion = 0;
+ sys_declined = 0;
+ sys_restricted = 0;
+ sys_badlength = 0;
+ sys_badauth = 0;
+ sys_limitrejected = 0;
+ sys_kodsent = 0;
+}
diff --git a/ntpd/ntp_refclock.c b/ntpd/ntp_refclock.c
new file mode 100644
index 0000000..f0e9b9e
--- /dev/null
+++ b/ntpd/ntp_refclock.c
@@ -0,0 +1,1332 @@
+/*
+ * ntp_refclock - processing support for reference clocks
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_tty.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_assert.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#ifdef REFCLOCK
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif /* KERNEL_PLL */
+
+#ifdef HAVE_PPSAPI
+#include "ppsapi_timepps.h"
+#include "refclock_atom.h"
+#endif /* HAVE_PPSAPI */
+
+/*
+ * Reference clock support is provided here by maintaining the fiction
+ * that the clock is actually a peer. As no packets are exchanged with
+ * a reference clock, however, we replace the transmit, receive and
+ * packet procedures with separate code to simulate them. Routines
+ * refclock_transmit() and refclock_receive() maintain the peer
+ * variables in a state analogous to an actual peer and pass reference
+ * clock data on through the filters. Routines refclock_peer() and
+ * refclock_unpeer() are called to initialize and terminate reference
+ * clock associations. A set of utility routines is included to open
+ * serial devices, process sample data, and to perform various debugging
+ * functions.
+ *
+ * The main interface used by these routines is the refclockproc
+ * structure, which contains for most drivers the decimal equivalants
+ * of the year, day, month, hour, second and millisecond/microsecond
+ * decoded from the ASCII timecode. Additional information includes
+ * the receive timestamp, exception report, statistics tallies, etc.
+ * In addition, there may be a driver-specific unit structure used for
+ * local control of the device.
+ *
+ * The support routines are passed a pointer to the peer structure,
+ * which is used for all peer-specific processing and contains a
+ * pointer to the refclockproc structure, which in turn contains a
+ * pointer to the unit structure, if used. The peer structure is
+ * identified by an interface address in the dotted quad form
+ * 127.127.t.u, where t is the clock type and u the unit.
+ */
+#define FUDGEFAC .1 /* fudge correction factor */
+#define LF 0x0a /* ASCII LF */
+
+int cal_enable; /* enable refclock calibrate */
+
+/*
+ * Forward declarations
+ */
+static int refclock_cmpl_fp (const void *, const void *);
+static int refclock_sample (struct refclockproc *);
+static int refclock_ioctl(int, u_int);
+
+
+/*
+ * refclock_report - note the occurance of an event
+ *
+ * This routine presently just remembers the report and logs it, but
+ * does nothing heroic for the trap handler. It tries to be a good
+ * citizen and bothers the system log only if things change.
+ */
+void
+refclock_report(
+ struct peer *peer,
+ int code
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ if (pp == NULL)
+ return;
+
+ switch (code) {
+
+ case CEVNT_TIMEOUT:
+ pp->noreply++;
+ break;
+
+ case CEVNT_BADREPLY:
+ pp->badformat++;
+ break;
+
+ case CEVNT_FAULT:
+ break;
+
+ case CEVNT_BADDATE:
+ case CEVNT_BADTIME:
+ pp->baddata++;
+ break;
+
+ default:
+ /* ignore others */
+ break;
+ }
+ if (pp->lastevent < 15)
+ pp->lastevent++;
+ if (pp->currentstatus != code) {
+ pp->currentstatus = (u_char)code;
+ report_event(PEVNT_CLOCK, peer, ceventstr(code));
+ }
+}
+
+
+/*
+ * init_refclock - initialize the reference clock drivers
+ *
+ * This routine calls each of the drivers in turn to initialize internal
+ * variables, if necessary. Most drivers have nothing to say at this
+ * point.
+ */
+void
+init_refclock(void)
+{
+ int i;
+
+ for (i = 0; i < (int)num_refclock_conf; i++)
+ if (refclock_conf[i]->clock_init != noentry)
+ (refclock_conf[i]->clock_init)();
+}
+
+
+/*
+ * refclock_newpeer - initialize and start a reference clock
+ *
+ * This routine allocates and initializes the interface structure which
+ * supports a reference clock in the form of an ordinary NTP peer. A
+ * driver-specific support routine completes the initialization, if
+ * used. Default peer variables which identify the clock and establish
+ * its reference ID and stratum are set here. It returns one if success
+ * and zero if the clock address is invalid or already running,
+ * insufficient resources are available or the driver declares a bum
+ * rap.
+ */
+int
+refclock_newpeer(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+
+ /*
+ * Check for valid clock address. If already running, shut it
+ * down first.
+ */
+ if (!ISREFCLOCKADR(&peer->srcadr)) {
+ msyslog(LOG_ERR,
+ "refclock_newpeer: clock address %s invalid",
+ stoa(&peer->srcadr));
+ return (0);
+ }
+ clktype = (u_char)REFCLOCKTYPE(&peer->srcadr);
+ unit = REFCLOCKUNIT(&peer->srcadr);
+ if (clktype >= num_refclock_conf ||
+ refclock_conf[clktype]->clock_start == noentry) {
+ msyslog(LOG_ERR,
+ "refclock_newpeer: clock type %d invalid\n",
+ clktype);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize interface structure
+ */
+ pp = emalloc_zero(sizeof(*pp));
+ peer->procptr = pp;
+
+ /*
+ * Initialize structures
+ */
+ peer->refclktype = clktype;
+ peer->refclkunit = (u_char)unit;
+ peer->flags |= FLAG_REFCLOCK;
+ peer->leap = LEAP_NOTINSYNC;
+ peer->stratum = STRATUM_REFCLOCK;
+ peer->ppoll = peer->maxpoll;
+ pp->type = clktype;
+ pp->conf = refclock_conf[clktype];
+ pp->timestarted = current_time;
+ pp->io.fd = -1;
+
+ /*
+ * Set peer.pmode based on the hmode. For appearances only.
+ */
+ switch (peer->hmode) {
+ case MODE_ACTIVE:
+ peer->pmode = MODE_PASSIVE;
+ break;
+
+ default:
+ peer->pmode = MODE_SERVER;
+ break;
+ }
+
+ /*
+ * Do driver dependent initialization. The above defaults
+ * can be wiggled, then finish up for consistency.
+ */
+ if (!((refclock_conf[clktype]->clock_start)(unit, peer))) {
+ refclock_unpeer(peer);
+ return (0);
+ }
+ peer->refid = pp->refid;
+ return (1);
+}
+
+
+/*
+ * refclock_unpeer - shut down a clock
+ */
+void
+refclock_unpeer(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ u_char clktype;
+ int unit;
+
+ /*
+ * Wiggle the driver to release its resources, then give back
+ * the interface structure.
+ */
+ if (NULL == peer->procptr)
+ return;
+
+ clktype = peer->refclktype;
+ unit = peer->refclkunit;
+ if (refclock_conf[clktype]->clock_shutdown != noentry)
+ (refclock_conf[clktype]->clock_shutdown)(unit, peer);
+ free(peer->procptr);
+ peer->procptr = NULL;
+}
+
+
+/*
+ * refclock_timer - called once per second for housekeeping.
+ */
+void
+refclock_timer(
+ struct peer *p
+ )
+{
+ struct refclockproc * pp;
+ int unit;
+
+ unit = p->refclkunit;
+ pp = p->procptr;
+ if (pp->conf->clock_timer != noentry)
+ (*pp->conf->clock_timer)(unit, p);
+ if (pp->action != NULL && pp->nextaction <= current_time)
+ (*pp->action)(p);
+}
+
+
+/*
+ * refclock_transmit - simulate the transmit procedure
+ *
+ * This routine implements the NTP transmit procedure for a reference
+ * clock. This provides a mechanism to call the driver at the NTP poll
+ * interval, as well as provides a reachability mechanism to detect a
+ * broken radio or other madness.
+ */
+void
+refclock_transmit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ u_char clktype;
+ int unit;
+
+ clktype = peer->refclktype;
+ unit = peer->refclkunit;
+ peer->sent++;
+ get_systime(&peer->xmt);
+
+ /*
+ * This is a ripoff of the peer transmit routine, but
+ * specialized for reference clocks. We do a little less
+ * protocol here and call the driver-specific transmit routine.
+ */
+ if (peer->burst == 0) {
+ u_char oreach;
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_transmit: at %ld %s\n",
+ current_time, stoa(&(peer->srcadr)));
+#endif
+
+ /*
+ * Update reachability and poll variables like the
+ * network code.
+ */
+ oreach = peer->reach & 0xfe;
+ peer->reach <<= 1;
+ if (!(peer->reach & 0x0f))
+ clock_filter(peer, 0., 0., MAXDISPERSE);
+ peer->outdate = current_time;
+ if (!peer->reach) {
+ if (oreach) {
+ report_event(PEVNT_UNREACH, peer, NULL);
+ peer->timereachable = current_time;
+ }
+ } else {
+ if (peer->flags & FLAG_BURST)
+ peer->burst = NSTAGE;
+ }
+ } else {
+ peer->burst--;
+ }
+ if (refclock_conf[clktype]->clock_poll != noentry)
+ (refclock_conf[clktype]->clock_poll)(unit, peer);
+ poll_update(peer, peer->hpoll);
+}
+
+
+/*
+ * Compare two doubles - used with qsort()
+ */
+static int
+refclock_cmpl_fp(
+ const void *p1,
+ const void *p2
+ )
+{
+ const double *dp1 = (const double *)p1;
+ const double *dp2 = (const double *)p2;
+
+ if (*dp1 < *dp2)
+ return -1;
+ if (*dp1 > *dp2)
+ return 1;
+ return 0;
+}
+
+
+/*
+ * refclock_process_offset - update median filter
+ *
+ * This routine uses the given offset and timestamps to construct a new
+ * entry in the median filter circular buffer. Samples that overflow the
+ * filter are quietly discarded.
+ */
+void
+refclock_process_offset(
+ struct refclockproc *pp, /* refclock structure pointer */
+ l_fp lasttim, /* last timecode timestamp */
+ l_fp lastrec, /* last receive timestamp */
+ double fudge
+ )
+{
+ l_fp lftemp;
+ double doffset;
+
+ pp->lastrec = lastrec;
+ lftemp = lasttim;
+ L_SUB(&lftemp, &lastrec);
+ LFPTOD(&lftemp, doffset);
+ SAMPLE(doffset + fudge);
+}
+
+
+/*
+ * refclock_process - process a sample from the clock
+ * refclock_process_f - refclock_process with other than time1 fudge
+ *
+ * This routine converts the timecode in the form days, hours, minutes,
+ * seconds and milliseconds/microseconds to internal timestamp format,
+ * then constructs a new entry in the median filter circular buffer.
+ * Return success (1) if the data are correct and consistent with the
+ * converntional calendar.
+ *
+ * Important for PPS users: Normally, the pp->lastrec is set to the
+ * system time when the on-time character is received and the pp->year,
+ * ..., pp->second decoded and the seconds fraction pp->nsec in
+ * nanoseconds). When a PPS offset is available, pp->nsec is forced to
+ * zero and the fraction for pp->lastrec is set to the PPS offset.
+ */
+int
+refclock_process_f(
+ struct refclockproc *pp, /* refclock structure pointer */
+ double fudge
+ )
+{
+ l_fp offset, ltemp;
+
+ /*
+ * Compute the timecode timestamp from the days, hours, minutes,
+ * seconds and milliseconds/microseconds of the timecode. Use
+ * clocktime() for the aggregate seconds and the msec/usec for
+ * the fraction, when present. Note that this code relies on the
+ * filesystem time for the years and does not use the years of
+ * the timecode.
+ */
+ if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT,
+ pp->lastrec.l_ui, &pp->yearstart, &offset.l_ui))
+ return (0);
+
+ offset.l_uf = 0;
+ DTOLFP(pp->nsec / 1e9, &ltemp);
+ L_ADD(&offset, &ltemp);
+ refclock_process_offset(pp, offset, pp->lastrec, fudge);
+ return (1);
+}
+
+
+int
+refclock_process(
+ struct refclockproc *pp /* refclock structure pointer */
+)
+{
+ return refclock_process_f(pp, pp->fudgetime1);
+}
+
+
+/*
+ * refclock_sample - process a pile of samples from the clock
+ *
+ * This routine implements a recursive median filter to suppress spikes
+ * in the data, as well as determine a performance statistic. It
+ * calculates the mean offset and RMS jitter. A time adjustment
+ * fudgetime1 can be added to the final offset to compensate for various
+ * systematic errors. The routine returns the number of samples
+ * processed, which could be zero.
+ */
+static int
+refclock_sample(
+ struct refclockproc *pp /* refclock structure pointer */
+ )
+{
+ size_t i, j, k, m, n;
+ double off[MAXSTAGE];
+ double offset;
+
+ /*
+ * Copy the raw offsets and sort into ascending order. Don't do
+ * anything if the buffer is empty.
+ */
+ n = 0;
+ while (pp->codeproc != pp->coderecv) {
+ pp->codeproc = (pp->codeproc + 1) % MAXSTAGE;
+ off[n] = pp->filter[pp->codeproc];
+ n++;
+ }
+ if (n == 0)
+ return (0);
+
+ if (n > 1)
+ qsort(off, n, sizeof(off[0]), refclock_cmpl_fp);
+
+ /*
+ * Reject the furthest from the median of the samples until
+ * approximately 60 percent of the samples remain.
+ */
+ i = 0; j = n;
+ m = n - (n * 4) / 10;
+ while ((j - i) > m) {
+ offset = off[(j + i) / 2];
+ if (off[j - 1] - offset < offset - off[i])
+ i++; /* reject low end */
+ else
+ j--; /* reject high end */
+ }
+
+ /*
+ * Determine the offset and jitter.
+ */
+ pp->offset = 0;
+ pp->jitter = 0;
+ for (k = i; k < j; k++) {
+ pp->offset += off[k];
+ if (k > i)
+ pp->jitter += SQUARE(off[k] - off[k - 1]);
+ }
+ pp->offset /= m;
+ pp->jitter = max(SQRT(pp->jitter / m), LOGTOD(sys_precision));
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "refclock_sample: n %d offset %.6f disp %.6f jitter %.6f\n",
+ (int)n, pp->offset, pp->disp, pp->jitter);
+#endif
+ return (int)n;
+}
+
+
+/*
+ * refclock_receive - simulate the receive and packet procedures
+ *
+ * This routine simulates the NTP receive and packet procedures for a
+ * reference clock. This provides a mechanism in which the ordinary NTP
+ * filter, selection and combining algorithms can be used to suppress
+ * misbehaving radios and to mitigate between them when more than one is
+ * available for backup.
+ */
+void
+refclock_receive(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_receive: at %lu %s\n",
+ current_time, stoa(&peer->srcadr));
+#endif
+
+ /*
+ * Do a little sanity dance and update the peer structure. Groom
+ * the median filter samples and give the data to the clock
+ * filter.
+ */
+ pp = peer->procptr;
+ peer->leap = pp->leap;
+ if (peer->leap == LEAP_NOTINSYNC)
+ return;
+
+ peer->received++;
+ peer->timereceived = current_time;
+ if (!peer->reach) {
+ report_event(PEVNT_REACH, peer, NULL);
+ peer->timereachable = current_time;
+ }
+ peer->reach |= 1;
+ peer->reftime = pp->lastref;
+ peer->aorg = pp->lastrec;
+ peer->rootdisp = pp->disp;
+ get_systime(&peer->dst);
+ if (!refclock_sample(pp))
+ return;
+
+ clock_filter(peer, pp->offset, 0., pp->jitter);
+ if (cal_enable && fabs(last_offset) < sys_mindisp && sys_peer !=
+ NULL) {
+ if (sys_peer->refclktype == REFCLK_ATOM_PPS &&
+ peer->refclktype != REFCLK_ATOM_PPS)
+ pp->fudgetime1 -= pp->offset * FUDGEFAC;
+ }
+}
+
+
+/*
+ * refclock_gtlin - groom next input line and extract timestamp
+ *
+ * This routine processes the timecode received from the clock and
+ * strips the parity bit and control characters. It returns the number
+ * of characters in the line followed by a NULL character ('\0'), which
+ * is not included in the count. In case of an empty line, the previous
+ * line is preserved.
+ */
+int
+refclock_gtlin(
+ struct recvbuf *rbufp, /* receive buffer pointer */
+ char *lineptr, /* current line pointer */
+ int bmax, /* remaining characters in line */
+ l_fp *tsptr /* pointer to timestamp returned */
+ )
+{
+ const char *sp, *spend;
+ char *dp, *dpend;
+ int dlen;
+
+ if (bmax <= 0)
+ return (0);
+
+ dp = lineptr;
+ dpend = dp + bmax - 1; /* leave room for NUL pad */
+ sp = (const char *)rbufp->recv_buffer;
+ spend = sp + rbufp->recv_length;
+
+ while (sp != spend && dp != dpend) {
+ char c;
+
+ c = *sp++ & 0x7f;
+ if (c >= 0x20 && c < 0x7f)
+ *dp++ = c;
+ }
+ /* Get length of data written to the destination buffer. If
+ * zero, do *not* place a NUL byte to preserve the previous
+ * buffer content.
+ */
+ dlen = dp - lineptr;
+ if (dlen)
+ *dp = '\0';
+ *tsptr = rbufp->recv_time;
+ DPRINTF(2, ("refclock_gtlin: fd %d time %s timecode %d %s\n",
+ rbufp->fd, ulfptoa(&rbufp->recv_time, 6), dlen,
+ (dlen != 0)
+ ? lineptr
+ : ""));
+ return (dlen);
+}
+
+
+/*
+ * refclock_gtraw - get next line/chunk of data
+ *
+ * This routine returns the raw data received from the clock in both
+ * canonical or raw modes. The terminal interface routines map CR to LF.
+ * In canonical mode this results in two lines, one containing data
+ * followed by LF and another containing only LF. In raw mode the
+ * interface routines can deliver arbitraty chunks of data from one
+ * character to a maximum specified by the calling routine. In either
+ * mode the routine returns the number of characters in the line
+ * followed by a NULL character ('\0'), which is not included in the
+ * count.
+ *
+ * *tsptr receives a copy of the buffer timestamp.
+ */
+int
+refclock_gtraw(
+ struct recvbuf *rbufp, /* receive buffer pointer */
+ char *lineptr, /* current line pointer */
+ int bmax, /* remaining characters in line */
+ l_fp *tsptr /* pointer to timestamp returned */
+ )
+{
+ if (bmax <= 0)
+ return (0);
+ bmax -= 1; /* leave room for trailing NUL */
+ if (bmax > rbufp->recv_length)
+ bmax = rbufp->recv_length;
+ memcpy(lineptr, rbufp->recv_buffer, bmax);
+ lineptr[bmax] = '\0';
+
+ *tsptr = rbufp->recv_time;
+ DPRINTF(2, ("refclock_gtraw: fd %d time %s timecode %d %s\n",
+ rbufp->fd, ulfptoa(&rbufp->recv_time, 6), bmax,
+ lineptr));
+ return (bmax);
+}
+
+
+/*
+ * indicate_refclock_packet()
+ *
+ * Passes a fragment of refclock input read from the device to the
+ * driver direct input routine, which may consume it (batch it for
+ * queuing once a logical unit is assembled). If it is not so
+ * consumed, queue it for the driver's receive entrypoint.
+ *
+ * The return value is TRUE if the data has been consumed as a fragment
+ * and should not be counted as a received packet.
+ */
+int
+indicate_refclock_packet(
+ struct refclockio * rio,
+ struct recvbuf * rb
+ )
+{
+ /* Does this refclock use direct input routine? */
+ if (rio->io_input != NULL && (*rio->io_input)(rb) == 0) {
+ /*
+ * data was consumed - nothing to pass up
+ * into block input machine
+ */
+ freerecvbuf(rb);
+
+ return TRUE;
+ }
+ add_full_recv_buffer(rb);
+
+ return FALSE;
+}
+
+
+/*
+ * process_refclock_packet()
+ *
+ * Used for deferred processing of 'io_input' on systems where threading
+ * is used (notably Windows). This is acting as a trampoline to make the
+ * real calls to the refclock functions.
+ */
+#ifdef HAVE_IO_COMPLETION_PORT
+void
+process_refclock_packet(
+ struct recvbuf * rb
+ )
+{
+ struct refclockio * rio;
+
+ /* get the refclockio structure from the receive buffer */
+ rio = &rb->recv_peer->procptr->io;
+
+ /* call 'clock_recv' if either there is no input function or the
+ * raw input function tells us to feed the packet to the
+ * receiver.
+ */
+ if (rio->io_input == NULL || (*rio->io_input)(rb) != 0) {
+ rio->recvcount++;
+ packets_received++;
+ handler_pkts++;
+ (*rio->clock_recv)(rb);
+ }
+}
+#endif /* HAVE_IO_COMPLETION_PORT */
+
+
+/*
+ * The following code does not apply to WINNT & VMS ...
+ */
+#if !defined(SYS_VXWORKS) && !defined(SYS_WINNT)
+#if defined(HAVE_TERMIOS) || defined(HAVE_SYSV_TTYS) || defined(HAVE_BSD_TTYS)
+
+/*
+ * refclock_open - open serial port for reference clock
+ *
+ * This routine opens a serial port for I/O and sets default options. It
+ * returns the file descriptor if successful, or logs an error and
+ * returns -1.
+ */
+int
+refclock_open(
+ char *dev, /* device name pointer */
+ u_int speed, /* serial port speed (code) */
+ u_int lflags /* line discipline flags */
+ )
+{
+ int fd;
+ int omode;
+#ifdef O_NONBLOCK
+ char trash[128]; /* litter bin for old input data */
+#endif
+
+ /*
+ * Open serial port and set default options
+ */
+ omode = O_RDWR;
+#ifdef O_NONBLOCK
+ omode |= O_NONBLOCK;
+#endif
+#ifdef O_NOCTTY
+ omode |= O_NOCTTY;
+#endif
+
+ fd = open(dev, omode, 0777);
+ /* refclock_open() long returned 0 on failure, avoid it. */
+ if (0 == fd) {
+ fd = dup(0);
+ SAVE_ERRNO(
+ close(0);
+ )
+ }
+ if (fd < 0) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR, "refclock_open %s: %m", dev);
+ )
+ return -1;
+ }
+ if (!refclock_setup(fd, speed, lflags)) {
+ close(fd);
+ return -1;
+ }
+ if (!refclock_ioctl(fd, lflags)) {
+ close(fd);
+ return -1;
+ }
+#ifdef O_NONBLOCK
+ /*
+ * We want to make sure there is no pending trash in the input
+ * buffer. Since we have non-blocking IO available, this is a
+ * good moment to read and dump all available outdated stuff
+ * that might have become toxic for the driver.
+ */
+ while (read(fd, trash, sizeof(trash)) > 0 || errno == EINTR)
+ /*NOP*/;
+#endif
+ return fd;
+}
+
+
+/*
+ * refclock_setup - initialize terminal interface structure
+ */
+int
+refclock_setup(
+ int fd, /* file descriptor */
+ u_int speed, /* serial port speed (code) */
+ u_int lflags /* line discipline flags */
+ )
+{
+ int i;
+ TTY ttyb, *ttyp;
+
+ /*
+ * By default, the serial line port is initialized in canonical
+ * (line-oriented) mode at specified line speed, 8 bits and no
+ * parity. LF ends the line and CR is mapped to LF. The break,
+ * erase and kill functions are disabled. There is a different
+ * section for each terminal interface, as selected at compile
+ * time. The flag bits can be used to set raw mode and echo.
+ */
+ ttyp = &ttyb;
+#ifdef HAVE_TERMIOS
+
+ /*
+ * POSIX serial line parameters (termios interface)
+ */
+ if (tcgetattr(fd, ttyp) < 0) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d tcgetattr: %m",
+ fd);
+ )
+ return FALSE;
+ }
+
+ /*
+ * Set canonical mode and local connection; set specified speed,
+ * 8 bits and no parity; map CR to NL; ignore break.
+ */
+ if (speed) {
+ u_int ltemp = 0;
+
+ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = CS8 | CLOCAL | CREAD;
+ if (lflags & LDISC_7O1) {
+ /* HP Z3801A needs 7-bit, odd parity */
+ ttyp->c_cflag = CS7 | PARENB | PARODD | CLOCAL | CREAD;
+ }
+ cfsetispeed(&ttyb, speed);
+ cfsetospeed(&ttyb, speed);
+ for (i = 0; i < NCCS; ++i)
+ ttyp->c_cc[i] = '\0';
+
+#if defined(TIOCMGET) && !defined(SCO5_CLOCK)
+
+ /*
+ * If we have modem control, check to see if modem leads
+ * are active; if so, set remote connection. This is
+ * necessary for the kernel pps mods to work.
+ */
+ if (ioctl(fd, TIOCMGET, (char *)&ltemp) < 0)
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TIOCMGET: %m", fd);
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_setup fd %d modem status: 0x%x\n",
+ fd, ltemp);
+#endif
+ if (ltemp & TIOCM_DSR && lflags & LDISC_REMOTE)
+ ttyp->c_cflag &= ~CLOCAL;
+#endif /* TIOCMGET */
+ }
+
+ /*
+ * Set raw and echo modes. These can be changed on-fly.
+ */
+ ttyp->c_lflag = ICANON;
+ if (lflags & LDISC_RAW) {
+ ttyp->c_lflag = 0;
+ ttyp->c_iflag = 0;
+ ttyp->c_cc[VMIN] = 1;
+ }
+ if (lflags & LDISC_ECHO)
+ ttyp->c_lflag |= ECHO;
+ if (tcsetattr(fd, TCSANOW, ttyp) < 0) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TCSANOW: %m",
+ fd);
+ )
+ return FALSE;
+ }
+
+ /*
+ * flush input and output buffers to discard any outdated stuff
+ * that might have become toxic for the driver. Failing to do so
+ * is logged, but we keep our fingers crossed otherwise.
+ */
+ if (tcflush(fd, TCIOFLUSH) < 0)
+ msyslog(LOG_ERR, "refclock_setup fd %d tcflush(): %m",
+ fd);
+#endif /* HAVE_TERMIOS */
+
+#ifdef HAVE_SYSV_TTYS
+
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ if (ioctl(fd, TCGETA, ttyp) < 0) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TCGETA: %m",
+ fd);
+ )
+ return FALSE;
+ }
+
+ /*
+ * Set canonical mode and local connection; set specified speed,
+ * 8 bits and no parity; map CR to NL; ignore break.
+ */
+ if (speed) {
+ u_int ltemp = 0;
+
+ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = speed | CS8 | CLOCAL | CREAD;
+ for (i = 0; i < NCCS; ++i)
+ ttyp->c_cc[i] = '\0';
+
+#if defined(TIOCMGET) && !defined(SCO5_CLOCK)
+
+ /*
+ * If we have modem control, check to see if modem leads
+ * are active; if so, set remote connection. This is
+ * necessary for the kernel pps mods to work.
+ */
+ if (ioctl(fd, TIOCMGET, (char *)&ltemp) < 0)
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TIOCMGET: %m", fd);
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_setup fd %d modem status: %x\n",
+ fd, ltemp);
+#endif
+ if (ltemp & TIOCM_DSR)
+ ttyp->c_cflag &= ~CLOCAL;
+#endif /* TIOCMGET */
+ }
+
+ /*
+ * Set raw and echo modes. These can be changed on-fly.
+ */
+ ttyp->c_lflag = ICANON;
+ if (lflags & LDISC_RAW) {
+ ttyp->c_lflag = 0;
+ ttyp->c_iflag = 0;
+ ttyp->c_cc[VMIN] = 1;
+ }
+ if (ioctl(fd, TCSETA, ttyp) < 0) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TCSETA: %m", fd);
+ )
+ return FALSE;
+ }
+#endif /* HAVE_SYSV_TTYS */
+
+#ifdef HAVE_BSD_TTYS
+
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ */
+ if (ioctl(fd, TIOCGETP, (char *)ttyp) < 0) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TIOCGETP: %m",
+ fd);
+ )
+ return FALSE;
+ }
+ if (speed)
+ ttyp->sg_ispeed = ttyp->sg_ospeed = speed;
+ ttyp->sg_flags = EVENP | ODDP | CRMOD;
+ if (ioctl(fd, TIOCSETP, (char *)ttyp) < 0) {
+ SAVE_ERRNO(
+ msyslog(LOG_ERR, "refclock_setup TIOCSETP: %m");
+ )
+ return FALSE;
+ }
+#endif /* HAVE_BSD_TTYS */
+ return(1);
+}
+#endif /* HAVE_TERMIOS || HAVE_SYSV_TTYS || HAVE_BSD_TTYS */
+
+
+/*
+ * refclock_ioctl - set serial port control functions
+ *
+ * This routine attempts to hide the internal, system-specific details
+ * of serial ports. It can handle POSIX (termios), SYSV (termio) and BSD
+ * (sgtty) interfaces with varying degrees of success. The routine sets
+ * up optional features such as tty_clk. The routine returns TRUE if
+ * successful.
+ */
+int
+refclock_ioctl(
+ int fd, /* file descriptor */
+ u_int lflags /* line discipline flags */
+ )
+{
+ /*
+ * simply return TRUE if no UNIX line discipline is supported
+ */
+ DPRINTF(1, ("refclock_ioctl: fd %d flags 0x%x\n", fd, lflags));
+
+ return TRUE;
+}
+#endif /* !defined(SYS_VXWORKS) && !defined(SYS_WINNT) */
+
+
+/*
+ * refclock_control - set and/or return clock values
+ *
+ * This routine is used mainly for debugging. It returns designated
+ * values from the interface structure that can be displayed using
+ * ntpdc and the clockstat command. It can also be used to initialize
+ * configuration variables, such as fudgetimes, fudgevalues, reference
+ * ID and stratum.
+ */
+void
+refclock_control(
+ sockaddr_u *srcadr,
+ const struct refclockstat *in,
+ struct refclockstat *out
+ )
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+
+ /*
+ * Check for valid address and running peer
+ */
+ if (!ISREFCLOCKADR(srcadr))
+ return;
+
+ clktype = (u_char)REFCLOCKTYPE(srcadr);
+ unit = REFCLOCKUNIT(srcadr);
+
+ peer = findexistingpeer(srcadr, NULL, NULL, -1, 0);
+
+ if (NULL == peer)
+ return;
+
+ NTP_INSIST(peer->procptr != NULL);
+ pp = peer->procptr;
+
+ /*
+ * Initialize requested data
+ */
+ if (in != NULL) {
+ if (in->haveflags & CLK_HAVETIME1)
+ pp->fudgetime1 = in->fudgetime1;
+ if (in->haveflags & CLK_HAVETIME2)
+ pp->fudgetime2 = in->fudgetime2;
+ if (in->haveflags & CLK_HAVEVAL1)
+ peer->stratum = pp->stratum = (u_char)in->fudgeval1;
+ if (in->haveflags & CLK_HAVEVAL2)
+ peer->refid = pp->refid = in->fudgeval2;
+ if (in->haveflags & CLK_HAVEFLAG1) {
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG1;
+ }
+ if (in->haveflags & CLK_HAVEFLAG2) {
+ pp->sloppyclockflag &= ~CLK_FLAG2;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG2;
+ }
+ if (in->haveflags & CLK_HAVEFLAG3) {
+ pp->sloppyclockflag &= ~CLK_FLAG3;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG3;
+ }
+ if (in->haveflags & CLK_HAVEFLAG4) {
+ pp->sloppyclockflag &= ~CLK_FLAG4;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG4;
+ }
+ }
+
+ /*
+ * Readback requested data
+ */
+ if (out != NULL) {
+ out->fudgeval1 = pp->stratum;
+ out->fudgeval2 = pp->refid;
+ out->haveflags = CLK_HAVEVAL1 | CLK_HAVEVAL2;
+ out->fudgetime1 = pp->fudgetime1;
+ if (0.0 != out->fudgetime1)
+ out->haveflags |= CLK_HAVETIME1;
+ out->fudgetime2 = pp->fudgetime2;
+ if (0.0 != out->fudgetime2)
+ out->haveflags |= CLK_HAVETIME2;
+ out->flags = (u_char) pp->sloppyclockflag;
+ if (CLK_FLAG1 & out->flags)
+ out->haveflags |= CLK_HAVEFLAG1;
+ if (CLK_FLAG2 & out->flags)
+ out->haveflags |= CLK_HAVEFLAG2;
+ if (CLK_FLAG3 & out->flags)
+ out->haveflags |= CLK_HAVEFLAG3;
+ if (CLK_FLAG4 & out->flags)
+ out->haveflags |= CLK_HAVEFLAG4;
+
+ out->timereset = current_time - pp->timestarted;
+ out->polls = pp->polls;
+ out->noresponse = pp->noreply;
+ out->badformat = pp->badformat;
+ out->baddata = pp->baddata;
+
+ out->lastevent = pp->lastevent;
+ out->currentstatus = pp->currentstatus;
+ out->type = pp->type;
+ out->clockdesc = pp->clockdesc;
+ out->lencode = (u_short)pp->lencode;
+ out->p_lastcode = pp->a_lastcode;
+ }
+
+ /*
+ * Give the stuff to the clock
+ */
+ if (refclock_conf[clktype]->clock_control != noentry)
+ (refclock_conf[clktype]->clock_control)(unit, in, out, peer);
+}
+
+
+/*
+ * refclock_buginfo - return debugging info
+ *
+ * This routine is used mainly for debugging. It returns designated
+ * values from the interface structure that can be displayed using
+ * ntpdc and the clkbug command.
+ */
+void
+refclock_buginfo(
+ sockaddr_u *srcadr, /* clock address */
+ struct refclockbug *bug /* output structure */
+ )
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ int clktype;
+ int unit;
+ unsigned u;
+
+ /*
+ * Check for valid address and peer structure
+ */
+ if (!ISREFCLOCKADR(srcadr))
+ return;
+
+ clktype = (u_char) REFCLOCKTYPE(srcadr);
+ unit = REFCLOCKUNIT(srcadr);
+
+ peer = findexistingpeer(srcadr, NULL, NULL, -1, 0);
+
+ if (NULL == peer || NULL == peer->procptr)
+ return;
+
+ pp = peer->procptr;
+
+ /*
+ * Copy structure values
+ */
+ bug->nvalues = 8;
+ bug->svalues = 0x0000003f;
+ bug->values[0] = pp->year;
+ bug->values[1] = pp->day;
+ bug->values[2] = pp->hour;
+ bug->values[3] = pp->minute;
+ bug->values[4] = pp->second;
+ bug->values[5] = pp->nsec;
+ bug->values[6] = pp->yearstart;
+ bug->values[7] = pp->coderecv;
+ bug->stimes = 0xfffffffc;
+ bug->times[0] = pp->lastref;
+ bug->times[1] = pp->lastrec;
+ for (u = 2; u < bug->ntimes; u++)
+ DTOLFP(pp->filter[u - 2], &bug->times[u]);
+
+ /*
+ * Give the stuff to the clock
+ */
+ if (refclock_conf[clktype]->clock_buginfo != noentry)
+ (refclock_conf[clktype]->clock_buginfo)(unit, bug, peer);
+}
+
+
+#ifdef HAVE_PPSAPI
+/*
+ * refclock_ppsapi - initialize/update ppsapi
+ *
+ * This routine is called after the fudge command to open the PPSAPI
+ * interface for later parameter setting after the fudge command.
+ */
+int
+refclock_ppsapi(
+ int fddev, /* fd device */
+ struct refclock_atom *ap /* atom structure pointer */
+ )
+{
+ if (ap->handle == 0) {
+ if (time_pps_create(fddev, &ap->handle) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ppsapi: time_pps_create: %m");
+ return (0);
+ }
+ }
+ return (1);
+}
+
+
+/*
+ * refclock_params - set ppsapi parameters
+ *
+ * This routine is called to set the PPSAPI parameters after the fudge
+ * command.
+ */
+int
+refclock_params(
+ int mode, /* mode bits */
+ struct refclock_atom *ap /* atom structure pointer */
+ )
+{
+ ZERO(ap->pps_params);
+ ap->pps_params.api_version = PPS_API_VERS_1;
+
+ /*
+ * Solaris serial ports provide PPS pulse capture only on the
+ * assert edge. FreeBSD serial ports provide capture on the
+ * clear edge, while FreeBSD parallel ports provide capture
+ * on the assert edge. Your mileage may vary.
+ */
+ if (mode & CLK_FLAG2)
+ ap->pps_params.mode = PPS_TSFMT_TSPEC | PPS_CAPTURECLEAR;
+ else
+ ap->pps_params.mode = PPS_TSFMT_TSPEC | PPS_CAPTUREASSERT;
+ if (time_pps_setparams(ap->handle, &ap->pps_params) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_params: time_pps_setparams: %m");
+ return (0);
+ }
+
+ /*
+ * If flag3 is lit, select the kernel PPS if we can.
+ */
+ if (mode & CLK_FLAG3) {
+ if (time_pps_kcbind(ap->handle, PPS_KC_HARDPPS,
+ ap->pps_params.mode & ~PPS_TSFMT_TSPEC,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_params: time_pps_kcbind: %m");
+ return (0);
+ }
+ hardpps_enable = 1;
+ }
+ return (1);
+}
+
+
+/*
+ * refclock_pps - called once per second
+ *
+ * This routine is called once per second. It snatches the PPS
+ * timestamp from the kernel and saves the sign-extended fraction in
+ * a circular buffer for processing at the next poll event.
+ */
+int
+refclock_pps(
+ struct peer *peer, /* peer structure pointer */
+ struct refclock_atom *ap, /* atom structure pointer */
+ int mode /* mode bits */
+ )
+{
+ struct refclockproc *pp;
+ pps_info_t pps_info;
+ struct timespec timeout;
+ double dtemp;
+
+ /*
+ * We require the clock to be synchronized before setting the
+ * parameters. When the parameters have been set, fetch the
+ * most recent PPS timestamp.
+ */
+ pp = peer->procptr;
+ if (ap->handle == 0)
+ return (0);
+
+ if (ap->pps_params.mode == 0 && sys_leap != LEAP_NOTINSYNC) {
+ if (refclock_params(pp->sloppyclockflag, ap) < 1)
+ return (0);
+ }
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ ZERO(pps_info);
+ if (time_pps_fetch(ap->handle, PPS_TSFMT_TSPEC, &pps_info,
+ &timeout) < 0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return (0);
+ }
+ timeout = ap->ts;
+ if (ap->pps_params.mode & PPS_CAPTUREASSERT)
+ ap->ts = pps_info.assert_timestamp;
+ else if (ap->pps_params.mode & PPS_CAPTURECLEAR)
+ ap->ts = pps_info.clear_timestamp;
+ else
+ return (0);
+
+ if (0 == memcmp(&timeout, &ap->ts, sizeof(timeout)))
+ return (0);
+
+ /*
+ * Convert to signed fraction offset and stuff in median filter.
+ */
+ pp->lastrec.l_ui = (u_int32)ap->ts.tv_sec + JAN_1970;
+ dtemp = ap->ts.tv_nsec / 1e9;
+ pp->lastrec.l_uf = (u_int32)(dtemp * FRAC);
+ if (dtemp > .5)
+ dtemp -= 1.;
+ SAMPLE(-dtemp + pp->fudgetime1);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("refclock_pps: %lu %f %f\n", current_time,
+ dtemp, pp->fudgetime1);
+#endif
+ return (1);
+}
+#endif /* HAVE_PPSAPI */
+#endif /* REFCLOCK */
diff --git a/ntpd/ntp_request.c b/ntpd/ntp_request.c
new file mode 100644
index 0000000..157304b
--- /dev/null
+++ b/ntpd/ntp_request.c
@@ -0,0 +1,2670 @@
+/*
+ * ntp_request.c - respond to information requests
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_request.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+#include "ntp_assert.h"
+
+#include <stdio.h>
+#include <stddef.h>
+#include <signal.h>
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#include <arpa/inet.h>
+
+#include "recvbuff.h"
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif /* KERNEL_PLL */
+
+/*
+ * Structure to hold request procedure information
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+#define NO_REQUEST (-1)
+/*
+ * Because we now have v6 addresses in the messages, we need to compensate
+ * for the larger size. Therefore, we introduce the alternate size to
+ * keep us friendly with older implementations. A little ugly.
+ */
+static int client_v6_capable = 0; /* the client can handle longer messages */
+
+#define v6sizeof(type) (client_v6_capable ? sizeof(type) : v4sizeof(type))
+
+struct req_proc {
+ short request_code; /* defined request code */
+ short needs_auth; /* true when authentication needed */
+ short sizeofitem; /* size of request data item (older size)*/
+ short v6_sizeofitem; /* size of request data item (new size)*/
+ void (*handler) (sockaddr_u *, endpt *,
+ struct req_pkt *); /* routine to handle request */
+};
+
+/*
+ * Universal request codes
+ */
+static const struct req_proc univ_codes[] = {
+ { NO_REQUEST, NOAUTH, 0, 0 }
+};
+
+static void req_ack (sockaddr_u *, endpt *, struct req_pkt *, int);
+static void * prepare_pkt (sockaddr_u *, endpt *,
+ struct req_pkt *, size_t);
+static void * more_pkt (void);
+static void flush_pkt (void);
+static void list_peers (sockaddr_u *, endpt *, struct req_pkt *);
+static void list_peers_sum (sockaddr_u *, endpt *, struct req_pkt *);
+static void peer_info (sockaddr_u *, endpt *, struct req_pkt *);
+static void peer_stats (sockaddr_u *, endpt *, struct req_pkt *);
+static void sys_info (sockaddr_u *, endpt *, struct req_pkt *);
+static void sys_stats (sockaddr_u *, endpt *, struct req_pkt *);
+static void mem_stats (sockaddr_u *, endpt *, struct req_pkt *);
+static void io_stats (sockaddr_u *, endpt *, struct req_pkt *);
+static void timer_stats (sockaddr_u *, endpt *, struct req_pkt *);
+static void loop_info (sockaddr_u *, endpt *, struct req_pkt *);
+static void do_conf (sockaddr_u *, endpt *, struct req_pkt *);
+static void do_unconf (sockaddr_u *, endpt *, struct req_pkt *);
+static void set_sys_flag (sockaddr_u *, endpt *, struct req_pkt *);
+static void clr_sys_flag (sockaddr_u *, endpt *, struct req_pkt *);
+static void setclr_flags (sockaddr_u *, endpt *, struct req_pkt *, u_long);
+static void list_restrict4 (restrict_u *, struct info_restrict **);
+static void list_restrict6 (restrict_u *, struct info_restrict **);
+static void list_restrict (sockaddr_u *, endpt *, struct req_pkt *);
+static void do_resaddflags (sockaddr_u *, endpt *, struct req_pkt *);
+static void do_ressubflags (sockaddr_u *, endpt *, struct req_pkt *);
+static void do_unrestrict (sockaddr_u *, endpt *, struct req_pkt *);
+static void do_restrict (sockaddr_u *, endpt *, struct req_pkt *, int);
+static void mon_getlist (sockaddr_u *, endpt *, struct req_pkt *);
+static void reset_stats (sockaddr_u *, endpt *, struct req_pkt *);
+static void reset_peer (sockaddr_u *, endpt *, struct req_pkt *);
+static void do_key_reread (sockaddr_u *, endpt *, struct req_pkt *);
+static void trust_key (sockaddr_u *, endpt *, struct req_pkt *);
+static void untrust_key (sockaddr_u *, endpt *, struct req_pkt *);
+static void do_trustkey (sockaddr_u *, endpt *, struct req_pkt *, u_long);
+static void get_auth_info (sockaddr_u *, endpt *, struct req_pkt *);
+static void req_get_traps (sockaddr_u *, endpt *, struct req_pkt *);
+static void req_set_trap (sockaddr_u *, endpt *, struct req_pkt *);
+static void req_clr_trap (sockaddr_u *, endpt *, struct req_pkt *);
+static void do_setclr_trap (sockaddr_u *, endpt *, struct req_pkt *, int);
+static void set_request_keyid (sockaddr_u *, endpt *, struct req_pkt *);
+static void set_control_keyid (sockaddr_u *, endpt *, struct req_pkt *);
+static void get_ctl_stats (sockaddr_u *, endpt *, struct req_pkt *);
+static void get_if_stats (sockaddr_u *, endpt *, struct req_pkt *);
+static void do_if_reload (sockaddr_u *, endpt *, struct req_pkt *);
+#ifdef KERNEL_PLL
+static void get_kernel_info (sockaddr_u *, endpt *, struct req_pkt *);
+#endif /* KERNEL_PLL */
+#ifdef REFCLOCK
+static void get_clock_info (sockaddr_u *, endpt *, struct req_pkt *);
+static void set_clock_fudge (sockaddr_u *, endpt *, struct req_pkt *);
+#endif /* REFCLOCK */
+#ifdef REFCLOCK
+static void get_clkbug_info (sockaddr_u *, endpt *, struct req_pkt *);
+#endif /* REFCLOCK */
+
+/*
+ * ntpd request codes
+ */
+static const struct req_proc ntp_codes[] = {
+ { REQ_PEER_LIST, NOAUTH, 0, 0, list_peers },
+ { REQ_PEER_LIST_SUM, NOAUTH, 0, 0, list_peers_sum },
+ { REQ_PEER_INFO, NOAUTH, v4sizeof(struct info_peer_list),
+ sizeof(struct info_peer_list), peer_info},
+ { REQ_PEER_STATS, NOAUTH, v4sizeof(struct info_peer_list),
+ sizeof(struct info_peer_list), peer_stats},
+ { REQ_SYS_INFO, NOAUTH, 0, 0, sys_info },
+ { REQ_SYS_STATS, NOAUTH, 0, 0, sys_stats },
+ { REQ_IO_STATS, NOAUTH, 0, 0, io_stats },
+ { REQ_MEM_STATS, NOAUTH, 0, 0, mem_stats },
+ { REQ_LOOP_INFO, NOAUTH, 0, 0, loop_info },
+ { REQ_TIMER_STATS, NOAUTH, 0, 0, timer_stats },
+ { REQ_CONFIG, AUTH, v4sizeof(struct conf_peer),
+ sizeof(struct conf_peer), do_conf },
+ { REQ_UNCONFIG, AUTH, v4sizeof(struct conf_unpeer),
+ sizeof(struct conf_unpeer), do_unconf },
+ { REQ_SET_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags),
+ sizeof(struct conf_sys_flags), set_sys_flag },
+ { REQ_CLR_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags),
+ sizeof(struct conf_sys_flags), clr_sys_flag },
+ { REQ_GET_RESTRICT, NOAUTH, 0, 0, list_restrict },
+ { REQ_RESADDFLAGS, AUTH, v4sizeof(struct conf_restrict),
+ sizeof(struct conf_restrict), do_resaddflags },
+ { REQ_RESSUBFLAGS, AUTH, v4sizeof(struct conf_restrict),
+ sizeof(struct conf_restrict), do_ressubflags },
+ { REQ_UNRESTRICT, AUTH, v4sizeof(struct conf_restrict),
+ sizeof(struct conf_restrict), do_unrestrict },
+ { REQ_MON_GETLIST, NOAUTH, 0, 0, mon_getlist },
+ { REQ_MON_GETLIST_1, NOAUTH, 0, 0, mon_getlist },
+ { REQ_RESET_STATS, AUTH, sizeof(struct reset_flags), 0, reset_stats },
+ { REQ_RESET_PEER, AUTH, v4sizeof(struct conf_unpeer),
+ sizeof(struct conf_unpeer), reset_peer },
+ { REQ_REREAD_KEYS, AUTH, 0, 0, do_key_reread },
+ { REQ_TRUSTKEY, AUTH, sizeof(u_long), sizeof(u_long), trust_key },
+ { REQ_UNTRUSTKEY, AUTH, sizeof(u_long), sizeof(u_long), untrust_key },
+ { REQ_AUTHINFO, NOAUTH, 0, 0, get_auth_info },
+ { REQ_TRAPS, NOAUTH, 0, 0, req_get_traps },
+ { REQ_ADD_TRAP, AUTH, v4sizeof(struct conf_trap),
+ sizeof(struct conf_trap), req_set_trap },
+ { REQ_CLR_TRAP, AUTH, v4sizeof(struct conf_trap),
+ sizeof(struct conf_trap), req_clr_trap },
+ { REQ_REQUEST_KEY, AUTH, sizeof(u_long), sizeof(u_long),
+ set_request_keyid },
+ { REQ_CONTROL_KEY, AUTH, sizeof(u_long), sizeof(u_long),
+ set_control_keyid },
+ { REQ_GET_CTLSTATS, NOAUTH, 0, 0, get_ctl_stats },
+#ifdef KERNEL_PLL
+ { REQ_GET_KERNEL, NOAUTH, 0, 0, get_kernel_info },
+#endif
+#ifdef REFCLOCK
+ { REQ_GET_CLOCKINFO, NOAUTH, sizeof(u_int32), sizeof(u_int32),
+ get_clock_info },
+ { REQ_SET_CLKFUDGE, AUTH, sizeof(struct conf_fudge),
+ sizeof(struct conf_fudge), set_clock_fudge },
+ { REQ_GET_CLKBUGINFO, NOAUTH, sizeof(u_int32), sizeof(u_int32),
+ get_clkbug_info },
+#endif
+ { REQ_IF_STATS, AUTH, 0, 0, get_if_stats },
+ { REQ_IF_RELOAD, AUTH, 0, 0, do_if_reload },
+
+ { NO_REQUEST, NOAUTH, 0, 0, 0 }
+};
+
+
+/*
+ * Authentication keyid used to authenticate requests. Zero means we
+ * don't allow writing anything.
+ */
+keyid_t info_auth_keyid;
+
+/*
+ * Statistic counters to keep track of requests and responses.
+ */
+u_long numrequests; /* number of requests we've received */
+u_long numresppkts; /* number of resp packets sent with data */
+
+/*
+ * lazy way to count errors, indexed by the error code
+ */
+u_long errorcounter[MAX_INFO_ERR + 1];
+
+/*
+ * A hack. To keep the authentication module clear of ntp-ism's, we
+ * include a time reset variable for its stats here.
+ */
+u_long auth_timereset;
+
+/*
+ * Response packet used by these routines. Also some state information
+ * so that we can handle packet formatting within a common set of
+ * subroutines. Note we try to enter data in place whenever possible,
+ * but the need to set the more bit correctly means we occasionally
+ * use the extra buffer and copy.
+ */
+static struct resp_pkt rpkt;
+static int reqver;
+static int seqno;
+static int nitems;
+static int itemsize;
+static int databytes;
+static char exbuf[RESP_DATA_SIZE];
+static int usingexbuf;
+static sockaddr_u *toaddr;
+static endpt *frominter;
+
+/*
+ * init_request - initialize request data
+ */
+void
+init_request (void)
+{
+ int i;
+
+ numrequests = 0;
+ numresppkts = 0;
+ auth_timereset = 0;
+ info_auth_keyid = 0; /* by default, can't do this */
+
+ for (i = 0; i < sizeof(errorcounter)/sizeof(errorcounter[0]); i++)
+ errorcounter[i] = 0;
+}
+
+
+/*
+ * req_ack - acknowledge request with no data
+ */
+static void
+req_ack(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt,
+ int errcode
+ )
+{
+ /*
+ * fill in the fields
+ */
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, 0, reqver);
+ rpkt.auth_seq = AUTH_SEQ(0, 0);
+ rpkt.implementation = inpkt->implementation;
+ rpkt.request = inpkt->request;
+ rpkt.err_nitems = ERR_NITEMS(errcode, 0);
+ rpkt.mbz_itemsize = MBZ_ITEMSIZE(0);
+
+ /*
+ * send packet and bump counters
+ */
+ sendpkt(srcadr, inter, -1, (struct pkt *)&rpkt, RESP_HEADER_SIZE);
+ errorcounter[errcode]++;
+}
+
+
+/*
+ * prepare_pkt - prepare response packet for transmission, return pointer
+ * to storage for data item.
+ */
+static void *
+prepare_pkt(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *pkt,
+ size_t structsize
+ )
+{
+ DPRINTF(4, ("request: preparing pkt\n"));
+
+ /*
+ * Fill in the implementation, request and itemsize fields
+ * since these won't change.
+ */
+ rpkt.implementation = pkt->implementation;
+ rpkt.request = pkt->request;
+ rpkt.mbz_itemsize = MBZ_ITEMSIZE(structsize);
+
+ /*
+ * Compute the static data needed to carry on.
+ */
+ toaddr = srcadr;
+ frominter = inter;
+ seqno = 0;
+ nitems = 0;
+ itemsize = structsize;
+ databytes = 0;
+ usingexbuf = 0;
+
+ /*
+ * return the beginning of the packet buffer.
+ */
+ return &rpkt.u;
+}
+
+
+/*
+ * more_pkt - return a data pointer for a new item.
+ */
+static void *
+more_pkt(void)
+{
+ /*
+ * If we were using the extra buffer, send the packet.
+ */
+ if (usingexbuf) {
+ DPRINTF(3, ("request: sending pkt\n"));
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, MORE_BIT, reqver);
+ rpkt.auth_seq = AUTH_SEQ(0, seqno);
+ rpkt.err_nitems = htons((u_short)nitems);
+ sendpkt(toaddr, frominter, -1, (struct pkt *)&rpkt,
+ RESP_HEADER_SIZE + databytes);
+ numresppkts++;
+
+ /*
+ * Copy data out of exbuf into the packet.
+ */
+ memcpy(&rpkt.u.data[0], exbuf, (unsigned)itemsize);
+ seqno++;
+ databytes = 0;
+ nitems = 0;
+ usingexbuf = 0;
+ }
+
+ databytes += itemsize;
+ nitems++;
+ if (databytes + itemsize <= RESP_DATA_SIZE) {
+ DPRINTF(4, ("request: giving him more data\n"));
+ /*
+ * More room in packet. Give him the
+ * next address.
+ */
+ return &rpkt.u.data[databytes];
+ } else {
+ /*
+ * No room in packet. Give him the extra
+ * buffer unless this was the last in the sequence.
+ */
+ DPRINTF(4, ("request: into extra buffer\n"));
+ if (seqno == MAXSEQ)
+ return NULL;
+ else {
+ usingexbuf = 1;
+ return exbuf;
+ }
+ }
+}
+
+
+/*
+ * flush_pkt - we're done, return remaining information.
+ */
+static void
+flush_pkt(void)
+{
+ DPRINTF(3, ("request: flushing packet, %d items\n", nitems));
+ /*
+ * Must send the last packet. If nothing in here and nothing
+ * has been sent, send an error saying no data to be found.
+ */
+ if (seqno == 0 && nitems == 0)
+ req_ack(toaddr, frominter, (struct req_pkt *)&rpkt,
+ INFO_ERR_NODATA);
+ else {
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, 0, reqver);
+ rpkt.auth_seq = AUTH_SEQ(0, seqno);
+ rpkt.err_nitems = htons((u_short)nitems);
+ sendpkt(toaddr, frominter, -1, (struct pkt *)&rpkt,
+ RESP_HEADER_SIZE+databytes);
+ numresppkts++;
+ }
+}
+
+
+
+/*
+ * Given a buffer, return the packet mode
+ */
+int
+get_packet_mode(struct recvbuf *rbufp)
+{
+ struct req_pkt *inpkt = (struct req_pkt *)&rbufp->recv_pkt;
+ return (INFO_MODE(inpkt->rm_vn_mode));
+}
+
+
+/*
+ * process_private - process private mode (7) packets
+ */
+void
+process_private(
+ struct recvbuf *rbufp,
+ int mod_okay
+ )
+{
+ static u_long quiet_until;
+ struct req_pkt *inpkt;
+ struct req_pkt_tail *tailinpkt;
+ sockaddr_u *srcadr;
+ endpt *inter;
+ const struct req_proc *proc;
+ int ec;
+ short temp_size;
+ l_fp ftmp;
+ double dtemp;
+ size_t recv_len;
+ size_t noslop_len;
+ size_t mac_len;
+
+ /*
+ * Initialize pointers, for convenience
+ */
+ recv_len = rbufp->recv_length;
+ inpkt = (struct req_pkt *)&rbufp->recv_pkt;
+ srcadr = &rbufp->recv_srcadr;
+ inter = rbufp->dstadr;
+
+ DPRINTF(3, ("process_private: impl %d req %d\n",
+ inpkt->implementation, inpkt->request));
+
+ /*
+ * Do some sanity checks on the packet. Return a format
+ * error if it fails.
+ */
+ ec = 0;
+ if ( (++ec, ISRESPONSE(inpkt->rm_vn_mode))
+ || (++ec, ISMORE(inpkt->rm_vn_mode))
+ || (++ec, INFO_VERSION(inpkt->rm_vn_mode) > NTP_VERSION)
+ || (++ec, INFO_VERSION(inpkt->rm_vn_mode) < NTP_OLDVERSION)
+ || (++ec, INFO_SEQ(inpkt->auth_seq) != 0)
+ || (++ec, INFO_ERR(inpkt->err_nitems) != 0)
+ || (++ec, INFO_MBZ(inpkt->mbz_itemsize) != 0)
+ || (++ec, rbufp->recv_length < REQ_LEN_HDR)
+ ) {
+ NLOG(NLOG_SYSEVENT)
+ if (current_time >= quiet_until) {
+ msyslog(LOG_ERR,
+ "process_private: drop test %d"
+ " failed, pkt from %s",
+ ec, stoa(srcadr));
+ quiet_until = current_time + 60;
+ }
+ return;
+ }
+
+ reqver = INFO_VERSION(inpkt->rm_vn_mode);
+
+ /*
+ * Get the appropriate procedure list to search.
+ */
+ if (inpkt->implementation == IMPL_UNIV)
+ proc = univ_codes;
+ else if ((inpkt->implementation == IMPL_XNTPD) ||
+ (inpkt->implementation == IMPL_XNTPD_OLD))
+ proc = ntp_codes;
+ else {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_IMPL);
+ return;
+ }
+
+ /*
+ * Search the list for the request codes. If it isn't one
+ * we know, return an error.
+ */
+ while (proc->request_code != NO_REQUEST) {
+ if (proc->request_code == (short) inpkt->request)
+ break;
+ proc++;
+ }
+ if (proc->request_code == NO_REQUEST) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_REQ);
+ return;
+ }
+
+ DPRINTF(4, ("found request in tables\n"));
+
+ /*
+ * If we need data, check to see if we have some. If we
+ * don't, check to see that there is none (picky, picky).
+ */
+
+ /* This part is a bit tricky, we want to be sure that the size
+ * returned is either the old or the new size. We also can find
+ * out if the client can accept both types of messages this way.
+ *
+ * Handle the exception of REQ_CONFIG. It can have two data sizes.
+ */
+ temp_size = INFO_ITEMSIZE(inpkt->mbz_itemsize);
+ if ((temp_size != proc->sizeofitem &&
+ temp_size != proc->v6_sizeofitem) &&
+ !(inpkt->implementation == IMPL_XNTPD &&
+ inpkt->request == REQ_CONFIG &&
+ temp_size == sizeof(struct old_conf_peer))) {
+ DPRINTF(3, ("process_private: wrong item size, received %d, should be %d or %d\n",
+ temp_size, proc->sizeofitem, proc->v6_sizeofitem));
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ if ((proc->sizeofitem != 0) &&
+ ((size_t)(temp_size * INFO_NITEMS(inpkt->err_nitems)) >
+ (recv_len - REQ_LEN_HDR))) {
+ DPRINTF(3, ("process_private: not enough data\n"));
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ switch (inpkt->implementation) {
+ case IMPL_XNTPD:
+ client_v6_capable = 1;
+ break;
+ case IMPL_XNTPD_OLD:
+ client_v6_capable = 0;
+ break;
+ default:
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ /*
+ * If we need to authenticate, do so. Note that an
+ * authenticatable packet must include a mac field, must
+ * have used key info_auth_keyid and must have included
+ * a time stamp in the appropriate field. The time stamp
+ * must be within INFO_TS_MAXSKEW of the receive
+ * time stamp.
+ */
+ if (proc->needs_auth && sys_authenticate) {
+
+ if (recv_len < (REQ_LEN_HDR +
+ (INFO_ITEMSIZE(inpkt->mbz_itemsize) *
+ INFO_NITEMS(inpkt->err_nitems)) +
+ REQ_TAIL_MIN)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ /*
+ * For 16-octet digests, regardless of itemsize and
+ * nitems, authenticated requests are a fixed size
+ * with the timestamp, key ID, and digest located
+ * at the end of the packet. Because the key ID
+ * determining the digest size precedes the digest,
+ * for larger digests the fixed size request scheme
+ * is abandoned and the timestamp, key ID, and digest
+ * are located relative to the start of the packet,
+ * with the digest size determined by the packet size.
+ */
+ noslop_len = REQ_LEN_HDR
+ + INFO_ITEMSIZE(inpkt->mbz_itemsize) *
+ INFO_NITEMS(inpkt->err_nitems)
+ + sizeof(inpkt->tstamp);
+ /* 32-bit alignment */
+ noslop_len = (noslop_len + 3) & ~3;
+ if (recv_len > (noslop_len + MAX_MAC_LEN))
+ mac_len = 20;
+ else
+ mac_len = recv_len - noslop_len;
+
+ tailinpkt = (void *)((char *)inpkt + recv_len -
+ (mac_len + sizeof(inpkt->tstamp)));
+
+ /*
+ * If this guy is restricted from doing this, don't let
+ * him. If the wrong key was used, or packet doesn't
+ * have mac, return.
+ */
+ if (!INFO_IS_AUTH(inpkt->auth_seq) || !info_auth_keyid
+ || ntohl(tailinpkt->keyid) != info_auth_keyid) {
+ DPRINTF(5, ("failed auth %d info_auth_keyid %u pkt keyid %u maclen %lu\n",
+ INFO_IS_AUTH(inpkt->auth_seq),
+ info_auth_keyid,
+ ntohl(tailinpkt->keyid), (u_long)mac_len));
+#ifdef DEBUG
+ msyslog(LOG_DEBUG,
+ "process_private: failed auth %d info_auth_keyid %u pkt keyid %u maclen %lu\n",
+ INFO_IS_AUTH(inpkt->auth_seq),
+ info_auth_keyid,
+ ntohl(tailinpkt->keyid), (u_long)mac_len);
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+ if (recv_len > REQ_LEN_NOMAC + MAX_MAC_LEN) {
+ DPRINTF(5, ("bad pkt length %lu\n",
+ (u_long)recv_len));
+ msyslog(LOG_ERR,
+ "process_private: bad pkt length %lu",
+ (u_long)recv_len);
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ if (!mod_okay || !authhavekey(info_auth_keyid)) {
+ DPRINTF(5, ("failed auth mod_okay %d\n",
+ mod_okay));
+#ifdef DEBUG
+ msyslog(LOG_DEBUG,
+ "process_private: failed auth mod_okay %d\n",
+ mod_okay);
+#endif
+ if (!mod_okay) {
+ sys_restricted++;
+ }
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+
+ /*
+ * calculate absolute time difference between xmit time stamp
+ * and receive time stamp. If too large, too bad.
+ */
+ NTOHL_FP(&tailinpkt->tstamp, &ftmp);
+ L_SUB(&ftmp, &rbufp->recv_time);
+ LFPTOD(&ftmp, dtemp);
+ if (fabs(dtemp) > INFO_TS_MAXSKEW) {
+ /*
+ * He's a loser. Tell him.
+ */
+ DPRINTF(5, ("xmit/rcv timestamp delta %g > INFO_TS_MAXSKEW %g\n",
+ dtemp, INFO_TS_MAXSKEW));
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+
+ /*
+ * So far so good. See if decryption works out okay.
+ */
+ if (!authdecrypt(info_auth_keyid, (u_int32 *)inpkt,
+ recv_len - mac_len, mac_len)) {
+ DPRINTF(5, ("authdecrypt failed\n"));
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+ }
+
+ DPRINTF(3, ("process_private: all okay, into handler\n"));
+ /*
+ * Packet is okay. Call the handler to send him data.
+ */
+ (proc->handler)(srcadr, inter, inpkt);
+}
+
+
+/*
+ * list_peers - send a list of the peers
+ */
+static void
+list_peers(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ struct info_peer_list *ip;
+ struct peer *pp;
+ int skip = 0;
+
+ ip = (struct info_peer_list *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_peer_list));
+ for (pp = peer_list; pp != NULL && ip != NULL; pp = pp->p_link) {
+ if (IS_IPV6(&pp->srcadr)) {
+ if (client_v6_capable) {
+ ip->addr6 = SOCK_ADDR6(&pp->srcadr);
+ ip->v6_flag = 1;
+ skip = 0;
+ } else {
+ skip = 1;
+ break;
+ }
+ } else {
+ ip->addr = NSRCADR(&pp->srcadr);
+ if (client_v6_capable)
+ ip->v6_flag = 0;
+ skip = 0;
+ }
+
+ if (!skip) {
+ ip->port = NSRCPORT(&pp->srcadr);
+ ip->hmode = pp->hmode;
+ ip->flags = 0;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->status == CTL_PST_SEL_SYNCCAND)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->status >= CTL_PST_SEL_SYSPEER)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip = (struct info_peer_list *)more_pkt();
+ }
+ } /* for pp */
+
+ flush_pkt();
+}
+
+
+/*
+ * list_peers_sum - return extended peer list
+ */
+static void
+list_peers_sum(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_peer_summary *ips;
+ register struct peer *pp;
+ l_fp ltmp;
+ register int skip;
+
+ DPRINTF(3, ("wants peer list summary\n"));
+
+ ips = (struct info_peer_summary *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_peer_summary));
+ for (pp = peer_list; pp != NULL && ips != NULL; pp = pp->p_link) {
+ DPRINTF(4, ("sum: got one\n"));
+ /*
+ * Be careful here not to return v6 peers when we
+ * want only v4.
+ */
+ if (IS_IPV6(&pp->srcadr)) {
+ if (client_v6_capable) {
+ ips->srcadr6 = SOCK_ADDR6(&pp->srcadr);
+ ips->v6_flag = 1;
+ if (pp->dstadr)
+ ips->dstadr6 = SOCK_ADDR6(&pp->dstadr->sin);
+ else
+ ZERO(ips->dstadr6);
+ skip = 0;
+ } else {
+ skip = 1;
+ break;
+ }
+ } else {
+ ips->srcadr = NSRCADR(&pp->srcadr);
+ if (client_v6_capable)
+ ips->v6_flag = 0;
+
+ if (pp->dstadr) {
+ if (!pp->processed)
+ ips->dstadr = NSRCADR(&pp->dstadr->sin);
+ else {
+ if (MDF_BCAST == pp->cast_flags)
+ ips->dstadr = NSRCADR(&pp->dstadr->bcast);
+ else if (pp->cast_flags) {
+ ips->dstadr = NSRCADR(&pp->dstadr->sin);
+ if (!ips->dstadr)
+ ips->dstadr = NSRCADR(&pp->dstadr->bcast);
+ }
+ }
+ } else
+ ips->dstadr = 0;
+
+ skip = 0;
+ }
+
+ if (!skip) {
+ ips->srcport = NSRCPORT(&pp->srcadr);
+ ips->stratum = pp->stratum;
+ ips->hpoll = pp->hpoll;
+ ips->ppoll = pp->ppoll;
+ ips->reach = pp->reach;
+ ips->flags = 0;
+ if (pp == sys_peer)
+ ips->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ips->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ips->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_PREFER)
+ ips->flags |= INFO_FLAG_PREFER;
+ if (pp->flags & FLAG_BURST)
+ ips->flags |= INFO_FLAG_BURST;
+ if (pp->status == CTL_PST_SEL_SYNCCAND)
+ ips->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->status >= CTL_PST_SEL_SYSPEER)
+ ips->flags |= INFO_FLAG_SHORTLIST;
+ ips->hmode = pp->hmode;
+ ips->delay = HTONS_FP(DTOFP(pp->delay));
+ DTOLFP(pp->offset, &ltmp);
+ HTONL_FP(&ltmp, &ips->offset);
+ ips->dispersion = HTONS_FP(DTOUFP(SQRT(pp->disp)));
+ }
+ ips = (struct info_peer_summary *)more_pkt();
+ } /* for pp */
+
+ flush_pkt();
+}
+
+
+/*
+ * peer_info - send information for one or more peers
+ */
+static void
+peer_info (
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ u_short items;
+ size_t item_sz;
+ char * datap;
+ struct info_peer_list ipl;
+ struct peer * pp;
+ struct info_peer * ip;
+ int i;
+ int j;
+ sockaddr_u addr;
+ l_fp ltmp;
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ item_sz = INFO_ITEMSIZE(inpkt->mbz_itemsize);
+ datap = inpkt->u.data;
+ if (item_sz != sizeof(ipl)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ ip = prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_peer));
+ while (items-- > 0 && ip != NULL) {
+ ZERO(ipl);
+ memcpy(&ipl, datap, item_sz);
+ ZERO_SOCK(&addr);
+ NSRCPORT(&addr) = ipl.port;
+ if (client_v6_capable && ipl.v6_flag) {
+ AF(&addr) = AF_INET6;
+ SOCK_ADDR6(&addr) = ipl.addr6;
+ } else {
+ AF(&addr) = AF_INET;
+ NSRCADR(&addr) = ipl.addr;
+ }
+#ifdef ISC_PLATFORM_HAVESALEN
+ addr.sa.sa_len = SOCKLEN(&addr);
+#endif
+ datap += item_sz;
+
+ pp = findexistingpeer(&addr, NULL, NULL, -1, 0);
+ if (NULL == pp)
+ continue;
+ if (IS_IPV6(srcadr)) {
+ if (pp->dstadr)
+ ip->dstadr6 =
+ (MDF_BCAST == pp->cast_flags)
+ ? SOCK_ADDR6(&pp->dstadr->bcast)
+ : SOCK_ADDR6(&pp->dstadr->sin);
+ else
+ ZERO(ip->dstadr6);
+
+ ip->srcadr6 = SOCK_ADDR6(&pp->srcadr);
+ ip->v6_flag = 1;
+ } else {
+ if (pp->dstadr) {
+ if (!pp->processed)
+ ip->dstadr = NSRCADR(&pp->dstadr->sin);
+ else {
+ if (MDF_BCAST == pp->cast_flags)
+ ip->dstadr = NSRCADR(&pp->dstadr->bcast);
+ else if (pp->cast_flags) {
+ ip->dstadr = NSRCADR(&pp->dstadr->sin);
+ if (!ip->dstadr)
+ ip->dstadr = NSRCADR(&pp->dstadr->bcast);
+ }
+ }
+ } else
+ ip->dstadr = 0;
+
+ ip->srcadr = NSRCADR(&pp->srcadr);
+ if (client_v6_capable)
+ ip->v6_flag = 0;
+ }
+ ip->srcport = NSRCPORT(&pp->srcadr);
+ ip->flags = 0;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ip->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_PREFER)
+ ip->flags |= INFO_FLAG_PREFER;
+ if (pp->flags & FLAG_BURST)
+ ip->flags |= INFO_FLAG_BURST;
+ if (pp->status == CTL_PST_SEL_SYNCCAND)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->status >= CTL_PST_SEL_SYSPEER)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip->leap = pp->leap;
+ ip->hmode = pp->hmode;
+ ip->keyid = pp->keyid;
+ ip->stratum = pp->stratum;
+ ip->ppoll = pp->ppoll;
+ ip->hpoll = pp->hpoll;
+ ip->precision = pp->precision;
+ ip->version = pp->version;
+ ip->reach = pp->reach;
+ ip->unreach = (u_char)pp->unreach;
+ ip->flash = (u_char)pp->flash;
+ ip->flash2 = (u_short)pp->flash;
+ ip->estbdelay = HTONS_FP(DTOFP(pp->delay));
+ ip->ttl = (u_char)pp->ttl;
+ ip->associd = htons(pp->associd);
+ ip->rootdelay = HTONS_FP(DTOUFP(pp->rootdelay));
+ ip->rootdispersion = HTONS_FP(DTOUFP(pp->rootdisp));
+ ip->refid = pp->refid;
+ HTONL_FP(&pp->reftime, &ip->reftime);
+ HTONL_FP(&pp->aorg, &ip->org);
+ HTONL_FP(&pp->rec, &ip->rec);
+ HTONL_FP(&pp->xmt, &ip->xmt);
+ j = pp->filter_nextpt - 1;
+ for (i = 0; i < NTP_SHIFT; i++, j--) {
+ if (j < 0)
+ j = NTP_SHIFT-1;
+ ip->filtdelay[i] = HTONS_FP(DTOFP(pp->filter_delay[j]));
+ DTOLFP(pp->filter_offset[j], &ltmp);
+ HTONL_FP(&ltmp, &ip->filtoffset[i]);
+ ip->order[i] = (u_char)((pp->filter_nextpt +
+ NTP_SHIFT - 1) -
+ pp->filter_order[i]);
+ if (ip->order[i] >= NTP_SHIFT)
+ ip->order[i] -= NTP_SHIFT;
+ }
+ DTOLFP(pp->offset, &ltmp);
+ HTONL_FP(&ltmp, &ip->offset);
+ ip->delay = HTONS_FP(DTOFP(pp->delay));
+ ip->dispersion = HTONS_FP(DTOUFP(SQRT(pp->disp)));
+ ip->selectdisp = HTONS_FP(DTOUFP(SQRT(pp->jitter)));
+ ip = more_pkt();
+ }
+ flush_pkt();
+}
+
+
+/*
+ * peer_stats - send statistics for one or more peers
+ */
+static void
+peer_stats (
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ u_short items;
+ size_t item_sz;
+ char * datap;
+ struct info_peer_list ipl;
+ struct peer * pp;
+ struct info_peer_stats *ip;
+ sockaddr_u addr;
+
+ DPRINTF(1, ("peer_stats: called\n"));
+ items = INFO_NITEMS(inpkt->err_nitems);
+ item_sz = INFO_ITEMSIZE(inpkt->mbz_itemsize);
+ datap = inpkt->u.data;
+ if (item_sz > sizeof(ipl)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ ip = prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_peer_stats));
+ while (items-- > 0 && ip != NULL) {
+ ZERO(ipl);
+ memcpy(&ipl, datap, item_sz);
+ ZERO(addr);
+ NSRCPORT(&addr) = ipl.port;
+ if (client_v6_capable && ipl.v6_flag) {
+ AF(&addr) = AF_INET6;
+ SOCK_ADDR6(&addr) = ipl.addr6;
+ } else {
+ AF(&addr) = AF_INET;
+ NSRCADR(&addr) = ipl.addr;
+ }
+#ifdef ISC_PLATFORM_HAVESALEN
+ addr.sa.sa_len = SOCKLEN(&addr);
+#endif
+ DPRINTF(1, ("peer_stats: looking for %s, %d, %d\n",
+ stoa(&addr), ipl.port, NSRCPORT(&addr)));
+
+ datap += item_sz;
+
+ pp = findexistingpeer(&addr, NULL, NULL, -1, 0);
+ if (NULL == pp)
+ continue;
+
+ DPRINTF(1, ("peer_stats: found %s\n", stoa(&addr)));
+
+ if (IS_IPV4(&pp->srcadr)) {
+ if (pp->dstadr) {
+ if (!pp->processed)
+ ip->dstadr = NSRCADR(&pp->dstadr->sin);
+ else {
+ if (MDF_BCAST == pp->cast_flags)
+ ip->dstadr = NSRCADR(&pp->dstadr->bcast);
+ else if (pp->cast_flags) {
+ ip->dstadr = NSRCADR(&pp->dstadr->sin);
+ if (!ip->dstadr)
+ ip->dstadr = NSRCADR(&pp->dstadr->bcast);
+ }
+ }
+ } else
+ ip->dstadr = 0;
+
+ ip->srcadr = NSRCADR(&pp->srcadr);
+ if (client_v6_capable)
+ ip->v6_flag = 0;
+ } else {
+ if (pp->dstadr)
+ ip->dstadr6 =
+ (MDF_BCAST == pp->cast_flags)
+ ? SOCK_ADDR6(&pp->dstadr->bcast)
+ : SOCK_ADDR6(&pp->dstadr->sin);
+ else
+ ZERO(ip->dstadr6);
+
+ ip->srcadr6 = SOCK_ADDR6(&pp->srcadr);
+ ip->v6_flag = 1;
+ }
+ ip->srcport = NSRCPORT(&pp->srcadr);
+ ip->flags = 0;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ip->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_PREFER)
+ ip->flags |= INFO_FLAG_PREFER;
+ if (pp->flags & FLAG_BURST)
+ ip->flags |= INFO_FLAG_BURST;
+ if (pp->flags & FLAG_IBURST)
+ ip->flags |= INFO_FLAG_IBURST;
+ if (pp->status == CTL_PST_SEL_SYNCCAND)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->status >= CTL_PST_SEL_SYSPEER)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip->flags = htons(ip->flags);
+ ip->timereceived = htonl((u_int32)(current_time - pp->timereceived));
+ ip->timetosend = htonl(pp->nextdate - current_time);
+ ip->timereachable = htonl((u_int32)(current_time - pp->timereachable));
+ ip->sent = htonl((u_int32)(pp->sent));
+ ip->processed = htonl((u_int32)(pp->processed));
+ ip->badauth = htonl((u_int32)(pp->badauth));
+ ip->bogusorg = htonl((u_int32)(pp->bogusorg));
+ ip->oldpkt = htonl((u_int32)(pp->oldpkt));
+ ip->seldisp = htonl((u_int32)(pp->seldisptoolarge));
+ ip->selbroken = htonl((u_int32)(pp->selbroken));
+ ip->candidate = pp->status;
+ ip = (struct info_peer_stats *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+/*
+ * sys_info - return system info
+ */
+static void
+sys_info(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_sys *is;
+
+ is = (struct info_sys *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_sys));
+
+ if (sys_peer) {
+ if (IS_IPV4(&sys_peer->srcadr)) {
+ is->peer = NSRCADR(&sys_peer->srcadr);
+ if (client_v6_capable)
+ is->v6_flag = 0;
+ } else if (client_v6_capable) {
+ is->peer6 = SOCK_ADDR6(&sys_peer->srcadr);
+ is->v6_flag = 1;
+ }
+ is->peer_mode = sys_peer->hmode;
+ } else {
+ is->peer = 0;
+ if (client_v6_capable) {
+ is->v6_flag = 0;
+ }
+ is->peer_mode = 0;
+ }
+
+ is->leap = sys_leap;
+ is->stratum = sys_stratum;
+ is->precision = sys_precision;
+ is->rootdelay = htonl(DTOFP(sys_rootdelay));
+ is->rootdispersion = htonl(DTOUFP(sys_rootdisp));
+ is->frequency = htonl(DTOFP(sys_jitter));
+ is->stability = htonl(DTOUFP(clock_stability * 1e6));
+ is->refid = sys_refid;
+ HTONL_FP(&sys_reftime, &is->reftime);
+
+ is->poll = sys_poll;
+
+ is->flags = 0;
+ if (sys_authenticate)
+ is->flags |= INFO_FLAG_AUTHENTICATE;
+ if (sys_bclient)
+ is->flags |= INFO_FLAG_BCLIENT;
+#ifdef REFCLOCK
+ if (cal_enable)
+ is->flags |= INFO_FLAG_CAL;
+#endif /* REFCLOCK */
+ if (kern_enable)
+ is->flags |= INFO_FLAG_KERNEL;
+ if (mon_enabled != MON_OFF)
+ is->flags |= INFO_FLAG_MONITOR;
+ if (ntp_enable)
+ is->flags |= INFO_FLAG_NTP;
+ if (hardpps_enable)
+ is->flags |= INFO_FLAG_PPS_SYNC;
+ if (stats_control)
+ is->flags |= INFO_FLAG_FILEGEN;
+ is->bdelay = HTONS_FP(DTOFP(sys_bdelay));
+ HTONL_UF(sys_authdelay.l_uf, &is->authdelay);
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * sys_stats - return system statistics
+ */
+static void
+sys_stats(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_sys_stats *ss;
+
+ ss = (struct info_sys_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_sys_stats));
+ ss->timeup = htonl((u_int32)current_time);
+ ss->timereset = htonl((u_int32)(current_time - sys_stattime));
+ ss->denied = htonl((u_int32)sys_restricted);
+ ss->oldversionpkt = htonl((u_int32)sys_oldversion);
+ ss->newversionpkt = htonl((u_int32)sys_newversion);
+ ss->unknownversion = htonl((u_int32)sys_declined);
+ ss->badlength = htonl((u_int32)sys_badlength);
+ ss->processed = htonl((u_int32)sys_processed);
+ ss->badauth = htonl((u_int32)sys_badauth);
+ ss->limitrejected = htonl((u_int32)sys_limitrejected);
+ ss->received = htonl((u_int32)sys_received);
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * mem_stats - return memory statistics
+ */
+static void
+mem_stats(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_mem_stats *ms;
+ register int i;
+
+ ms = (struct info_mem_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_mem_stats));
+
+ ms->timereset = htonl((u_int32)(current_time - peer_timereset));
+ ms->totalpeermem = htons((u_short)total_peer_structs);
+ ms->freepeermem = htons((u_short)peer_free_count);
+ ms->findpeer_calls = htonl((u_int32)findpeer_calls);
+ ms->allocations = htonl((u_int32)peer_allocations);
+ ms->demobilizations = htonl((u_int32)peer_demobilizations);
+
+ for (i = 0; i < NTP_HASH_SIZE; i++)
+ ms->hashcount[i] = (u_char)
+ max((u_int)peer_hash_count[i], UCHAR_MAX);
+
+ more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * io_stats - return io statistics
+ */
+static void
+io_stats(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ struct info_io_stats *io;
+
+ io = (struct info_io_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_io_stats));
+
+ io->timereset = htonl((u_int32)(current_time - io_timereset));
+ io->totalrecvbufs = htons((u_short) total_recvbuffs());
+ io->freerecvbufs = htons((u_short) free_recvbuffs());
+ io->fullrecvbufs = htons((u_short) full_recvbuffs());
+ io->lowwater = htons((u_short) lowater_additions());
+ io->dropped = htonl((u_int32)packets_dropped);
+ io->ignored = htonl((u_int32)packets_ignored);
+ io->received = htonl((u_int32)packets_received);
+ io->sent = htonl((u_int32)packets_sent);
+ io->notsent = htonl((u_int32)packets_notsent);
+ io->interrupts = htonl((u_int32)handler_calls);
+ io->int_received = htonl((u_int32)handler_pkts);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * timer_stats - return timer statistics
+ */
+static void
+timer_stats(
+ sockaddr_u * srcadr,
+ endpt * inter,
+ struct req_pkt * inpkt
+ )
+{
+ struct info_timer_stats * ts;
+ u_long sincereset;
+
+ ts = (struct info_timer_stats *)prepare_pkt(srcadr, inter,
+ inpkt, sizeof(*ts));
+
+ sincereset = current_time - timer_timereset;
+ ts->timereset = htonl((u_int32)sincereset);
+ ts->alarms = ts->timereset;
+ ts->overflows = htonl((u_int32)alarm_overflow);
+ ts->xmtcalls = htonl((u_int32)timer_xmtcalls);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * loop_info - return the current state of the loop filter
+ */
+static void
+loop_info(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ struct info_loop *li;
+ l_fp ltmp;
+
+ li = (struct info_loop *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_loop));
+
+ DTOLFP(last_offset, &ltmp);
+ HTONL_FP(&ltmp, &li->last_offset);
+ DTOLFP(drift_comp * 1e6, &ltmp);
+ HTONL_FP(&ltmp, &li->drift_comp);
+ li->compliance = htonl((u_int32)(tc_counter));
+ li->watchdog_timer = htonl((u_int32)(current_time - sys_epoch));
+
+ more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * do_conf - add a peer to the configuration list
+ */
+static void
+do_conf(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ u_short items;
+ size_t item_sz;
+ u_int fl;
+ char * datap;
+ struct conf_peer temp_cp;
+ sockaddr_u peeraddr;
+
+ /*
+ * Do a check of everything to see that it looks
+ * okay. If not, complain about it. Note we are
+ * very picky here.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ item_sz = INFO_ITEMSIZE(inpkt->mbz_itemsize);
+ datap = inpkt->u.data;
+ if (item_sz > sizeof(temp_cp)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ while (items-- > 0) {
+ ZERO(temp_cp);
+ memcpy(&temp_cp, datap, item_sz);
+ ZERO_SOCK(&peeraddr);
+
+ fl = 0;
+ if (temp_cp.flags & CONF_FLAG_PREFER)
+ fl |= FLAG_PREFER;
+ if (temp_cp.flags & CONF_FLAG_BURST)
+ fl |= FLAG_BURST;
+ if (temp_cp.flags & CONF_FLAG_IBURST)
+ fl |= FLAG_IBURST;
+#ifdef AUTOKEY
+ if (temp_cp.flags & CONF_FLAG_SKEY)
+ fl |= FLAG_SKEY;
+#endif /* AUTOKEY */
+ if (client_v6_capable && temp_cp.v6_flag) {
+ AF(&peeraddr) = AF_INET6;
+ SOCK_ADDR6(&peeraddr) = temp_cp.peeraddr6;
+ } else {
+ AF(&peeraddr) = AF_INET;
+ NSRCADR(&peeraddr) = temp_cp.peeraddr;
+ /*
+ * Make sure the address is valid
+ */
+ if (!ISREFCLOCKADR(&peeraddr) &&
+ ISBADADR(&peeraddr)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ }
+ NSRCPORT(&peeraddr) = htons(NTP_PORT);
+#ifdef ISC_PLATFORM_HAVESALEN
+ peeraddr.sa.sa_len = SOCKLEN(&peeraddr);
+#endif
+
+ /* XXX W2DO? minpoll/maxpoll arguments ??? */
+ if (peer_config(&peeraddr, NULL, NULL,
+ temp_cp.hmode, temp_cp.version, temp_cp.minpoll,
+ temp_cp.maxpoll, fl, temp_cp.ttl, temp_cp.keyid,
+ NULL) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ datap += item_sz;
+ }
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_unconf - remove a peer from the configuration list
+ */
+static void
+do_unconf(
+ sockaddr_u * srcadr,
+ endpt * inter,
+ struct req_pkt *inpkt
+ )
+{
+ u_short items;
+ size_t item_sz;
+ char * datap;
+ struct conf_unpeer temp_cp;
+ struct peer * p;
+ sockaddr_u peeraddr;
+ int bad;
+ int found;
+
+ /*
+ * This is a bit unstructured, but I like to be careful.
+ * We check to see that every peer exists and is actually
+ * configured. If so, we remove them. If not, we return
+ * an error.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ item_sz = INFO_ITEMSIZE(inpkt->mbz_itemsize);
+ datap = inpkt->u.data;
+ if (item_sz > sizeof(temp_cp)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ bad = FALSE;
+ while (items-- > 0 && !bad) {
+ ZERO(temp_cp);
+ memcpy(&temp_cp, datap, item_sz);
+ ZERO_SOCK(&peeraddr);
+ if (client_v6_capable && temp_cp.v6_flag) {
+ AF(&peeraddr) = AF_INET6;
+ SOCK_ADDR6(&peeraddr) = temp_cp.peeraddr6;
+ } else {
+ AF(&peeraddr) = AF_INET;
+ NSRCADR(&peeraddr) = temp_cp.peeraddr;
+ }
+ SET_PORT(&peeraddr, NTP_PORT);
+#ifdef ISC_PLATFORM_HAVESALEN
+ peeraddr.sa.sa_len = SOCKLEN(&peeraddr);
+#endif
+ found = FALSE;
+ p = NULL;
+
+ DPRINTF(1, ("searching for %s\n", stoa(&peeraddr)));
+
+ while (!found) {
+ p = findexistingpeer(&peeraddr, NULL, p, -1, 0);
+ if (NULL == p)
+ break;
+ if (FLAG_CONFIG & p->flags)
+ found = TRUE;
+ }
+ if (!found)
+ bad = TRUE;
+
+ datap += item_sz;
+ }
+
+ if (bad) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ /*
+ * Now do it in earnest.
+ */
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ datap = inpkt->u.data;
+
+ while (items-- > 0) {
+ ZERO(temp_cp);
+ memcpy(&temp_cp, datap, item_sz);
+ ZERO(peeraddr);
+ if (client_v6_capable && temp_cp.v6_flag) {
+ AF(&peeraddr) = AF_INET6;
+ SOCK_ADDR6(&peeraddr) = temp_cp.peeraddr6;
+ } else {
+ AF(&peeraddr) = AF_INET;
+ NSRCADR(&peeraddr) = temp_cp.peeraddr;
+ }
+ SET_PORT(&peeraddr, NTP_PORT);
+#ifdef ISC_PLATFORM_HAVESALEN
+ peeraddr.sa.sa_len = SOCKLEN(&peeraddr);
+#endif
+ found = FALSE;
+ p = NULL;
+
+ while (!found) {
+ p = findexistingpeer(&peeraddr, NULL, p, -1, 0);
+ if (NULL == p)
+ break;
+ if (FLAG_CONFIG & p->flags)
+ found = TRUE;
+ }
+ INSIST(found);
+ INSIST(NULL != p);
+
+ peer_clear(p, "GONE");
+ unpeer(p);
+
+ datap += item_sz;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * set_sys_flag - set system flags
+ */
+static void
+set_sys_flag(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ setclr_flags(srcadr, inter, inpkt, 1);
+}
+
+
+/*
+ * clr_sys_flag - clear system flags
+ */
+static void
+clr_sys_flag(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ setclr_flags(srcadr, inter, inpkt, 0);
+}
+
+
+/*
+ * setclr_flags - do the grunge work of flag setting/clearing
+ */
+static void
+setclr_flags(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt,
+ u_long set
+ )
+{
+ struct conf_sys_flags *sf;
+ u_int32 flags;
+
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "setclr_flags: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ sf = (struct conf_sys_flags *)&inpkt->u;
+ flags = ntohl(sf->flags);
+
+ if (flags & ~(SYS_FLAG_BCLIENT | SYS_FLAG_PPS |
+ SYS_FLAG_NTP | SYS_FLAG_KERNEL | SYS_FLAG_MONITOR |
+ SYS_FLAG_FILEGEN | SYS_FLAG_AUTH | SYS_FLAG_CAL)) {
+ msyslog(LOG_ERR, "setclr_flags: extra flags: %#x",
+ flags & ~(SYS_FLAG_BCLIENT | SYS_FLAG_PPS |
+ SYS_FLAG_NTP | SYS_FLAG_KERNEL |
+ SYS_FLAG_MONITOR | SYS_FLAG_FILEGEN |
+ SYS_FLAG_AUTH | SYS_FLAG_CAL));
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ if (flags & SYS_FLAG_BCLIENT)
+ proto_config(PROTO_BROADCLIENT, set, 0., NULL);
+ if (flags & SYS_FLAG_PPS)
+ proto_config(PROTO_PPS, set, 0., NULL);
+ if (flags & SYS_FLAG_NTP)
+ proto_config(PROTO_NTP, set, 0., NULL);
+ if (flags & SYS_FLAG_KERNEL)
+ proto_config(PROTO_KERNEL, set, 0., NULL);
+ if (flags & SYS_FLAG_MONITOR)
+ proto_config(PROTO_MONITOR, set, 0., NULL);
+ if (flags & SYS_FLAG_FILEGEN)
+ proto_config(PROTO_FILEGEN, set, 0., NULL);
+ if (flags & SYS_FLAG_AUTH)
+ proto_config(PROTO_AUTHENTICATE, set, 0., NULL);
+ if (flags & SYS_FLAG_CAL)
+ proto_config(PROTO_CAL, set, 0., NULL);
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+/*
+ * list_restrict4 - recursive helper for list_restrict dumps IPv4
+ * restriction list in reverse order.
+ */
+static void
+list_restrict4(
+ restrict_u * res,
+ struct info_restrict ** ppir
+ )
+{
+ struct info_restrict * pir;
+
+ if (res->link != NULL)
+ list_restrict4(res->link, ppir);
+
+ pir = *ppir;
+ pir->addr = htonl(res->u.v4.addr);
+ if (client_v6_capable)
+ pir->v6_flag = 0;
+ pir->mask = htonl(res->u.v4.mask);
+ pir->count = htonl(res->count);
+ pir->flags = htons(res->flags);
+ pir->mflags = htons(res->mflags);
+ *ppir = (struct info_restrict *)more_pkt();
+}
+
+
+/*
+ * list_restrict6 - recursive helper for list_restrict dumps IPv6
+ * restriction list in reverse order.
+ */
+static void
+list_restrict6(
+ restrict_u * res,
+ struct info_restrict ** ppir
+ )
+{
+ struct info_restrict * pir;
+
+ if (res->link != NULL)
+ list_restrict6(res->link, ppir);
+
+ pir = *ppir;
+ pir->addr6 = res->u.v6.addr;
+ pir->mask6 = res->u.v6.mask;
+ pir->v6_flag = 1;
+ pir->count = htonl(res->count);
+ pir->flags = htons(res->flags);
+ pir->mflags = htons(res->mflags);
+ *ppir = (struct info_restrict *)more_pkt();
+}
+
+
+/*
+ * list_restrict - return the restrict list
+ */
+static void
+list_restrict(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ struct info_restrict *ir;
+
+ DPRINTF(3, ("wants restrict list summary\n"));
+
+ ir = (struct info_restrict *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_restrict));
+
+ /*
+ * The restriction lists are kept sorted in the reverse order
+ * than they were originally. To preserve the output semantics,
+ * dump each list in reverse order. A recursive helper function
+ * achieves that.
+ */
+ list_restrict4(restrictlist4, &ir);
+ if (client_v6_capable)
+ list_restrict6(restrictlist6, &ir);
+ flush_pkt();
+}
+
+
+/*
+ * do_resaddflags - add flags to a restrict entry (or create one)
+ */
+static void
+do_resaddflags(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_FLAGS);
+}
+
+
+
+/*
+ * do_ressubflags - remove flags from a restrict entry
+ */
+static void
+do_ressubflags(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_UNFLAG);
+}
+
+
+/*
+ * do_unrestrict - remove a restrict entry from the list
+ */
+static void
+do_unrestrict(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_REMOVE);
+}
+
+
+/*
+ * do_restrict - do the dirty stuff of dealing with restrictions
+ */
+static void
+do_restrict(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt,
+ int op
+ )
+{
+ char * datap;
+ struct conf_restrict cr;
+ u_short items;
+ size_t item_sz;
+ sockaddr_u matchaddr;
+ sockaddr_u matchmask;
+ int bad;
+
+ /*
+ * Do a check of the flags to make sure that only
+ * the NTPPORT flag is set, if any. If not, complain
+ * about it. Note we are very picky here.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ item_sz = INFO_ITEMSIZE(inpkt->mbz_itemsize);
+ datap = inpkt->u.data;
+ if (item_sz > sizeof(cr)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ bad = FALSE;
+ while (items-- > 0 && !bad) {
+ memcpy(&cr, datap, item_sz);
+ cr.flags = ntohs(cr.flags);
+ cr.mflags = ntohs(cr.mflags);
+ if (~RESM_NTPONLY & cr.mflags)
+ bad |= 1;
+ if (~RES_ALLFLAGS & cr.flags)
+ bad |= 2;
+ if (INADDR_ANY != cr.mask) {
+ if (client_v6_capable && cr.v6_flag) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&cr.addr6))
+ bad |= 4;
+ } else {
+ if (INADDR_ANY == cr.addr)
+ bad |= 8;
+ }
+ }
+ datap += item_sz;
+ }
+
+ if (bad) {
+ msyslog(LOG_ERR, "do_restrict: bad = %#x", bad);
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ /*
+ * Looks okay, try it out
+ */
+ ZERO_SOCK(&matchaddr);
+ ZERO_SOCK(&matchmask);
+ datap = inpkt->u.data;
+
+ while (items-- > 0) {
+ memcpy(&cr, datap, item_sz);
+ cr.flags = ntohs(cr.flags);
+ cr.mflags = ntohs(cr.mflags);
+ if (client_v6_capable && cr.v6_flag) {
+ AF(&matchaddr) = AF_INET6;
+ AF(&matchmask) = AF_INET6;
+ SOCK_ADDR6(&matchaddr) = cr.addr6;
+ SOCK_ADDR6(&matchmask) = cr.mask6;
+ } else {
+ AF(&matchaddr) = AF_INET;
+ AF(&matchmask) = AF_INET;
+ NSRCADR(&matchaddr) = cr.addr;
+ NSRCADR(&matchmask) = cr.mask;
+ }
+ hack_restrict(op, &matchaddr, &matchmask, cr.mflags,
+ cr.flags, 0);
+ datap += item_sz;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * mon_getlist - return monitor data
+ */
+static void
+mon_getlist(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+}
+
+
+/*
+ * Module entry points and the flags they correspond with
+ */
+struct reset_entry {
+ int flag; /* flag this corresponds to */
+ void (*handler)(void); /* routine to handle request */
+};
+
+struct reset_entry reset_entries[] = {
+ { RESET_FLAG_ALLPEERS, peer_all_reset },
+ { RESET_FLAG_IO, io_clr_stats },
+ { RESET_FLAG_SYS, proto_clr_stats },
+ { RESET_FLAG_MEM, peer_clr_stats },
+ { RESET_FLAG_TIMER, timer_clr_stats },
+ { RESET_FLAG_AUTH, reset_auth_stats },
+ { RESET_FLAG_CTL, ctl_clr_stats },
+ { 0, 0 }
+};
+
+/*
+ * reset_stats - reset statistic counters here and there
+ */
+static void
+reset_stats(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ struct reset_flags *rflags;
+ u_long flags;
+ struct reset_entry *rent;
+
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "reset_stats: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ rflags = (struct reset_flags *)&inpkt->u;
+ flags = ntohl(rflags->flags);
+
+ if (flags & ~RESET_ALLFLAGS) {
+ msyslog(LOG_ERR, "reset_stats: reset leaves %#lx",
+ flags & ~RESET_ALLFLAGS);
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ for (rent = reset_entries; rent->flag != 0; rent++) {
+ if (flags & rent->flag)
+ (*rent->handler)();
+ }
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * reset_peer - clear a peer's statistics
+ */
+static void
+reset_peer(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ u_short items;
+ size_t item_sz;
+ char * datap;
+ struct conf_unpeer cp;
+ struct peer * p;
+ sockaddr_u peeraddr;
+ int bad;
+
+ /*
+ * We check first to see that every peer exists. If not,
+ * we return an error.
+ */
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ item_sz = INFO_ITEMSIZE(inpkt->mbz_itemsize);
+ datap = inpkt->u.data;
+ if (item_sz > sizeof(cp)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ bad = FALSE;
+ while (items-- > 0 && !bad) {
+ ZERO(cp);
+ memcpy(&cp, datap, item_sz);
+ ZERO_SOCK(&peeraddr);
+ if (client_v6_capable && cp.v6_flag) {
+ AF(&peeraddr) = AF_INET6;
+ SOCK_ADDR6(&peeraddr) = cp.peeraddr6;
+ } else {
+ AF(&peeraddr) = AF_INET;
+ NSRCADR(&peeraddr) = cp.peeraddr;
+ }
+
+#ifdef ISC_PLATFORM_HAVESALEN
+ peeraddr.sa.sa_len = SOCKLEN(&peeraddr);
+#endif
+ p = findexistingpeer(&peeraddr, NULL, NULL, -1, 0);
+ if (NULL == p)
+ bad++;
+ datap += item_sz;
+ }
+
+ if (bad) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ /*
+ * Now do it in earnest.
+ */
+
+ datap = inpkt->u.data;
+ while (items-- > 0) {
+ ZERO(cp);
+ memcpy(&cp, datap, item_sz);
+ ZERO_SOCK(&peeraddr);
+ if (client_v6_capable && cp.v6_flag) {
+ AF(&peeraddr) = AF_INET6;
+ SOCK_ADDR6(&peeraddr) = cp.peeraddr6;
+ } else {
+ AF(&peeraddr) = AF_INET;
+ NSRCADR(&peeraddr) = cp.peeraddr;
+ }
+ SET_PORT(&peeraddr, 123);
+#ifdef ISC_PLATFORM_HAVESALEN
+ peeraddr.sa.sa_len = SOCKLEN(&peeraddr);
+#endif
+ p = findexistingpeer(&peeraddr, NULL, NULL, -1, 0);
+ while (p != NULL) {
+ peer_reset(p);
+ p = findexistingpeer(&peeraddr, NULL, p, -1, 0);
+ }
+ datap += item_sz;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_key_reread - reread the encryption key file
+ */
+static void
+do_key_reread(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ rereadkeys();
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * trust_key - make one or more keys trusted
+ */
+static void
+trust_key(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_trustkey(srcadr, inter, inpkt, 1);
+}
+
+
+/*
+ * untrust_key - make one or more keys untrusted
+ */
+static void
+untrust_key(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_trustkey(srcadr, inter, inpkt, 0);
+}
+
+
+/*
+ * do_trustkey - make keys either trustable or untrustable
+ */
+static void
+do_trustkey(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt,
+ u_long trust
+ )
+{
+ register u_long *kp;
+ register int items;
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ kp = (u_long *)&inpkt->u;
+ while (items-- > 0) {
+ authtrust(*kp, trust);
+ kp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * get_auth_info - return some stats concerning the authentication module
+ */
+static void
+get_auth_info(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_auth *ia;
+
+ ia = (struct info_auth *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_auth));
+
+ ia->numkeys = htonl((u_int32)authnumkeys);
+ ia->numfreekeys = htonl((u_int32)authnumfreekeys);
+ ia->keylookups = htonl((u_int32)authkeylookups);
+ ia->keynotfound = htonl((u_int32)authkeynotfound);
+ ia->encryptions = htonl((u_int32)authencryptions);
+ ia->decryptions = htonl((u_int32)authdecryptions);
+ ia->keyuncached = htonl((u_int32)authkeyuncached);
+ ia->expired = htonl((u_int32)authkeyexpired);
+ ia->timereset = htonl((u_int32)(current_time - auth_timereset));
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+
+/*
+ * reset_auth_stats - reset the authentication stat counters. Done here
+ * to keep ntp-isms out of the authentication module
+ */
+void
+reset_auth_stats(void)
+{
+ authkeylookups = 0;
+ authkeynotfound = 0;
+ authencryptions = 0;
+ authdecryptions = 0;
+ authkeyuncached = 0;
+ auth_timereset = current_time;
+}
+
+
+/*
+ * req_get_traps - return information about current trap holders
+ */
+static void
+req_get_traps(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ struct info_trap *it;
+ struct ctl_trap *tr;
+ int i;
+
+ if (num_ctl_traps == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ it = (struct info_trap *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_trap));
+
+ for (i = 0, tr = ctl_traps; i < COUNTOF(ctl_traps); i++, tr++) {
+ if (tr->tr_flags & TRAP_INUSE) {
+ if (IS_IPV4(&tr->tr_addr)) {
+ if (tr->tr_localaddr == any_interface)
+ it->local_address = 0;
+ else
+ it->local_address
+ = NSRCADR(&tr->tr_localaddr->sin);
+ it->trap_address = NSRCADR(&tr->tr_addr);
+ if (client_v6_capable)
+ it->v6_flag = 0;
+ } else {
+ if (!client_v6_capable)
+ continue;
+ it->local_address6
+ = SOCK_ADDR6(&tr->tr_localaddr->sin);
+ it->trap_address6 = SOCK_ADDR6(&tr->tr_addr);
+ it->v6_flag = 1;
+ }
+ it->trap_port = NSRCPORT(&tr->tr_addr);
+ it->sequence = htons(tr->tr_sequence);
+ it->settime = htonl((u_int32)(current_time - tr->tr_settime));
+ it->origtime = htonl((u_int32)(current_time - tr->tr_origtime));
+ it->resets = htonl((u_int32)tr->tr_resets);
+ it->flags = htonl((u_int32)tr->tr_flags);
+ it = (struct info_trap *)more_pkt();
+ }
+ }
+ flush_pkt();
+}
+
+
+/*
+ * req_set_trap - configure a trap
+ */
+static void
+req_set_trap(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_setclr_trap(srcadr, inter, inpkt, 1);
+}
+
+
+
+/*
+ * req_clr_trap - unconfigure a trap
+ */
+static void
+req_clr_trap(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_setclr_trap(srcadr, inter, inpkt, 0);
+}
+
+
+
+/*
+ * do_setclr_trap - do the grunge work of (un)configuring a trap
+ */
+static void
+do_setclr_trap(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt,
+ int set
+ )
+{
+ register struct conf_trap *ct;
+ register endpt *linter;
+ int res;
+ sockaddr_u laddr;
+
+ /*
+ * Prepare sockaddr
+ */
+ ZERO_SOCK(&laddr);
+ AF(&laddr) = AF(srcadr);
+ SET_PORT(&laddr, NTP_PORT);
+
+ /*
+ * Restrict ourselves to one item only. This eliminates
+ * the error reporting problem.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "do_setclr_trap: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ ct = (struct conf_trap *)&inpkt->u;
+
+ /*
+ * Look for the local interface. If none, use the default.
+ */
+ if (ct->local_address == 0) {
+ linter = any_interface;
+ } else {
+ if (IS_IPV4(&laddr))
+ NSRCADR(&laddr) = ct->local_address;
+ else
+ SOCK_ADDR6(&laddr) = ct->local_address6;
+ linter = findinterface(&laddr);
+ if (NULL == linter) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ }
+
+ if (IS_IPV4(&laddr))
+ NSRCADR(&laddr) = ct->trap_address;
+ else
+ SOCK_ADDR6(&laddr) = ct->trap_address6;
+ if (ct->trap_port)
+ NSRCPORT(&laddr) = ct->trap_port;
+ else
+ SET_PORT(&laddr, TRAPPORT);
+
+ if (set) {
+ res = ctlsettrap(&laddr, linter, 0,
+ INFO_VERSION(inpkt->rm_vn_mode));
+ } else {
+ res = ctlclrtrap(&laddr, linter, 0);
+ }
+
+ if (!res) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ } else {
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+ }
+ return;
+}
+
+
+
+/*
+ * set_request_keyid - set the keyid used to authenticate requests
+ */
+static void
+set_request_keyid(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ keyid_t *pkeyid;
+
+ /*
+ * Restrict ourselves to one item only.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "set_request_keyid: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ pkeyid = (keyid_t *)&inpkt->u;
+ info_auth_keyid = ntohl(*pkeyid);
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+
+/*
+ * set_control_keyid - set the keyid used to authenticate requests
+ */
+static void
+set_control_keyid(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ keyid_t *pkeyid;
+
+ /*
+ * Restrict ourselves to one item only.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "set_control_keyid: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ pkeyid = (keyid_t *)&inpkt->u;
+ ctl_auth_keyid = ntohl(*pkeyid);
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+
+/*
+ * get_ctl_stats - return some stats concerning the control message module
+ */
+static void
+get_ctl_stats(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_control *ic;
+
+ ic = (struct info_control *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_control));
+
+ ic->ctltimereset = htonl((u_int32)(current_time - ctltimereset));
+ ic->numctlreq = htonl((u_int32)numctlreq);
+ ic->numctlbadpkts = htonl((u_int32)numctlbadpkts);
+ ic->numctlresponses = htonl((u_int32)numctlresponses);
+ ic->numctlfrags = htonl((u_int32)numctlfrags);
+ ic->numctlerrors = htonl((u_int32)numctlerrors);
+ ic->numctltooshort = htonl((u_int32)numctltooshort);
+ ic->numctlinputresp = htonl((u_int32)numctlinputresp);
+ ic->numctlinputfrag = htonl((u_int32)numctlinputfrag);
+ ic->numctlinputerr = htonl((u_int32)numctlinputerr);
+ ic->numctlbadoffset = htonl((u_int32)numctlbadoffset);
+ ic->numctlbadversion = htonl((u_int32)numctlbadversion);
+ ic->numctldatatooshort = htonl((u_int32)numctldatatooshort);
+ ic->numctlbadop = htonl((u_int32)numctlbadop);
+ ic->numasyncmsgs = htonl((u_int32)numasyncmsgs);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+#ifdef KERNEL_PLL
+/*
+ * get_kernel_info - get kernel pll/pps information
+ */
+static void
+get_kernel_info(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_kernel *ik;
+ struct timex ntx;
+
+ if (!pll_control) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ ZERO(ntx);
+ if (ntp_adjtime(&ntx) < 0)
+ msyslog(LOG_ERR, "get_kernel_info: ntp_adjtime() failed: %m");
+ ik = (struct info_kernel *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_kernel));
+
+ /*
+ * pll variables
+ */
+ ik->offset = htonl((u_int32)ntx.offset);
+ ik->freq = htonl((u_int32)ntx.freq);
+ ik->maxerror = htonl((u_int32)ntx.maxerror);
+ ik->esterror = htonl((u_int32)ntx.esterror);
+ ik->status = htons(ntx.status);
+ ik->constant = htonl((u_int32)ntx.constant);
+ ik->precision = htonl((u_int32)ntx.precision);
+ ik->tolerance = htonl((u_int32)ntx.tolerance);
+
+ /*
+ * pps variables
+ */
+ ik->ppsfreq = htonl((u_int32)ntx.ppsfreq);
+ ik->jitter = htonl((u_int32)ntx.jitter);
+ ik->shift = htons(ntx.shift);
+ ik->stabil = htonl((u_int32)ntx.stabil);
+ ik->jitcnt = htonl((u_int32)ntx.jitcnt);
+ ik->calcnt = htonl((u_int32)ntx.calcnt);
+ ik->errcnt = htonl((u_int32)ntx.errcnt);
+ ik->stbcnt = htonl((u_int32)ntx.stbcnt);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+#endif /* KERNEL_PLL */
+
+
+#ifdef REFCLOCK
+/*
+ * get_clock_info - get info about a clock
+ */
+static void
+get_clock_info(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_clock *ic;
+ register u_int32 *clkaddr;
+ register int items;
+ struct refclockstat clock_stat;
+ sockaddr_u addr;
+ l_fp ltmp;
+
+ ZERO_SOCK(&addr);
+ AF(&addr) = AF_INET;
+#ifdef ISC_PLATFORM_HAVESALEN
+ addr.sa.sa_len = SOCKLEN(&addr);
+#endif
+ SET_PORT(&addr, NTP_PORT);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ clkaddr = &inpkt->u.u32[0];
+
+ ic = (struct info_clock *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_clock));
+
+ while (items-- > 0) {
+ NSRCADR(&addr) = *clkaddr++;
+ if (!ISREFCLOCKADR(&addr) || NULL ==
+ findexistingpeer(&addr, NULL, NULL, -1, 0)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ clock_stat.kv_list = (struct ctl_var *)0;
+
+ refclock_control(&addr, NULL, &clock_stat);
+
+ ic->clockadr = NSRCADR(&addr);
+ ic->type = clock_stat.type;
+ ic->flags = clock_stat.flags;
+ ic->lastevent = clock_stat.lastevent;
+ ic->currentstatus = clock_stat.currentstatus;
+ ic->polls = htonl((u_int32)clock_stat.polls);
+ ic->noresponse = htonl((u_int32)clock_stat.noresponse);
+ ic->badformat = htonl((u_int32)clock_stat.badformat);
+ ic->baddata = htonl((u_int32)clock_stat.baddata);
+ ic->timestarted = htonl((u_int32)clock_stat.timereset);
+ DTOLFP(clock_stat.fudgetime1, &ltmp);
+ HTONL_FP(&ltmp, &ic->fudgetime1);
+ DTOLFP(clock_stat.fudgetime2, &ltmp);
+ HTONL_FP(&ltmp, &ic->fudgetime2);
+ ic->fudgeval1 = htonl((u_int32)clock_stat.fudgeval1);
+ ic->fudgeval2 = htonl(clock_stat.fudgeval2);
+
+ free_varlist(clock_stat.kv_list);
+
+ ic = (struct info_clock *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+
+/*
+ * set_clock_fudge - get a clock's fudge factors
+ */
+static void
+set_clock_fudge(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct conf_fudge *cf;
+ register int items;
+ struct refclockstat clock_stat;
+ sockaddr_u addr;
+ l_fp ltmp;
+
+ ZERO(addr);
+ ZERO(clock_stat);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cf = (struct conf_fudge *)&inpkt->u;
+
+ while (items-- > 0) {
+ AF(&addr) = AF_INET;
+ NSRCADR(&addr) = cf->clockadr;
+#ifdef ISC_PLATFORM_HAVESALEN
+ addr.sa.sa_len = SOCKLEN(&addr);
+#endif
+ SET_PORT(&addr, NTP_PORT);
+ if (!ISREFCLOCKADR(&addr) || NULL ==
+ findexistingpeer(&addr, NULL, NULL, -1, 0)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ switch(ntohl(cf->which)) {
+ case FUDGE_TIME1:
+ NTOHL_FP(&cf->fudgetime, &ltmp);
+ LFPTOD(&ltmp, clock_stat.fudgetime1);
+ clock_stat.haveflags = CLK_HAVETIME1;
+ break;
+ case FUDGE_TIME2:
+ NTOHL_FP(&cf->fudgetime, &ltmp);
+ LFPTOD(&ltmp, clock_stat.fudgetime2);
+ clock_stat.haveflags = CLK_HAVETIME2;
+ break;
+ case FUDGE_VAL1:
+ clock_stat.fudgeval1 = ntohl(cf->fudgeval_flags);
+ clock_stat.haveflags = CLK_HAVEVAL1;
+ break;
+ case FUDGE_VAL2:
+ clock_stat.fudgeval2 = ntohl(cf->fudgeval_flags);
+ clock_stat.haveflags = CLK_HAVEVAL2;
+ break;
+ case FUDGE_FLAGS:
+ clock_stat.flags = (u_char) (ntohl(cf->fudgeval_flags) & 0xf);
+ clock_stat.haveflags =
+ (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4);
+ break;
+ default:
+ msyslog(LOG_ERR, "set_clock_fudge: default!");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ refclock_control(&addr, &clock_stat, (struct refclockstat *)0);
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+#endif
+
+#ifdef REFCLOCK
+/*
+ * get_clkbug_info - get debugging info about a clock
+ */
+static void
+get_clkbug_info(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register int i;
+ register struct info_clkbug *ic;
+ register u_int32 *clkaddr;
+ register int items;
+ struct refclockbug bug;
+ sockaddr_u addr;
+
+ ZERO_SOCK(&addr);
+ AF(&addr) = AF_INET;
+#ifdef ISC_PLATFORM_HAVESALEN
+ addr.sa.sa_len = SOCKLEN(&addr);
+#endif
+ SET_PORT(&addr, NTP_PORT);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ clkaddr = (u_int32 *)&inpkt->u;
+
+ ic = (struct info_clkbug *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_clkbug));
+
+ while (items-- > 0) {
+ NSRCADR(&addr) = *clkaddr++;
+ if (!ISREFCLOCKADR(&addr) || NULL ==
+ findexistingpeer(&addr, NULL, NULL, -1, 0)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ ZERO(bug);
+ refclock_buginfo(&addr, &bug);
+ if (bug.nvalues == 0 && bug.ntimes == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ ic->clockadr = NSRCADR(&addr);
+ i = bug.nvalues;
+ if (i > NUMCBUGVALUES)
+ i = NUMCBUGVALUES;
+ ic->nvalues = (u_char)i;
+ ic->svalues = htons((u_short) (bug.svalues & ((1<<i)-1)));
+ while (--i >= 0)
+ ic->values[i] = htonl(bug.values[i]);
+
+ i = bug.ntimes;
+ if (i > NUMCBUGTIMES)
+ i = NUMCBUGTIMES;
+ ic->ntimes = (u_char)i;
+ ic->stimes = htonl(bug.stimes);
+ while (--i >= 0) {
+ HTONL_FP(&bug.times[i], &ic->times[i]);
+ }
+
+ ic = (struct info_clkbug *)more_pkt();
+ }
+ flush_pkt();
+}
+#endif
+
+/*
+ * receiver of interface structures
+ */
+static void
+fill_info_if_stats(void *data, interface_info_t *interface_info)
+{
+ struct info_if_stats **ifsp = (struct info_if_stats **)data;
+ struct info_if_stats *ifs = *ifsp;
+ endpt *ep = interface_info->ep;
+
+ ZERO(*ifs);
+
+ if (IS_IPV6(&ep->sin)) {
+ if (!client_v6_capable) {
+ return;
+ }
+ ifs->v6_flag = 1;
+ ifs->unaddr.addr6 = SOCK_ADDR6(&ep->sin);
+ ifs->unbcast.addr6 = SOCK_ADDR6(&ep->bcast);
+ ifs->unmask.addr6 = SOCK_ADDR6(&ep->mask);
+ } else {
+ ifs->v6_flag = 0;
+ ifs->unaddr.addr = SOCK_ADDR4(&ep->sin);
+ ifs->unbcast.addr = SOCK_ADDR4(&ep->bcast);
+ ifs->unmask.addr = SOCK_ADDR4(&ep->mask);
+ }
+ ifs->v6_flag = htonl(ifs->v6_flag);
+ strlcpy(ifs->name, ep->name, sizeof(ifs->name));
+ ifs->family = htons(ep->family);
+ ifs->flags = htonl(ep->flags);
+ ifs->last_ttl = htonl(ep->last_ttl);
+ ifs->num_mcast = htonl(ep->num_mcast);
+ ifs->received = htonl(ep->received);
+ ifs->sent = htonl(ep->sent);
+ ifs->notsent = htonl(ep->notsent);
+ ifs->ifindex = htonl(ep->ifindex);
+ /* scope no longer in endpt, in in6_addr typically */
+ ifs->scopeid = ifs->ifindex;
+ ifs->ifnum = htonl(ep->ifnum);
+ ifs->uptime = htonl(current_time - ep->starttime);
+ ifs->ignore_packets = ep->ignore_packets;
+ ifs->peercnt = htonl(ep->peercnt);
+ ifs->action = interface_info->action;
+
+ *ifsp = (struct info_if_stats *)more_pkt();
+}
+
+/*
+ * get_if_stats - get interface statistics
+ */
+static void
+get_if_stats(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ struct info_if_stats *ifs;
+
+ DPRINTF(3, ("wants interface statistics\n"));
+
+ ifs = (struct info_if_stats *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_if_stats));
+
+ interface_enumerate(fill_info_if_stats, &ifs);
+
+ flush_pkt();
+}
+
+static void
+do_if_reload(
+ sockaddr_u *srcadr,
+ endpt *inter,
+ struct req_pkt *inpkt
+ )
+{
+ struct info_if_stats *ifs;
+
+ DPRINTF(3, ("wants interface reload\n"));
+
+ ifs = (struct info_if_stats *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_if_stats));
+
+ interface_update(fill_info_if_stats, &ifs);
+
+ flush_pkt();
+}
+
diff --git a/ntpd/ntp_restrict.c b/ntpd/ntp_restrict.c
new file mode 100644
index 0000000..6e75667
--- /dev/null
+++ b/ntpd/ntp_restrict.c
@@ -0,0 +1,669 @@
+/*
+ * ntp_restrict.c - determine host restrictions
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_if.h"
+#include "ntp_lists.h"
+#include "ntp_stdlib.h"
+#include "ntp_assert.h"
+
+/*
+ * This code keeps a simple address-and-mask list of hosts we want
+ * to place restrictions on (or remove them from). The restrictions
+ * are implemented as a set of flags which tell you what the host
+ * can't do. There is a subroutine entry to return the flags. The
+ * list is kept sorted to reduce the average number of comparisons
+ * and make sure you get the set of restrictions most specific to
+ * the address.
+ *
+ * The algorithm is that, when looking up a host, it is first assumed
+ * that the default set of restrictions will apply. It then searches
+ * down through the list. Whenever it finds a match it adopts the
+ * match's flags instead. When you hit the point where the sorted
+ * address is greater than the target, you return with the last set of
+ * flags you found. Because of the ordering of the list, the most
+ * specific match will provide the final set of flags.
+ *
+ * This was originally intended to restrict you from sync'ing to your
+ * own broadcasts when you are doing that, by restricting yourself from
+ * your own interfaces. It was also thought it would sometimes be useful
+ * to keep a misbehaving host or two from abusing your primary clock. It
+ * has been expanded, however, to suit the needs of those with more
+ * restrictive access policies.
+ */
+/*
+ * We will use two lists, one for IPv4 addresses and one for IPv6
+ * addresses. This is not protocol-independant but for now I can't
+ * find a way to respect this. We'll check this later... JFB 07/2001
+ */
+#define MASK_IPV6_ADDR(dst, src, msk) \
+ do { \
+ int idx; \
+ for (idx = 0; idx < COUNTOF((dst)->s6_addr); idx++) { \
+ (dst)->s6_addr[idx] = (src)->s6_addr[idx] \
+ & (msk)->s6_addr[idx]; \
+ } \
+ } while (0)
+
+/*
+ * We allocate INC_RESLIST{4|6} entries to the free list whenever empty.
+ * Auto-tune these to be just less than 1KB (leaving at least 16 bytes
+ * for allocator overhead).
+ */
+#define INC_RESLIST4 ((1024 - 16) / V4_SIZEOF_RESTRICT_U)
+#define INC_RESLIST6 ((1024 - 16) / V6_SIZEOF_RESTRICT_U)
+
+/*
+ * The restriction list
+ */
+restrict_u *restrictlist4;
+restrict_u *restrictlist6;
+static int restrictcount; /* count in the restrict lists */
+
+/*
+ * The free list and associated counters. Also some uninteresting
+ * stat counters.
+ */
+static restrict_u *resfree4; /* available entries (free list) */
+static restrict_u *resfree6;
+
+static u_long res_calls;
+static u_long res_found;
+static u_long res_not_found;
+
+/*
+ * Count number of restriction entries referring to RES_LIMITED, to
+ * control implicit activation/deactivation of the MRU monlist.
+ */
+static u_long res_limited_refcnt;
+
+/*
+ * Our default entries.
+ */
+static restrict_u restrict_def4;
+static restrict_u restrict_def6;
+
+/*
+ * "restrict source ..." enabled knob and restriction bits.
+ */
+static int restrict_source_enabled;
+static u_short restrict_source_flags;
+static u_short restrict_source_mflags;
+
+/*
+ * private functions
+ */
+static restrict_u * alloc_res4(void);
+static restrict_u * alloc_res6(void);
+static void free_res(restrict_u *, int);
+static void inc_res_limited(void);
+static void dec_res_limited(void);
+static restrict_u * match_restrict4_addr(u_int32, u_short);
+static restrict_u * match_restrict6_addr(const struct in6_addr *,
+ u_short);
+static restrict_u * match_restrict_entry(const restrict_u *, int);
+static int res_sorts_before4(restrict_u *, restrict_u *);
+static int res_sorts_before6(restrict_u *, restrict_u *);
+
+
+/*
+ * init_restrict - initialize the restriction data structures
+ */
+void
+init_restrict(void)
+{
+ /*
+ * The restriction lists begin with a default entry with address
+ * and mask 0, which will match any entry. The lists are kept
+ * sorted by descending address followed by descending mask:
+ *
+ * address mask
+ * 192.168.0.0 255.255.255.0 kod limited noquery nopeer
+ * 192.168.0.0 255.255.0.0 kod limited
+ * 0.0.0.0 0.0.0.0 kod limited noquery
+ *
+ * The first entry which matches an address is used. With the
+ * example restrictions above, 192.168.0.0/24 matches the first
+ * entry, the rest of 192.168.0.0/16 matches the second, and
+ * everything else matches the third (default).
+ *
+ * Note this achieves the same result a little more efficiently
+ * than the documented behavior, which is to keep the lists
+ * sorted by ascending address followed by ascending mask, with
+ * the _last_ matching entry used.
+ *
+ * An additional wrinkle is we may have multiple entries with
+ * the same address and mask but differing match flags (mflags).
+ * At present there is only one, RESM_NTPONLY. Entries with
+ * RESM_NTPONLY are sorted earlier so they take precedence over
+ * any otherwise similar entry without. Again, this is the same
+ * behavior as but reversed implementation compared to the docs.
+ *
+ */
+ LINK_SLIST(restrictlist4, &restrict_def4, link);
+ LINK_SLIST(restrictlist6, &restrict_def6, link);
+ restrictcount = 2;
+}
+
+
+static restrict_u *
+alloc_res4(void)
+{
+ const size_t cb = V4_SIZEOF_RESTRICT_U;
+ const size_t count = INC_RESLIST4;
+ restrict_u * rl;
+ restrict_u * res;
+ int i;
+
+ UNLINK_HEAD_SLIST(res, resfree4, link);
+ if (res != NULL)
+ return res;
+
+ rl = emalloc_zero(count * cb);
+ /* link all but the first onto free list */
+ res = (void *)((char *)rl + (count - 1) * cb);
+ for (i = count - 1; i > 0; i--) {
+ LINK_SLIST(resfree4, res, link);
+ res = (void *)((char *)res - cb);
+ }
+ NTP_INSIST(rl == res);
+ /* allocate the first */
+ return res;
+}
+
+
+static restrict_u *
+alloc_res6(void)
+{
+ const size_t cb = V6_SIZEOF_RESTRICT_U;
+ const size_t count = INC_RESLIST6;
+ restrict_u * rl;
+ restrict_u * res;
+ int i;
+
+ UNLINK_HEAD_SLIST(res, resfree6, link);
+ if (res != NULL)
+ return res;
+
+ rl = emalloc_zero(count * cb);
+ /* link all but the first onto free list */
+ res = (void *)((char *)rl + (count - 1) * cb);
+ for (i = count - 1; i > 0; i--) {
+ LINK_SLIST(resfree6, res, link);
+ res = (void *)((char *)res - cb);
+ }
+ NTP_INSIST(rl == res);
+ /* allocate the first */
+ return res;
+}
+
+
+static void
+free_res(
+ restrict_u * res,
+ int v6
+ )
+{
+ restrict_u ** plisthead;
+ restrict_u * unlinked;
+
+ restrictcount--;
+ if (RES_LIMITED & res->flags)
+ dec_res_limited();
+
+ if (v6)
+ plisthead = &restrictlist6;
+ else
+ plisthead = &restrictlist4;
+ UNLINK_SLIST(unlinked, *plisthead, res, link, restrict_u);
+ NTP_INSIST(unlinked == res);
+
+ if (v6) {
+ zero_mem(res, V6_SIZEOF_RESTRICT_U);
+ plisthead = &resfree6;
+ } else {
+ zero_mem(res, V4_SIZEOF_RESTRICT_U);
+ plisthead = &resfree4;
+ }
+ LINK_SLIST(*plisthead, res, link);
+}
+
+
+static void
+inc_res_limited(void)
+{
+ if (!res_limited_refcnt)
+ mon_start(MON_RES);
+ res_limited_refcnt++;
+}
+
+
+static void
+dec_res_limited(void)
+{
+ res_limited_refcnt--;
+ if (!res_limited_refcnt)
+ mon_stop(MON_RES);
+}
+
+
+static restrict_u *
+match_restrict4_addr(
+ u_int32 addr,
+ u_short port
+ )
+{
+ const int v6 = 0;
+ restrict_u * res;
+ restrict_u * next;
+
+ for (res = restrictlist4; res != NULL; res = next) {
+ next = res->link;
+ if (res->expire &&
+ res->expire <= current_time)
+ free_res(res, v6);
+ if (res->u.v4.addr == (addr & res->u.v4.mask)
+ && (!(RESM_NTPONLY & res->mflags)
+ || NTP_PORT == port))
+ break;
+ }
+ return res;
+}
+
+
+static restrict_u *
+match_restrict6_addr(
+ const struct in6_addr * addr,
+ u_short port
+ )
+{
+ const int v6 = 1;
+ restrict_u * res;
+ restrict_u * next;
+ struct in6_addr masked;
+
+ for (res = restrictlist6; res != NULL; res = next) {
+ next = res->link;
+ NTP_INSIST(next != res);
+ if (res->expire &&
+ res->expire <= current_time)
+ free_res(res, v6);
+ MASK_IPV6_ADDR(&masked, addr, &res->u.v6.mask);
+ if (ADDR6_EQ(&masked, &res->u.v6.addr)
+ && (!(RESM_NTPONLY & res->mflags)
+ || NTP_PORT == port))
+ break;
+ }
+ return res;
+}
+
+
+/*
+ * match_restrict_entry - find an exact match on a restrict list.
+ *
+ * Exact match is addr, mask, and mflags all equal.
+ * In order to use more common code for IPv4 and IPv6, this routine
+ * requires the caller to populate a restrict_u with mflags and either
+ * the v4 or v6 address and mask as appropriate. Other fields in the
+ * input restrict_u are ignored.
+ */
+static restrict_u *
+match_restrict_entry(
+ const restrict_u * pmatch,
+ int v6
+ )
+{
+ restrict_u *res;
+ restrict_u *rlist;
+ size_t cb;
+
+ if (v6) {
+ rlist = restrictlist6;
+ cb = sizeof(pmatch->u.v6);
+ } else {
+ rlist = restrictlist4;
+ cb = sizeof(pmatch->u.v4);
+ }
+
+ for (res = rlist; res != NULL; res = res->link)
+ if (res->mflags == pmatch->mflags &&
+ !memcmp(&res->u, &pmatch->u, cb))
+ break;
+ return res;
+}
+
+
+/*
+ * res_sorts_before4 - compare two restrict4 entries
+ *
+ * Returns nonzero if r1 sorts before r2. We sort by descending
+ * address, then descending mask, then descending mflags, so sorting
+ * before means having a higher value.
+ */
+static int
+res_sorts_before4(
+ restrict_u *r1,
+ restrict_u *r2
+ )
+{
+ int r1_before_r2;
+
+ if (r1->u.v4.addr > r2->u.v4.addr)
+ r1_before_r2 = 1;
+ else if (r1->u.v4.addr < r2->u.v4.addr)
+ r1_before_r2 = 0;
+ else if (r1->u.v4.mask > r2->u.v4.mask)
+ r1_before_r2 = 1;
+ else if (r1->u.v4.mask < r2->u.v4.mask)
+ r1_before_r2 = 0;
+ else if (r1->mflags > r2->mflags)
+ r1_before_r2 = 1;
+ else
+ r1_before_r2 = 0;
+
+ return r1_before_r2;
+}
+
+
+/*
+ * res_sorts_before6 - compare two restrict6 entries
+ *
+ * Returns nonzero if r1 sorts before r2. We sort by descending
+ * address, then descending mask, then descending mflags, so sorting
+ * before means having a higher value.
+ */
+static int
+res_sorts_before6(
+ restrict_u *r1,
+ restrict_u *r2
+ )
+{
+ int r1_before_r2;
+ int cmp;
+
+ cmp = ADDR6_CMP(&r1->u.v6.addr, &r2->u.v6.addr);
+ if (cmp > 0) /* r1->addr > r2->addr */
+ r1_before_r2 = 1;
+ else if (cmp < 0) /* r2->addr > r1->addr */
+ r1_before_r2 = 0;
+ else {
+ cmp = ADDR6_CMP(&r1->u.v6.mask, &r2->u.v6.mask);
+ if (cmp > 0) /* r1->mask > r2->mask*/
+ r1_before_r2 = 1;
+ else if (cmp < 0) /* r2->mask > r1->mask */
+ r1_before_r2 = 0;
+ else if (r1->mflags > r2->mflags)
+ r1_before_r2 = 1;
+ else
+ r1_before_r2 = 0;
+ }
+
+ return r1_before_r2;
+}
+
+
+/*
+ * restrictions - return restrictions for this host
+ */
+u_short
+restrictions(
+ sockaddr_u *srcadr
+ )
+{
+ restrict_u *match;
+ struct in6_addr *pin6;
+ u_short flags;
+
+ res_calls++;
+ flags = 0;
+ /* IPv4 source address */
+ if (IS_IPV4(srcadr)) {
+ /*
+ * Ignore any packets with a multicast source address
+ * (this should be done early in the receive process,
+ * not later!)
+ */
+ if (IN_CLASSD(SRCADR(srcadr)))
+ return (int)RES_IGNORE;
+
+ match = match_restrict4_addr(SRCADR(srcadr),
+ SRCPORT(srcadr));
+ match->count++;
+ /*
+ * res_not_found counts only use of the final default
+ * entry, not any "restrict default ntpport ...", which
+ * would be just before the final default.
+ */
+ if (&restrict_def4 == match)
+ res_not_found++;
+ else
+ res_found++;
+ flags = match->flags;
+ }
+
+ /* IPv6 source address */
+ if (IS_IPV6(srcadr)) {
+ pin6 = PSOCK_ADDR6(srcadr);
+
+ /*
+ * Ignore any packets with a multicast source address
+ * (this should be done early in the receive process,
+ * not later!)
+ */
+ if (IN6_IS_ADDR_MULTICAST(pin6))
+ return (int)RES_IGNORE;
+
+ match = match_restrict6_addr(pin6, SRCPORT(srcadr));
+ match->count++;
+ if (&restrict_def6 == match)
+ res_not_found++;
+ else
+ res_found++;
+ flags = match->flags;
+ }
+ return (flags);
+}
+
+
+/*
+ * hack_restrict - add/subtract/manipulate entries on the restrict list
+ */
+void
+hack_restrict(
+ int op,
+ sockaddr_u * resaddr,
+ sockaddr_u * resmask,
+ u_short mflags,
+ u_short flags,
+ u_long expire
+ )
+{
+ int v6;
+ restrict_u match;
+ restrict_u * res;
+ restrict_u ** plisthead;
+
+ DPRINTF(1, ("restrict: op %d addr %s mask %s mflags %08x flags %08x\n",
+ op, stoa(resaddr), stoa(resmask), mflags, flags));
+
+ if (NULL == resaddr) {
+ NTP_REQUIRE(NULL == resmask);
+ NTP_REQUIRE(RESTRICT_FLAGS == op);
+ restrict_source_flags = flags;
+ restrict_source_mflags = mflags;
+ restrict_source_enabled = 1;
+ return;
+ }
+
+ ZERO(match);
+ /* silence VC9 potentially uninit warnings */
+ res = NULL;
+ v6 = 0;
+
+ if (IS_IPV4(resaddr)) {
+ v6 = 0;
+ /*
+ * Get address and mask in host byte order for easy
+ * comparison as u_int32
+ */
+ match.u.v4.addr = SRCADR(resaddr);
+ match.u.v4.mask = SRCADR(resmask);
+ match.u.v4.addr &= match.u.v4.mask;
+
+ } else if (IS_IPV6(resaddr)) {
+ v6 = 1;
+ /*
+ * Get address and mask in network byte order for easy
+ * comparison as byte sequences (e.g. memcmp())
+ */
+ match.u.v6.mask = SOCK_ADDR6(resmask);
+ MASK_IPV6_ADDR(&match.u.v6.addr, PSOCK_ADDR6(resaddr),
+ &match.u.v6.mask);
+
+ } else /* not IPv4 nor IPv6 */
+ NTP_REQUIRE(0);
+
+ match.flags = flags;
+ match.mflags = mflags;
+ match.expire = expire;
+ res = match_restrict_entry(&match, v6);
+
+ switch (op) {
+
+ case RESTRICT_FLAGS:
+ /*
+ * Here we add bits to the flags. If this is a
+ * new restriction add it.
+ */
+ if (NULL == res) {
+ if (v6) {
+ res = alloc_res6();
+ memcpy(res, &match,
+ V6_SIZEOF_RESTRICT_U);
+ plisthead = &restrictlist6;
+ } else {
+ res = alloc_res4();
+ memcpy(res, &match,
+ V4_SIZEOF_RESTRICT_U);
+ plisthead = &restrictlist4;
+ }
+ LINK_SORT_SLIST(
+ *plisthead, res,
+ (v6)
+ ? res_sorts_before6(res, L_S_S_CUR())
+ : res_sorts_before4(res, L_S_S_CUR()),
+ link, restrict_u);
+ restrictcount++;
+ if (RES_LIMITED & flags)
+ inc_res_limited();
+ } else {
+ if ((RES_LIMITED & flags) &&
+ !(RES_LIMITED & res->flags))
+ inc_res_limited();
+ res->flags |= flags;
+ }
+ break;
+
+ case RESTRICT_UNFLAG:
+ /*
+ * Remove some bits from the flags. If we didn't
+ * find this one, just return.
+ */
+ if (res != NULL) {
+ if ((RES_LIMITED & res->flags)
+ && (RES_LIMITED & flags))
+ dec_res_limited();
+ res->flags &= ~flags;
+ }
+ break;
+
+ case RESTRICT_REMOVE:
+ case RESTRICT_REMOVEIF:
+ /*
+ * Remove an entry from the table entirely if we
+ * found one. Don't remove the default entry and
+ * don't remove an interface entry.
+ */
+ if (res != NULL
+ && (RESTRICT_REMOVEIF == op
+ || !(RESM_INTERFACE & res->mflags))
+ && res != &restrict_def4
+ && res != &restrict_def6)
+ free_res(res, v6);
+ break;
+
+ default: /* unknown op */
+ NTP_INSIST(0);
+ break;
+ }
+
+}
+
+
+/*
+ * restrict_source - maintains dynamic "restrict source ..." entries as
+ * peers come and go.
+ */
+void
+restrict_source(
+ sockaddr_u * addr,
+ int farewell, /* 0 to add, 1 to remove */
+ u_long expire /* 0 is infinite, valid until */
+ )
+{
+ sockaddr_u onesmask;
+ restrict_u * res;
+ int found_specific;
+
+ if (!restrict_source_enabled || SOCK_UNSPEC(addr) ||
+ IS_MCAST(addr) || ISREFCLOCKADR(addr))
+ return;
+
+ NTP_REQUIRE(AF_INET == AF(addr) || AF_INET6 == AF(addr));
+
+ SET_HOSTMASK(&onesmask, AF(addr));
+ if (farewell) {
+ hack_restrict(RESTRICT_REMOVE, addr, &onesmask,
+ 0, 0, 0);
+ DPRINTF(1, ("restrict_source: %s removed", stoa(addr)));
+ return;
+ }
+
+ /*
+ * If there is a specific entry for this address, hands
+ * off, as it is condidered more specific than "restrict
+ * server ...".
+ * However, if the specific entry found is a fleeting one
+ * added by pool_xmit() before soliciting, replace it
+ * immediately regardless of the expire value to make way
+ * for the more persistent entry.
+ */
+ if (IS_IPV4(addr)) {
+ res = match_restrict4_addr(SRCADR(addr), SRCPORT(addr));
+ found_specific = (SRCADR(&onesmask) == res->u.v4.mask);
+ } else {
+ res = match_restrict6_addr(&SOCK_ADDR6(addr),
+ SRCPORT(addr));
+ found_specific = ADDR6_EQ(&res->u.v6.mask,
+ &SOCK_ADDR6(&onesmask));
+ }
+ if (!expire && found_specific && res->expire) {
+ found_specific = 0;
+ free_res(res, IS_IPV6(addr));
+ }
+ if (found_specific)
+ return;
+
+ hack_restrict(RESTRICT_FLAGS, addr, &onesmask,
+ restrict_source_mflags, restrict_source_flags,
+ expire);
+ DPRINTF(1, ("restrict_source: %s host restriction added\n",
+ stoa(addr)));
+}
diff --git a/ntpd/ntp_scanner.c b/ntpd/ntp_scanner.c
new file mode 100644
index 0000000..3e09856
--- /dev/null
+++ b/ntpd/ntp_scanner.c
@@ -0,0 +1,755 @@
+
+/* ntp_scanner.c
+ *
+ * The source code for a simple lexical analyzer.
+ *
+ * Written By: Sachin Kamboj
+ * University of Delaware
+ * Newark, DE 19711
+ * Copyright (c) 2006
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "ntpd.h"
+#include "ntp_config.h"
+#include "ntpsim.h"
+#include "ntp_scanner.h"
+#include "ntp_parser.h"
+
+/* ntp_keyword.h declares finite state machine and token text */
+#include "ntp_keyword.h"
+
+
+
+/* SCANNER GLOBAL VARIABLES
+ * ------------------------
+ */
+
+#define MAX_LEXEME (1024 + 1) /* The maximum size of a lexeme */
+char yytext[MAX_LEXEME]; /* Buffer for storing the input text/lexeme */
+u_int32 conf_file_sum; /* Simple sum of characters read */
+
+
+
+
+/* CONSTANTS
+ * ---------
+ */
+
+
+/* SCANNER GLOBAL VARIABLES
+ * ------------------------
+ */
+const char special_chars[] = "{}(),;|=";
+
+
+/* FUNCTIONS
+ * ---------
+ */
+
+static int is_keyword(char *lexeme, follby *pfollowedby);
+
+
+/*
+ * keyword() - Return the keyword associated with token T_ identifier.
+ * See also token_name() for the string-ized T_ identifier.
+ * Example: keyword(T_Server) returns "server"
+ * token_name(T_Server) returns "T_Server"
+ */
+const char *
+keyword(
+ int token
+ )
+{
+ int i;
+ const char *text;
+
+ i = token - LOWEST_KEYWORD_ID;
+
+ if (i >= 0 && i < COUNTOF(keyword_text))
+ text = keyword_text[i];
+ else
+ text = NULL;
+
+ return (text != NULL)
+ ? text
+ : "(keyword not found)";
+}
+
+
+/* FILE INTERFACE
+ * --------------
+ * We define a couple of wrapper functions around the standard C fgetc
+ * and ungetc functions in order to include positional bookkeeping
+ */
+
+struct FILE_INFO *
+F_OPEN(
+ const char *path,
+ const char *mode
+ )
+{
+ struct FILE_INFO *my_info;
+
+ my_info = emalloc(sizeof *my_info);
+
+ my_info->line_no = 1;
+ my_info->col_no = 0;
+ my_info->prev_line_col_no = 0;
+ my_info->prev_token_col_no = 0;
+ my_info->fname = path;
+
+ my_info->fd = fopen(path, mode);
+ if (NULL == my_info->fd) {
+ free(my_info);
+ return NULL;
+ }
+ return my_info;
+}
+
+int
+FGETC(
+ struct FILE_INFO *stream
+ )
+{
+ int ch;
+
+ do
+ ch = fgetc(stream->fd);
+ while (EOF != ch && (CHAR_MIN > ch || ch > CHAR_MAX));
+
+ if (EOF != ch) {
+ if (input_from_file)
+ conf_file_sum += (u_char)ch;
+ ++stream->col_no;
+ if (ch == '\n') {
+ stream->prev_line_col_no = stream->col_no;
+ ++stream->line_no;
+ stream->col_no = 1;
+ }
+ }
+
+ return ch;
+}
+
+/* BUGS: 1. Function will fail on more than one line of pushback
+ * 2. No error checking is done to see if ungetc fails
+ * SK: I don't think its worth fixing these bugs for our purposes ;-)
+ */
+int
+UNGETC(
+ int ch,
+ struct FILE_INFO *stream
+ )
+{
+ if (input_from_file)
+ conf_file_sum -= (u_char)ch;
+ if (ch == '\n') {
+ stream->col_no = stream->prev_line_col_no;
+ stream->prev_line_col_no = -1;
+ --stream->line_no;
+ }
+ --stream->col_no;
+ return ungetc(ch, stream->fd);
+}
+
+int
+FCLOSE(
+ struct FILE_INFO *stream
+ )
+{
+ int ret_val = fclose(stream->fd);
+
+ if (!ret_val)
+ free(stream);
+ return ret_val;
+}
+
+/* STREAM INTERFACE
+ * ----------------
+ * Provide a wrapper for the stream functions so that the
+ * stream can either read from a file or from a character
+ * array.
+ * NOTE: This is not very efficient for reading from character
+ * arrays, but needed to allow remote configuration where the
+ * configuration command is provided through ntpq.
+ *
+ * The behavior of there two functions is determined by the
+ * input_from_file flag.
+ */
+
+static int
+get_next_char(
+ struct FILE_INFO *ip_file
+ )
+{
+ char ch;
+
+ if (input_from_file)
+ return FGETC(ip_file);
+ else {
+ if (remote_config.buffer[remote_config.pos] == '\0')
+ return EOF;
+ else {
+ ip_file->col_no++;
+ ch = remote_config.buffer[remote_config.pos++];
+ if (ch == '\n') {
+ ip_file->prev_line_col_no = ip_file->col_no;
+ ++ip_file->line_no;
+ ip_file->col_no = 1;
+ }
+ return ch;
+ }
+ }
+}
+
+static void
+push_back_char(
+ struct FILE_INFO *ip_file,
+ int ch
+ )
+{
+ if (input_from_file)
+ UNGETC(ch, ip_file);
+ else {
+ if (ch == '\n') {
+ ip_file->col_no = ip_file->prev_line_col_no;
+ ip_file->prev_line_col_no = -1;
+ --ip_file->line_no;
+ }
+ --ip_file->col_no;
+
+ remote_config.pos--;
+ }
+}
+
+
+
+/* STATE MACHINES
+ * --------------
+ */
+
+/* Keywords */
+static int
+is_keyword(
+ char *lexeme,
+ follby *pfollowedby
+ )
+{
+ follby fb;
+ int curr_s; /* current state index */
+ int token;
+ int i;
+
+ curr_s = SCANNER_INIT_S;
+ token = 0;
+
+ for (i = 0; lexeme[i]; i++) {
+ while (curr_s && (lexeme[i] != SS_CH(sst[curr_s])))
+ curr_s = SS_OTHER_N(sst[curr_s]);
+
+ if (curr_s && (lexeme[i] == SS_CH(sst[curr_s]))) {
+ if ('\0' == lexeme[i + 1]
+ && FOLLBY_NON_ACCEPTING
+ != SS_FB(sst[curr_s])) {
+ fb = SS_FB(sst[curr_s]);
+ *pfollowedby = fb;
+ token = curr_s;
+ break;
+ }
+ curr_s = SS_MATCH_N(sst[curr_s]);
+ } else
+ break;
+ }
+
+ return token;
+}
+
+
+/* Integer */
+static int
+is_integer(
+ char *lexeme
+ )
+{
+ int i;
+ int is_neg;
+ u_int u_val;
+
+ i = 0;
+
+ /* Allow a leading minus sign */
+ if (lexeme[i] == '-') {
+ i++;
+ is_neg = TRUE;
+ } else {
+ is_neg = FALSE;
+ }
+
+ /* Check that all the remaining characters are digits */
+ for (; lexeme[i] != '\0'; i++) {
+ if (!isdigit(lexeme[i]))
+ return FALSE;
+ }
+
+ if (is_neg)
+ return TRUE;
+
+ /* Reject numbers that fit in unsigned but not in signed int */
+ if (1 == sscanf(lexeme, "%u", &u_val))
+ return (u_val <= INT_MAX);
+ else
+ return FALSE;
+}
+
+
+/* U_int -- assumes is_integer() has returned FALSE */
+static int
+is_u_int(
+ char *lexeme
+ )
+{
+ int i;
+ int is_hex;
+
+ i = 0;
+ if ('0' == lexeme[i] && 'x' == tolower(lexeme[i + 1])) {
+ i += 2;
+ is_hex = TRUE;
+ } else {
+ is_hex = FALSE;
+ }
+
+ /* Check that all the remaining characters are digits */
+ for (; lexeme[i] != '\0'; i++) {
+ if (is_hex && !isxdigit(lexeme[i]))
+ return FALSE;
+ if (!is_hex && !isdigit(lexeme[i]))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/* Double */
+static int
+is_double(
+ char *lexeme
+ )
+{
+ u_int num_digits = 0; /* Number of digits read */
+ u_int i;
+
+ i = 0;
+
+ /* Check for an optional '+' or '-' */
+ if ('+' == lexeme[i] || '-' == lexeme[i])
+ i++;
+
+ /* Read the integer part */
+ for (; lexeme[i] && isdigit(lexeme[i]); i++)
+ num_digits++;
+
+ /* Check for the optional decimal point */
+ if ('.' == lexeme[i]) {
+ i++;
+ /* Check for any digits after the decimal point */
+ for (; lexeme[i] && isdigit(lexeme[i]); i++)
+ num_digits++;
+ }
+
+ /*
+ * The number of digits in both the decimal part and the
+ * fraction part must not be zero at this point
+ */
+ if (!num_digits)
+ return 0;
+
+ /* Check if we are done */
+ if (!lexeme[i])
+ return 1;
+
+ /* There is still more input, read the exponent */
+ if ('e' == tolower(lexeme[i]))
+ i++;
+ else
+ return 0;
+
+ /* Read an optional Sign */
+ if ('+' == lexeme[i] || '-' == lexeme[i])
+ i++;
+
+ /* Now read the exponent part */
+ while (lexeme[i] && isdigit(lexeme[i]))
+ i++;
+
+ /* Check if we are done */
+ if (!lexeme[i])
+ return 1;
+ else
+ return 0;
+}
+
+
+/* is_special() - Test whether a character is a token */
+static inline int
+is_special(
+ int ch
+ )
+{
+ return strchr(special_chars, ch) != NULL;
+}
+
+
+static int
+is_EOC(
+ int ch
+ )
+{
+ if ((old_config_style && (ch == '\n')) ||
+ (!old_config_style && (ch == ';')))
+ return 1;
+ return 0;
+}
+
+
+char *
+quote_if_needed(char *str)
+{
+ char *ret;
+ size_t len;
+ size_t octets;
+
+ len = strlen(str);
+ octets = len + 2 + 1;
+ ret = emalloc(octets);
+ if ('"' != str[0]
+ && (strcspn(str, special_chars) < len
+ || strchr(str, ' ') != NULL)) {
+ snprintf(ret, octets, "\"%s\"", str);
+ } else
+ strlcpy(ret, str, octets);
+
+ return ret;
+}
+
+
+static int
+create_string_token(
+ char *lexeme
+ )
+{
+ char *pch;
+
+ /*
+ * ignore end of line whitespace
+ */
+ pch = lexeme;
+ while (*pch && isspace(*pch))
+ pch++;
+
+ if (!*pch) {
+ yylval.Integer = T_EOC;
+ return yylval.Integer;
+ }
+
+ yylval.String = estrdup(lexeme);
+ return T_String;
+}
+
+
+/*
+ * yylex() - function that does the actual scanning.
+ * Bison expects this function to be called yylex and for it to take no
+ * input and return an int.
+ * Conceptually yylex "returns" yylval as well as the actual return
+ * value representing the token or type.
+ */
+int
+yylex(
+ struct FILE_INFO *ip_file
+ )
+{
+ static follby followedby = FOLLBY_TOKEN;
+ int i;
+ int instring;
+ int yylval_was_set;
+ int converted;
+ int token; /* The return value */
+ int ch;
+
+ if (input_from_file)
+ ip_file = fp[curr_include_level];
+ instring = FALSE;
+ yylval_was_set = FALSE;
+
+ do {
+ /* Ignore whitespace at the beginning */
+ while (EOF != (ch = get_next_char(ip_file)) &&
+ isspace(ch) &&
+ !is_EOC(ch))
+ ; /* Null Statement */
+
+ if (EOF == ch) {
+
+ if (!input_from_file || !curr_include_level)
+ return 0;
+
+ FCLOSE(fp[curr_include_level]);
+ ip_file = fp[--curr_include_level];
+ token = T_EOC;
+ goto normal_return;
+
+ } else if (is_EOC(ch)) {
+
+ /* end FOLLBY_STRINGS_TO_EOC effect */
+ followedby = FOLLBY_TOKEN;
+ token = T_EOC;
+ goto normal_return;
+
+ } else if (is_special(ch) && FOLLBY_TOKEN == followedby) {
+ /* special chars are their own token values */
+ token = ch;
+ /*
+ * '=' outside simulator configuration implies
+ * a single string following as in:
+ * setvar Owner = "The Boss" default
+ */
+ if ('=' == ch && old_config_style)
+ followedby = FOLLBY_STRING;
+ yytext[0] = (char)ch;
+ yytext[1] = '\0';
+ goto normal_return;
+ } else
+ push_back_char(ip_file, ch);
+
+ /* save the position of start of the token */
+ ip_file->prev_token_line_no = ip_file->line_no;
+ ip_file->prev_token_col_no = ip_file->col_no;
+
+ /* Read in the lexeme */
+ i = 0;
+ while (EOF != (ch = get_next_char(ip_file))) {
+
+ yytext[i] = (char)ch;
+
+ /* Break on whitespace or a special character */
+ if (isspace(ch) || is_EOC(ch)
+ || '"' == ch
+ || (FOLLBY_TOKEN == followedby
+ && is_special(ch)))
+ break;
+
+ /* Read the rest of the line on reading a start
+ of comment character */
+ if ('#' == ch) {
+ while (EOF != (ch = get_next_char(ip_file))
+ && '\n' != ch)
+ ; /* Null Statement */
+ break;
+ }
+
+ i++;
+ if (i >= COUNTOF(yytext))
+ goto lex_too_long;
+ }
+ /* Pick up all of the string inside between " marks, to
+ * end of line. If we make it to EOL without a
+ * terminating " assume it for them.
+ *
+ * XXX - HMS: I'm not sure we want to assume the closing "
+ */
+ if ('"' == ch) {
+ instring = TRUE;
+ while (EOF != (ch = get_next_char(ip_file)) &&
+ ch != '"' && ch != '\n') {
+ yytext[i++] = (char)ch;
+ if (i >= COUNTOF(yytext))
+ goto lex_too_long;
+ }
+ /*
+ * yytext[i] will be pushed back as not part of
+ * this lexeme, but any closing quote should
+ * not be pushed back, so we read another char.
+ */
+ if ('"' == ch)
+ ch = get_next_char(ip_file);
+ }
+ /* Pushback the last character read that is not a part
+ * of this lexeme.
+ * If the last character read was an EOF, pushback a
+ * newline character. This is to prevent a parse error
+ * when there is no newline at the end of a file.
+ */
+ if (EOF == ch)
+ push_back_char(ip_file, '\n');
+ else
+ push_back_char(ip_file, ch);
+ yytext[i] = '\0';
+ } while (i == 0);
+
+ /* Now return the desired token */
+
+ /* First make sure that the parser is *not* expecting a string
+ * as the next token (based on the previous token that was
+ * returned) and that we haven't read a string.
+ */
+
+ if (followedby == FOLLBY_TOKEN && !instring) {
+ token = is_keyword(yytext, &followedby);
+ if (token) {
+ /*
+ * T_Server is exceptional as it forces the
+ * following token to be a string in the
+ * non-simulator parts of the configuration,
+ * but in the simulator configuration section,
+ * "server" is followed by "=" which must be
+ * recognized as a token not a string.
+ */
+ if (T_Server == token && !old_config_style)
+ followedby = FOLLBY_TOKEN;
+ goto normal_return;
+ } else if (is_integer(yytext)) {
+ yylval_was_set = TRUE;
+ errno = 0;
+ if ((yylval.Integer = strtol(yytext, NULL, 10)) == 0
+ && ((errno == EINVAL) || (errno == ERANGE))) {
+ msyslog(LOG_ERR,
+ "Integer cannot be represented: %s",
+ yytext);
+ if (input_from_file) {
+ exit(1);
+ } else {
+ /* force end of parsing */
+ yylval.Integer = 0;
+ return 0;
+ }
+ }
+ token = T_Integer;
+ goto normal_return;
+ } else if (is_u_int(yytext)) {
+ yylval_was_set = TRUE;
+ if ('0' == yytext[0] &&
+ 'x' == tolower(yytext[1]))
+ converted = sscanf(&yytext[2], "%x",
+ &yylval.U_int);
+ else
+ converted = sscanf(yytext, "%u",
+ &yylval.U_int);
+ if (1 != converted) {
+ msyslog(LOG_ERR,
+ "U_int cannot be represented: %s",
+ yytext);
+ if (input_from_file) {
+ exit(1);
+ } else {
+ /* force end of parsing */
+ yylval.Integer = 0;
+ return 0;
+ }
+ }
+ token = T_U_int;
+ goto normal_return;
+ } else if (is_double(yytext)) {
+ yylval_was_set = TRUE;
+ errno = 0;
+ if ((yylval.Double = atof(yytext)) == 0 && errno == ERANGE) {
+ msyslog(LOG_ERR,
+ "Double too large to represent: %s",
+ yytext);
+ exit(1);
+ } else {
+ token = T_Double;
+ goto normal_return;
+ }
+ } else {
+ /* Default: Everything is a string */
+ yylval_was_set = TRUE;
+ token = create_string_token(yytext);
+ goto normal_return;
+ }
+ }
+
+ /*
+ * Either followedby is not FOLLBY_TOKEN or this lexeme is part
+ * of a string. Hence, we need to return T_String.
+ *
+ * _Except_ we might have a -4 or -6 flag on a an association
+ * configuration line (server, peer, pool, etc.).
+ *
+ * This is a terrible hack, but the grammar is ambiguous so we
+ * don't have a choice. [SK]
+ *
+ * The ambiguity is in the keyword scanner, not ntp_parser.y.
+ * We do not require server addresses be quoted in ntp.conf,
+ * complicating the scanner's job. To avoid trying (and
+ * failing) to match an IP address or DNS name to a keyword,
+ * the association keywords use FOLLBY_STRING in the keyword
+ * table, which tells the scanner to force the next token to be
+ * a T_String, so it does not try to match a keyword but rather
+ * expects a string when -4/-6 modifiers to server, peer, etc.
+ * are encountered.
+ * restrict -4 and restrict -6 parsing works correctly without
+ * this hack, as restrict uses FOLLBY_TOKEN. [DH]
+ */
+ if ('-' == yytext[0]) {
+ if ('4' == yytext[1]) {
+ token = T_Ipv4_flag;
+ goto normal_return;
+ } else if ('6' == yytext[1]) {
+ token = T_Ipv6_flag;
+ goto normal_return;
+ }
+ }
+
+ instring = FALSE;
+ if (FOLLBY_STRING == followedby)
+ followedby = FOLLBY_TOKEN;
+
+ yylval_was_set = TRUE;
+ token = create_string_token(yytext);
+
+normal_return:
+ if (T_EOC == token)
+ DPRINTF(4,("\t<end of command>\n"));
+ else
+ DPRINTF(4, ("yylex: lexeme '%s' -> %s\n", yytext,
+ token_name(token)));
+
+ if (!yylval_was_set)
+ yylval.Integer = token;
+
+ return token;
+
+lex_too_long:
+ yytext[min(sizeof(yytext) - 1, 50)] = 0;
+ msyslog(LOG_ERR,
+ "configuration item on line %d longer than limit of %lu, began with '%s'",
+ ip_file->line_no, (u_long)min(sizeof(yytext) - 1, 50),
+ yytext);
+
+ /*
+ * If we hit the length limit reading the startup configuration
+ * file, abort.
+ */
+ if (input_from_file)
+ exit(sizeof(yytext) - 1);
+
+ /*
+ * If it's runtime configuration via ntpq :config treat it as
+ * if the configuration text ended before the too-long lexeme,
+ * hostname, or string.
+ */
+ yylval.Integer = 0;
+ return 0;
+}
diff --git a/ntpd/ntp_scanner.h b/ntpd/ntp_scanner.h
new file mode 100644
index 0000000..6797637
--- /dev/null
+++ b/ntpd/ntp_scanner.h
@@ -0,0 +1,130 @@
+/* ntp_scanner.h
+ *
+ * The header file for a simple lexical analyzer.
+ *
+ * Written By: Sachin Kamboj
+ * University of Delaware
+ * Newark, DE 19711
+ * Copyright (c) 2006
+ */
+
+#ifndef NTP_SCANNER_H
+#define NTP_SCANNER_H
+
+#include "ntp_config.h"
+
+/*
+ * ntp.conf syntax is slightly irregular in that some tokens such as
+ * hostnames do not require quoting even if they might otherwise be
+ * recognized as T_ terminal tokens. This hand-crafted lexical scanner
+ * uses a "followed by" value associated with each keyword to indicate
+ * normal scanning of the next token, forced scanning of the next token
+ * alone as a T_String, or forced scanning of all tokens to the end of
+ * the command as T_String.
+ * In the past the identifiers for this functionality ended in _ARG:
+ *
+ * NO_ARG -> FOLLBY_TOKEN
+ * SINGLE_ARG -> FOLLBY_STRING
+ * MULTIPLE_ARG -> FOLLBY_STRINGS_TO_EOC
+ *
+ * Note that some tokens use FOLLBY_TOKEN even though they sometimes
+ * are followed by strings. FOLLBY_STRING is used only when needed to
+ * avoid the keyword scanner matching a token where a string is needed.
+ *
+ * FOLLBY_NON_ACCEPT is an overloading of this field to distinguish
+ * non-accepting states (where the state number does not match a T_
+ * value).
+ */
+typedef enum {
+ FOLLBY_TOKEN = 0,
+ FOLLBY_STRING,
+ FOLLBY_STRINGS_TO_EOC,
+ FOLLBY_NON_ACCEPTING
+} follby;
+
+#define MAXLINE 1024 /* maximum length of line */
+#define MAXINCLUDELEVEL 5 /* maximum include file levels */
+
+/* STRUCTURES
+ * ----------
+ */
+
+/*
+ * Define a structure to hold the FSA for the keywords.
+ * The structure is actually a trie.
+ *
+ * To save space, a single u_int32 encodes four fields, and a fifth
+ * (the token completed for terminal states) is implied by the index of
+ * the rule within the scan state array, taking advantage of the fact
+ * there are more scan states than the highest T_ token number.
+ *
+ * The lowest 8 bits hold the character the state matches on.
+ * Bits 8 and 9 hold the followedby value (0 - 3). For non-accepting
+ * states (which do not match a completed token) the followedby
+ * value 3 (FOLLBY_NONACCEPTING) denotes that fact. For accepting
+ * states, values 0 - 2 control whether the scanner forces the
+ * following token(s) to strings.
+ * Bits 10 through 20 hold the next state to check not matching
+ * this state's character.
+ * Bits 21 through 31 hold the next state to check matching the char.
+ */
+
+#define S_ST(ch, fb, match_n, other_n) ( \
+ (u_char)((ch) & 0xff) | \
+ ((u_int32)(fb) << 8) | \
+ ((u_int32)(match_n) << 10) | \
+ ((u_int32)(other_n) << 21) \
+)
+
+#define SS_CH(ss) ((char)(u_char)((ss) & 0xff))
+#define SS_FB(ss) (((u_int)(ss) >> 8) & 0x3)
+#define SS_MATCH_N(ss) (((u_int)(ss) >> 10) & 0x7ff)
+#define SS_OTHER_N(ss) (((u_int)(ss) >> 21) & 0x7ff)
+
+typedef u_int32 scan_state;
+
+
+/* Structure to hold a filename, file pointer and positional info */
+struct FILE_INFO {
+ const char * fname; /* Path to the file */
+ FILE * fd; /* File Descriptor */
+ int line_no; /* Line Number */
+ int col_no; /* Column Number */
+ int prev_line_col_no; /* Col No on the
+ previous line when a
+ '\n' was seen */
+ int prev_token_line_no; /* Line at start of
+ token */
+ int prev_token_col_no; /* Col No at start of
+ token */
+ int err_line_no;
+ int err_col_no;
+};
+
+
+/* SCANNER GLOBAL VARIABLES
+ * ------------------------
+ */
+extern config_tree cfgt; /* Parser output stored here */
+extern int curr_include_level; /* The current include level */
+
+/* VARIOUS EXTERNAL DECLARATIONS
+ * -----------------------------
+ */
+extern int old_config_style;
+extern int input_from_file;
+extern struct FILE_INFO *fp[];
+
+/* VARIOUS SUBROUTINE DECLARATIONS
+ * -------------------------------
+ */
+extern const char *keyword(int token);
+extern char *quote_if_needed(char *str);
+int yylex(struct FILE_INFO *);
+
+struct FILE_INFO *F_OPEN(const char *path, const char *mode);
+int FGETC(struct FILE_INFO *stream);
+int UNGETC(int ch, struct FILE_INFO *stream);
+int FCLOSE(struct FILE_INFO *stream);
+
+#endif /* NTP_SCANNER_H */
diff --git a/ntpd/ntp_signd.c b/ntpd/ntp_signd.c
new file mode 100644
index 0000000..2ba11d0
--- /dev/null
+++ b/ntpd/ntp_signd.c
@@ -0,0 +1,239 @@
+/* Copyright 2008, Red Hat, Inc.
+ Copyright 2008, Andrew Tridgell.
+ Licenced under the same terms as NTP itself.
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_NTP_SIGND
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+#include "ntp_control.h"
+#include "ntp_string.h"
+
+#include <stdio.h>
+#include <stddef.h>
+#ifdef HAVE_LIBSCF_H
+#include <libscf.h>
+#include <unistd.h>
+#endif /* HAVE_LIBSCF_H */
+
+#include <sys/un.h>
+
+/* socket routines by tridge - from junkcode.samba.org */
+
+/*
+ connect to a unix domain socket
+*/
+static int
+ux_socket_connect(const char *name)
+{
+ int fd;
+ struct sockaddr_un addr;
+ if (!name) {
+ return -1;
+ }
+
+ ZERO(addr);
+ addr.sun_family = AF_UNIX;
+ strlcpy(addr.sun_path, name, sizeof(addr.sun_path));
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1) {
+ return -1;
+ }
+
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+
+/*
+ keep writing until its all sent
+*/
+static int
+write_all(int fd, const void *buf, size_t len)
+{
+ size_t total = 0;
+ while (len) {
+ int n = write(fd, buf, len);
+ if (n <= 0) return total;
+ buf = n + (char *)buf;
+ len -= n;
+ total += n;
+ }
+ return total;
+}
+
+/*
+ keep reading until its all read
+*/
+static int
+read_all(int fd, void *buf, size_t len)
+{
+ size_t total = 0;
+ while (len) {
+ int n = read(fd, buf, len);
+ if (n <= 0) return total;
+ buf = n + (char *)buf;
+ len -= n;
+ total += n;
+ }
+ return total;
+}
+
+/*
+ send a packet in length prefix format
+*/
+static int
+send_packet(int fd, const char *buf, uint32_t len)
+{
+ uint32_t net_len = htonl(len);
+ if (write_all(fd, &net_len, sizeof(net_len)) != sizeof(net_len)) return -1;
+ if (write_all(fd, buf, len) != len) return -1;
+ return 0;
+}
+
+/*
+ receive a packet in length prefix format
+*/
+static int
+recv_packet(int fd, char **buf, uint32_t *len)
+{
+ if (read_all(fd, len, sizeof(*len)) != sizeof(*len)) return -1;
+ *len = ntohl(*len);
+ (*buf) = emalloc(*len);
+ if (read_all(fd, *buf, *len) != *len) {
+ free(*buf);
+ return -1;
+ }
+ return 0;
+}
+
+void
+send_via_ntp_signd(
+ struct recvbuf *rbufp, /* receive packet pointer */
+ int xmode,
+ keyid_t xkeyid,
+ int flags,
+ struct pkt *xpkt
+ )
+{
+
+ /* We are here because it was detected that the client
+ * sent an all-zero signature, and we therefore know
+ * it's windows trying to talk to an AD server
+ *
+ * Because we don't want to dive into Samba's secrets
+ * database just to find the long-term kerberos key
+ * that is re-used as the NTP key, we instead hand the
+ * packet over to Samba to sign, and return to us.
+ *
+ * The signing method Samba will use is described by
+ * Microsoft in MS-SNTP, found here:
+ * http://msdn.microsoft.com/en-us/library/cc212930.aspx
+ */
+
+ int fd, sendlen;
+ struct samba_key_in {
+ uint32_t version;
+ uint32_t op;
+ uint32_t packet_id;
+ uint32_t key_id_le;
+ struct pkt pkt;
+ } samba_pkt;
+
+ struct samba_key_out {
+ uint32_t version;
+ uint32_t op;
+ uint32_t packet_id;
+ struct pkt pkt;
+ } samba_reply;
+
+ char full_socket[256];
+
+ char *reply = NULL;
+ uint32_t reply_len;
+
+ ZERO(samba_pkt);
+ samba_pkt.op = 0; /* Sign message */
+ /* This will be echoed into the reply - a different
+ * impelementation might want multiple packets
+ * awaiting signing */
+
+ samba_pkt.packet_id = 1;
+
+ /* Swap the byte order back - it's actually little
+ * endian on the wire, but it was read above as
+ * network byte order */
+ samba_pkt.key_id_le = htonl(xkeyid);
+ samba_pkt.pkt = *xpkt;
+
+ snprintf(full_socket, sizeof(full_socket), "%s/socket", ntp_signd_socket);
+
+ fd = ux_socket_connect(full_socket);
+ /* Only continue with this if we can talk to Samba */
+ if (fd != -1) {
+ /* Send old packet to Samba, expect response */
+ /* Packet to Samba is quite simple:
+ All values BIG endian except key ID as noted
+ [packet size as BE] - 4 bytes
+ [protocol version (0)] - 4 bytes
+ [packet ID] - 4 bytes
+ [operation (sign message=0)] - 4 bytes
+ [key id] - LITTLE endian (as on wire) - 4 bytes
+ [message to sign] - as marshalled, without signature
+ */
+
+ if (send_packet(fd, (char *)&samba_pkt, offsetof(struct samba_key_in, pkt) + LEN_PKT_NOMAC) != 0) {
+ /* Huh? could not talk to Samba... */
+ close(fd);
+ return;
+ }
+
+ if (recv_packet(fd, &reply, &reply_len) != 0) {
+ if (reply) {
+ free(reply);
+ }
+ close(fd);
+ return;
+ }
+ /* Return packet is also simple:
+ [packet size] - network byte order - 4 bytes
+ [protocol version (0)] network byte order - - 4 bytes
+ [operation (signed success=3, failure=4)] network byte order - - 4 byte
+ (optional) [signed message] - as provided before, with signature appended
+ */
+
+ if (reply_len <= sizeof(samba_reply)) {
+ memcpy(&samba_reply, reply, reply_len);
+ if (ntohl(samba_reply.op) == 3 && reply_len > offsetof(struct samba_key_out, pkt)) {
+ sendlen = reply_len - offsetof(struct samba_key_out, pkt);
+ xpkt = &samba_reply.pkt;
+ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, xpkt, sendlen);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "transmit ntp_signd packet: at %ld %s->%s mode %d keyid %08x len %d\n",
+ current_time, ntoa(&rbufp->dstadr->sin),
+ ntoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen);
+#endif
+ }
+ }
+
+ if (reply) {
+ free(reply);
+ }
+ close(fd);
+
+ }
+}
+#endif
diff --git a/ntpd/ntp_timer.c b/ntpd/ntp_timer.c
new file mode 100644
index 0000000..958c8db
--- /dev/null
+++ b/ntpd/ntp_timer.c
@@ -0,0 +1,570 @@
+/*
+ * ntp_timer.c - event timer support routines
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntp_machine.h"
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#include "ntp_calendar.h"
+#include "ntp_leapsec.h"
+
+#if defined(HAVE_IO_COMPLETION_PORT)
+# include "ntp_iocompletionport.h"
+# include "ntp_timer.h"
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+#ifdef HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif /* KERNEL_PLL */
+
+#ifdef AUTOKEY
+#include <openssl/rand.h>
+#endif /* AUTOKEY */
+
+
+/* TC_ERR represents the timer_create() error return value. */
+#ifdef SYS_VXWORKS
+#define TC_ERR ERROR
+#else
+#define TC_ERR (-1)
+#endif
+
+static void check_leapsec(u_int32, const time_t*, int/*BOOL*/);
+
+/*
+ * These routines provide support for the event timer. The timer is
+ * implemented by an interrupt routine which sets a flag once every
+ * second, and a timer routine which is called when the mainline code
+ * gets around to seeing the flag. The timer routine dispatches the
+ * clock adjustment code if its time has come, then searches the timer
+ * queue for expiries which are dispatched to the transmit procedure.
+ * Finally, we call the hourly procedure to do cleanup and print a
+ * message.
+ */
+volatile int interface_interval; /* init_io() sets def. 300s */
+
+/*
+ * Alarm flag. The mainline code imports this.
+ */
+volatile int alarm_flag;
+
+/*
+ * The counters and timeouts
+ */
+static u_long interface_timer; /* interface update timer */
+static u_long adjust_timer; /* second timer */
+static u_long stats_timer; /* stats timer */
+static u_long leapf_timer; /* Report leapfile problems once/day */
+static u_long huffpuff_timer; /* huff-n'-puff timer */
+static u_long worker_idle_timer;/* next check for idle intres */
+u_long leapsec; /* seconds to next leap (proximity class) */
+int leapdif; /* TAI difference step at next leap second*/
+u_long orphwait; /* orphan wait time */
+#ifdef AUTOKEY
+static u_long revoke_timer; /* keys revoke timer */
+static u_long keys_timer; /* session key timer */
+u_long sys_revoke = KEY_REVOKE; /* keys revoke timeout (log2 s) */
+u_long sys_automax = NTP_AUTOMAX; /* key list timeout (log2 s) */
+#endif /* AUTOKEY */
+
+/*
+ * Statistics counter for the interested.
+ */
+volatile u_long alarm_overflow;
+
+u_long current_time; /* seconds since startup */
+
+/*
+ * Stats. Number of overflows and number of calls to transmit().
+ */
+u_long timer_timereset;
+u_long timer_overflows;
+u_long timer_xmtcalls;
+
+#if defined(VMS)
+static int vmstimer[2]; /* time for next timer AST */
+static int vmsinc[2]; /* timer increment */
+#endif /* VMS */
+
+#ifdef SYS_WINNT
+HANDLE WaitableTimerHandle;
+#else
+static RETSIGTYPE alarming (int);
+#endif /* SYS_WINNT */
+
+#if !defined(VMS)
+# if !defined SYS_WINNT || defined(SYS_CYGWIN32)
+# ifdef HAVE_TIMER_CREATE
+static timer_t timer_id;
+typedef struct itimerspec intervaltimer;
+# define itv_frac tv_nsec
+# else
+typedef struct itimerval intervaltimer;
+# define itv_frac tv_usec
+# endif
+intervaltimer itimer;
+# endif
+#endif
+
+#if !defined(SYS_WINNT) && !defined(VMS)
+void set_timer_or_die(const intervaltimer *);
+#endif
+
+
+#if !defined(SYS_WINNT) && !defined(VMS)
+void
+set_timer_or_die(
+ const intervaltimer * ptimer
+ )
+{
+ const char * setfunc;
+ int rc;
+
+# ifdef HAVE_TIMER_CREATE
+ setfunc = "timer_settime";
+ rc = timer_settime(timer_id, 0, &itimer, NULL);
+# else
+ setfunc = "setitimer";
+ rc = setitimer(ITIMER_REAL, &itimer, NULL);
+# endif
+ if (-1 == rc) {
+ msyslog(LOG_ERR, "interval timer %s failed, %m",
+ setfunc);
+ exit(1);
+ }
+}
+#endif /* !SYS_WINNT && !VMS */
+
+
+/*
+ * reinit_timer - reinitialize interval timer after a clock step.
+ */
+void
+reinit_timer(void)
+{
+#if !defined(SYS_WINNT) && !defined(VMS)
+ ZERO(itimer);
+# ifdef HAVE_TIMER_CREATE
+ timer_gettime(timer_id, &itimer);
+# else
+ getitimer(ITIMER_REAL, &itimer);
+# endif
+ if (itimer.it_value.tv_sec < 0 ||
+ itimer.it_value.tv_sec > (1 << EVENT_TIMEOUT))
+ itimer.it_value.tv_sec = (1 << EVENT_TIMEOUT);
+ if (itimer.it_value.itv_frac < 0)
+ itimer.it_value.itv_frac = 0;
+ if (0 == itimer.it_value.tv_sec &&
+ 0 == itimer.it_value.itv_frac)
+ itimer.it_value.tv_sec = (1 << EVENT_TIMEOUT);
+ itimer.it_interval.tv_sec = (1 << EVENT_TIMEOUT);
+ itimer.it_interval.itv_frac = 0;
+ set_timer_or_die(&itimer);
+# endif /* VMS */
+}
+
+
+/*
+ * init_timer - initialize the timer data structures
+ */
+void
+init_timer(void)
+{
+ /*
+ * Initialize...
+ */
+ alarm_flag = FALSE;
+ alarm_overflow = 0;
+ adjust_timer = 1;
+ stats_timer = SECSPERHR;
+ leapf_timer = SECSPERDAY;
+ huffpuff_timer = 0;
+ interface_timer = 0;
+ current_time = 0;
+ timer_overflows = 0;
+ timer_xmtcalls = 0;
+ timer_timereset = 0;
+
+#ifndef SYS_WINNT
+ /*
+ * Set up the alarm interrupt. The first comes 2**EVENT_TIMEOUT
+ * seconds from now and they continue on every 2**EVENT_TIMEOUT
+ * seconds.
+ */
+# ifndef VMS
+# ifdef HAVE_TIMER_CREATE
+ if (TC_ERR == timer_create(CLOCK_REALTIME, NULL, &timer_id)) {
+ msyslog(LOG_ERR, "timer_create failed, %m");
+ exit(1);
+ }
+# endif
+ signal_no_reset(SIGALRM, alarming);
+ itimer.it_interval.tv_sec =
+ itimer.it_value.tv_sec = (1 << EVENT_TIMEOUT);
+ itimer.it_interval.itv_frac = itimer.it_value.itv_frac = 0;
+ set_timer_or_die(&itimer);
+# else /* VMS follows */
+ vmsinc[0] = 10000000; /* 1 sec */
+ vmsinc[1] = 0;
+ lib$emul(&(1<<EVENT_TIMEOUT), &vmsinc, &0, &vmsinc);
+
+ sys$gettim(&vmstimer); /* that's "now" as abstime */
+
+ lib$addx(&vmsinc, &vmstimer, &vmstimer);
+ sys$setimr(0, &vmstimer, alarming, alarming, 0);
+# endif /* VMS */
+#else /* SYS_WINNT follows */
+ /*
+ * Set up timer interrupts for every 2**EVENT_TIMEOUT seconds
+ * Under Windows/NT,
+ */
+
+ WaitableTimerHandle = CreateWaitableTimer(NULL, FALSE, NULL);
+ if (WaitableTimerHandle == NULL) {
+ msyslog(LOG_ERR, "CreateWaitableTimer failed: %m");
+ exit(1);
+ }
+ else {
+ DWORD Period;
+ LARGE_INTEGER DueTime;
+ BOOL rc;
+
+ Period = (1 << EVENT_TIMEOUT) * 1000;
+ DueTime.QuadPart = Period * 10000i64;
+ rc = SetWaitableTimer(WaitableTimerHandle, &DueTime,
+ Period, NULL, NULL, FALSE);
+ if (!rc) {
+ msyslog(LOG_ERR, "SetWaitableTimer failed: %m");
+ exit(1);
+ }
+ }
+
+#endif /* SYS_WINNT */
+}
+
+
+/*
+ * intres_timeout_req(s) is invoked in the parent to schedule an idle
+ * timeout to fire in s seconds, if not reset earlier by a call to
+ * intres_timeout_req(0), which clears any pending timeout. When the
+ * timeout expires, worker_idle_timer_fired() is invoked (again, in the
+ * parent).
+ *
+ * sntp and ntpd each provide implementations adapted to their timers.
+ */
+void
+intres_timeout_req(
+ u_int seconds /* 0 cancels */
+ )
+{
+ if (0 == seconds) {
+ worker_idle_timer = 0;
+ return;
+ }
+ worker_idle_timer = current_time + seconds;
+}
+
+
+/*
+ * timer - event timer
+ */
+void
+timer(void)
+{
+ struct peer * p;
+ struct peer * next_peer;
+ l_fp now;
+ time_t tnow;
+
+ /*
+ * The basic timerevent is one second. This is used to adjust the
+ * system clock in time and frequency, implement the kiss-o'-death
+ * function and the association polling function.
+ */
+ current_time++;
+ if (adjust_timer <= current_time) {
+ adjust_timer += 1;
+ adj_host_clock();
+#ifdef REFCLOCK
+ for (p = peer_list; p != NULL; p = next_peer) {
+ next_peer = p->p_link;
+ if (FLAG_REFCLOCK & p->flags)
+ refclock_timer(p);
+ }
+#endif /* REFCLOCK */
+ }
+
+ /*
+ * Now dispatch any peers whose event timer has expired. Be
+ * careful here, since the peer structure might go away as the
+ * result of the call.
+ */
+ for (p = peer_list; p != NULL; p = next_peer) {
+ next_peer = p->p_link;
+
+ /*
+ * Restrain the non-burst packet rate not more
+ * than one packet every 16 seconds. This is
+ * usually tripped using iburst and minpoll of
+ * 128 s or less.
+ */
+ if (p->throttle > 0)
+ p->throttle--;
+ if (p->nextdate <= current_time) {
+#ifdef REFCLOCK
+ if (FLAG_REFCLOCK & p->flags)
+ refclock_transmit(p);
+ else
+#endif /* REFCLOCK */
+ transmit(p);
+ }
+ }
+
+ /*
+ * Orphan mode is active when enabled and when no servers less
+ * than the orphan stratum are available. A server with no other
+ * synchronization source is an orphan. It shows offset zero and
+ * reference ID the loopback address.
+ */
+ if (sys_orphan < STRATUM_UNSPEC && sys_peer == NULL &&
+ current_time > orphwait) {
+ if (sys_leap == LEAP_NOTINSYNC) {
+ sys_leap = LEAP_NOWARNING;
+#ifdef AUTOKEY
+ if (crypto_flags)
+ crypto_update();
+#endif /* AUTOKEY */
+ }
+ sys_stratum = (u_char)sys_orphan;
+ if (sys_stratum > 1)
+ sys_refid = htonl(LOOPBACKADR);
+ else
+ memcpy(&sys_refid, "LOOP", 4);
+ sys_offset = 0;
+ sys_rootdelay = 0;
+ sys_rootdisp = 0;
+ }
+
+ get_systime(&now);
+ time(&tnow);
+
+ /*
+ * Leapseconds. Get time and defer to worker if either something
+ * is imminent or every 8th second.
+ */
+ if (leapsec > LSPROX_NOWARN || 0 == (current_time & 7))
+ check_leapsec(now.l_ui, &tnow,
+ (sys_leap == LEAP_NOTINSYNC));
+ if (sys_leap != LEAP_NOTINSYNC) {
+ if (leapsec >= LSPROX_ANNOUNCE && leapdif) {
+ if (leapdif > 0)
+ sys_leap = LEAP_ADDSECOND;
+ else
+ sys_leap = LEAP_DELSECOND;
+ } else {
+ sys_leap = LEAP_NOWARNING;
+ }
+ }
+
+ /*
+ * Update huff-n'-puff filter.
+ */
+ if (huffpuff_timer <= current_time) {
+ huffpuff_timer += HUFFPUFF;
+ huffpuff();
+ }
+
+#ifdef AUTOKEY
+ /*
+ * Garbage collect expired keys.
+ */
+ if (keys_timer <= current_time) {
+ keys_timer += 1 << sys_automax;
+ auth_agekeys();
+ }
+
+ /*
+ * Generate new private value. This causes all associations
+ * to regenerate cookies.
+ */
+ if (revoke_timer && revoke_timer <= current_time) {
+ revoke_timer += 1 << sys_revoke;
+ RAND_bytes((u_char *)&sys_private, 4);
+ }
+#endif /* AUTOKEY */
+
+ /*
+ * Interface update timer
+ */
+ if (interface_interval && interface_timer <= current_time) {
+ timer_interfacetimeout(current_time +
+ interface_interval);
+ DPRINTF(2, ("timer: interface update\n"));
+ interface_update(NULL, NULL);
+ }
+
+ if (worker_idle_timer && worker_idle_timer <= current_time)
+ worker_idle_timer_fired();
+
+ /*
+ * Finally, write hourly stats and do the hourly
+ * and daily leapfile checks.
+ */
+ if (stats_timer <= current_time) {
+ stats_timer += SECSPERHR;
+ write_stats();
+ if (leapf_timer <= current_time) {
+ leapf_timer += SECSPERDAY;
+ check_leap_file(TRUE, now.l_ui, &tnow);
+ } else {
+ check_leap_file(FALSE, now.l_ui, &tnow);
+ }
+ }
+}
+
+
+#ifndef SYS_WINNT
+/*
+ * alarming - tell the world we've been alarmed
+ */
+static RETSIGTYPE
+alarming(
+ int sig
+ )
+{
+# ifdef DEBUG
+ const char *msg = "alarming: initializing TRUE\n";
+# endif
+
+ if (!initializing) {
+ if (alarm_flag) {
+ alarm_overflow++;
+# ifdef DEBUG
+ msg = "alarming: overflow\n";
+# endif
+ } else {
+# ifndef VMS
+ alarm_flag++;
+# else
+ /* VMS AST routine, increment is no good */
+ alarm_flag = 1;
+# endif
+# ifdef DEBUG
+ msg = "alarming: normal\n";
+# endif
+ }
+ }
+# ifdef VMS
+ lib$addx(&vmsinc, &vmstimer, &vmstimer);
+ sys$setimr(0, &vmstimer, alarming, alarming, 0);
+# endif
+# ifdef DEBUG
+ if (debug >= 4)
+ write(1, msg, strlen(msg));
+# endif
+}
+#endif /* SYS_WINNT */
+
+
+void
+timer_interfacetimeout(u_long timeout)
+{
+ interface_timer = timeout;
+}
+
+
+/*
+ * timer_clr_stats - clear timer module stat counters
+ */
+void
+timer_clr_stats(void)
+{
+ timer_overflows = 0;
+ timer_xmtcalls = 0;
+ timer_timereset = current_time;
+}
+
+static void
+check_leapsec(
+ u_int32 now ,
+ const time_t * tpiv ,
+ int/*BOOL*/ reset)
+{
+ leap_result_t lsdata;
+ u_int32 lsprox;
+
+#ifndef SYS_WINNT /* WinNT port has its own leap second handling */
+# ifdef KERNEL_PLL
+ leapsec_electric(pll_control && kern_enable);
+# else
+ leapsec_electric(0);
+# endif
+#endif
+ if (reset) {
+ lsprox = LSPROX_NOWARN;
+ leapsec_reset_frame();
+ memset(&lsdata, 0, sizeof(lsdata));
+ } else if (leapsec_query(&lsdata, now, tpiv)) {
+ /* Full hit. Eventually step the clock, but always
+ * announce the leap event has happened.
+ */
+ if (lsdata.warped < 0) {
+ step_systime(lsdata.warped);
+ msyslog(LOG_NOTICE, "Inserting positive leap second.");
+ } else if (lsdata.warped > 0) {
+ step_systime(lsdata.warped);
+ msyslog(LOG_NOTICE, "Inserting negative leap second.");
+ }
+ report_event(EVNT_LEAP, NULL, NULL);
+ lsprox = LSPROX_NOWARN;
+ leapsec = LSPROX_NOWARN;
+ sys_tai = lsdata.tai_offs;
+ } else {
+ lsprox = lsdata.proximity;
+ sys_tai = lsdata.tai_offs;
+ }
+
+ /* We guard against panic alarming during the red alert phase.
+ * Strange and evil things might happen if we go from stone cold
+ * to piping hot in one step. If things are already that wobbly,
+ * we let the normal clock correction take over, even if a jump
+ * is involved.
+ * Also make sure the alarming events are edge-triggered, that is,
+ * ceated only when the threshold is crossed.
+ */
+ if ( (leapsec > 0 || lsprox < LSPROX_ALERT)
+ && leapsec < lsprox ) {
+ if ( leapsec < LSPROX_SCHEDULE
+ && lsprox >= LSPROX_SCHEDULE) {
+ if (lsdata.dynamic)
+ report_event(PEVNT_ARMED, sys_peer, NULL);
+ else
+ report_event(EVNT_ARMED, NULL, NULL);
+ }
+ leapsec = lsprox;
+ }
+ if (leapsec > lsprox) {
+ if ( leapsec >= LSPROX_SCHEDULE
+ && lsprox < LSPROX_SCHEDULE) {
+ report_event(EVNT_DISARMED, NULL, NULL);
+ }
+ leapsec = lsprox;
+ }
+
+ if (leapsec >= LSPROX_SCHEDULE)
+ leapdif = lsdata.tai_diff;
+ else
+ leapdif = 0;
+}
diff --git a/ntpd/ntp_util.c b/ntpd/ntp_util.c
new file mode 100644
index 0000000..a7da52b
--- /dev/null
+++ b/ntpd/ntp_util.c
@@ -0,0 +1,1031 @@
+/*
+ * ntp_util.c - stuff I didn't have any other place for
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_unixtime.h"
+#include "ntp_filegen.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+#include "ntp_assert.h"
+#include "ntp_calendar.h"
+#include "ntp_leapsec.h"
+#include "lib_strbuf.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <sys/stat.h>
+
+#ifdef HAVE_IEEEFP_H
+# include <ieeefp.h>
+#endif
+#ifdef HAVE_MATH_H
+# include <math.h>
+#endif
+
+#if defined(VMS)
+# include <descrip.h>
+#endif /* VMS */
+
+/*
+ * Defines used by the leapseconds stuff
+ */
+#define MAX_TAI 100 /* max TAI offset (s) */
+#define L_DAY 86400UL /* seconds per day */
+#define L_YEAR (L_DAY * 365) /* days per year */
+#define L_LYEAR (L_YEAR + L_DAY) /* days per leap year */
+#define L_4YEAR (L_LYEAR + 3 * L_YEAR) /* days per leap cycle */
+#define L_CENT (L_4YEAR * 25) /* days per century */
+
+/*
+ * This contains odds and ends, including the hourly stats, various
+ * configuration items, leapseconds stuff, etc.
+ */
+/*
+ * File names
+ */
+static char *key_file_name; /* keys file name */
+static char *leapfile_name; /* leapseconds file name */
+static struct stat leapfile_stat; /* leapseconds file stat() buffer */
+static int /*BOOL*/have_leapfile = FALSE;
+char *stats_drift_file; /* frequency file name */
+static char *stats_temp_file; /* temp frequency file name */
+static double wander_resid; /* last frequency update */
+double wander_threshold = 1e-7; /* initial frequency threshold */
+
+/*
+ * Statistics file stuff
+ */
+#ifndef NTP_VAR
+# ifndef SYS_WINNT
+# define NTP_VAR "/var/NTP/" /* NOTE the trailing '/' */
+# else
+# define NTP_VAR "c:\\var\\ntp\\" /* NOTE the trailing '\\' */
+# endif /* SYS_WINNT */
+#endif
+
+
+char statsdir[MAXFILENAME] = NTP_VAR;
+static FILEGEN peerstats;
+static FILEGEN loopstats;
+static FILEGEN clockstats;
+static FILEGEN rawstats;
+static FILEGEN sysstats;
+static FILEGEN protostats;
+static FILEGEN cryptostats;
+static FILEGEN timingstats;
+
+/*
+ * This controls whether stats are written to the fileset. Provided
+ * so that ntpdc can turn off stats when the file system fills up.
+ */
+int stats_control;
+
+/*
+ * Last frequency written to file.
+ */
+static double prev_drift_comp; /* last frequency update */
+
+/*
+ * Function prototypes
+ */
+static void record_sys_stats(void);
+ void ntpd_time_stepped(void);
+static void check_leap_expiration(int, uint32_t, const time_t*);
+
+/*
+ * Prototypes
+ */
+#ifdef DEBUG
+void uninit_util(void);
+#endif
+
+/*
+ * uninit_util - free memory allocated by init_util
+ */
+#ifdef DEBUG
+void
+uninit_util(void)
+{
+#if defined(_MSC_VER) && defined (_DEBUG)
+ _CrtCheckMemory();
+#endif
+ if (stats_drift_file) {
+ free(stats_drift_file);
+ free(stats_temp_file);
+ stats_drift_file = NULL;
+ stats_temp_file = NULL;
+ }
+ if (key_file_name) {
+ free(key_file_name);
+ key_file_name = NULL;
+ }
+ filegen_unregister("peerstats");
+ filegen_unregister("loopstats");
+ filegen_unregister("clockstats");
+ filegen_unregister("rawstats");
+ filegen_unregister("sysstats");
+ filegen_unregister("protostats");
+#ifdef AUTOKEY
+ filegen_unregister("cryptostats");
+#endif /* AUTOKEY */
+#ifdef DEBUG_TIMING
+ filegen_unregister("timingstats");
+#endif /* DEBUG_TIMING */
+
+#if defined(_MSC_VER) && defined (_DEBUG)
+ _CrtCheckMemory();
+#endif
+}
+#endif /* DEBUG */
+
+
+/*
+ * init_util - initialize the util module of ntpd
+ */
+void
+init_util(void)
+{
+ filegen_register(statsdir, "peerstats", &peerstats);
+ filegen_register(statsdir, "loopstats", &loopstats);
+ filegen_register(statsdir, "clockstats", &clockstats);
+ filegen_register(statsdir, "rawstats", &rawstats);
+ filegen_register(statsdir, "sysstats", &sysstats);
+ filegen_register(statsdir, "protostats", &protostats);
+ filegen_register(statsdir, "cryptostats", &cryptostats);
+ filegen_register(statsdir, "timingstats", &timingstats);
+ /*
+ * register with libntp ntp_set_tod() to call us back
+ * when time is stepped.
+ */
+ step_callback = &ntpd_time_stepped;
+#ifdef DEBUG
+ atexit(&uninit_util);
+#endif /* DEBUG */
+}
+
+
+/*
+ * hourly_stats - print some interesting stats
+ */
+void
+write_stats(void)
+{
+ FILE *fp;
+#ifdef DOSYNCTODR
+ struct timeval tv;
+#if !defined(VMS)
+ int prio_set;
+#endif
+#ifdef HAVE_GETCLOCK
+ struct timespec ts;
+#endif
+ int o_prio;
+
+ /*
+ * Sometimes having a Sun can be a drag.
+ *
+ * The kernel variable dosynctodr controls whether the system's
+ * soft clock is kept in sync with the battery clock. If it
+ * is zero, then the soft clock is not synced, and the battery
+ * clock is simply left to rot. That means that when the system
+ * reboots, the battery clock (which has probably gone wacky)
+ * sets the soft clock. That means ntpd starts off with a very
+ * confused idea of what time it is. It then takes a large
+ * amount of time to figure out just how wacky the battery clock
+ * has made things drift, etc, etc. The solution is to make the
+ * battery clock sync up to system time. The way to do THAT is
+ * to simply set the time of day to the current time of day, but
+ * as quickly as possible. This may, or may not be a sensible
+ * thing to do.
+ *
+ * CAVEAT: settimeofday() steps the sun clock by about 800 us,
+ * so setting DOSYNCTODR seems a bad idea in the
+ * case of us resolution
+ */
+
+#if !defined(VMS)
+ /*
+ * (prr) getpriority returns -1 on error, but -1 is also a valid
+ * return value (!), so instead we have to zero errno before the
+ * call and check it for non-zero afterwards.
+ */
+ errno = 0;
+ prio_set = 0;
+ o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */
+
+ /*
+ * (prr) if getpriority succeeded, call setpriority to raise
+ * scheduling priority as high as possible. If that succeeds
+ * as well, set the prio_set flag so we remember to reset
+ * priority to its previous value below. Note that on Solaris
+ * 2.6 (and beyond?), both getpriority and setpriority will fail
+ * with ESRCH, because sched_setscheduler (called from main) put
+ * us in the real-time scheduling class which setpriority
+ * doesn't know about. Being in the real-time class is better
+ * than anything setpriority can do, anyhow, so this error is
+ * silently ignored.
+ */
+ if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0))
+ prio_set = 1; /* overdrive */
+#endif /* VMS */
+#ifdef HAVE_GETCLOCK
+ (void) getclock(TIMEOFDAY, &ts);
+ tv.tv_sec = ts.tv_sec;
+ tv.tv_usec = ts.tv_nsec / 1000;
+#else /* not HAVE_GETCLOCK */
+ GETTIMEOFDAY(&tv,(struct timezone *)NULL);
+#endif /* not HAVE_GETCLOCK */
+ if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0)
+ msyslog(LOG_ERR, "can't sync battery time: %m");
+#if !defined(VMS)
+ if (prio_set)
+ setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */
+#endif /* VMS */
+#endif /* DOSYNCTODR */
+ record_sys_stats();
+ if (stats_drift_file != 0) {
+
+ /*
+ * When the frequency file is written, initialize the
+ * prev_drift_comp and wander_resid. Thereafter,
+ * reduce the wander_resid by half each hour. When
+ * the difference between the prev_drift_comp and
+ * drift_comp is less than the wander_resid, update
+ * the frequncy file. This minimizes the file writes to
+ * nonvolaile storage.
+ */
+#ifdef DEBUG
+ if (debug)
+ printf("write_stats: frequency %.6lf thresh %.6lf, freq %.6lf\n",
+ (prev_drift_comp - drift_comp) * 1e6, wander_resid *
+ 1e6, drift_comp * 1e6);
+#endif
+ if (fabs(prev_drift_comp - drift_comp) < wander_resid) {
+ wander_resid *= 0.5;
+ return;
+ }
+ prev_drift_comp = drift_comp;
+ wander_resid = wander_threshold;
+ if ((fp = fopen(stats_temp_file, "w")) == NULL) {
+ msyslog(LOG_ERR, "frequency file %s: %m",
+ stats_temp_file);
+ return;
+ }
+ fprintf(fp, "%.3f\n", drift_comp * 1e6);
+ (void)fclose(fp);
+ /* atomic */
+#ifdef SYS_WINNT
+ if (_unlink(stats_drift_file)) /* rename semantics differ under NT */
+ msyslog(LOG_WARNING,
+ "Unable to remove prior drift file %s, %m",
+ stats_drift_file);
+#endif /* SYS_WINNT */
+
+#ifndef NO_RENAME
+ if (rename(stats_temp_file, stats_drift_file))
+ msyslog(LOG_WARNING,
+ "Unable to rename temp drift file %s to %s, %m",
+ stats_temp_file, stats_drift_file);
+#else
+ /* we have no rename NFS of ftp in use */
+ if ((fp = fopen(stats_drift_file, "w")) ==
+ NULL) {
+ msyslog(LOG_ERR,
+ "frequency file %s: %m",
+ stats_drift_file);
+ return;
+ }
+#endif
+
+#if defined(VMS)
+ /* PURGE */
+ {
+ $DESCRIPTOR(oldvers,";-1");
+ struct dsc$descriptor driftdsc = {
+ strlen(stats_drift_file), 0, 0,
+ stats_drift_file };
+ while(lib$delete_file(&oldvers,
+ &driftdsc) & 1);
+ }
+#endif
+ }
+}
+
+
+/*
+ * stats_config - configure the stats operation
+ */
+void
+stats_config(
+ int item,
+ const char *invalue /* only one type so far */
+ )
+{
+ FILE *fp;
+ const char *value;
+ int len;
+ double old_drift;
+ l_fp now;
+ time_t ttnow;
+#ifndef VMS
+ const char temp_ext[] = ".TEMP";
+#else
+ const char temp_ext[] = "-TEMP";
+#endif
+
+ /*
+ * Expand environment strings under Windows NT, since the
+ * command interpreter doesn't do this, the program must.
+ */
+#ifdef SYS_WINNT
+ char newvalue[MAX_PATH], parameter[MAX_PATH];
+
+ if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) {
+ switch (item) {
+ case STATS_FREQ_FILE:
+ strlcpy(parameter, "STATS_FREQ_FILE",
+ sizeof(parameter));
+ break;
+
+ case STATS_LEAP_FILE:
+ strlcpy(parameter, "STATS_LEAP_FILE",
+ sizeof(parameter));
+ break;
+
+ case STATS_STATSDIR:
+ strlcpy(parameter, "STATS_STATSDIR",
+ sizeof(parameter));
+ break;
+
+ case STATS_PID_FILE:
+ strlcpy(parameter, "STATS_PID_FILE",
+ sizeof(parameter));
+ break;
+
+ default:
+ strlcpy(parameter, "UNKNOWN",
+ sizeof(parameter));
+ break;
+ }
+ value = invalue;
+ msyslog(LOG_ERR,
+ "ExpandEnvironmentStrings(%s) failed: %m\n",
+ parameter);
+ } else {
+ value = newvalue;
+ }
+#else
+ value = invalue;
+#endif /* SYS_WINNT */
+
+ switch (item) {
+
+ /*
+ * Open and read frequency file.
+ */
+ case STATS_FREQ_FILE:
+ if (!value || (len = strlen(value)) == 0)
+ break;
+
+ stats_drift_file = erealloc(stats_drift_file, len + 1);
+ stats_temp_file = erealloc(stats_temp_file,
+ len + sizeof(".TEMP"));
+ memcpy(stats_drift_file, value, (size_t)(len+1));
+ memcpy(stats_temp_file, value, (size_t)len);
+ memcpy(stats_temp_file + len, temp_ext, sizeof(temp_ext));
+
+ /*
+ * Open drift file and read frequency. If the file is
+ * missing or contains errors, tell the loop to reset.
+ */
+ if ((fp = fopen(stats_drift_file, "r")) == NULL)
+ break;
+
+ if (fscanf(fp, "%lf", &old_drift) != 1) {
+ msyslog(LOG_ERR,
+ "format error frequency file %s",
+ stats_drift_file);
+ fclose(fp);
+ break;
+
+ }
+ fclose(fp);
+ loop_config(LOOP_FREQ, old_drift);
+ prev_drift_comp = drift_comp;
+ break;
+
+ /*
+ * Specify statistics directory.
+ */
+ case STATS_STATSDIR:
+
+ /* - 1 since value may be missing the DIR_SEP. */
+ if (strlen(value) >= sizeof(statsdir) - 1) {
+ msyslog(LOG_ERR,
+ "statsdir too long (>%d, sigh)",
+ (int)sizeof(statsdir) - 2);
+ } else {
+ int add_dir_sep;
+ int value_l;
+
+ /* Add a DIR_SEP unless we already have one. */
+ value_l = strlen(value);
+ if (0 == value_l)
+ add_dir_sep = FALSE;
+ else
+ add_dir_sep = (DIR_SEP !=
+ value[value_l - 1]);
+
+ if (add_dir_sep)
+ snprintf(statsdir, sizeof(statsdir),
+ "%s%c", value, DIR_SEP);
+ else
+ snprintf(statsdir, sizeof(statsdir),
+ "%s", value);
+ filegen_statsdir();
+ }
+ break;
+
+ /*
+ * Open pid file.
+ */
+ case STATS_PID_FILE:
+ if ((fp = fopen(value, "w")) == NULL) {
+ msyslog(LOG_ERR, "pid file %s: %m",
+ value);
+ break;
+ }
+ fprintf(fp, "%d", (int)getpid());
+ fclose(fp);
+ break;
+
+ /*
+ * Read leapseconds file.
+ *
+ * Note: Currently a leap file without SHA1 signature is
+ * accepted, but if there is a signature line, the signature
+ * must be valid or the file is rejected.
+ */
+ case STATS_LEAP_FILE:
+ if (!value || (len = strlen(value)) == 0)
+ break;
+
+ leapfile_name = erealloc(leapfile_name, len + 1);
+ memcpy(leapfile_name, value, len + 1);
+
+ if (leapsec_load_file(
+ leapfile_name, &leapfile_stat, TRUE, TRUE))
+ {
+ leap_signature_t lsig;
+
+ get_systime(&now);
+ time(&ttnow);
+ leapsec_getsig(&lsig);
+ mprintf_event(EVNT_TAI, NULL,
+ "%d leap %s %s %s",
+ lsig.taiof,
+ fstostr(lsig.ttime),
+ leapsec_expired(now.l_ui, NULL)
+ ? "expired"
+ : "expires",
+ fstostr(lsig.etime));
+
+ have_leapfile = TRUE;
+
+ /* force an immediate daily expiration check of
+ * the leap seconds table
+ */
+ check_leap_expiration(TRUE, now.l_ui, &ttnow);
+ }
+ break;
+
+ default:
+ /* oh well */
+ break;
+ }
+}
+
+
+/*
+ * record_peer_stats - write peer statistics to file
+ *
+ * file format:
+ * day (MJD)
+ * time (s past UTC midnight)
+ * IP address
+ * status word (hex)
+ * offset
+ * delay
+ * dispersion
+ * jitter
+*/
+void
+record_peer_stats(
+ sockaddr_u *addr,
+ int status,
+ double offset, /* offset */
+ double delay, /* delay */
+ double dispersion, /* dispersion */
+ double jitter /* jitter */
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&peerstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (peerstats.fp != NULL) {
+ fprintf(peerstats.fp,
+ "%lu %s %s %x %.9f %.9f %.9f %.9f\n", day,
+ ulfptoa(&now, 3), stoa(addr), status, offset,
+ delay, dispersion, jitter);
+ fflush(peerstats.fp);
+ }
+}
+
+
+/*
+ * record_loop_stats - write loop filter statistics to file
+ *
+ * file format:
+ * day (MJD)
+ * time (s past midnight)
+ * offset
+ * frequency (PPM)
+ * jitter
+ * wnder (PPM)
+ * time constant (log2)
+ */
+void
+record_loop_stats(
+ double offset, /* offset */
+ double freq, /* frequency (PPM) */
+ double jitter, /* jitter */
+ double wander, /* wander (PPM) */
+ int spoll
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&loopstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (loopstats.fp != NULL) {
+ fprintf(loopstats.fp, "%lu %s %.9f %.3f %.9f %.6f %d\n",
+ day, ulfptoa(&now, 3), offset, freq * 1e6, jitter,
+ wander * 1e6, spoll);
+ fflush(loopstats.fp);
+ }
+}
+
+
+/*
+ * record_clock_stats - write clock statistics to file
+ *
+ * file format:
+ * day (MJD)
+ * time (s past midnight)
+ * IP address
+ * text message
+ */
+void
+record_clock_stats(
+ sockaddr_u *addr,
+ const char *text /* timecode string */
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&clockstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (clockstats.fp != NULL) {
+ fprintf(clockstats.fp, "%lu %s %s %s\n", day,
+ ulfptoa(&now, 3), stoa(addr), text);
+ fflush(clockstats.fp);
+ }
+}
+
+
+/*
+ * mprintf_clock_stats - write clock statistics to file with
+ * msnprintf-style formatting.
+ */
+int
+mprintf_clock_stats(
+ sockaddr_u *addr,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+ int rc;
+ char msg[512];
+
+ va_start(ap, fmt);
+ rc = mvsnprintf(msg, sizeof(msg), fmt, ap);
+ va_end(ap);
+ if (stats_control)
+ record_clock_stats(addr, msg);
+
+ return rc;
+}
+
+/*
+ * record_raw_stats - write raw timestamps to file
+ *
+ * file format
+ * day (MJD)
+ * time (s past midnight)
+ * peer ip address
+ * IP address
+ * t1 t2 t3 t4 timestamps
+ */
+void
+record_raw_stats(
+ sockaddr_u *srcadr,
+ sockaddr_u *dstadr,
+ l_fp *t1, /* originate timestamp */
+ l_fp *t2, /* receive timestamp */
+ l_fp *t3, /* transmit timestamp */
+ l_fp *t4, /* destination timestamp */
+ int leap,
+ int version,
+ int mode,
+ int stratum,
+ int poll,
+ int precision,
+ double root_delay, /* seconds */
+ double root_dispersion,/* seconds */
+ u_int32 refid
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&rawstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (rawstats.fp != NULL) {
+ fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s %d %d %d %d %d %d %.6f %.6f %s\n",
+ day, ulfptoa(&now, 3),
+ stoa(srcadr), dstadr ? stoa(dstadr) : "-",
+ ulfptoa(t1, 9), ulfptoa(t2, 9),
+ ulfptoa(t3, 9), ulfptoa(t4, 9),
+ leap, version, mode, stratum, poll, precision,
+ root_delay, root_dispersion, refid_str(refid, stratum));
+ fflush(rawstats.fp);
+ }
+}
+
+
+/*
+ * record_sys_stats - write system statistics to file
+ *
+ * file format
+ * day (MJD)
+ * time (s past midnight)
+ * time since reset
+ * packets recieved
+ * packets for this host
+ * current version
+ * old version
+ * access denied
+ * bad length or format
+ * bad authentication
+ * declined
+ * rate exceeded
+ * KoD sent
+ */
+void
+record_sys_stats(void)
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&sysstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (sysstats.fp != NULL) {
+ fprintf(sysstats.fp,
+ "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
+ day, ulfptoa(&now, 3), current_time - sys_stattime,
+ sys_received, sys_processed, sys_newversion,
+ sys_oldversion, sys_restricted, sys_badlength,
+ sys_badauth, sys_declined, sys_limitrejected,
+ sys_kodsent);
+ fflush(sysstats.fp);
+ proto_clr_stats();
+ }
+}
+
+
+/*
+ * record_proto_stats - write system statistics to file
+ *
+ * file format
+ * day (MJD)
+ * time (s past midnight)
+ * text message
+ */
+void
+record_proto_stats(
+ char *str /* text string */
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&protostats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (protostats.fp != NULL) {
+ fprintf(protostats.fp, "%lu %s %s\n", day,
+ ulfptoa(&now, 3), str);
+ fflush(protostats.fp);
+ }
+}
+
+
+#ifdef AUTOKEY
+/*
+ * record_crypto_stats - write crypto statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * peer ip address
+ * text message
+ */
+void
+record_crypto_stats(
+ sockaddr_u *addr,
+ const char *text /* text message */
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&cryptostats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (cryptostats.fp != NULL) {
+ if (addr == NULL)
+ fprintf(cryptostats.fp, "%lu %s 0.0.0.0 %s\n",
+ day, ulfptoa(&now, 3), text);
+ else
+ fprintf(cryptostats.fp, "%lu %s %s %s\n",
+ day, ulfptoa(&now, 3), stoa(addr), text);
+ fflush(cryptostats.fp);
+ }
+}
+#endif /* AUTOKEY */
+
+
+#ifdef DEBUG_TIMING
+/*
+ * record_timing_stats - write timing statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * text message
+ */
+void
+record_timing_stats(
+ const char *text /* text message */
+ )
+{
+ static unsigned int flshcnt;
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&timingstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (timingstats.fp != NULL) {
+ fprintf(timingstats.fp, "%lu %s %s\n", day, lfptoa(&now,
+ 3), text);
+ if (++flshcnt % 100 == 0)
+ fflush(timingstats.fp);
+ }
+}
+#endif
+
+
+/*
+ * check_leap_file - See if the leapseconds file has been updated.
+ *
+ * Returns: n/a
+ *
+ * Note: This loads a new leapfile on the fly. Currently a leap file
+ * without SHA1 signature is accepted, but if there is a signature line,
+ * the signature must be valid or the file is rejected.
+ */
+void
+check_leap_file(
+ int is_daily_check,
+ uint32_t ntptime ,
+ const time_t *systime
+ )
+{
+ /* just do nothing if there is no leap file */
+ if ( ! (leapfile_name && *leapfile_name))
+ return;
+
+ /* try to load leapfile, force it if no leapfile loaded yet */
+ if (leapsec_load_file(
+ leapfile_name, &leapfile_stat,
+ !have_leapfile, is_daily_check))
+ have_leapfile = TRUE;
+ else if (!have_leapfile)
+ return;
+
+ check_leap_expiration(is_daily_check, ntptime, systime);
+}
+
+/*
+ * check expiration of a loaded leap table
+ */
+static void
+check_leap_expiration(
+ int is_daily_check,
+ uint32_t ntptime ,
+ const time_t *systime
+ )
+{
+ static const char * const logPrefix = "leapsecond file";
+ int rc;
+
+ /* test the expiration of the leap data and log with proper
+ * level and frequency (once/hour or once/day, depending on the
+ * state.
+ */
+ rc = leapsec_daystolive(ntptime, systime);
+ if (rc == 0) {
+ msyslog(LOG_WARNING,
+ "%s ('%s'): will expire in less than one day",
+ logPrefix, leapfile_name);
+ } else if (is_daily_check && rc < 28) {
+ if (rc < 0)
+ msyslog(LOG_ERR,
+ "%s ('%s'): expired less than %d day%s ago",
+ logPrefix, leapfile_name, -rc, (rc == -1 ? "" : "s"));
+ else
+ msyslog(LOG_WARNING,
+ "%s ('%s'): will expire in less than %d days",
+ logPrefix, leapfile_name, 1+rc);
+ }
+}
+
+
+/*
+ * getauthkeys - read the authentication keys from the specified file
+ */
+void
+getauthkeys(
+ const char *keyfile
+ )
+{
+ int len;
+
+ len = strlen(keyfile);
+ if (!len)
+ return;
+
+#ifndef SYS_WINNT
+ key_file_name = erealloc(key_file_name, len + 1);
+ memcpy(key_file_name, keyfile, len + 1);
+#else
+ key_file_name = erealloc(key_file_name, _MAX_PATH);
+ if (len + 1 > _MAX_PATH)
+ return;
+ if (!ExpandEnvironmentStrings(keyfile, key_file_name,
+ _MAX_PATH)) {
+ msyslog(LOG_ERR,
+ "ExpandEnvironmentStrings(KEY_FILE) failed: %m");
+ strlcpy(key_file_name, keyfile, _MAX_PATH);
+ }
+ key_file_name = erealloc(key_file_name,
+ 1 + strlen(key_file_name));
+#endif /* SYS_WINNT */
+
+ authreadkeys(key_file_name);
+}
+
+
+/*
+ * rereadkeys - read the authentication key file over again.
+ */
+void
+rereadkeys(void)
+{
+ if (NULL != key_file_name)
+ authreadkeys(key_file_name);
+}
+
+
+#if notyet
+/*
+ * ntp_exit - document explicitly that ntpd has exited
+ */
+void
+ntp_exit(int retval)
+{
+ msyslog(LOG_ERR, "EXITING with return code %d", retval);
+ exit(retval);
+}
+#endif
+
+/*
+ * fstostr - prettyprint NTP seconds
+ */
+char * fstostr(
+ time_t ntp_stamp
+ )
+{
+ char * buf;
+ struct calendar tm;
+
+ LIB_GETBUF(buf);
+ if (ntpcal_ntp_to_date(&tm, (u_int32)ntp_stamp, NULL) < 0)
+ snprintf(buf, LIB_BUFLENGTH, "ntpcal_ntp_to_date: %ld: range error",
+ (long)ntp_stamp);
+ else
+ snprintf(buf, LIB_BUFLENGTH, "%04d%02d%02d%02d%02d",
+ tm.year, tm.month, tm.monthday,
+ tm.hour, tm.minute);
+ return buf;
+}
+
+
+/*
+ * ntpd_time_stepped is called back by step_systime(), allowing ntpd
+ * to do any one-time processing necessitated by the step.
+ */
+void
+ntpd_time_stepped(void)
+{
+ u_int saved_mon_enabled;
+
+ /*
+ * flush the monitor MRU list which contains l_fp timestamps
+ * which should not be compared across the step.
+ */
+ if (MON_OFF != mon_enabled) {
+ saved_mon_enabled = mon_enabled;
+ mon_stop(MON_OFF);
+ mon_start(saved_mon_enabled);
+ }
+
+ /* inform interpolating Windows code to allow time to go back */
+#ifdef SYS_WINNT
+ win_time_stepped();
+#endif
+}
diff --git a/ntpd/ntpd-opts.c b/ntpd/ntpd-opts.c
new file mode 100644
index 0000000..4c298c5
--- /dev/null
+++ b/ntpd/ntpd-opts.c
@@ -0,0 +1,1952 @@
+/*
+ * EDIT THIS FILE WITH CAUTION (ntpd-opts.c)
+ *
+ * It has been AutoGen-ed December 2, 2014 at 08:54:08 AM by AutoGen 5.18.5pre4
+ * From the definitions ntpd-opts.def
+ * and the template file options
+ *
+ * Generated from AutoOpts 41:0:16 templates.
+ *
+ * AutoOpts is a copyrighted work. This source file is not encumbered
+ * by AutoOpts licensing, but is provided under the licensing terms chosen
+ * by the ntpd author or copyright holder. AutoOpts is
+ * licensed under the terms of the LGPL. The redistributable library
+ * (``libopts'') is licensed under the terms of either the LGPL or, at the
+ * users discretion, the BSD license. See the AutoOpts and/or libopts sources
+ * for details.
+ *
+ * The ntpd program is copyrighted and licensed
+ * under the following terms:
+ *
+ * Copyright (C) 1970-2014 The University of Delaware, all rights reserved.
+ * This is free software. It is licensed for use, modification and
+ * redistribution under the terms of the NTP License, copies of which
+ * can be seen at:
+ * <http://ntp.org/license>
+ * <http://opensource.org/licenses/ntp-license.php>
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and that
+ * both the copyright notice and this permission notice appear in
+ * supporting documentation, and that the name The University of Delaware not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The University of Delaware makes no
+ * representations about the suitability this software for any purpose. It
+ * is provided "as is" without express or implied warranty.
+ */
+
+#ifndef __doxygen__
+#define OPTION_CODE_COMPILE 1
+#include "ntpd-opts.h"
+#include <sys/types.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern FILE * option_usage_fp;
+#define zCopyright (ntpd_opt_strs+0)
+#define zLicenseDescrip (ntpd_opt_strs+314)
+
+/*
+ * global included definitions
+ */
+#ifdef __windows
+ extern int atoi(const char *);
+#else
+# include <stdlib.h>
+#endif
+
+#ifdef __windows
+ extern int atoi(const char*);
+#else
+# include <stdlib.h>
+#endif
+
+#ifndef NULL
+# define NULL 0
+#endif
+
+/**
+ * static const strings for ntpd options
+ */
+static char const ntpd_opt_strs[3011] =
+/* 0 */ "ntpd 4.2.7p482\n"
+ "Copyright (C) 1970-2014 The University of Delaware, all rights reserved.\n"
+ "This is free software. It is licensed for use, modification and\n"
+ "redistribution under the terms of the NTP License, copies of which\n"
+ "can be seen at:\n"
+ " <http://ntp.org/license>\n"
+ " <http://opensource.org/licenses/ntp-license.php>\n\0"
+/* 314 */ "Permission to use, copy, modify, and distribute this software and its\n"
+ "documentation for any purpose with or without fee is hereby granted,\n"
+ "provided that the above copyright notice appears in all copies and that\n"
+ "both the copyright notice and this permission notice appear in supporting\n"
+ "documentation, and that the name The University of Delaware not be used in\n"
+ "advertising or publicity pertaining to distribution of the software without\n"
+ "specific, written prior permission. The University of Delaware makes no\n"
+ "representations about the suitability this software for any purpose. It is\n"
+ "provided \"as is\" without express or implied warranty.\n\0"
+/* 954 */ "Force IPv4 DNS name resolution\0"
+/* 985 */ "IPV4\0"
+/* 990 */ "ipv4\0"
+/* 995 */ "Force IPv6 DNS name resolution\0"
+/* 1026 */ "IPV6\0"
+/* 1031 */ "ipv6\0"
+/* 1036 */ "Require crypto authentication\0"
+/* 1066 */ "AUTHREQ\0"
+/* 1074 */ "authreq\0"
+/* 1082 */ "Do not require crypto authentication\0"
+/* 1119 */ "AUTHNOREQ\0"
+/* 1129 */ "authnoreq\0"
+/* 1139 */ "Allow us to sync to broadcast servers\0"
+/* 1177 */ "BCASTSYNC\0"
+/* 1187 */ "bcastsync\0"
+/* 1197 */ "configuration file name\0"
+/* 1221 */ "CONFIGFILE\0"
+/* 1232 */ "configfile\0"
+/* 1243 */ "Increase debug verbosity level\0"
+/* 1274 */ "DEBUG_LEVEL\0"
+/* 1286 */ "debug-level\0"
+/* 1298 */ "Set the debug verbosity level\0"
+/* 1328 */ "SET_DEBUG_LEVEL\0"
+/* 1344 */ "set-debug-level\0"
+/* 1360 */ "frequency drift file name\0"
+/* 1386 */ "DRIFTFILE\0"
+/* 1396 */ "driftfile\0"
+/* 1406 */ "Allow the first adjustment to be Big\0"
+/* 1443 */ "PANICGATE\0"
+/* 1453 */ "panicgate\0"
+/* 1463 */ "Jail directory\0"
+/* 1478 */ "JAILDIR\0"
+/* 1486 */ "jaildir\0"
+/* 1494 */ "built without --enable-clockctl or --enable-linuxcaps or --enable-solarisprivs\0"
+/* 1573 */ "Listen on an interface name or address\0"
+/* 1612 */ "INTERFACE\0"
+/* 1622 */ "interface\0"
+/* 1632 */ "path to symmetric keys\0"
+/* 1655 */ "KEYFILE\0"
+/* 1663 */ "keyfile\0"
+/* 1671 */ "path to the log file\0"
+/* 1692 */ "LOGFILE\0"
+/* 1700 */ "logfile\0"
+/* 1708 */ "Do not listen to virtual interfaces\0"
+/* 1744 */ "NOVIRTUALIPS\0"
+/* 1757 */ "novirtualips\0"
+/* 1770 */ "Modify Multimedia Timer (Windows only)\0"
+/* 1809 */ "MODIFYMMTIMER\0"
+/* 1823 */ "modifymmtimer\0"
+/* 1837 */ "Do not fork\0"
+/* 1849 */ "NOFORK\0"
+/* 1856 */ "nofork\0"
+/* 1863 */ "Run at high priority\0"
+/* 1884 */ "NICE\0"
+/* 1889 */ "nice\0"
+/* 1894 */ "path to the PID file\0"
+/* 1915 */ "PIDFILE\0"
+/* 1923 */ "pidfile\0"
+/* 1931 */ "Process priority\0"
+/* 1948 */ "PRIORITY\0"
+/* 1957 */ "priority\0"
+/* 1966 */ "Set the time and quit\0"
+/* 1988 */ "QUIT\0"
+/* 1993 */ "quit\0"
+/* 1998 */ "Broadcast/propagation delay\0"
+/* 2026 */ "PROPAGATIONDELAY\0"
+/* 2043 */ "propagationdelay\0"
+/* 2060 */ "Save parsed configuration and quit\0"
+/* 2095 */ "SAVECONFIGQUIT\0"
+/* 2110 */ "saveconfigquit\0"
+/* 2125 */ "Statistics file location\0"
+/* 2150 */ "STATSDIR\0"
+/* 2159 */ "statsdir\0"
+/* 2168 */ "Trusted key number\0"
+/* 2187 */ "TRUSTEDKEY\0"
+/* 2198 */ "trustedkey\0"
+/* 2209 */ "Run as userid (or userid:groupid)\0"
+/* 2243 */ "USER\0"
+/* 2248 */ "user\0"
+/* 2253 */ "interval in seconds between scans for new or dropped interfaces\0"
+/* 2317 */ "UPDATEINTERVAL\0"
+/* 2332 */ "updateinterval\0"
+/* 2347 */ "make ARG an ntp variable (RW)\0"
+/* 2377 */ "VAR\0"
+/* 2381 */ "var\0"
+/* 2385 */ "make ARG an ntp variable (RW|DEF)\0"
+/* 2419 */ "DVAR\0"
+/* 2424 */ "dvar\0"
+/* 2429 */ "Seconds to wait for first clock sync\0"
+/* 2466 */ "WAIT_SYNC\0"
+/* 2476 */ "wait-sync\0"
+/* 2486 */ "Slew up to 600 seconds\0"
+/* 2509 */ "SLEW\0"
+/* 2514 */ "slew\0"
+/* 2519 */ "Use CPU cycle counter (Windows only)\0"
+/* 2556 */ "USEPCC\0"
+/* 2563 */ "usepcc\0"
+/* 2570 */ "Force CPU cycle counter use (Windows only)\0"
+/* 2613 */ "PCCFREQ\0"
+/* 2621 */ "pccfreq\0"
+/* 2629 */ "Register with mDNS as a NTP server\0"
+/* 2664 */ "MDNS\0"
+/* 2669 */ "mdns\0"
+/* 2674 */ "display extended usage information and exit\0"
+/* 2718 */ "help\0"
+/* 2723 */ "extended usage information passed thru pager\0"
+/* 2768 */ "more-help\0"
+/* 2778 */ "output version information and exit\0"
+/* 2814 */ "version\0"
+/* 2822 */ "NTPD\0"
+/* 2827 */ "ntpd - NTP daemon program - Ver. 4.2.7p482\n"
+ "Usage: %s [ -<flag> [<val>] | --<name>[{=| }<val>] ]... \\\n"
+ "\t\t[ <server1> ... <serverN> ]\n\0"
+/* 2960 */ "http://bugs.ntp.org, bugs@ntp.org\0"
+/* 2994 */ "\n\0"
+/* 2996 */ "ntpd 4.2.7p482";
+
+/**
+ * ipv4 option description with
+ * "Must also have options" and "Incompatible options":
+ */
+/** Descriptive text for the ipv4 option */
+#define IPV4_DESC (ntpd_opt_strs+954)
+/** Upper-cased name for the ipv4 option */
+#define IPV4_NAME (ntpd_opt_strs+985)
+/** Name string for the ipv4 option */
+#define IPV4_name (ntpd_opt_strs+990)
+/** Other options that appear in conjunction with the ipv4 option */
+static int const aIpv4CantList[] = {
+ INDEX_OPT_IPV6, NO_EQUIVALENT };
+/** Compiled in flag settings for the ipv4 option */
+#define IPV4_FLAGS (OPTST_DISABLED)
+
+/**
+ * ipv6 option description with
+ * "Must also have options" and "Incompatible options":
+ */
+/** Descriptive text for the ipv6 option */
+#define IPV6_DESC (ntpd_opt_strs+995)
+/** Upper-cased name for the ipv6 option */
+#define IPV6_NAME (ntpd_opt_strs+1026)
+/** Name string for the ipv6 option */
+#define IPV6_name (ntpd_opt_strs+1031)
+/** Other options that appear in conjunction with the ipv6 option */
+static int const aIpv6CantList[] = {
+ INDEX_OPT_IPV4, NO_EQUIVALENT };
+/** Compiled in flag settings for the ipv6 option */
+#define IPV6_FLAGS (OPTST_DISABLED)
+
+/**
+ * authreq option description with
+ * "Must also have options" and "Incompatible options":
+ */
+/** Descriptive text for the authreq option */
+#define AUTHREQ_DESC (ntpd_opt_strs+1036)
+/** Upper-cased name for the authreq option */
+#define AUTHREQ_NAME (ntpd_opt_strs+1066)
+/** Name string for the authreq option */
+#define AUTHREQ_name (ntpd_opt_strs+1074)
+/** Other options that appear in conjunction with the authreq option */
+static int const aAuthreqCantList[] = {
+ INDEX_OPT_AUTHNOREQ, NO_EQUIVALENT };
+/** Compiled in flag settings for the authreq option */
+#define AUTHREQ_FLAGS (OPTST_DISABLED)
+
+/**
+ * authnoreq option description with
+ * "Must also have options" and "Incompatible options":
+ */
+/** Descriptive text for the authnoreq option */
+#define AUTHNOREQ_DESC (ntpd_opt_strs+1082)
+/** Upper-cased name for the authnoreq option */
+#define AUTHNOREQ_NAME (ntpd_opt_strs+1119)
+/** Name string for the authnoreq option */
+#define AUTHNOREQ_name (ntpd_opt_strs+1129)
+/** Other options that appear in conjunction with the authnoreq option */
+static int const aAuthnoreqCantList[] = {
+ INDEX_OPT_AUTHREQ, NO_EQUIVALENT };
+/** Compiled in flag settings for the authnoreq option */
+#define AUTHNOREQ_FLAGS (OPTST_DISABLED)
+
+/**
+ * bcastsync option description:
+ */
+/** Descriptive text for the bcastsync option */
+#define BCASTSYNC_DESC (ntpd_opt_strs+1139)
+/** Upper-cased name for the bcastsync option */
+#define BCASTSYNC_NAME (ntpd_opt_strs+1177)
+/** Name string for the bcastsync option */
+#define BCASTSYNC_name (ntpd_opt_strs+1187)
+/** Compiled in flag settings for the bcastsync option */
+#define BCASTSYNC_FLAGS (OPTST_DISABLED)
+
+/**
+ * configfile option description:
+ */
+/** Descriptive text for the configfile option */
+#define CONFIGFILE_DESC (ntpd_opt_strs+1197)
+/** Upper-cased name for the configfile option */
+#define CONFIGFILE_NAME (ntpd_opt_strs+1221)
+/** Name string for the configfile option */
+#define CONFIGFILE_name (ntpd_opt_strs+1232)
+/** Compiled in flag settings for the configfile option */
+#define CONFIGFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/**
+ * debug-level option description:
+ */
+/** Descriptive text for the debug-level option */
+#define DEBUG_LEVEL_DESC (ntpd_opt_strs+1243)
+/** Upper-cased name for the debug-level option */
+#define DEBUG_LEVEL_NAME (ntpd_opt_strs+1274)
+/** Name string for the debug-level option */
+#define DEBUG_LEVEL_name (ntpd_opt_strs+1286)
+/** Compiled in flag settings for the debug-level option */
+#define DEBUG_LEVEL_FLAGS (OPTST_DISABLED)
+
+/**
+ * set-debug-level option description:
+ */
+/** Descriptive text for the set-debug-level option */
+#define SET_DEBUG_LEVEL_DESC (ntpd_opt_strs+1298)
+/** Upper-cased name for the set-debug-level option */
+#define SET_DEBUG_LEVEL_NAME (ntpd_opt_strs+1328)
+/** Name string for the set-debug-level option */
+#define SET_DEBUG_LEVEL_name (ntpd_opt_strs+1344)
+/** Compiled in flag settings for the set-debug-level option */
+#define SET_DEBUG_LEVEL_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC))
+
+/**
+ * driftfile option description:
+ */
+/** Descriptive text for the driftfile option */
+#define DRIFTFILE_DESC (ntpd_opt_strs+1360)
+/** Upper-cased name for the driftfile option */
+#define DRIFTFILE_NAME (ntpd_opt_strs+1386)
+/** Name string for the driftfile option */
+#define DRIFTFILE_name (ntpd_opt_strs+1396)
+/** Compiled in flag settings for the driftfile option */
+#define DRIFTFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/**
+ * panicgate option description:
+ */
+/** Descriptive text for the panicgate option */
+#define PANICGATE_DESC (ntpd_opt_strs+1406)
+/** Upper-cased name for the panicgate option */
+#define PANICGATE_NAME (ntpd_opt_strs+1443)
+/** Name string for the panicgate option */
+#define PANICGATE_name (ntpd_opt_strs+1453)
+/** Compiled in flag settings for the panicgate option */
+#define PANICGATE_FLAGS (OPTST_DISABLED)
+
+/**
+ * jaildir option description:
+ */
+#ifdef HAVE_DROPROOT
+/** Descriptive text for the jaildir option */
+#define JAILDIR_DESC (ntpd_opt_strs+1463)
+/** Upper-cased name for the jaildir option */
+#define JAILDIR_NAME (ntpd_opt_strs+1478)
+/** Name string for the jaildir option */
+#define JAILDIR_name (ntpd_opt_strs+1486)
+/** Compiled in flag settings for the jaildir option */
+#define JAILDIR_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+#else /* disable jaildir */
+#define JAILDIR_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define JAILDIR_NAME NULL
+/** Descriptive text for the jaildir option */
+#define JAILDIR_DESC (ntpd_opt_strs+1494)
+#define JAILDIR_name (ntpd_opt_strs+1486)
+#endif /* HAVE_DROPROOT */
+
+/**
+ * interface option description:
+ */
+/** Descriptive text for the interface option */
+#define INTERFACE_DESC (ntpd_opt_strs+1573)
+/** Upper-cased name for the interface option */
+#define INTERFACE_NAME (ntpd_opt_strs+1612)
+/** Name string for the interface option */
+#define INTERFACE_name (ntpd_opt_strs+1622)
+/** Compiled in flag settings for the interface option */
+#define INTERFACE_FLAGS (OPTST_DISABLED | OPTST_STACKED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/**
+ * keyfile option description:
+ */
+/** Descriptive text for the keyfile option */
+#define KEYFILE_DESC (ntpd_opt_strs+1632)
+/** Upper-cased name for the keyfile option */
+#define KEYFILE_NAME (ntpd_opt_strs+1655)
+/** Name string for the keyfile option */
+#define KEYFILE_name (ntpd_opt_strs+1663)
+/** Compiled in flag settings for the keyfile option */
+#define KEYFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/**
+ * logfile option description:
+ */
+/** Descriptive text for the logfile option */
+#define LOGFILE_DESC (ntpd_opt_strs+1671)
+/** Upper-cased name for the logfile option */
+#define LOGFILE_NAME (ntpd_opt_strs+1692)
+/** Name string for the logfile option */
+#define LOGFILE_name (ntpd_opt_strs+1700)
+/** Compiled in flag settings for the logfile option */
+#define LOGFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/**
+ * novirtualips option description:
+ */
+/** Descriptive text for the novirtualips option */
+#define NOVIRTUALIPS_DESC (ntpd_opt_strs+1708)
+/** Upper-cased name for the novirtualips option */
+#define NOVIRTUALIPS_NAME (ntpd_opt_strs+1744)
+/** Name string for the novirtualips option */
+#define NOVIRTUALIPS_name (ntpd_opt_strs+1757)
+/** Compiled in flag settings for the novirtualips option */
+#define NOVIRTUALIPS_FLAGS (OPTST_DISABLED)
+
+/**
+ * modifymmtimer option description:
+ */
+#ifdef SYS_WINNT
+/** Descriptive text for the modifymmtimer option */
+#define MODIFYMMTIMER_DESC (ntpd_opt_strs+1770)
+/** Upper-cased name for the modifymmtimer option */
+#define MODIFYMMTIMER_NAME (ntpd_opt_strs+1809)
+/** Name string for the modifymmtimer option */
+#define MODIFYMMTIMER_name (ntpd_opt_strs+1823)
+/** Compiled in flag settings for the modifymmtimer option */
+#define MODIFYMMTIMER_FLAGS (OPTST_DISABLED)
+
+#else /* disable modifymmtimer */
+#define MODIFYMMTIMER_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define MODIFYMMTIMER_NAME NULL
+#define MODIFYMMTIMER_DESC NULL
+#define MODIFYMMTIMER_name NULL
+#endif /* SYS_WINNT */
+
+/**
+ * nofork option description with
+ * "Must also have options" and "Incompatible options":
+ */
+/** Descriptive text for the nofork option */
+#define NOFORK_DESC (ntpd_opt_strs+1837)
+/** Upper-cased name for the nofork option */
+#define NOFORK_NAME (ntpd_opt_strs+1849)
+/** Name string for the nofork option */
+#define NOFORK_name (ntpd_opt_strs+1856)
+/** Other options that appear in conjunction with the nofork option */
+static int const aNoforkCantList[] = {
+ INDEX_OPT_WAIT_SYNC, NO_EQUIVALENT };
+/** Compiled in flag settings for the nofork option */
+#define NOFORK_FLAGS (OPTST_DISABLED)
+
+/**
+ * nice option description:
+ */
+/** Descriptive text for the nice option */
+#define NICE_DESC (ntpd_opt_strs+1863)
+/** Upper-cased name for the nice option */
+#define NICE_NAME (ntpd_opt_strs+1884)
+/** Name string for the nice option */
+#define NICE_name (ntpd_opt_strs+1889)
+/** Compiled in flag settings for the nice option */
+#define NICE_FLAGS (OPTST_DISABLED)
+
+/**
+ * pidfile option description:
+ */
+/** Descriptive text for the pidfile option */
+#define PIDFILE_DESC (ntpd_opt_strs+1894)
+/** Upper-cased name for the pidfile option */
+#define PIDFILE_NAME (ntpd_opt_strs+1915)
+/** Name string for the pidfile option */
+#define PIDFILE_name (ntpd_opt_strs+1923)
+/** Compiled in flag settings for the pidfile option */
+#define PIDFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/**
+ * priority option description:
+ */
+/** Descriptive text for the priority option */
+#define PRIORITY_DESC (ntpd_opt_strs+1931)
+/** Upper-cased name for the priority option */
+#define PRIORITY_NAME (ntpd_opt_strs+1948)
+/** Name string for the priority option */
+#define PRIORITY_name (ntpd_opt_strs+1957)
+/** Compiled in flag settings for the priority option */
+#define PRIORITY_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC))
+
+/**
+ * quit option description with
+ * "Must also have options" and "Incompatible options":
+ */
+/** Descriptive text for the quit option */
+#define QUIT_DESC (ntpd_opt_strs+1966)
+/** Upper-cased name for the quit option */
+#define QUIT_NAME (ntpd_opt_strs+1988)
+/** Name string for the quit option */
+#define QUIT_name (ntpd_opt_strs+1993)
+/** Other options that appear in conjunction with the quit option */
+static int const aQuitCantList[] = {
+ INDEX_OPT_SAVECONFIGQUIT,
+ INDEX_OPT_WAIT_SYNC, NO_EQUIVALENT };
+/** Compiled in flag settings for the quit option */
+#define QUIT_FLAGS (OPTST_DISABLED)
+
+/**
+ * propagationdelay option description:
+ */
+/** Descriptive text for the propagationdelay option */
+#define PROPAGATIONDELAY_DESC (ntpd_opt_strs+1998)
+/** Upper-cased name for the propagationdelay option */
+#define PROPAGATIONDELAY_NAME (ntpd_opt_strs+2026)
+/** Name string for the propagationdelay option */
+#define PROPAGATIONDELAY_name (ntpd_opt_strs+2043)
+/** Compiled in flag settings for the propagationdelay option */
+#define PROPAGATIONDELAY_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/**
+ * saveconfigquit option description with
+ * "Must also have options" and "Incompatible options":
+ */
+#ifdef SAVECONFIG
+/** Descriptive text for the saveconfigquit option */
+#define SAVECONFIGQUIT_DESC (ntpd_opt_strs+2060)
+/** Upper-cased name for the saveconfigquit option */
+#define SAVECONFIGQUIT_NAME (ntpd_opt_strs+2095)
+/** Name string for the saveconfigquit option */
+#define SAVECONFIGQUIT_name (ntpd_opt_strs+2110)
+/** Other options that appear in conjunction with the saveconfigquit option */
+static int const aSaveconfigquitCantList[] = {
+ INDEX_OPT_QUIT,
+ INDEX_OPT_WAIT_SYNC, NO_EQUIVALENT };
+/** Compiled in flag settings for the saveconfigquit option */
+#define SAVECONFIGQUIT_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+#else /* disable saveconfigquit */
+#define SAVECONFIGQUIT_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define aSaveconfigquitCantList NULL
+#define SAVECONFIGQUIT_NAME NULL
+#define SAVECONFIGQUIT_DESC NULL
+#define SAVECONFIGQUIT_name NULL
+#endif /* SAVECONFIG */
+
+/**
+ * statsdir option description:
+ */
+/** Descriptive text for the statsdir option */
+#define STATSDIR_DESC (ntpd_opt_strs+2125)
+/** Upper-cased name for the statsdir option */
+#define STATSDIR_NAME (ntpd_opt_strs+2150)
+/** Name string for the statsdir option */
+#define STATSDIR_name (ntpd_opt_strs+2159)
+/** Compiled in flag settings for the statsdir option */
+#define STATSDIR_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/**
+ * trustedkey option description:
+ */
+/** Descriptive text for the trustedkey option */
+#define TRUSTEDKEY_DESC (ntpd_opt_strs+2168)
+/** Upper-cased name for the trustedkey option */
+#define TRUSTEDKEY_NAME (ntpd_opt_strs+2187)
+/** Name string for the trustedkey option */
+#define TRUSTEDKEY_name (ntpd_opt_strs+2198)
+/** Compiled in flag settings for the trustedkey option */
+#define TRUSTEDKEY_FLAGS (OPTST_DISABLED | OPTST_STACKED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/**
+ * user option description:
+ */
+#ifdef HAVE_DROPROOT
+/** Descriptive text for the user option */
+#define USER_DESC (ntpd_opt_strs+2209)
+/** Upper-cased name for the user option */
+#define USER_NAME (ntpd_opt_strs+2243)
+/** Name string for the user option */
+#define USER_name (ntpd_opt_strs+2248)
+/** Compiled in flag settings for the user option */
+#define USER_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+#else /* disable user */
+#define USER_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define USER_NAME NULL
+/** Descriptive text for the user option */
+#define USER_DESC (ntpd_opt_strs+1494)
+#define USER_name (ntpd_opt_strs+2248)
+#endif /* HAVE_DROPROOT */
+
+/**
+ * updateinterval option description:
+ */
+/** Descriptive text for the updateinterval option */
+#define UPDATEINTERVAL_DESC (ntpd_opt_strs+2253)
+/** Upper-cased name for the updateinterval option */
+#define UPDATEINTERVAL_NAME (ntpd_opt_strs+2317)
+/** Name string for the updateinterval option */
+#define UPDATEINTERVAL_name (ntpd_opt_strs+2332)
+/** Compiled in flag settings for the updateinterval option */
+#define UPDATEINTERVAL_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC))
+
+/**
+ * var option description:
+ */
+/** Descriptive text for the var option */
+#define VAR_DESC (ntpd_opt_strs+2347)
+/** Upper-cased name for the var option */
+#define VAR_NAME (ntpd_opt_strs+2377)
+/** Name string for the var option */
+#define VAR_name (ntpd_opt_strs+2381)
+/** Compiled in flag settings for the var option */
+#define VAR_FLAGS (OPTST_DISABLED | OPTST_STACKED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/**
+ * dvar option description:
+ */
+/** Descriptive text for the dvar option */
+#define DVAR_DESC (ntpd_opt_strs+2385)
+/** Upper-cased name for the dvar option */
+#define DVAR_NAME (ntpd_opt_strs+2419)
+/** Name string for the dvar option */
+#define DVAR_name (ntpd_opt_strs+2424)
+/** Compiled in flag settings for the dvar option */
+#define DVAR_FLAGS (OPTST_DISABLED | OPTST_STACKED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/**
+ * wait-sync option description with
+ * "Must also have options" and "Incompatible options":
+ */
+#ifdef HAVE_WORKING_FORK
+/** Descriptive text for the wait-sync option */
+#define WAIT_SYNC_DESC (ntpd_opt_strs+2429)
+/** Upper-cased name for the wait-sync option */
+#define WAIT_SYNC_NAME (ntpd_opt_strs+2466)
+/** Name string for the wait-sync option */
+#define WAIT_SYNC_name (ntpd_opt_strs+2476)
+/** Other options that appear in conjunction with the wait-sync option */
+static int const aWait_SyncCantList[] = {
+ INDEX_OPT_NOFORK,
+ INDEX_OPT_QUIT,
+ INDEX_OPT_SAVECONFIGQUIT, NO_EQUIVALENT };
+/** Compiled in flag settings for the wait-sync option */
+#define WAIT_SYNC_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC))
+
+#else /* disable wait-sync */
+#define WAIT_SYNC_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define aWait_SyncCantList NULL
+#define WAIT_SYNC_NAME NULL
+#define WAIT_SYNC_DESC NULL
+#define WAIT_SYNC_name NULL
+#endif /* HAVE_WORKING_FORK */
+
+/**
+ * slew option description:
+ */
+/** Descriptive text for the slew option */
+#define SLEW_DESC (ntpd_opt_strs+2486)
+/** Upper-cased name for the slew option */
+#define SLEW_NAME (ntpd_opt_strs+2509)
+/** Name string for the slew option */
+#define SLEW_name (ntpd_opt_strs+2514)
+/** Compiled in flag settings for the slew option */
+#define SLEW_FLAGS (OPTST_DISABLED)
+
+/**
+ * usepcc option description:
+ */
+#ifdef SYS_WINNT
+/** Descriptive text for the usepcc option */
+#define USEPCC_DESC (ntpd_opt_strs+2519)
+/** Upper-cased name for the usepcc option */
+#define USEPCC_NAME (ntpd_opt_strs+2556)
+/** Name string for the usepcc option */
+#define USEPCC_name (ntpd_opt_strs+2563)
+/** Compiled in flag settings for the usepcc option */
+#define USEPCC_FLAGS (OPTST_DISABLED)
+
+#else /* disable usepcc */
+#define USEPCC_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define USEPCC_NAME NULL
+#define USEPCC_DESC NULL
+#define USEPCC_name NULL
+#endif /* SYS_WINNT */
+
+/**
+ * pccfreq option description:
+ */
+#ifdef SYS_WINNT
+/** Descriptive text for the pccfreq option */
+#define PCCFREQ_DESC (ntpd_opt_strs+2570)
+/** Upper-cased name for the pccfreq option */
+#define PCCFREQ_NAME (ntpd_opt_strs+2613)
+/** Name string for the pccfreq option */
+#define PCCFREQ_name (ntpd_opt_strs+2621)
+/** Compiled in flag settings for the pccfreq option */
+#define PCCFREQ_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+#else /* disable pccfreq */
+#define PCCFREQ_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define PCCFREQ_NAME NULL
+#define PCCFREQ_DESC NULL
+#define PCCFREQ_name NULL
+#endif /* SYS_WINNT */
+
+/**
+ * mdns option description:
+ */
+#ifdef HAVE_DNSREGISTRATION
+/** Descriptive text for the mdns option */
+#define MDNS_DESC (ntpd_opt_strs+2629)
+/** Upper-cased name for the mdns option */
+#define MDNS_NAME (ntpd_opt_strs+2664)
+/** Name string for the mdns option */
+#define MDNS_name (ntpd_opt_strs+2669)
+/** Compiled in flag settings for the mdns option */
+#define MDNS_FLAGS (OPTST_DISABLED)
+
+#else /* disable mdns */
+#define MDNS_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define MDNS_NAME NULL
+#define MDNS_DESC NULL
+#define MDNS_name NULL
+#endif /* HAVE_DNSREGISTRATION */
+
+/*
+ * Help/More_Help/Version option descriptions:
+ */
+#define HELP_DESC (ntpd_opt_strs+2674)
+#define HELP_name (ntpd_opt_strs+2718)
+#ifdef HAVE_WORKING_FORK
+#define MORE_HELP_DESC (ntpd_opt_strs+2723)
+#define MORE_HELP_name (ntpd_opt_strs+2768)
+#define MORE_HELP_FLAGS (OPTST_IMM | OPTST_NO_INIT)
+#else
+#define MORE_HELP_DESC HELP_DESC
+#define MORE_HELP_name HELP_name
+#define MORE_HELP_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#endif
+#ifdef NO_OPTIONAL_OPT_ARGS
+# define VER_FLAGS (OPTST_IMM | OPTST_NO_INIT)
+#else
+# define VER_FLAGS (OPTST_SET_ARGTYPE(OPARG_TYPE_STRING) | \
+ OPTST_ARG_OPTIONAL | OPTST_IMM | OPTST_NO_INIT)
+#endif
+#define VER_DESC (ntpd_opt_strs+2778)
+#define VER_name (ntpd_opt_strs+2814)
+/**
+ * Declare option callback procedures
+ */
+extern tOptProc
+ ntpOptionPrintVersion, optionBooleanVal, optionNestedVal,
+ optionNumericVal, optionPagedUsage, optionResetOpt,
+ optionStackArg, optionTimeDate, optionTimeVal,
+ optionUnstackArg, optionVendorOption;
+static tOptProc
+ doOptDebug_Level, doUsageOpt;
+#define VER_PROC ntpOptionPrintVersion
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Define the ntpd Option Descriptions.
+ * This is an array of OPTION_CT entries, one for each
+ * option that the ntpd program responds to.
+ */
+static tOptDesc optDesc[OPTION_CT] = {
+ { /* entry idx, value */ 0, VALUE_OPT_IPV4,
+ /* equiv idx, value */ 0, VALUE_OPT_IPV4,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ IPV4_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --ipv4 */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aIpv4CantList,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ IPV4_DESC, IPV4_NAME, IPV4_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 1, VALUE_OPT_IPV6,
+ /* equiv idx, value */ 1, VALUE_OPT_IPV6,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ IPV6_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --ipv6 */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aIpv6CantList,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ IPV6_DESC, IPV6_NAME, IPV6_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 2, VALUE_OPT_AUTHREQ,
+ /* equiv idx, value */ 2, VALUE_OPT_AUTHREQ,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ AUTHREQ_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --authreq */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aAuthreqCantList,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ AUTHREQ_DESC, AUTHREQ_NAME, AUTHREQ_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 3, VALUE_OPT_AUTHNOREQ,
+ /* equiv idx, value */ 3, VALUE_OPT_AUTHNOREQ,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ AUTHNOREQ_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --authnoreq */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aAuthnoreqCantList,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ AUTHNOREQ_DESC, AUTHNOREQ_NAME, AUTHNOREQ_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 4, VALUE_OPT_BCASTSYNC,
+ /* equiv idx, value */ 4, VALUE_OPT_BCASTSYNC,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ BCASTSYNC_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --bcastsync */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ BCASTSYNC_DESC, BCASTSYNC_NAME, BCASTSYNC_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 5, VALUE_OPT_CONFIGFILE,
+ /* equiv idx, value */ 5, VALUE_OPT_CONFIGFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ CONFIGFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --configfile */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ CONFIGFILE_DESC, CONFIGFILE_NAME, CONFIGFILE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 6, VALUE_OPT_DEBUG_LEVEL,
+ /* equiv idx, value */ 6, VALUE_OPT_DEBUG_LEVEL,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ DEBUG_LEVEL_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --debug-level */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doOptDebug_Level,
+ /* desc, NAME, name */ DEBUG_LEVEL_DESC, DEBUG_LEVEL_NAME, DEBUG_LEVEL_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 7, VALUE_OPT_SET_DEBUG_LEVEL,
+ /* equiv idx, value */ 7, VALUE_OPT_SET_DEBUG_LEVEL,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ SET_DEBUG_LEVEL_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --set-debug-level */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionNumericVal,
+ /* desc, NAME, name */ SET_DEBUG_LEVEL_DESC, SET_DEBUG_LEVEL_NAME, SET_DEBUG_LEVEL_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 8, VALUE_OPT_DRIFTFILE,
+ /* equiv idx, value */ 8, VALUE_OPT_DRIFTFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ DRIFTFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --driftfile */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ DRIFTFILE_DESC, DRIFTFILE_NAME, DRIFTFILE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 9, VALUE_OPT_PANICGATE,
+ /* equiv idx, value */ 9, VALUE_OPT_PANICGATE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ PANICGATE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --panicgate */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ PANICGATE_DESC, PANICGATE_NAME, PANICGATE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 10, VALUE_OPT_JAILDIR,
+ /* equiv idx, value */ 10, VALUE_OPT_JAILDIR,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ JAILDIR_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --jaildir */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ JAILDIR_DESC, JAILDIR_NAME, JAILDIR_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 11, VALUE_OPT_INTERFACE,
+ /* equiv idx, value */ 11, VALUE_OPT_INTERFACE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ INTERFACE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --interface */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ INTERFACE_DESC, INTERFACE_NAME, INTERFACE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 12, VALUE_OPT_KEYFILE,
+ /* equiv idx, value */ 12, VALUE_OPT_KEYFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ KEYFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --keyfile */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ KEYFILE_DESC, KEYFILE_NAME, KEYFILE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 13, VALUE_OPT_LOGFILE,
+ /* equiv idx, value */ 13, VALUE_OPT_LOGFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ LOGFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --logfile */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ LOGFILE_DESC, LOGFILE_NAME, LOGFILE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 14, VALUE_OPT_NOVIRTUALIPS,
+ /* equiv idx, value */ 14, VALUE_OPT_NOVIRTUALIPS,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ NOVIRTUALIPS_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --novirtualips */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ NOVIRTUALIPS_DESC, NOVIRTUALIPS_NAME, NOVIRTUALIPS_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 15, VALUE_OPT_MODIFYMMTIMER,
+ /* equiv idx, value */ 15, VALUE_OPT_MODIFYMMTIMER,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ MODIFYMMTIMER_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --modifymmtimer */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ MODIFYMMTIMER_DESC, MODIFYMMTIMER_NAME, MODIFYMMTIMER_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 16, VALUE_OPT_NOFORK,
+ /* equiv idx, value */ 16, VALUE_OPT_NOFORK,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ NOFORK_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --nofork */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aNoforkCantList,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ NOFORK_DESC, NOFORK_NAME, NOFORK_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 17, VALUE_OPT_NICE,
+ /* equiv idx, value */ 17, VALUE_OPT_NICE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ NICE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --nice */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ NICE_DESC, NICE_NAME, NICE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 18, VALUE_OPT_PIDFILE,
+ /* equiv idx, value */ 18, VALUE_OPT_PIDFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PIDFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --pidfile */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ PIDFILE_DESC, PIDFILE_NAME, PIDFILE_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 19, VALUE_OPT_PRIORITY,
+ /* equiv idx, value */ 19, VALUE_OPT_PRIORITY,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PRIORITY_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --priority */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionNumericVal,
+ /* desc, NAME, name */ PRIORITY_DESC, PRIORITY_NAME, PRIORITY_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 20, VALUE_OPT_QUIT,
+ /* equiv idx, value */ 20, VALUE_OPT_QUIT,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ QUIT_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --quit */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aQuitCantList,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ QUIT_DESC, QUIT_NAME, QUIT_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 21, VALUE_OPT_PROPAGATIONDELAY,
+ /* equiv idx, value */ 21, VALUE_OPT_PROPAGATIONDELAY,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PROPAGATIONDELAY_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --propagationdelay */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ PROPAGATIONDELAY_DESC, PROPAGATIONDELAY_NAME, PROPAGATIONDELAY_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 22, VALUE_OPT_SAVECONFIGQUIT,
+ /* equiv idx, value */ 22, VALUE_OPT_SAVECONFIGQUIT,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ SAVECONFIGQUIT_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --saveconfigquit */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aSaveconfigquitCantList,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ SAVECONFIGQUIT_DESC, SAVECONFIGQUIT_NAME, SAVECONFIGQUIT_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 23, VALUE_OPT_STATSDIR,
+ /* equiv idx, value */ 23, VALUE_OPT_STATSDIR,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ STATSDIR_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --statsdir */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ STATSDIR_DESC, STATSDIR_NAME, STATSDIR_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 24, VALUE_OPT_TRUSTEDKEY,
+ /* equiv idx, value */ 24, VALUE_OPT_TRUSTEDKEY,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ TRUSTEDKEY_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --trustedkey */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ TRUSTEDKEY_DESC, TRUSTEDKEY_NAME, TRUSTEDKEY_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 25, VALUE_OPT_USER,
+ /* equiv idx, value */ 25, VALUE_OPT_USER,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ USER_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --user */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ USER_DESC, USER_NAME, USER_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 26, VALUE_OPT_UPDATEINTERVAL,
+ /* equiv idx, value */ 26, VALUE_OPT_UPDATEINTERVAL,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ UPDATEINTERVAL_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --updateinterval */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionNumericVal,
+ /* desc, NAME, name */ UPDATEINTERVAL_DESC, UPDATEINTERVAL_NAME, UPDATEINTERVAL_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 27, VALUE_OPT_VAR,
+ /* equiv idx, value */ 27, VALUE_OPT_VAR,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ VAR_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --var */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ VAR_DESC, VAR_NAME, VAR_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 28, VALUE_OPT_DVAR,
+ /* equiv idx, value */ 28, VALUE_OPT_DVAR,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ DVAR_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --dvar */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ DVAR_DESC, DVAR_NAME, DVAR_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 29, VALUE_OPT_WAIT_SYNC,
+ /* equiv idx, value */ 29, VALUE_OPT_WAIT_SYNC,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ WAIT_SYNC_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --wait-sync */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aWait_SyncCantList,
+ /* option proc */ optionNumericVal,
+ /* desc, NAME, name */ WAIT_SYNC_DESC, WAIT_SYNC_NAME, WAIT_SYNC_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 30, VALUE_OPT_SLEW,
+ /* equiv idx, value */ 30, VALUE_OPT_SLEW,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ SLEW_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --slew */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ SLEW_DESC, SLEW_NAME, SLEW_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 31, VALUE_OPT_USEPCC,
+ /* equiv idx, value */ 31, VALUE_OPT_USEPCC,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ USEPCC_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --usepcc */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ USEPCC_DESC, USEPCC_NAME, USEPCC_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 32, VALUE_OPT_PCCFREQ,
+ /* equiv idx, value */ 32, VALUE_OPT_PCCFREQ,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PCCFREQ_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --pccfreq */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ PCCFREQ_DESC, PCCFREQ_NAME, PCCFREQ_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 33, VALUE_OPT_MDNS,
+ /* equiv idx, value */ 33, VALUE_OPT_MDNS,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ MDNS_FLAGS, 0,
+ /* last opt argumnt */ { NULL }, /* --mdns */
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ MDNS_DESC, MDNS_NAME, MDNS_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_VERSION, VALUE_OPT_VERSION,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_VERSION,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ VER_FLAGS, AOUSE_VERSION,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ VER_PROC,
+ /* desc, NAME, name */ VER_DESC, NULL, VER_name,
+ /* disablement strs */ NULL, NULL },
+
+
+
+ { /* entry idx, value */ INDEX_OPT_HELP, VALUE_OPT_HELP,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_HELP,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ OPTST_IMM | OPTST_NO_INIT, AOUSE_HELP,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doUsageOpt,
+ /* desc, NAME, name */ HELP_DESC, NULL, HELP_name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_MORE_HELP, VALUE_OPT_MORE_HELP,
+ /* equiv idx value */ NO_EQUIVALENT, VALUE_OPT_MORE_HELP,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ MORE_HELP_FLAGS, AOUSE_MORE_HELP,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionPagedUsage,
+ /* desc, NAME, name */ MORE_HELP_DESC, NULL, MORE_HELP_name,
+ /* disablement strs */ NULL, NULL }
+};
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/** Reference to the upper cased version of ntpd. */
+#define zPROGNAME (ntpd_opt_strs+2822)
+/** Reference to the title line for ntpd usage. */
+#define zUsageTitle (ntpd_opt_strs+2827)
+/** There is no ntpd configuration file. */
+#define zRcName NULL
+/** There are no directories to search for ntpd config files. */
+#define apzHomeList NULL
+/** The ntpd program bug email address. */
+#define zBugsAddr (ntpd_opt_strs+2960)
+/** Clarification/explanation of what ntpd does. */
+#define zExplain (ntpd_opt_strs+2994)
+/** Extra detail explaining what ntpd does. */
+#define zDetail (NULL)
+/** The full version string for ntpd. */
+#define zFullVersion (ntpd_opt_strs+2996)
+/* extracted from optcode.tlib near line 364 */
+
+#if defined(ENABLE_NLS)
+# define OPTPROC_BASE OPTPROC_TRANSLATE
+ static tOptionXlateProc translate_option_strings;
+#else
+# define OPTPROC_BASE OPTPROC_NONE
+# define translate_option_strings NULL
+#endif /* ENABLE_NLS */
+
+#define ntpd_full_usage (NULL)
+#define ntpd_short_usage (NULL)
+
+#endif /* not defined __doxygen__ */
+
+/*
+ * Create the static procedure(s) declared above.
+ */
+/**
+ * The callout function that invokes the optionUsage function.
+ *
+ * @param[in] opts the AutoOpts option description structure
+ * @param[in] od the descriptor for the "help" (usage) option.
+ * @noreturn
+ */
+static void
+doUsageOpt(tOptions * opts, tOptDesc * od)
+{
+ int ex_code;
+ ex_code = NTPD_EXIT_SUCCESS;
+ optionUsage(&ntpdOptions, ex_code);
+ /* NOTREACHED */
+ exit(1);
+ (void)opts;
+ (void)od;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Code to handle the debug-level option.
+ *
+ * @param[in] pOptions the ntpd options data structure
+ * @param[in,out] pOptDesc the option descriptor for this option.
+ */
+static void
+doOptDebug_Level(tOptions* pOptions, tOptDesc* pOptDesc)
+{
+ /*
+ * Be sure the flag-code[0] handles special values for the options pointer
+ * viz. (poptions <= OPTPROC_EMIT_LIMIT) *and also* the special flag bit
+ * ((poptdesc->fOptState & OPTST_RESET) != 0) telling the option to
+ * reset its state.
+ */
+ /* extracted from debug-opt.def, line 15 */
+OPT_VALUE_SET_DEBUG_LEVEL++;
+ (void)pOptDesc;
+ (void)pOptions;
+}
+/* extracted from optmain.tlib near line 1245 */
+
+/**
+ * The directory containing the data associated with ntpd.
+ */
+#ifndef PKGDATADIR
+# define PKGDATADIR ""
+#endif
+
+/**
+ * Information about the person or institution that packaged ntpd
+ * for the current distribution.
+ */
+#ifndef WITH_PACKAGER
+# define ntpd_packager_info NULL
+#else
+/** Packager information for ntpd. */
+static char const ntpd_packager_info[] =
+ "Packaged by " WITH_PACKAGER
+
+# ifdef WITH_PACKAGER_VERSION
+ " ("WITH_PACKAGER_VERSION")"
+# endif
+
+# ifdef WITH_PACKAGER_BUG_REPORTS
+ "\nReport ntpd bugs to " WITH_PACKAGER_BUG_REPORTS
+# endif
+ "\n";
+#endif
+#ifndef __doxygen__
+
+#endif /* __doxygen__ */
+/**
+ * The option definitions for ntpd. The one structure that
+ * binds them all.
+ */
+tOptions ntpdOptions = {
+ OPTIONS_STRUCT_VERSION,
+ 0, NULL, /* original argc + argv */
+ ( OPTPROC_BASE
+ + OPTPROC_ERRSTOP
+ + OPTPROC_SHORTOPT
+ + OPTPROC_LONGOPT
+ + OPTPROC_NO_REQ_OPT
+ + OPTPROC_ENVIRON
+ + OPTPROC_MISUSE ),
+ 0, NULL, /* current option index, current option */
+ NULL, NULL, zPROGNAME,
+ zRcName, zCopyright, zLicenseDescrip,
+ zFullVersion, apzHomeList, zUsageTitle,
+ zExplain, zDetail, optDesc,
+ zBugsAddr, /* address to send bugs to */
+ NULL, NULL, /* extensions/saved state */
+ optionUsage, /* usage procedure */
+ translate_option_strings, /* translation procedure */
+ /*
+ * Indexes to special options
+ */
+ { INDEX_OPT_MORE_HELP, /* more-help option index */
+ NO_EQUIVALENT, /* save option index */
+ NO_EQUIVALENT, /* '-#' option index */
+ NO_EQUIVALENT /* index of default opt */
+ },
+ 37 /* full option count */, 34 /* user option count */,
+ ntpd_full_usage, ntpd_short_usage,
+ NULL, NULL,
+ PKGDATADIR, ntpd_packager_info
+};
+
+#if ENABLE_NLS
+/**
+ * This code is designed to translate translatable option text for the
+ * ntpd program. These translations happen upon entry
+ * to optionProcess().
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef HAVE_DCGETTEXT
+# include <gettext.h>
+#endif
+#include <autoopts/usage-txt.h>
+
+static char * AO_gettext(char const * pz);
+static void coerce_it(void ** s);
+
+/**
+ * AutoGen specific wrapper function for gettext. It relies on the macro _()
+ * to convert from English to the target language, then strdup-duplicates the
+ * result string. It tries the "libopts" domain first, then whatever has been
+ * set via the \a textdomain(3) call.
+ *
+ * @param[in] pz the input text used as a lookup key.
+ * @returns the translated text (if there is one),
+ * or the original text (if not).
+ */
+static char *
+AO_gettext(char const * pz)
+{
+ char * res;
+ if (pz == NULL)
+ return NULL;
+#ifdef HAVE_DCGETTEXT
+ /*
+ * While processing the option_xlateable_txt data, try to use the
+ * "libopts" domain. Once we switch to the option descriptor data,
+ * do *not* use that domain.
+ */
+ if (option_xlateable_txt.field_ct != 0) {
+ res = dgettext("libopts", pz);
+ if (res == pz)
+ res = (char *)(void *)_(pz);
+ } else
+ res = (char *)(void *)_(pz);
+#else
+ res = (char *)(void *)_(pz);
+#endif
+ if (res == pz)
+ return res;
+ res = strdup(res);
+ if (res == NULL) {
+ fputs(_("No memory for duping translated strings\n"), stderr);
+ exit(NTPD_EXIT_FAILURE);
+ }
+ return res;
+}
+
+/**
+ * All the pointers we use are marked "* const", but they are stored in
+ * writable memory. Coerce the mutability and set the pointer.
+ */
+static void coerce_it(void ** s) { *s = AO_gettext(*s);
+}
+
+/**
+ * Translate all the translatable strings in the ntpdOptions
+ * structure defined above. This is done only once.
+ */
+static void
+translate_option_strings(void)
+{
+ tOptions * const opts = &ntpdOptions;
+
+ /*
+ * Guard against re-translation. It won't work. The strings will have
+ * been changed by the first pass through this code. One shot only.
+ */
+ if (option_xlateable_txt.field_ct != 0) {
+ /*
+ * Do the translations. The first pointer follows the field count
+ * field. The field count field is the size of a pointer.
+ */
+ char ** ppz = (char**)(void*)&(option_xlateable_txt);
+ int ix = option_xlateable_txt.field_ct;
+
+ do {
+ ppz++; /* skip over field_ct */
+ *ppz = AO_gettext(*ppz);
+ } while (--ix > 0);
+ /* prevent re-translation and disable "libopts" domain lookup */
+ option_xlateable_txt.field_ct = 0;
+
+ coerce_it((void*)&(opts->pzCopyright));
+ coerce_it((void*)&(opts->pzCopyNotice));
+ coerce_it((void*)&(opts->pzFullVersion));
+ coerce_it((void*)&(opts->pzUsageTitle));
+ coerce_it((void*)&(opts->pzExplain));
+ coerce_it((void*)&(opts->pzDetail));
+ {
+ tOptDesc * od = opts->pOptDesc;
+ for (ix = opts->optCt; ix > 0; ix--, od++)
+ coerce_it((void*)&(od->pzText));
+ }
+ }
+}
+#endif /* ENABLE_NLS */
+
+#ifdef DO_NOT_COMPILE_THIS_CODE_IT_IS_FOR_GETTEXT
+/** I18N function strictly for xgettext. Do not compile. */
+static void bogus_function(void) {
+ /* TRANSLATORS:
+
+ The following dummy function was crated solely so that xgettext can
+ extract the correct strings. These strings are actually referenced
+ by a field name in the ntpdOptions structure noted in the
+ comments below. The literal text is defined in ntpd_opt_strs.
+
+ NOTE: the strings below are segmented with respect to the source string
+ ntpd_opt_strs. The strings above are handed off for translation
+ at run time a paragraph at a time. Consequently, they are presented here
+ for translation a paragraph at a time.
+
+ ALSO: often the description for an option will reference another option
+ by name. These are set off with apostrophe quotes (I hope). Do not
+ translate option names.
+ */
+ /* referenced via ntpdOptions.pzCopyright */
+ puts(_("ntpd 4.2.7p482\n\
+Copyright (C) 1970-2014 The University of Delaware, all rights reserved.\n\
+This is free software. It is licensed for use, modification and\n\
+redistribution under the terms of the NTP License, copies of which\n\
+can be seen at:\n"));
+ puts(_(" <http://ntp.org/license>\n\
+ <http://opensource.org/licenses/ntp-license.php>\n"));
+
+ /* referenced via ntpdOptions.pzCopyNotice */
+ puts(_("Permission to use, copy, modify, and distribute this software and its\n\
+documentation for any purpose with or without fee is hereby granted,\n\
+provided that the above copyright notice appears in all copies and that\n\
+both the copyright notice and this permission notice appear in supporting\n\
+documentation, and that the name The University of Delaware not be used in\n\
+advertising or publicity pertaining to distribution of the software without\n\
+specific, written prior permission. The University of Delaware makes no\n\
+representations about the suitability this software for any purpose. It is\n\
+provided \"as is\" without express or implied warranty.\n"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Force IPv4 DNS name resolution"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Force IPv6 DNS name resolution"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Require crypto authentication"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Do not require crypto authentication"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Allow us to sync to broadcast servers"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("configuration file name"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Increase debug verbosity level"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Set the debug verbosity level"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("frequency drift file name"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Allow the first adjustment to be Big"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Jail directory"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("built without --enable-clockctl or --enable-linuxcaps or --enable-solarisprivs"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Listen on an interface name or address"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("path to symmetric keys"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("path to the log file"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Do not listen to virtual interfaces"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Modify Multimedia Timer (Windows only)"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Do not fork"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Run at high priority"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("path to the PID file"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Process priority"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Set the time and quit"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Broadcast/propagation delay"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Save parsed configuration and quit"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Statistics file location"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Trusted key number"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Run as userid (or userid:groupid)"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("built without --enable-clockctl or --enable-linuxcaps or --enable-solarisprivs"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("interval in seconds between scans for new or dropped interfaces"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("make ARG an ntp variable (RW)"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("make ARG an ntp variable (RW|DEF)"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Seconds to wait for first clock sync"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Slew up to 600 seconds"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Use CPU cycle counter (Windows only)"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Force CPU cycle counter use (Windows only)"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("Register with mDNS as a NTP server"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("display extended usage information and exit"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("extended usage information passed thru pager"));
+
+ /* referenced via ntpdOptions.pOptDesc->pzText */
+ puts(_("output version information and exit"));
+
+ /* referenced via ntpdOptions.pzUsageTitle */
+ puts(_("ntpd - NTP daemon program - Ver. 4.2.7p482\n\
+Usage: %s [ -<flag> [<val>] | --<name>[{=| }<val>] ]... \\\n\
+\t\t[ <server1> ... <serverN> ]\n"));
+
+ /* referenced via ntpdOptions.pzExplain */
+ puts(_("\n"));
+
+ /* referenced via ntpdOptions.pzFullVersion */
+ puts(_("ntpd 4.2.7p482"));
+
+ /* referenced via ntpdOptions.pzFullUsage */
+ puts(_("<<<NOT-FOUND>>>"));
+
+ /* referenced via ntpdOptions.pzShortUsage */
+ puts(_("<<<NOT-FOUND>>>"));
+ /* LIBOPTS-MESSAGES: */
+#line 67 "../autoopts.c"
+ puts(_("allocation of %d bytes failed\n"));
+#line 93 "../autoopts.c"
+ puts(_("allocation of %d bytes failed\n"));
+#line 53 "../init.c"
+ puts(_("AutoOpts function called without option descriptor\n"));
+#line 86 "../init.c"
+ puts(_("\tThis exceeds the compiled library version: "));
+#line 84 "../init.c"
+ puts(_("Automated Options Processing Error!\n"
+ "\t%s called AutoOpts function with structure version %d:%d:%d.\n"));
+#line 80 "../autoopts.c"
+ puts(_("realloc of %d bytes at 0x%p failed\n"));
+#line 88 "../init.c"
+ puts(_("\tThis is less than the minimum library version: "));
+#line 121 "../version.c"
+ puts(_("Automated Options version %s\n"
+ "\tCopyright (C) 1999-2014 by Bruce Korb - all rights reserved\n"));
+#line 82 "../makeshell.c"
+ puts(_("(AutoOpts bug): %s.\n"));
+#line 90 "../reset.c"
+ puts(_("optionResetOpt() called, but reset-option not configured"));
+#line 292 "../usage.c"
+ puts(_("could not locate the 'help' option"));
+#line 336 "../autoopts.c"
+ puts(_("optionProcess() was called with invalid data"));
+#line 748 "../usage.c"
+ puts(_("invalid argument type specified"));
+#line 598 "../find.c"
+ puts(_("defaulted to option with optional arg"));
+#line 76 "../alias.c"
+ puts(_("aliasing option is out of range."));
+#line 234 "../enum.c"
+ puts(_("%s error: the keyword '%s' is ambiguous for %s\n"));
+#line 108 "../find.c"
+ puts(_(" The following options match:\n"));
+#line 293 "../find.c"
+ puts(_("%s: ambiguous option name: %s (matches %d options)\n"));
+#line 161 "../check.c"
+ puts(_("%s: Command line arguments required\n"));
+#line 43 "../alias.c"
+ puts(_("%d %s%s options allowed\n"));
+#line 89 "../makeshell.c"
+ puts(_("%s error %d (%s) calling %s for '%s'\n"));
+#line 301 "../makeshell.c"
+ puts(_("interprocess pipe"));
+#line 168 "../version.c"
+ puts(_("error: version option argument '%c' invalid. Use:\n"
+ "\t'v' - version only\n"
+ "\t'c' - version and copyright\n"
+ "\t'n' - version and full copyright notice\n"));
+#line 58 "../check.c"
+ puts(_("%s error: the '%s' and '%s' options conflict\n"));
+#line 217 "../find.c"
+ puts(_("%s: The '%s' option has been disabled."));
+#line 430 "../find.c"
+ puts(_("%s: The '%s' option has been disabled."));
+#line 38 "../alias.c"
+ puts(_("-equivalence"));
+#line 469 "../find.c"
+ puts(_("%s: illegal option -- %c\n"));
+#line 110 "../reset.c"
+ puts(_("%s: illegal option -- %c\n"));
+#line 271 "../find.c"
+ puts(_("%s: illegal option -- %s\n"));
+#line 755 "../find.c"
+ puts(_("%s: illegal option -- %s\n"));
+#line 118 "../reset.c"
+ puts(_("%s: illegal option -- %s\n"));
+#line 335 "../find.c"
+ puts(_("%s: unknown vendor extension option -- %s\n"));
+#line 159 "../enum.c"
+ puts(_(" or an integer from %d through %d\n"));
+#line 169 "../enum.c"
+ puts(_(" or an integer from %d through %d\n"));
+#line 747 "../usage.c"
+ puts(_("%s error: invalid option descriptor for %s\n"));
+#line 1081 "../usage.c"
+ puts(_("%s error: invalid option descriptor for %s\n"));
+#line 385 "../find.c"
+ puts(_("%s: invalid option name: %s\n"));
+#line 527 "../find.c"
+ puts(_("%s: The '%s' option requires an argument.\n"));
+#line 156 "../autoopts.c"
+ puts(_("(AutoOpts bug): Equivalenced option '%s' was equivalenced to both\n"
+ "\t'%s' and '%s'."));
+#line 94 "../check.c"
+ puts(_("%s error: The %s option is required\n"));
+#line 632 "../find.c"
+ puts(_("%s: The '%s' option cannot have an argument.\n"));
+#line 151 "../check.c"
+ puts(_("%s: Command line arguments are not allowed.\n"));
+#line 535 "../save.c"
+ puts(_("error %d (%s) creating %s\n"));
+#line 234 "../enum.c"
+ puts(_("%s error: '%s' does not match any %s keywords.\n"));
+#line 93 "../reset.c"
+ puts(_("%s error: The '%s' option requires an argument.\n"));
+#line 184 "../save.c"
+ puts(_("error %d (%s) stat-ing %s\n"));
+#line 238 "../save.c"
+ puts(_("error %d (%s) stat-ing %s\n"));
+#line 143 "../restore.c"
+ puts(_("%s error: no saved option state\n"));
+#line 231 "../autoopts.c"
+ puts(_("'%s' is not a command line option.\n"));
+#line 111 "../time.c"
+ puts(_("%s error: '%s' is not a recognizable date/time.\n"));
+#line 132 "../save.c"
+ puts(_("'%s' not defined\n"));
+#line 50 "../time.c"
+ puts(_("%s error: '%s' is not a recognizable time duration.\n"));
+#line 92 "../check.c"
+ puts(_("%s error: The %s option must appear %d times.\n"));
+#line 164 "../numeric.c"
+ puts(_("%s error: '%s' is not a recognizable number.\n"));
+#line 200 "../enum.c"
+ puts(_("%s error: %s exceeds %s keyword count\n"));
+#line 330 "../usage.c"
+ puts(_("Try '%s %s' for more information.\n"));
+#line 45 "../alias.c"
+ puts(_("one %s%s option allowed\n"));
+#line 203 "../makeshell.c"
+ puts(_("standard output"));
+#line 938 "../makeshell.c"
+ puts(_("standard output"));
+#line 274 "../usage.c"
+ puts(_("standard output"));
+#line 415 "../usage.c"
+ puts(_("standard output"));
+#line 625 "../usage.c"
+ puts(_("standard output"));
+#line 175 "../version.c"
+ puts(_("standard output"));
+#line 274 "../usage.c"
+ puts(_("standard error"));
+#line 415 "../usage.c"
+ puts(_("standard error"));
+#line 625 "../usage.c"
+ puts(_("standard error"));
+#line 175 "../version.c"
+ puts(_("standard error"));
+#line 203 "../makeshell.c"
+ puts(_("write"));
+#line 938 "../makeshell.c"
+ puts(_("write"));
+#line 273 "../usage.c"
+ puts(_("write"));
+#line 414 "../usage.c"
+ puts(_("write"));
+#line 624 "../usage.c"
+ puts(_("write"));
+#line 174 "../version.c"
+ puts(_("write"));
+#line 60 "../numeric.c"
+ puts(_("%s error: %s option value %ld is out of range.\n"));
+#line 44 "../check.c"
+ puts(_("%s error: %s option requires the %s option\n"));
+#line 131 "../save.c"
+ puts(_("%s warning: cannot save options - %s not regular file\n"));
+#line 183 "../save.c"
+ puts(_("%s warning: cannot save options - %s not regular file\n"));
+#line 237 "../save.c"
+ puts(_("%s warning: cannot save options - %s not regular file\n"));
+#line 256 "../save.c"
+ puts(_("%s warning: cannot save options - %s not regular file\n"));
+#line 534 "../save.c"
+ puts(_("%s warning: cannot save options - %s not regular file\n"));
+ /* END-LIBOPTS-MESSAGES */
+
+ /* USAGE-TEXT: */
+#line 873 "../usage.c"
+ puts(_("\t\t\t\t- an alternate for '%s'\n"));
+#line 1148 "../usage.c"
+ puts(_("Version, usage and configuration options:"));
+#line 924 "../usage.c"
+ puts(_("\t\t\t\t- default option for unnamed options\n"));
+#line 837 "../usage.c"
+ puts(_("\t\t\t\t- disabled as '--%s'\n"));
+#line 1117 "../usage.c"
+ puts(_(" --- %-14s %s\n"));
+#line 1115 "../usage.c"
+ puts(_("This option has been disabled"));
+#line 864 "../usage.c"
+ puts(_("\t\t\t\t- enabled by default\n"));
+#line 40 "../alias.c"
+ puts(_("%s error: only "));
+#line 1194 "../usage.c"
+ puts(_(" - examining environment variables named %s_*\n"));
+#line 168 "../file.c"
+ puts(_("\t\t\t\t- file must not pre-exist\n"));
+#line 172 "../file.c"
+ puts(_("\t\t\t\t- file must pre-exist\n"));
+#line 380 "../usage.c"
+ puts(_("Options are specified by doubled hyphens and their name or by a single\n"
+ "hyphen and the flag character.\n"));
+#line 916 "../makeshell.c"
+ puts(_("\n"
+ "= = = = = = = =\n\n"
+ "This incarnation of genshell will produce\n"
+ "a shell script to parse the options for %s:\n\n"));
+#line 166 "../enum.c"
+ puts(_(" or an integer mask with any of the lower %d bits set\n"));
+#line 897 "../usage.c"
+ puts(_("\t\t\t\t- is a set membership option\n"));
+#line 918 "../usage.c"
+ puts(_("\t\t\t\t- must appear between %d and %d times\n"));
+#line 382 "../usage.c"
+ puts(_("Options are specified by single or double hyphens and their name.\n"));
+#line 904 "../usage.c"
+ puts(_("\t\t\t\t- may appear multiple times\n"));
+#line 891 "../usage.c"
+ puts(_("\t\t\t\t- may not be preset\n"));
+#line 1309 "../usage.c"
+ puts(_(" Arg Option-Name Description\n"));
+#line 1245 "../usage.c"
+ puts(_(" Flg Arg Option-Name Description\n"));
+#line 1303 "../usage.c"
+ puts(_(" Flg Arg Option-Name Description\n"));
+#line 1304 "../usage.c"
+ puts(_(" %3s %s"));
+#line 1310 "../usage.c"
+ puts(_(" %3s %s"));
+#line 387 "../usage.c"
+ puts(_("The '-#<number>' option may omit the hash char\n"));
+#line 383 "../usage.c"
+ puts(_("All arguments are named options.\n"));
+#line 971 "../usage.c"
+ puts(_(" - reading file %s"));
+#line 409 "../usage.c"
+ puts(_("\n"
+ "Please send bug reports to: <%s>\n"));
+#line 100 "../version.c"
+ puts(_("\n"
+ "Please send bug reports to: <%s>\n"));
+#line 129 "../version.c"
+ puts(_("\n"
+ "Please send bug reports to: <%s>\n"));
+#line 903 "../usage.c"
+ puts(_("\t\t\t\t- may NOT appear - preset only\n"));
+#line 944 "../usage.c"
+ puts(_("\n"
+ "The following option preset mechanisms are supported:\n"));
+#line 1192 "../usage.c"
+ puts(_("\n"
+ "The following option preset mechanisms are supported:\n"));
+#line 682 "../usage.c"
+ puts(_("prohibits these options:\n"));
+#line 677 "../usage.c"
+ puts(_("prohibits the option '%s'\n"));
+#line 81 "../numeric.c"
+ puts(_("%s%ld to %ld"));
+#line 79 "../numeric.c"
+ puts(_("%sgreater than or equal to %ld"));
+#line 75 "../numeric.c"
+ puts(_("%s%ld exactly"));
+#line 68 "../numeric.c"
+ puts(_("%sit must lie in one of the ranges:\n"));
+#line 68 "../numeric.c"
+ puts(_("%sit must be in the range:\n"));
+#line 88 "../numeric.c"
+ puts(_(", or\n"));
+#line 66 "../numeric.c"
+ puts(_("%sis scalable with a suffix: k/K/m/M/g/G/t/T\n"));
+#line 77 "../numeric.c"
+ puts(_("%sless than or equal to %ld"));
+#line 390 "../usage.c"
+ puts(_("Operands and options may be intermixed. They will be reordered.\n"));
+#line 652 "../usage.c"
+ puts(_("requires the option '%s'\n"));
+#line 655 "../usage.c"
+ puts(_("requires these options:\n"));
+#line 1321 "../usage.c"
+ puts(_(" Arg Option-Name Req? Description\n"));
+#line 1315 "../usage.c"
+ puts(_(" Flg Arg Option-Name Req? Description\n"));
+#line 167 "../enum.c"
+ puts(_("or you may use a numeric representation. Preceding these with a '!'\n"
+ "will clear the bits, specifying 'none' will clear all bits, and 'all'\n"
+ "will set them all. Multiple entries may be passed as an option\n"
+ "argument list.\n"));
+#line 910 "../usage.c"
+ puts(_("\t\t\t\t- may appear up to %d times\n"));
+#line 77 "../enum.c"
+ puts(_("The valid \"%s\" option keywords are:\n"));
+#line 1152 "../usage.c"
+ puts(_("The next option supports vendor supported extra options:"));
+#line 773 "../usage.c"
+ puts(_("These additional options are:"));
+ /* END-USAGE-TEXT */
+}
+#endif /* uncompilable code */
+#ifdef __cplusplus
+}
+#endif
+/* ntpd-opts.c ends here */
diff --git a/ntpd/ntpd-opts.def b/ntpd/ntpd-opts.def
new file mode 100644
index 0000000..dee484d
--- /dev/null
+++ b/ntpd/ntpd-opts.def
@@ -0,0 +1,611 @@
+/* -*- Mode: Text -*- */
+
+autogen definitions options;
+
+#include copyright.def
+
+prog-name = "ntpd";
+prog-title = "NTP daemon program";
+argument = "[ <server1> ... <serverN> ]";
+
+#include ntpdbase-opts.def
+
+/* explain: Additional information whenever the usage routine is invoked */
+explain = <<- _END_EXPLAIN
+ _END_EXPLAIN;
+
+doc-section = {
+ ds-type = 'DESCRIPTION';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_PROG_MDOC_DESCRIP
+The
+.Nm
+utility is an operating system daemon which sets
+and maintains the system time of day in synchronism with Internet
+standard time servers.
+It is a complete implementation of the
+Network Time Protocol (NTP) version 4, as defined by RFC-5905,
+but also retains compatibility with
+version 3, as defined by RFC-1305, and versions 1
+and 2, as defined by RFC-1059 and RFC-1119, respectively.
+.Pp
+The
+.Nm
+utility does most computations in 64-bit floating point
+arithmetic and does relatively clumsy 64-bit fixed point operations
+only when necessary to preserve the ultimate precision, about 232
+picoseconds.
+While the ultimate precision is not achievable with
+ordinary workstations and networks of today, it may be required
+with future gigahertz CPU clocks and gigabit LANs.
+.Pp
+Ordinarily,
+.Nm
+reads the
+.Xr ntp.conf 5
+configuration file at startup time in order to determine the
+synchronization sources and operating modes.
+It is also possible to
+specify a working, although limited, configuration entirely on the
+command line, obviating the need for a configuration file.
+This may
+be particularly useful when the local host is to be configured as a
+broadcast/multicast client, with all peers being determined by
+listening to broadcasts at run time.
+.Pp
+If NetInfo support is built into
+.Nm ,
+then
+.Nm
+will attempt to read its configuration from the
+NetInfo if the default
+.Xr ntp.conf 5
+file cannot be read and no file is
+specified by the
+.Fl c
+option.
+.Pp
+Various internal
+.Nm
+variables can be displayed and
+configuration options altered while the
+.Nm
+is running
+using the
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+utility programs.
+.Pp
+When
+.Nm
+starts it looks at the value of
+.Xr umask 2 ,
+and if zero
+.Nm
+will set the
+.Xr umask 2
+to 022.
+ _END_PROG_MDOC_DESCRIP;
+};
+
+doc-section = {
+ ds-type = 'USAGE';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_MDOC_USAGE
+.Ss "How NTP Operates"
+The
+.Nm
+utility operates by exchanging messages with
+one or more configured servers over a range of designated poll intervals.
+When
+started, whether for the first or subsequent times, the program
+requires several exchanges from the majority of these servers so
+the signal processing and mitigation algorithms can accumulate and
+groom the data and set the clock.
+In order to protect the network
+from bursts, the initial poll interval for each server is delayed
+an interval randomized over a few seconds.
+At the default initial poll
+interval of 64s, several minutes can elapse before the clock is
+set.
+This initial delay to set the clock
+can be safely and dramatically reduced using the
+.Cm iburst
+keyword with the
+.Ic server
+configuration
+command, as described in
+.Xr ntp.conf 5 .
+.Pp
+Most operating systems and hardware of today incorporate a
+time-of-year (TOY) chip to maintain the time during periods when
+the power is off.
+When the machine is booted, the chip is used to
+initialize the operating system time.
+After the machine has
+synchronized to a NTP server, the operating system corrects the
+chip from time to time.
+In the default case, if
+.Nm
+detects that the time on the host
+is more than 1000s from the server time,
+.Nm
+assumes something must be terribly wrong and the only
+reliable action is for the operator to intervene and set the clock
+by hand.
+(Reasons for this include there is no TOY chip,
+or its battery is dead, or that the TOY chip is just of poor quality.)
+This causes
+.Nm
+to exit with a panic message to
+the system log.
+The
+.Fl g
+option overrides this check and the
+clock will be set to the server time regardless of the chip time
+(up to 68 years in the past or future \(em
+this is a limitation of the NTPv4 protocol).
+However, and to protect against broken hardware, such as when the
+CMOS battery fails or the clock counter becomes defective, once the
+clock has been set an error greater than 1000s will cause
+.Nm
+to exit anyway.
+.Pp
+Under ordinary conditions,
+.Nm
+adjusts the clock in
+small steps so that the timescale is effectively continuous and
+without discontinuities.
+Under conditions of extreme network
+congestion, the roundtrip delay jitter can exceed three seconds and
+the synchronization distance, which is equal to one-half the
+roundtrip delay plus error budget terms, can become very large.
+The
+.Nm
+algorithms discard sample offsets exceeding 128 ms,
+unless the interval during which no sample offset is less than 128
+ms exceeds 900s.
+The first sample after that, no matter what the
+offset, steps the clock to the indicated time.
+In practice this
+reduces the false alarm rate where the clock is stepped in error to
+a vanishingly low incidence.
+.Pp
+As the result of this behavior, once the clock has been set it
+very rarely strays more than 128 ms even under extreme cases of
+network path congestion and jitter.
+Sometimes, in particular when
+.Nm
+is first started without a valid drift file
+on a system with a large intrinsic drift
+the error might grow to exceed 128 ms,
+which would cause the clock to be set backwards
+if the local clock time is more than 128 s
+in the future relative to the server.
+In some applications, this behavior may be unacceptable.
+There are several solutions, however.
+If the
+.Fl x
+option is included on the command line, the clock will
+never be stepped and only slew corrections will be used.
+But this choice comes with a cost that
+should be carefully explored before deciding to use
+the
+.Fl x
+option.
+The maximum slew rate possible is limited
+to 500 parts-per-million (PPM) as a consequence of the correctness
+principles on which the NTP protocol and algorithm design are
+based.
+As a result, the local clock can take a long time to
+converge to an acceptable offset, about 2,000 s for each second the
+clock is outside the acceptable range.
+During this interval the
+local clock will not be consistent with any other network clock and
+the system cannot be used for distributed applications that require
+correctly synchronized network time.
+.Pp
+In spite of the above precautions, sometimes when large
+frequency errors are present the resulting time offsets stray
+outside the 128-ms range and an eventual step or slew time
+correction is required.
+If following such a correction the
+frequency error is so large that the first sample is outside the
+acceptable range,
+.Nm
+enters the same state as when the
+.Pa ntp.drift
+file is not present.
+The intent of this behavior
+is to quickly correct the frequency and restore operation to the
+normal tracking mode.
+In the most extreme cases
+(the host
+.Cm time.ien.it
+comes to mind), there may be occasional
+step/slew corrections and subsequent frequency corrections.
+It
+helps in these cases to use the
+.Cm burst
+keyword when
+configuring the server, but
+ONLY
+when you have permission to do so from the owner of the target host.
+.Pp
+Finally,
+in the past many startup scripts would run
+.Xr ntpdate 1ntpdatemdoc
+to get the system clock close to correct before starting
+.Xr ntpd 1ntpdmdoc ,
+but this was never more than a mediocre hack and is no longer needed.
+If you are following the instructions in
+.Sx "Starting NTP (Best Current Practice)"
+and you still need to set the system time before starting
+.Nm ,
+please open a bug report and document what is going on,
+and then look at using
+.Xr sntp 1sntpmdoc .
+.Pp
+There is a way to start
+.Xr ntpd 1ntpdmdoc
+that often addresses all of the problems mentioned above.
+.Ss "Starting NTP (Best Current Practice)"
+First, use the
+.Cm iburst
+option on your
+.Cm server
+entries.
+.Pp
+If you can also keep a good
+.Pa ntp.drift
+file then
+.Xr ntpd 1ntpdmdoc
+will effectively "warm-start" and your system's clock will
+be stable in under 11 seconds' time.
+.Pp
+As soon as possible in the startup sequence, start
+.Xr ntpd 1ntpdmdoc
+with at least the
+.Fl g
+and perhaps the
+.Fl N
+options.
+Then,
+start the rest of your "normal" processes.
+This will give
+.Xr ntpd 1ntpdmdoc
+as much time as possible to get the system's clock synchronized and stable.
+.Pp
+Finally,
+if you have processes like
+.Cm dovecot
+or database servers
+that require
+monotonically-increasing time,
+run
+.Xr ntp-wait 1ntp-waitmdoc
+as late as possible in the boot sequence
+(perhaps with the
+.Fl v
+flag)
+and after
+.Xr ntp-wait 1ntp-waitmdoc
+exits successfully
+it is as safe as it will ever be to start any process that require
+stable time.
+.Ss "Frequency Discipline"
+The
+.Nm
+behavior at startup depends on whether the
+frequency file, usually
+.Pa ntp.drift ,
+exists.
+This file
+contains the latest estimate of clock frequency error.
+When the
+.Nm
+is started and the file does not exist, the
+.Nm
+enters a special mode designed to quickly adapt to
+the particular system clock oscillator time and frequency error.
+This takes approximately 15 minutes, after which the time and
+frequency are set to nominal values and the
+.Nm
+enters
+normal mode, where the time and frequency are continuously tracked
+relative to the server.
+After one hour the frequency file is
+created and the current frequency offset written to it.
+When the
+.Nm
+is started and the file does exist, the
+.Nm
+frequency is initialized from the file and enters normal mode
+immediately.
+After that the current frequency offset is written to
+the file at hourly intervals.
+.Ss "Operating Modes"
+The
+.Nm
+utility can operate in any of several modes, including
+symmetric active/passive, client/server broadcast/multicast and
+manycast, as described in the
+.Qq Association Management
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+It normally operates continuously while
+monitoring for small changes in frequency and trimming the clock
+for the ultimate precision.
+However, it can operate in a one-time
+mode where the time is set from an external server and frequency is
+set from a previously recorded frequency file.
+A
+broadcast/multicast or manycast client can discover remote servers,
+compute server-client propagation delay correction factors and
+configure itself automatically.
+This makes it possible to deploy a
+fleet of workstations without specifying configuration details
+specific to the local environment.
+.Pp
+By default,
+.Nm
+runs in continuous mode where each of
+possibly several external servers is polled at intervals determined
+by an intricate state machine.
+The state machine measures the
+incidental roundtrip delay jitter and oscillator frequency wander
+and determines the best poll interval using a heuristic algorithm.
+Ordinarily, and in most operating environments, the state machine
+will start with 64s intervals and eventually increase in steps to
+1024s.
+A small amount of random variation is introduced in order to
+avoid bunching at the servers.
+In addition, should a server become
+unreachable for some time, the poll interval is increased in steps
+to 1024s in order to reduce network overhead.
+.Pp
+In some cases it may not be practical for
+.Nm
+to run continuously.
+A common workaround has been to run the
+.Xr ntpdate 1ntpdatemdoc
+or
+.Xr sntp 1sntpmdoc
+programs from a
+.Xr cron 8
+job at designated
+times.
+However, these programs do not have the crafted signal
+processing, error checking or mitigation algorithms of
+.Nm .
+The
+.Fl q
+option is intended for this purpose.
+Setting this option will cause
+.Nm
+to exit just after
+setting the clock for the first time.
+The procedure for initially
+setting the clock is the same as in continuous mode; most
+applications will probably want to specify the
+.Cm iburst
+keyword with the
+.Ic server
+configuration command.
+With this
+keyword a volley of messages are exchanged to groom the data and
+the clock is set in about 10 s.
+If nothing is heard after a
+couple of minutes, the daemon times out and exits.
+After a suitable
+period of mourning, the
+.Xr ntpdate 1ntpdatemdoc
+program will be
+retired.
+.Pp
+When kernel support is available to discipline the clock
+frequency, which is the case for stock Solaris, Tru64, Linux and
+.Fx ,
+a useful feature is available to discipline the clock
+frequency.
+First,
+.Nm
+is run in continuous mode with
+selected servers in order to measure and record the intrinsic clock
+frequency offset in the frequency file.
+It may take some hours for
+the frequency and offset to settle down.
+Then the
+.Nm
+is
+stopped and run in one-time mode as required.
+At each startup, the
+frequency is read from the file and initializes the kernel
+frequency.
+.Ss "Poll Interval Control"
+This version of NTP includes an intricate state machine to
+reduce the network load while maintaining a quality of
+synchronization consistent with the observed jitter and wander.
+There are a number of ways to tailor the operation in order enhance
+accuracy by reducing the interval or to reduce network overhead by
+increasing it.
+However, the user is advised to carefully consider
+the consequences of changing the poll adjustment range from the
+default minimum of 64 s to the default maximum of 1,024 s.
+The
+default minimum can be changed with the
+.Ic tinker
+.Cm minpoll
+command to a value not less than 16 s.
+This value is used for all
+configured associations, unless overridden by the
+.Cm minpoll
+option on the configuration command.
+Note that most device drivers
+will not operate properly if the poll interval is less than 64 s
+and that the broadcast server and manycast client associations will
+also use the default, unless overridden.
+.Pp
+In some cases involving dial up or toll services, it may be
+useful to increase the minimum interval to a few tens of minutes
+and maximum interval to a day or so.
+Under normal operation
+conditions, once the clock discipline loop has stabilized the
+interval will be increased in steps from the minimum to the
+maximum.
+However, this assumes the intrinsic clock frequency error
+is small enough for the discipline loop correct it.
+The capture
+range of the loop is 500 PPM at an interval of 64s decreasing by a
+factor of two for each doubling of interval.
+At a minimum of 1,024
+s, for example, the capture range is only 31 PPM.
+If the intrinsic
+error is greater than this, the drift file
+.Pa ntp.drift
+will
+have to be specially tailored to reduce the residual error below
+this limit.
+Once this is done, the drift file is automatically
+updated once per hour and is available to initialize the frequency
+on subsequent daemon restarts.
+.Ss "The huff-n'-puff Filter"
+In scenarios where a considerable amount of data are to be
+downloaded or uploaded over telephone modems, timekeeping quality
+can be seriously degraded.
+This occurs because the differential
+delays on the two directions of transmission can be quite large.
+In
+many cases the apparent time errors are so large as to exceed the
+step threshold and a step correction can occur during and after the
+data transfer is in progress.
+.Pp
+The huff-n'-puff filter is designed to correct the apparent time
+offset in these cases.
+It depends on knowledge of the propagation
+delay when no other traffic is present.
+In common scenarios this
+occurs during other than work hours.
+The filter maintains a shift
+register that remembers the minimum delay over the most recent
+interval measured usually in hours.
+Under conditions of severe
+delay, the filter corrects the apparent offset using the sign of
+the offset and the difference between the apparent delay and
+minimum delay.
+The name of the filter reflects the negative (huff)
+and positive (puff) correction, which depends on the sign of the
+offset.
+.Pp
+The filter is activated by the
+.Ic tinker
+command and
+.Cm huffpuff
+keyword, as described in
+.Xr ntp.conf 5 .
+ _END_MDOC_USAGE;
+};
+
+doc-section = {
+ ds-type = 'FILES';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_MDOC_FILES
+.Bl -tag -width /etc/ntp.drift -compact
+.It Pa /etc/ntp.conf
+the default name of the configuration file
+.It Pa /etc/ntp.drift
+the default name of the drift file
+.It Pa /etc/ntp.keys
+the default name of the key file
+.El
+ _END_MDOC_FILES;
+};
+
+doc-section = {
+ ds-type = 'SEE ALSO';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_MDOC_SEE_ALSO
+.Xr ntp.conf 5 ,
+.Xr ntpdate 1ntpdatemdoc ,
+.Xr ntpdc 1ntpdcmdoc ,
+.Xr ntpq 1ntpqmdoc ,
+.Xr sntp 1sntpmdoc
+.Pp
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+.Li http://www.ntp.org/ .
+A snapshot of this documentation is available in HTML format in
+.Pa /usr/share/doc/ntp .
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 1)
+.%O RFC1059
+.Re
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 2)
+.%O RFC1119
+.Re
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 3)
+.%O RFC1305
+.Re
+.Rs
+.%A David L. Mills
+.%A J. Martin, Ed.
+.%A J. Burbank
+.%A W. Kasch
+.%T Network Time Protocol Version 4: Protocol and Algorithms Specification
+.%O RFC5905
+.Re
+.Rs
+.%A David L. Mills
+.%A B. Haberman, Ed.
+.%T Network Time Protocol Version 4: Autokey Specification
+.%O RFC5906
+.Re
+.Rs
+.%A H. Gerstung
+.%A C. Elliott
+.%A B. Haberman, Ed.
+.%T Definitions of Managed Objects for Network Time Protocol Version 4: (NTPv4)
+.%O RFC5907
+.Re
+.Rs
+.%A R. Gayraud
+.%A B. Lourdelet
+.%T Network Time Protocol (NTP) Server Option for DHCPv6
+.%O RFC5908
+.Re
+ _END_MDOC_SEE_ALSO;
+};
+
+doc-section = {
+ ds-type = 'BUGS';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_MDOC_BUGS
+The
+.Nm
+utility has gotten rather fat.
+While not huge, it has gotten
+larger than might be desirable for an elevated-priority
+.Nm
+running on a workstation, particularly since many of
+the fancy features which consume the space were designed more with
+a busy primary server, rather than a high stratum workstation in
+mind.
+ _END_MDOC_BUGS;
+};
+
+doc-section = {
+ ds-type = 'NOTES';
+ ds-format = 'mdoc';
+ ds-text = <<- _END_MDOC_NOTES
+Portions of this document came from FreeBSD.
+ _END_MDOC_NOTES;
+};
diff --git a/ntpd/ntpd-opts.h b/ntpd/ntpd-opts.h
new file mode 100644
index 0000000..7a33779
--- /dev/null
+++ b/ntpd/ntpd-opts.h
@@ -0,0 +1,458 @@
+/*
+ * EDIT THIS FILE WITH CAUTION (ntpd-opts.h)
+ *
+ * It has been AutoGen-ed December 2, 2014 at 08:54:07 AM by AutoGen 5.18.5pre4
+ * From the definitions ntpd-opts.def
+ * and the template file options
+ *
+ * Generated from AutoOpts 41:0:16 templates.
+ *
+ * AutoOpts is a copyrighted work. This header file is not encumbered
+ * by AutoOpts licensing, but is provided under the licensing terms chosen
+ * by the ntpd author or copyright holder. AutoOpts is
+ * licensed under the terms of the LGPL. The redistributable library
+ * (``libopts'') is licensed under the terms of either the LGPL or, at the
+ * users discretion, the BSD license. See the AutoOpts and/or libopts sources
+ * for details.
+ *
+ * The ntpd program is copyrighted and licensed
+ * under the following terms:
+ *
+ * Copyright (C) 1970-2014 The University of Delaware, all rights reserved.
+ * This is free software. It is licensed for use, modification and
+ * redistribution under the terms of the NTP License, copies of which
+ * can be seen at:
+ * <http://ntp.org/license>
+ * <http://opensource.org/licenses/ntp-license.php>
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and that
+ * both the copyright notice and this permission notice appear in
+ * supporting documentation, and that the name The University of Delaware not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The University of Delaware makes no
+ * representations about the suitability this software for any purpose. It
+ * is provided "as is" without express or implied warranty.
+ */
+/**
+ * This file contains the programmatic interface to the Automated
+ * Options generated for the ntpd program.
+ * These macros are documented in the AutoGen info file in the
+ * "AutoOpts" chapter. Please refer to that doc for usage help.
+ */
+#ifndef AUTOOPTS_NTPD_OPTS_H_GUARD
+#define AUTOOPTS_NTPD_OPTS_H_GUARD 1
+#include "config.h"
+#include <autoopts/options.h>
+
+/**
+ * Ensure that the library used for compiling this generated header is at
+ * least as new as the version current when the header template was released
+ * (not counting patch version increments). Also ensure that the oldest
+ * tolerable version is at least as old as what was current when the header
+ * template was released.
+ */
+#define AO_TEMPLATE_VERSION 167936
+#if (AO_TEMPLATE_VERSION < OPTIONS_MINIMUM_VERSION) \
+ || (AO_TEMPLATE_VERSION > OPTIONS_STRUCT_VERSION)
+# error option template version mismatches autoopts/options.h header
+ Choke Me.
+#endif
+
+/**
+ * Enumeration of each option type for ntpd
+ */
+typedef enum {
+ INDEX_OPT_IPV4 = 0,
+ INDEX_OPT_IPV6 = 1,
+ INDEX_OPT_AUTHREQ = 2,
+ INDEX_OPT_AUTHNOREQ = 3,
+ INDEX_OPT_BCASTSYNC = 4,
+ INDEX_OPT_CONFIGFILE = 5,
+ INDEX_OPT_DEBUG_LEVEL = 6,
+ INDEX_OPT_SET_DEBUG_LEVEL = 7,
+ INDEX_OPT_DRIFTFILE = 8,
+ INDEX_OPT_PANICGATE = 9,
+ INDEX_OPT_JAILDIR = 10,
+ INDEX_OPT_INTERFACE = 11,
+ INDEX_OPT_KEYFILE = 12,
+ INDEX_OPT_LOGFILE = 13,
+ INDEX_OPT_NOVIRTUALIPS = 14,
+ INDEX_OPT_MODIFYMMTIMER = 15,
+ INDEX_OPT_NOFORK = 16,
+ INDEX_OPT_NICE = 17,
+ INDEX_OPT_PIDFILE = 18,
+ INDEX_OPT_PRIORITY = 19,
+ INDEX_OPT_QUIT = 20,
+ INDEX_OPT_PROPAGATIONDELAY = 21,
+ INDEX_OPT_SAVECONFIGQUIT = 22,
+ INDEX_OPT_STATSDIR = 23,
+ INDEX_OPT_TRUSTEDKEY = 24,
+ INDEX_OPT_USER = 25,
+ INDEX_OPT_UPDATEINTERVAL = 26,
+ INDEX_OPT_VAR = 27,
+ INDEX_OPT_DVAR = 28,
+ INDEX_OPT_WAIT_SYNC = 29,
+ INDEX_OPT_SLEW = 30,
+ INDEX_OPT_USEPCC = 31,
+ INDEX_OPT_PCCFREQ = 32,
+ INDEX_OPT_MDNS = 33,
+ INDEX_OPT_VERSION = 34,
+ INDEX_OPT_HELP = 35,
+ INDEX_OPT_MORE_HELP = 36
+} teOptIndex;
+/** count of all options for ntpd */
+#define OPTION_CT 37
+/** ntpd version */
+#define NTPD_VERSION "4.2.7p482"
+/** Full ntpd version text */
+#define NTPD_FULL_VERSION "ntpd 4.2.7p482"
+
+/**
+ * Interface defines for all options. Replace "n" with the UPPER_CASED
+ * option name (as in the teOptIndex enumeration above).
+ * e.g. HAVE_OPT(IPV4)
+ */
+#define DESC(n) (ntpdOptions.pOptDesc[INDEX_OPT_## n])
+/** 'true' if an option has been specified in any way */
+#define HAVE_OPT(n) (! UNUSED_OPT(& DESC(n)))
+/** The string argument to an option. The argument type must be \"string\". */
+#define OPT_ARG(n) (DESC(n).optArg.argString)
+/** Mask the option state revealing how an option was specified.
+ * It will be one and only one of \a OPTST_SET, \a OPTST_PRESET,
+ * \a OPTST_DEFINED, \a OPTST_RESET or zero.
+ */
+#define STATE_OPT(n) (DESC(n).fOptState & OPTST_SET_MASK)
+/** Count of option's occurrances *on the command line*. */
+#define COUNT_OPT(n) (DESC(n).optOccCt)
+/** mask of \a OPTST_SET and \a OPTST_DEFINED. */
+#define ISSEL_OPT(n) (SELECTED_OPT(&DESC(n)))
+/** 'true' if \a HAVE_OPT would yield 'false'. */
+#define ISUNUSED_OPT(n) (UNUSED_OPT(& DESC(n)))
+/** 'true' if OPTST_DISABLED bit not set. */
+#define ENABLED_OPT(n) (! DISABLED_OPT(& DESC(n)))
+/** number of stacked option arguments.
+ * Valid only for stacked option arguments. */
+#define STACKCT_OPT(n) (((tArgList*)(DESC(n).optCookie))->useCt)
+/** stacked argument vector.
+ * Valid only for stacked option arguments. */
+#define STACKLST_OPT(n) (((tArgList*)(DESC(n).optCookie))->apzArgs)
+/** Reset an option. */
+#define CLEAR_OPT(n) STMTS( \
+ DESC(n).fOptState &= OPTST_PERSISTENT_MASK; \
+ if ((DESC(n).fOptState & OPTST_INITENABLED) == 0) \
+ DESC(n).fOptState |= OPTST_DISABLED; \
+ DESC(n).optCookie = NULL )
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/**
+ * Enumeration of ntpd exit codes
+ */
+typedef enum {
+ NTPD_EXIT_SUCCESS = 0,
+ NTPD_EXIT_FAILURE = 1,
+ NTPD_EXIT_USAGE_ERROR = 64,
+ NTPD_EXIT_LIBOPTS_FAILURE = 70
+} ntpd_exit_code_t;
+/** @} */
+/**
+ * Make sure there are no #define name conflicts with the option names
+ */
+#ifndef NO_OPTION_NAME_WARNINGS
+# ifdef IPV4
+# warning undefining IPV4 due to option name conflict
+# undef IPV4
+# endif
+# ifdef IPV6
+# warning undefining IPV6 due to option name conflict
+# undef IPV6
+# endif
+# ifdef AUTHREQ
+# warning undefining AUTHREQ due to option name conflict
+# undef AUTHREQ
+# endif
+# ifdef AUTHNOREQ
+# warning undefining AUTHNOREQ due to option name conflict
+# undef AUTHNOREQ
+# endif
+# ifdef BCASTSYNC
+# warning undefining BCASTSYNC due to option name conflict
+# undef BCASTSYNC
+# endif
+# ifdef CONFIGFILE
+# warning undefining CONFIGFILE due to option name conflict
+# undef CONFIGFILE
+# endif
+# ifdef DEBUG_LEVEL
+# warning undefining DEBUG_LEVEL due to option name conflict
+# undef DEBUG_LEVEL
+# endif
+# ifdef SET_DEBUG_LEVEL
+# warning undefining SET_DEBUG_LEVEL due to option name conflict
+# undef SET_DEBUG_LEVEL
+# endif
+# ifdef DRIFTFILE
+# warning undefining DRIFTFILE due to option name conflict
+# undef DRIFTFILE
+# endif
+# ifdef PANICGATE
+# warning undefining PANICGATE due to option name conflict
+# undef PANICGATE
+# endif
+# ifdef JAILDIR
+# warning undefining JAILDIR due to option name conflict
+# undef JAILDIR
+# endif
+# ifdef INTERFACE
+# warning undefining INTERFACE due to option name conflict
+# undef INTERFACE
+# endif
+# ifdef KEYFILE
+# warning undefining KEYFILE due to option name conflict
+# undef KEYFILE
+# endif
+# ifdef LOGFILE
+# warning undefining LOGFILE due to option name conflict
+# undef LOGFILE
+# endif
+# ifdef NOVIRTUALIPS
+# warning undefining NOVIRTUALIPS due to option name conflict
+# undef NOVIRTUALIPS
+# endif
+# ifdef MODIFYMMTIMER
+# warning undefining MODIFYMMTIMER due to option name conflict
+# undef MODIFYMMTIMER
+# endif
+# ifdef NOFORK
+# warning undefining NOFORK due to option name conflict
+# undef NOFORK
+# endif
+# ifdef NICE
+# warning undefining NICE due to option name conflict
+# undef NICE
+# endif
+# ifdef PIDFILE
+# warning undefining PIDFILE due to option name conflict
+# undef PIDFILE
+# endif
+# ifdef PRIORITY
+# warning undefining PRIORITY due to option name conflict
+# undef PRIORITY
+# endif
+# ifdef QUIT
+# warning undefining QUIT due to option name conflict
+# undef QUIT
+# endif
+# ifdef PROPAGATIONDELAY
+# warning undefining PROPAGATIONDELAY due to option name conflict
+# undef PROPAGATIONDELAY
+# endif
+# ifdef SAVECONFIGQUIT
+# warning undefining SAVECONFIGQUIT due to option name conflict
+# undef SAVECONFIGQUIT
+# endif
+# ifdef STATSDIR
+# warning undefining STATSDIR due to option name conflict
+# undef STATSDIR
+# endif
+# ifdef TRUSTEDKEY
+# warning undefining TRUSTEDKEY due to option name conflict
+# undef TRUSTEDKEY
+# endif
+# ifdef USER
+# warning undefining USER due to option name conflict
+# undef USER
+# endif
+# ifdef UPDATEINTERVAL
+# warning undefining UPDATEINTERVAL due to option name conflict
+# undef UPDATEINTERVAL
+# endif
+# ifdef VAR
+# warning undefining VAR due to option name conflict
+# undef VAR
+# endif
+# ifdef DVAR
+# warning undefining DVAR due to option name conflict
+# undef DVAR
+# endif
+# ifdef WAIT_SYNC
+# warning undefining WAIT_SYNC due to option name conflict
+# undef WAIT_SYNC
+# endif
+# ifdef SLEW
+# warning undefining SLEW due to option name conflict
+# undef SLEW
+# endif
+# ifdef USEPCC
+# warning undefining USEPCC due to option name conflict
+# undef USEPCC
+# endif
+# ifdef PCCFREQ
+# warning undefining PCCFREQ due to option name conflict
+# undef PCCFREQ
+# endif
+# ifdef MDNS
+# warning undefining MDNS due to option name conflict
+# undef MDNS
+# endif
+#else /* NO_OPTION_NAME_WARNINGS */
+# undef IPV4
+# undef IPV6
+# undef AUTHREQ
+# undef AUTHNOREQ
+# undef BCASTSYNC
+# undef CONFIGFILE
+# undef DEBUG_LEVEL
+# undef SET_DEBUG_LEVEL
+# undef DRIFTFILE
+# undef PANICGATE
+# undef JAILDIR
+# undef INTERFACE
+# undef KEYFILE
+# undef LOGFILE
+# undef NOVIRTUALIPS
+# undef MODIFYMMTIMER
+# undef NOFORK
+# undef NICE
+# undef PIDFILE
+# undef PRIORITY
+# undef QUIT
+# undef PROPAGATIONDELAY
+# undef SAVECONFIGQUIT
+# undef STATSDIR
+# undef TRUSTEDKEY
+# undef USER
+# undef UPDATEINTERVAL
+# undef VAR
+# undef DVAR
+# undef WAIT_SYNC
+# undef SLEW
+# undef USEPCC
+# undef PCCFREQ
+# undef MDNS
+#endif /* NO_OPTION_NAME_WARNINGS */
+
+/**
+ * Interface defines for specific options.
+ * @{
+ */
+#define VALUE_OPT_IPV4 '4'
+#define VALUE_OPT_IPV6 '6'
+#define VALUE_OPT_AUTHREQ 'a'
+#define VALUE_OPT_AUTHNOREQ 'A'
+#define VALUE_OPT_BCASTSYNC 'b'
+#define VALUE_OPT_CONFIGFILE 'c'
+#define VALUE_OPT_DEBUG_LEVEL 'd'
+#define VALUE_OPT_SET_DEBUG_LEVEL 'D'
+
+#define OPT_VALUE_SET_DEBUG_LEVEL (DESC(SET_DEBUG_LEVEL).optArg.argInt)
+#define VALUE_OPT_DRIFTFILE 'f'
+#define VALUE_OPT_PANICGATE 'g'
+#define VALUE_OPT_JAILDIR 'i'
+#define VALUE_OPT_INTERFACE 'I'
+#define VALUE_OPT_KEYFILE 'k'
+#define VALUE_OPT_LOGFILE 'l'
+#define VALUE_OPT_NOVIRTUALIPS 'L'
+#define VALUE_OPT_MODIFYMMTIMER 'M'
+#define VALUE_OPT_NOFORK 'n'
+#define VALUE_OPT_NICE 'N'
+#define VALUE_OPT_PIDFILE 'p'
+#define VALUE_OPT_PRIORITY 'P'
+
+#define OPT_VALUE_PRIORITY (DESC(PRIORITY).optArg.argInt)
+#define VALUE_OPT_QUIT 'q'
+#define VALUE_OPT_PROPAGATIONDELAY 'r'
+#define VALUE_OPT_SAVECONFIGQUIT 0x1001
+#define VALUE_OPT_STATSDIR 's'
+#define VALUE_OPT_TRUSTEDKEY 't'
+#define VALUE_OPT_USER 'u'
+#define VALUE_OPT_UPDATEINTERVAL 'U'
+
+#define OPT_VALUE_UPDATEINTERVAL (DESC(UPDATEINTERVAL).optArg.argInt)
+#define VALUE_OPT_VAR 0x1002
+#define VALUE_OPT_DVAR 0x1003
+#define VALUE_OPT_WAIT_SYNC 'w'
+#ifdef HAVE_WORKING_FORK
+#define OPT_VALUE_WAIT_SYNC (DESC(WAIT_SYNC).optArg.argInt)
+#endif /* HAVE_WORKING_FORK */
+#define VALUE_OPT_SLEW 'x'
+#define VALUE_OPT_USEPCC 0x1004
+#define VALUE_OPT_PCCFREQ 0x1005
+#define VALUE_OPT_MDNS 'm'
+/** option flag (value) for help-value option */
+#define VALUE_OPT_HELP '?'
+/** option flag (value) for more-help-value option */
+#define VALUE_OPT_MORE_HELP '!'
+/** option flag (value) for version-value option */
+#define VALUE_OPT_VERSION 0x1006
+/*
+ * Interface defines not associated with particular options
+ */
+#define ERRSKIP_OPTERR STMTS(ntpdOptions.fOptSet &= ~OPTPROC_ERRSTOP)
+#define ERRSTOP_OPTERR STMTS(ntpdOptions.fOptSet |= OPTPROC_ERRSTOP)
+#define RESTART_OPT(n) STMTS( \
+ ntpdOptions.curOptIdx = (n); \
+ ntpdOptions.pzCurOpt = NULL )
+#define START_OPT RESTART_OPT(1)
+#define USAGE(c) (*ntpdOptions.pUsageProc)(&ntpdOptions, c)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* * * * * *
+ *
+ * Declare the ntpd option descriptor.
+ */
+extern tOptions ntpdOptions;
+
+#if defined(ENABLE_NLS)
+# ifndef _
+# include <stdio.h>
+# ifndef HAVE_GETTEXT
+ extern char * gettext(char const *);
+# else
+# include <libintl.h>
+# endif
+
+# ifndef ATTRIBUTE_FORMAT_ARG
+# define ATTRIBUTE_FORMAT_ARG(_a)
+# endif
+
+static inline char* aoGetsText(char const* pz) ATTRIBUTE_FORMAT_ARG(1);
+static inline char* aoGetsText(char const* pz) {
+ if (pz == NULL) return NULL;
+ return (char*)gettext(pz);
+}
+# define _(s) aoGetsText(s)
+# endif /* _() */
+
+# define OPT_NO_XLAT_CFG_NAMES STMTS(ntpdOptions.fOptSet |= \
+ OPTPROC_NXLAT_OPT_CFG;)
+# define OPT_NO_XLAT_OPT_NAMES STMTS(ntpdOptions.fOptSet |= \
+ OPTPROC_NXLAT_OPT|OPTPROC_NXLAT_OPT_CFG;)
+
+# define OPT_XLAT_CFG_NAMES STMTS(ntpdOptions.fOptSet &= \
+ ~(OPTPROC_NXLAT_OPT|OPTPROC_NXLAT_OPT_CFG);)
+# define OPT_XLAT_OPT_NAMES STMTS(ntpdOptions.fOptSet &= \
+ ~OPTPROC_NXLAT_OPT;)
+
+#else /* ENABLE_NLS */
+# define OPT_NO_XLAT_CFG_NAMES
+# define OPT_NO_XLAT_OPT_NAMES
+
+# define OPT_XLAT_CFG_NAMES
+# define OPT_XLAT_OPT_NAMES
+
+# ifndef _
+# define _(_s) _s
+# endif
+#endif /* ENABLE_NLS */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* AUTOOPTS_NTPD_OPTS_H_GUARD */
+
+/* ntpd-opts.h ends here */
diff --git a/ntpd/ntpd.1ntpdman b/ntpd/ntpd.1ntpdman
new file mode 100644
index 0000000..810e071
--- /dev/null
+++ b/ntpd/ntpd.1ntpdman
@@ -0,0 +1,987 @@
+.de1 NOP
+. it 1 an-trap
+. if \\n[.$] \,\\$*\/
+..
+.ie t \
+.ds B-Font [CB]
+.ds I-Font [CI]
+.ds R-Font [CR]
+.el \
+.ds B-Font B
+.ds I-Font I
+.ds R-Font R
+.TH ntpd 1ntpdman "02 Dec 2014" "4.2.7p482" "User Commands"
+.\"
+.\" EDIT THIS FILE WITH CAUTION (/tmp/.ag-AtaWSL/ag-MtaORL)
+.\"
+.\" It has been AutoGen-ed December 2, 2014 at 08:56:46 AM by AutoGen 5.18.5pre4
+.\" From the definitions ntpd-opts.def
+.\" and the template file agman-cmd.tpl
+.SH NAME
+\f\*[B-Font]ntpd\fP
+\- NTP daemon program
+.SH SYNOPSIS
+\f\*[B-Font]ntpd\fP
+.\" Mixture of short (flag) options and long options
+[\f\*[B-Font]\-flags\f[]]
+[\f\*[B-Font]\-flag\f[] [\f\*[I-Font]value\f[]]]
+[\f\*[B-Font]\-\-option-name\f[][[=| ]\f\*[I-Font]value\f[]]]
+[ <server1> ... <serverN> ]
+.sp \n(Ppu
+.ne 2
+
+.SH DESCRIPTION
+The
+\f\*[B-Font]ntpd\fP
+utility is an operating system daemon which sets
+and maintains the system time of day in synchronism with Internet
+standard time servers.
+It is a complete implementation of the
+Network Time Protocol (NTP) version 4, as defined by RFC-5905,
+but also retains compatibility with
+version 3, as defined by RFC-1305, and versions 1
+and 2, as defined by RFC-1059 and RFC-1119, respectively.
+.sp \n(Ppu
+.ne 2
+
+The
+\f\*[B-Font]ntpd\fP
+utility does most computations in 64-bit floating point
+arithmetic and does relatively clumsy 64-bit fixed point operations
+only when necessary to preserve the ultimate precision, about 232
+picoseconds.
+While the ultimate precision is not achievable with
+ordinary workstations and networks of today, it may be required
+with future gigahertz CPU clocks and gigabit LANs.
+.sp \n(Ppu
+.ne 2
+
+Ordinarily,
+\f\*[B-Font]ntpd\fP
+reads the
+\fCntp.conf\fR(5)\f[]
+configuration file at startup time in order to determine the
+synchronization sources and operating modes.
+It is also possible to
+specify a working, although limited, configuration entirely on the
+command line, obviating the need for a configuration file.
+This may
+be particularly useful when the local host is to be configured as a
+broadcast/multicast client, with all peers being determined by
+listening to broadcasts at run time.
+.sp \n(Ppu
+.ne 2
+
+If NetInfo support is built into
+\f\*[B-Font]ntpd\fP,
+then
+\f\*[B-Font]ntpd\fP
+will attempt to read its configuration from the
+NetInfo if the default
+\fCntp.conf\fR(5)\f[]
+file cannot be read and no file is
+specified by the
+\f\*[B-Font]\-c\f[]
+option.
+.sp \n(Ppu
+.ne 2
+
+Various internal
+\f\*[B-Font]ntpd\fP
+variables can be displayed and
+configuration options altered while the
+\f\*[B-Font]ntpd\fP
+is running
+using the
+\fCntpq\fR(1ntpqmdoc)\f[]
+and
+\fCntpdc\fR(1ntpdcmdoc)\f[]
+utility programs.
+.sp \n(Ppu
+.ne 2
+
+When
+\f\*[B-Font]ntpd\fP
+starts it looks at the value of
+\fCumask\fR(2)\f[],
+and if zero
+\f\*[B-Font]ntpd\fP
+will set the
+\fCumask\fR(2)\f[]
+to 022.
+.SH "OPTIONS"
+.TP
+.NOP \f\*[B-Font]\-4\f[], \f\*[B-Font]\-\-ipv4\f[]
+Force IPv4 DNS name resolution.
+This option must not appear in combination with any of the following options:
+ipv6.
+.sp
+Force DNS resolution of following host names on the command line
+to the IPv4 namespace.
+.TP
+.NOP \f\*[B-Font]\-6\f[], \f\*[B-Font]\-\-ipv6\f[]
+Force IPv6 DNS name resolution.
+This option must not appear in combination with any of the following options:
+ipv4.
+.sp
+Force DNS resolution of following host names on the command line
+to the IPv6 namespace.
+.TP
+.NOP \f\*[B-Font]\-a\f[], \f\*[B-Font]\-\-authreq\f[]
+Require crypto authentication.
+This option must not appear in combination with any of the following options:
+authnoreq.
+.sp
+Require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is the default.
+.TP
+.NOP \f\*[B-Font]\-A\f[], \f\*[B-Font]\-\-authnoreq\f[]
+Do not require crypto authentication.
+This option must not appear in combination with any of the following options:
+authreq.
+.sp
+Do not require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is almost never a good idea.
+.TP
+.NOP \f\*[B-Font]\-b\f[], \f\*[B-Font]\-\-bcastsync\f[]
+Allow us to sync to broadcast servers.
+.sp
+.TP
+.NOP \f\*[B-Font]\-c\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-configfile\f[]=\f\*[I-Font]string\f[]
+configuration file name.
+.sp
+The name and path of the configuration file,
+\fI/etc/ntp.conf\fP
+by default.
+.TP
+.NOP \f\*[B-Font]\-d\f[], \f\*[B-Font]\-\-debug\-level\f[]
+Increase debug verbosity level.
+This option may appear an unlimited number of times.
+.sp
+.TP
+.NOP \f\*[B-Font]\-D\f[] \f\*[I-Font]number\f[], \f\*[B-Font]\-\-set\-debug\-level\f[]=\f\*[I-Font]number\f[]
+Set the debug verbosity level.
+This option may appear an unlimited number of times.
+This option takes an integer number as its argument.
+.sp
+.TP
+.NOP \f\*[B-Font]\-f\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-driftfile\f[]=\f\*[I-Font]string\f[]
+frequency drift file name.
+.sp
+The name and path of the frequency file,
+\fI/etc/ntp.drift\fP
+by default.
+This is the same operation as the
+\fBdriftfile\fP \fIdriftfile\fP
+configuration specification in the
+\fI/etc/ntp.conf\fP
+file.
+.TP
+.NOP \f\*[B-Font]\-g\f[], \f\*[B-Font]\-\-panicgate\f[]
+Allow the first adjustment to be Big.
+This option may appear an unlimited number of times.
+.sp
+Normally,
+\fBntpd\fP
+exits with a message to the system log if the offset exceeds the panic threshold, which is 1000 s by default. This option allows the time to be set to any value without restriction; however, this can happen only once. If the threshold is exceeded after that,
+\fBntpd\fP
+will exit with a message to the system log. This option can be used with the
+\fB-q\fP
+and
+\fB-x\fP
+options.
+See the
+\fBtinker\fP
+configuration file directive for other options.
+.TP
+.NOP \f\*[B-Font]\-i\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-jaildir\f[]=\f\*[I-Font]string\f[]
+Jail directory.
+.sp
+Chroot the server to the directory
+\fIjaildir\fP
+.
+This option also implies that the server attempts to drop root privileges at startup.
+You may need to also specify a
+\fB-u\fP
+option.
+This option is only available if the OS supports adjusting the clock
+without full root privileges.
+This option is supported under NetBSD (configure with
+\fB--enable-clockctl\fP) or Linux (configure with
+\fB--enable-linuxcaps\fP) or Solaris (configure with \fB--enable-solarisprivs\fP).
+.TP
+.NOP \f\*[B-Font]\-I\f[] \f\*[I-Font]iface\f[], \f\*[B-Font]\-\-interface\f[]=\f\*[I-Font]iface\f[]
+Listen on an interface name or address.
+This option may appear an unlimited number of times.
+.sp
+Open the network address given, or all the addresses associated with the
+given interface name. This option may appear multiple times. This option
+also implies not opening other addresses, except wildcard and localhost.
+This option is deprecated. Please consider using the configuration file
+\fBinterface\fP command, which is more versatile.
+.TP
+.NOP \f\*[B-Font]\-k\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-keyfile\f[]=\f\*[I-Font]string\f[]
+path to symmetric keys.
+.sp
+Specify the name and path of the symmetric key file.
+\fI/etc/ntp.keys\fP
+is the default.
+This is the same operation as the
+\fBkeys\fP \fIkeyfile\fP
+configuration file directive.
+.TP
+.NOP \f\*[B-Font]\-l\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-logfile\f[]=\f\*[I-Font]string\f[]
+path to the log file.
+.sp
+Specify the name and path of the log file.
+The default is the system log file.
+This is the same operation as the
+\fBlogfile\fP \fIlogfile\fP
+configuration file directive.
+.TP
+.NOP \f\*[B-Font]\-L\f[], \f\*[B-Font]\-\-novirtualips\f[]
+Do not listen to virtual interfaces.
+.sp
+Do not listen to virtual interfaces, defined as those with
+names containing a colon. This option is deprecated. Please
+consider using the configuration file \fBinterface\fP command, which
+is more versatile.
+.TP
+.NOP \f\*[B-Font]\-M\f[], \f\*[B-Font]\-\-modifymmtimer\f[]
+Modify Multimedia Timer (Windows only).
+.sp
+Set the Windows Multimedia Timer to highest resolution. This
+ensures the resolution does not change while ntpd is running,
+avoiding timekeeping glitches associated with changes.
+.TP
+.NOP \f\*[B-Font]\-n\f[], \f\*[B-Font]\-\-nofork\f[]
+Do not fork.
+This option must not appear in combination with any of the following options:
+wait-sync.
+.sp
+.TP
+.NOP \f\*[B-Font]\-N\f[], \f\*[B-Font]\-\-nice\f[]
+Run at high priority.
+.sp
+To the extent permitted by the operating system, run
+\fBntpd\fP
+at the highest priority.
+.TP
+.NOP \f\*[B-Font]\-p\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-pidfile\f[]=\f\*[I-Font]string\f[]
+path to the PID file.
+.sp
+Specify the name and path of the file used to record
+\fBntpd\fP's
+process ID.
+This is the same operation as the
+\fBpidfile\fP \fIpidfile\fP
+configuration file directive.
+.TP
+.NOP \f\*[B-Font]\-P\f[] \f\*[I-Font]number\f[], \f\*[B-Font]\-\-priority\f[]=\f\*[I-Font]number\f[]
+Process priority.
+This option takes an integer number as its argument.
+.sp
+To the extent permitted by the operating system, run
+\fBntpd\fP
+at the specified
+\fBsched_setscheduler(SCHED_FIFO)\fP
+priority.
+.TP
+.NOP \f\*[B-Font]\-q\f[], \f\*[B-Font]\-\-quit\f[]
+Set the time and quit.
+This option must not appear in combination with any of the following options:
+saveconfigquit, wait-sync.
+.sp
+\fBntpd\fP
+will not daemonize and will exit after the clock is first
+synchronized. This behavior mimics that of the
+\fBntpdate\fP
+program, which will soon be replaced with a shell script.
+The
+\fB-g\fP
+and
+\fB-x\fP
+options can be used with this option.
+Note: The kernel time discipline is disabled with this option.
+.TP
+.NOP \f\*[B-Font]\-r\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-propagationdelay\f[]=\f\*[I-Font]string\f[]
+Broadcast/propagation delay.
+.sp
+Specify the default propagation delay from the broadcast/multicast server to this client. This is necessary only if the delay cannot be computed automatically by the protocol.
+.TP
+.NOP \f\*[B-Font]\-\-saveconfigquit\f[]=\f\*[I-Font]string\f[]
+Save parsed configuration and quit.
+This option must not appear in combination with any of the following options:
+quit, wait-sync.
+.sp
+Cause \fBntpd\fP to parse its startup configuration file and save an
+equivalent to the given filename and exit. This option was
+designed for automated testing.
+.TP
+.NOP \f\*[B-Font]\-s\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-statsdir\f[]=\f\*[I-Font]string\f[]
+Statistics file location.
+.sp
+Specify the directory path for files created by the statistics facility.
+This is the same operation as the
+\fBstatsdir\fP \fIstatsdir\fP
+configuration file directive.
+.TP
+.NOP \f\*[B-Font]\-t\f[] \f\*[I-Font]tkey\f[], \f\*[B-Font]\-\-trustedkey\f[]=\f\*[I-Font]tkey\f[]
+Trusted key number.
+This option may appear an unlimited number of times.
+.sp
+Add the specified key number to the trusted key list.
+.TP
+.NOP \f\*[B-Font]\-u\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-user\f[]=\f\*[I-Font]string\f[]
+Run as userid (or userid:groupid).
+.sp
+Specify a user, and optionally a group, to switch to.
+This option is only available if the OS supports adjusting the clock
+without full root privileges.
+This option is supported under NetBSD (configure with
+\fB--enable-clockctl\fP) or Linux (configure with
+\fB--enable-linuxcaps\fP) or Solaris (configure with \fB--enable-solarisprivs\fP).
+.TP
+.NOP \f\*[B-Font]\-U\f[] \f\*[I-Font]number\f[], \f\*[B-Font]\-\-updateinterval\f[]=\f\*[I-Font]number\f[]
+interval in seconds between scans for new or dropped interfaces.
+This option takes an integer number as its argument.
+.sp
+Give the time in seconds between two scans for new or dropped interfaces.
+For systems with routing socket support the scans will be performed shortly after the interface change
+has been detected by the system.
+Use 0 to disable scanning. 60 seconds is the minimum time between scans.
+.TP
+.NOP \f\*[B-Font]\-\-var\f[]=\f\*[I-Font]nvar\f[]
+make ARG an ntp variable (RW).
+This option may appear an unlimited number of times.
+.sp
+.TP
+.NOP \f\*[B-Font]\-\-dvar\f[]=\f\*[I-Font]ndvar\f[]
+make ARG an ntp variable (RW|DEF).
+This option may appear an unlimited number of times.
+.sp
+.TP
+.NOP \f\*[B-Font]\-w\f[] \f\*[I-Font]number\f[], \f\*[B-Font]\-\-wait\-sync\f[]=\f\*[I-Font]number\f[]
+Seconds to wait for first clock sync.
+This option must not appear in combination with any of the following options:
+nofork, quit, saveconfigquit.
+This option takes an integer number as its argument.
+.sp
+If greater than zero, alters \fBntpd\fP's behavior when forking to
+daemonize. Instead of exiting with status 0 immediately after
+the fork, the parent waits up to the specified number of
+seconds for the child to first synchronize the clock. The exit
+status is zero (success) if the clock was synchronized,
+otherwise it is \fBETIMEDOUT\fP.
+This provides the option for a script starting \fBntpd\fP to easily
+wait for the first set of the clock before proceeding.
+.TP
+.NOP \f\*[B-Font]\-x\f[], \f\*[B-Font]\-\-slew\f[]
+Slew up to 600 seconds.
+.sp
+Normally, the time is slewed if the offset is less than the step threshold, which is 128 ms by default, and stepped if above the threshold.
+This option sets the threshold to 600 s, which is well within the accuracy window to set the clock manually.
+Note: Since the slew rate of typical Unix kernels is limited to 0.5 ms/s, each second of adjustment requires an amortization interval of 2000 s.
+Thus, an adjustment as much as 600 s will take almost 14 days to complete.
+This option can be used with the
+\fB-g\fP
+and
+\fB-q\fP
+options.
+See the
+\fBtinker\fP
+configuration file directive for other options.
+Note: The kernel time discipline is disabled with this option.
+.TP
+.NOP \f\*[B-Font]\-\-usepcc\f[]
+Use CPU cycle counter (Windows only).
+.sp
+Attempt to substitute the CPU counter for \fBQueryPerformanceCounter\fP.
+The CPU counter and \fBQueryPerformanceCounter\fP are compared, and if
+they have the same frequency, the CPU counter (RDTSC on x86) is
+used directly, saving the overhead of a system call.
+.TP
+.NOP \f\*[B-Font]\-\-pccfreq\f[]=\f\*[I-Font]string\f[]
+Force CPU cycle counter use (Windows only).
+.sp
+Force substitution the CPU counter for \fBQueryPerformanceCounter\fP.
+The CPU counter (RDTSC on x86) is used unconditionally with the
+given frequency (in Hz).
+.TP
+.NOP \f\*[B-Font]\-m\f[], \f\*[B-Font]\-\-mdns\f[]
+Register with mDNS as a NTP server.
+.sp
+Registers as an NTP server with the local mDNS server which allows
+the server to be discovered via mDNS client lookup.
+.TP
+.NOP \f\*[B-Font]\-\&?\f[], \f\*[B-Font]\-\-help\f[]
+Display usage information and exit.
+.TP
+.NOP \f\*[B-Font]\-\&!\f[], \f\*[B-Font]\-\-more-help\f[]
+Pass the extended usage information through a pager.
+.TP
+.NOP \f\*[B-Font]\-\-version\f[] [{\f\*[I-Font]v|c|n\f[]}]
+Output version of program and exit. The default mode is `v', a simple
+version. The `c' mode will print copyright information and `n' will
+print the full copyright notice.
+.PP
+.SH "OPTION PRESETS"
+Any option that is not marked as \fInot presettable\fP may be preset
+by loading values from environment variables named:
+.nf
+ \fBNTPD_<option-name>\fP or \fBNTPD\fP
+.fi
+.ad
+.SH USAGE
+.SS "How NTP Operates"
+The
+\f\*[B-Font]ntpd\fP
+utility operates by exchanging messages with
+one or more configured servers over a range of designated poll intervals.
+When
+started, whether for the first or subsequent times, the program
+requires several exchanges from the majority of these servers so
+the signal processing and mitigation algorithms can accumulate and
+groom the data and set the clock.
+In order to protect the network
+from bursts, the initial poll interval for each server is delayed
+an interval randomized over a few seconds.
+At the default initial poll
+interval of 64s, several minutes can elapse before the clock is
+set.
+This initial delay to set the clock
+can be safely and dramatically reduced using the
+\f\*[B-Font]iburst\f[]
+keyword with the
+\f\*[B-Font]server\f[]
+configuration
+command, as described in
+\fCntp.conf\fR(5)\f[].
+.sp \n(Ppu
+.ne 2
+
+Most operating systems and hardware of today incorporate a
+time-of-year (TOY) chip to maintain the time during periods when
+the power is off.
+When the machine is booted, the chip is used to
+initialize the operating system time.
+After the machine has
+synchronized to a NTP server, the operating system corrects the
+chip from time to time.
+In the default case, if
+\f\*[B-Font]ntpd\fP
+detects that the time on the host
+is more than 1000s from the server time,
+\f\*[B-Font]ntpd\fP
+assumes something must be terribly wrong and the only
+reliable action is for the operator to intervene and set the clock
+by hand.
+(Reasons for this include there is no TOY chip,
+or its battery is dead, or that the TOY chip is just of poor quality.)
+This causes
+\f\*[B-Font]ntpd\fP
+to exit with a panic message to
+the system log.
+The
+\f\*[B-Font]\-g\f[]
+option overrides this check and the
+clock will be set to the server time regardless of the chip time
+(up to 68 years in the past or future \(em
+this is a limitation of the NTPv4 protocol).
+However, and to protect against broken hardware, such as when the
+CMOS battery fails or the clock counter becomes defective, once the
+clock has been set an error greater than 1000s will cause
+\f\*[B-Font]ntpd\fP
+to exit anyway.
+.sp \n(Ppu
+.ne 2
+
+Under ordinary conditions,
+\f\*[B-Font]ntpd\fP
+adjusts the clock in
+small steps so that the timescale is effectively continuous and
+without discontinuities.
+Under conditions of extreme network
+congestion, the roundtrip delay jitter can exceed three seconds and
+the synchronization distance, which is equal to one-half the
+roundtrip delay plus error budget terms, can become very large.
+The
+\f\*[B-Font]ntpd\fP
+algorithms discard sample offsets exceeding 128 ms,
+unless the interval during which no sample offset is less than 128
+ms exceeds 900s.
+The first sample after that, no matter what the
+offset, steps the clock to the indicated time.
+In practice this
+reduces the false alarm rate where the clock is stepped in error to
+a vanishingly low incidence.
+.sp \n(Ppu
+.ne 2
+
+As the result of this behavior, once the clock has been set it
+very rarely strays more than 128 ms even under extreme cases of
+network path congestion and jitter.
+Sometimes, in particular when
+\f\*[B-Font]ntpd\fP
+is first started without a valid drift file
+on a system with a large intrinsic drift
+the error might grow to exceed 128 ms,
+which would cause the clock to be set backwards
+if the local clock time is more than 128 s
+in the future relative to the server.
+In some applications, this behavior may be unacceptable.
+There are several solutions, however.
+If the
+\f\*[B-Font]\-x\f[]
+option is included on the command line, the clock will
+never be stepped and only slew corrections will be used.
+But this choice comes with a cost that
+should be carefully explored before deciding to use
+the
+\f\*[B-Font]\-x\f[]
+option.
+The maximum slew rate possible is limited
+to 500 parts-per-million (PPM) as a consequence of the correctness
+principles on which the NTP protocol and algorithm design are
+based.
+As a result, the local clock can take a long time to
+converge to an acceptable offset, about 2,000 s for each second the
+clock is outside the acceptable range.
+During this interval the
+local clock will not be consistent with any other network clock and
+the system cannot be used for distributed applications that require
+correctly synchronized network time.
+.sp \n(Ppu
+.ne 2
+
+In spite of the above precautions, sometimes when large
+frequency errors are present the resulting time offsets stray
+outside the 128-ms range and an eventual step or slew time
+correction is required.
+If following such a correction the
+frequency error is so large that the first sample is outside the
+acceptable range,
+\f\*[B-Font]ntpd\fP
+enters the same state as when the
+\fIntp.drift\f[]
+file is not present.
+The intent of this behavior
+is to quickly correct the frequency and restore operation to the
+normal tracking mode.
+In the most extreme cases
+(the host
+\f\*[B-Font]time.ien.it\f[]
+comes to mind), there may be occasional
+step/slew corrections and subsequent frequency corrections.
+It
+helps in these cases to use the
+\f\*[B-Font]burst\f[]
+keyword when
+configuring the server, but
+ONLY
+when you have permission to do so from the owner of the target host.
+.sp \n(Ppu
+.ne 2
+
+Finally,
+in the past many startup scripts would run
+\fCntpdate\fR(1ntpdatemdoc)\f[]
+to get the system clock close to correct before starting
+\fCntpd\fR(1ntpdmdoc)\f[],
+but this was never more than a mediocre hack and is no longer needed.
+If you are following the instructions in
+\fIStarting NTP (Best Current Practice)\f[]
+and you still need to set the system time before starting
+\f\*[B-Font]ntpd\fP,
+please open a bug report and document what is going on,
+and then look at using
+\fCsntp\fR(1sntpmdoc)\f[].
+.sp \n(Ppu
+.ne 2
+
+There is a way to start
+\fCntpd\fR(1ntpdmdoc)\f[]
+that often addresses all of the problems mentioned above.
+.SS "Starting NTP (Best Current Practice)"
+First, use the
+\f\*[B-Font]iburst\f[]
+option on your
+\f\*[B-Font]server\f[]
+entries.
+.sp \n(Ppu
+.ne 2
+
+If you can also keep a good
+\fIntp.drift\f[]
+file then
+\fCntpd\fR(1ntpdmdoc)\f[]
+will effectively "warm-start" and your system's clock will
+be stable in under 11 seconds' time.
+.sp \n(Ppu
+.ne 2
+
+As soon as possible in the startup sequence, start
+\fCntpd\fR(1ntpdmdoc)\f[]
+with at least the
+\f\*[B-Font]\-g\f[]
+and perhaps the
+\f\*[B-Font]\-N\f[]
+options.
+Then,
+start the rest of your "normal" processes.
+This will give
+\fCntpd\fR(1ntpdmdoc)\f[]
+as much time as possible to get the system's clock synchronized and stable.
+.sp \n(Ppu
+.ne 2
+
+Finally,
+if you have processes like
+\f\*[B-Font]dovecot\f[]
+or database servers
+that require
+monotonically-increasing time,
+run
+\fCntp-wait\fR(1ntp-waitmdoc)\f[]
+as late as possible in the boot sequence
+(perhaps with the
+\f\*[B-Font]\-v\f[]
+flag)
+and after
+\fCntp-wait\fR(1ntp-waitmdoc)\f[]
+exits successfully
+it is as safe as it will ever be to start any process that require
+stable time.
+.SS "Frequency Discipline"
+The
+\f\*[B-Font]ntpd\fP
+behavior at startup depends on whether the
+frequency file, usually
+\fIntp.drift\f[],
+exists.
+This file
+contains the latest estimate of clock frequency error.
+When the
+\f\*[B-Font]ntpd\fP
+is started and the file does not exist, the
+\f\*[B-Font]ntpd\fP
+enters a special mode designed to quickly adapt to
+the particular system clock oscillator time and frequency error.
+This takes approximately 15 minutes, after which the time and
+frequency are set to nominal values and the
+\f\*[B-Font]ntpd\fP
+enters
+normal mode, where the time and frequency are continuously tracked
+relative to the server.
+After one hour the frequency file is
+created and the current frequency offset written to it.
+When the
+\f\*[B-Font]ntpd\fP
+is started and the file does exist, the
+\f\*[B-Font]ntpd\fP
+frequency is initialized from the file and enters normal mode
+immediately.
+After that the current frequency offset is written to
+the file at hourly intervals.
+.SS "Operating Modes"
+The
+\f\*[B-Font]ntpd\fP
+utility can operate in any of several modes, including
+symmetric active/passive, client/server broadcast/multicast and
+manycast, as described in the
+"Association Management"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+It normally operates continuously while
+monitoring for small changes in frequency and trimming the clock
+for the ultimate precision.
+However, it can operate in a one-time
+mode where the time is set from an external server and frequency is
+set from a previously recorded frequency file.
+A
+broadcast/multicast or manycast client can discover remote servers,
+compute server-client propagation delay correction factors and
+configure itself automatically.
+This makes it possible to deploy a
+fleet of workstations without specifying configuration details
+specific to the local environment.
+.sp \n(Ppu
+.ne 2
+
+By default,
+\f\*[B-Font]ntpd\fP
+runs in continuous mode where each of
+possibly several external servers is polled at intervals determined
+by an intricate state machine.
+The state machine measures the
+incidental roundtrip delay jitter and oscillator frequency wander
+and determines the best poll interval using a heuristic algorithm.
+Ordinarily, and in most operating environments, the state machine
+will start with 64s intervals and eventually increase in steps to
+1024s.
+A small amount of random variation is introduced in order to
+avoid bunching at the servers.
+In addition, should a server become
+unreachable for some time, the poll interval is increased in steps
+to 1024s in order to reduce network overhead.
+.sp \n(Ppu
+.ne 2
+
+In some cases it may not be practical for
+\f\*[B-Font]ntpd\fP
+to run continuously.
+A common workaround has been to run the
+\fCntpdate\fR(1ntpdatemdoc)\f[]
+or
+\fCsntp\fR(1sntpmdoc)\f[]
+programs from a
+\fCcron\fR(8)\f[]
+job at designated
+times.
+However, these programs do not have the crafted signal
+processing, error checking or mitigation algorithms of
+\f\*[B-Font]ntpd\fP.
+The
+\f\*[B-Font]\-q\f[]
+option is intended for this purpose.
+Setting this option will cause
+\f\*[B-Font]ntpd\fP
+to exit just after
+setting the clock for the first time.
+The procedure for initially
+setting the clock is the same as in continuous mode; most
+applications will probably want to specify the
+\f\*[B-Font]iburst\f[]
+keyword with the
+\f\*[B-Font]server\f[]
+configuration command.
+With this
+keyword a volley of messages are exchanged to groom the data and
+the clock is set in about 10 s.
+If nothing is heard after a
+couple of minutes, the daemon times out and exits.
+After a suitable
+period of mourning, the
+\fCntpdate\fR(1ntpdatemdoc)\f[]
+program will be
+retired.
+.sp \n(Ppu
+.ne 2
+
+When kernel support is available to discipline the clock
+frequency, which is the case for stock Solaris, Tru64, Linux and
+FreeBSD,
+a useful feature is available to discipline the clock
+frequency.
+First,
+\f\*[B-Font]ntpd\fP
+is run in continuous mode with
+selected servers in order to measure and record the intrinsic clock
+frequency offset in the frequency file.
+It may take some hours for
+the frequency and offset to settle down.
+Then the
+\f\*[B-Font]ntpd\fP
+is
+stopped and run in one-time mode as required.
+At each startup, the
+frequency is read from the file and initializes the kernel
+frequency.
+.SS "Poll Interval Control"
+This version of NTP includes an intricate state machine to
+reduce the network load while maintaining a quality of
+synchronization consistent with the observed jitter and wander.
+There are a number of ways to tailor the operation in order enhance
+accuracy by reducing the interval or to reduce network overhead by
+increasing it.
+However, the user is advised to carefully consider
+the consequences of changing the poll adjustment range from the
+default minimum of 64 s to the default maximum of 1,024 s.
+The
+default minimum can be changed with the
+\f\*[B-Font]tinker\f[]
+\f\*[B-Font]minpoll\f[]
+command to a value not less than 16 s.
+This value is used for all
+configured associations, unless overridden by the
+\f\*[B-Font]minpoll\f[]
+option on the configuration command.
+Note that most device drivers
+will not operate properly if the poll interval is less than 64 s
+and that the broadcast server and manycast client associations will
+also use the default, unless overridden.
+.sp \n(Ppu
+.ne 2
+
+In some cases involving dial up or toll services, it may be
+useful to increase the minimum interval to a few tens of minutes
+and maximum interval to a day or so.
+Under normal operation
+conditions, once the clock discipline loop has stabilized the
+interval will be increased in steps from the minimum to the
+maximum.
+However, this assumes the intrinsic clock frequency error
+is small enough for the discipline loop correct it.
+The capture
+range of the loop is 500 PPM at an interval of 64s decreasing by a
+factor of two for each doubling of interval.
+At a minimum of 1,024
+s, for example, the capture range is only 31 PPM.
+If the intrinsic
+error is greater than this, the drift file
+\fIntp.drift\f[]
+will
+have to be specially tailored to reduce the residual error below
+this limit.
+Once this is done, the drift file is automatically
+updated once per hour and is available to initialize the frequency
+on subsequent daemon restarts.
+.SS "The huff-n'-puff Filter"
+In scenarios where a considerable amount of data are to be
+downloaded or uploaded over telephone modems, timekeeping quality
+can be seriously degraded.
+This occurs because the differential
+delays on the two directions of transmission can be quite large.
+In
+many cases the apparent time errors are so large as to exceed the
+step threshold and a step correction can occur during and after the
+data transfer is in progress.
+.sp \n(Ppu
+.ne 2
+
+The huff-n'-puff filter is designed to correct the apparent time
+offset in these cases.
+It depends on knowledge of the propagation
+delay when no other traffic is present.
+In common scenarios this
+occurs during other than work hours.
+The filter maintains a shift
+register that remembers the minimum delay over the most recent
+interval measured usually in hours.
+Under conditions of severe
+delay, the filter corrects the apparent offset using the sign of
+the offset and the difference between the apparent delay and
+minimum delay.
+The name of the filter reflects the negative (huff)
+and positive (puff) correction, which depends on the sign of the
+offset.
+.sp \n(Ppu
+.ne 2
+
+The filter is activated by the
+\f\*[B-Font]tinker\f[]
+command and
+\f\*[B-Font]huffpuff\f[]
+keyword, as described in
+\fCntp.conf\fR(5)\f[].
+.SH "ENVIRONMENT"
+See \fBOPTION PRESETS\fP for configuration environment variables.
+.SH FILES
+.TP 15
+.NOP \fI/etc/ntp.conf\f[]
+the default name of the configuration file
+.br
+.ns
+.TP 15
+.NOP \fI/etc/ntp.drift\f[]
+the default name of the drift file
+.br
+.ns
+.TP 15
+.NOP \fI/etc/ntp.keys\f[]
+the default name of the key file
+.PP
+.SH "EXIT STATUS"
+One of the following exit values will be returned:
+.TP
+.NOP 0 " (EXIT_SUCCESS)"
+Successful program execution.
+.TP
+.NOP 1 " (EXIT_FAILURE)"
+The operation failed or the command syntax was not valid.
+.TP
+.NOP 70 " (EX_SOFTWARE)"
+libopts had an internal operational error. Please report
+it to autogen-users@lists.sourceforge.net. Thank you.
+.PP
+.SH "SEE ALSO"
+\fCntp.conf\fR(5)\f[],
+\fCntpdate\fR(1ntpdatemdoc)\f[],
+\fCntpdc\fR(1ntpdcmdoc)\f[],
+\fCntpq\fR(1ntpqmdoc)\f[],
+\fCsntp\fR(1sntpmdoc)\f[]
+.sp \n(Ppu
+.ne 2
+
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+\f[C]http://www.ntp.org/\f[].
+A snapshot of this documentation is available in HTML format in
+\fI/usr/share/doc/ntp\f[].
+David L. Mills,
+\fINetwork Time Protocol (Version 1)\fR,
+RFC1059
+.PP
+
+David L. Mills,
+\fINetwork Time Protocol (Version 2)\fR,
+RFC1119
+.PP
+
+David L. Mills,
+\fINetwork Time Protocol (Version 3)\fR,
+RFC1305
+.PP
+
+David L. Mills and J. Martin, Ed. and J. Burbank and W. Kasch,
+\fINetwork Time Protocol Version 4: Protocol and Algorithms Specification\fR,
+RFC5905
+.PP
+
+David L. Mills and B. Haberman, Ed.,
+\fINetwork Time Protocol Version 4: Autokey Specification\fR,
+RFC5906
+.PP
+
+H. Gerstung and C. Elliott and B. Haberman, Ed.,
+\fIDefinitions of Managed Objects for Network Time Protocol Version 4: (NTPv4)\fR,
+RFC5907
+.PP
+
+R. Gayraud and B. Lourdelet,
+\fINetwork Time Protocol (NTP) Server Option for DHCPv6\fR,
+RFC5908
+.PP
+
+.SH "AUTHORS"
+The University of Delaware
+.SH "COPYRIGHT"
+Copyright (C) 1970-2014 The University of Delaware all rights reserved.
+This program is released under the terms of the NTP license, <http://ntp.org/license>.
+.SH BUGS
+The
+\f\*[B-Font]ntpd\fP
+utility has gotten rather fat.
+While not huge, it has gotten
+larger than might be desirable for an elevated-priority
+\f\*[B-Font]ntpd\fP
+running on a workstation, particularly since many of
+the fancy features which consume the space were designed more with
+a busy primary server, rather than a high stratum workstation in
+mind.
+.sp \n(Ppu
+.ne 2
+
+Please send bug reports to: http://bugs.ntp.org, bugs@ntp.org
+.SH NOTES
+Portions of this document came from FreeBSD.
+.sp \n(Ppu
+.ne 2
+
+This manual page was \fIAutoGen\fP-erated from the \fBntpd\fP
+option definitions.
diff --git a/ntpd/ntpd.1ntpdmdoc b/ntpd/ntpd.1ntpdmdoc
new file mode 100644
index 0000000..5b02dd1
--- /dev/null
+++ b/ntpd/ntpd.1ntpdmdoc
@@ -0,0 +1,891 @@
+.Dd December 2 2014
+.Dt NTPD 1ntpdmdoc User Commands
+.Os
+.\" EDIT THIS FILE WITH CAUTION (ntpd-opts.mdoc)
+.\"
+.\" It has been AutoGen-ed December 2, 2014 at 08:57:03 AM by AutoGen 5.18.5pre4
+.\" From the definitions ntpd-opts.def
+.\" and the template file agmdoc-cmd.tpl
+.Sh NAME
+.Nm ntpd
+.Nd NTP daemon program
+.Sh SYNOPSIS
+.Nm
+.\" Mixture of short (flag) options and long options
+.Op Fl flags
+.Op Fl flag Op Ar value
+.Op Fl \-option\-name Ns Oo Oo Ns "=| " Oc Ns Ar value Oc
+[ <server1> ... <serverN> ]
+.Pp
+.Sh DESCRIPTION
+The
+.Nm
+utility is an operating system daemon which sets
+and maintains the system time of day in synchronism with Internet
+standard time servers.
+It is a complete implementation of the
+Network Time Protocol (NTP) version 4, as defined by RFC\-5905,
+but also retains compatibility with
+version 3, as defined by RFC\-1305, and versions 1
+and 2, as defined by RFC\-1059 and RFC\-1119, respectively.
+.Pp
+The
+.Nm
+utility does most computations in 64\-bit floating point
+arithmetic and does relatively clumsy 64\-bit fixed point operations
+only when necessary to preserve the ultimate precision, about 232
+picoseconds.
+While the ultimate precision is not achievable with
+ordinary workstations and networks of today, it may be required
+with future gigahertz CPU clocks and gigabit LANs.
+.Pp
+Ordinarily,
+.Nm
+reads the
+.Xr ntp.conf 5
+configuration file at startup time in order to determine the
+synchronization sources and operating modes.
+It is also possible to
+specify a working, although limited, configuration entirely on the
+command line, obviating the need for a configuration file.
+This may
+be particularly useful when the local host is to be configured as a
+broadcast/multicast client, with all peers being determined by
+listening to broadcasts at run time.
+.Pp
+If NetInfo support is built into
+.Nm ,
+then
+.Nm
+will attempt to read its configuration from the
+NetInfo if the default
+.Xr ntp.conf 5
+file cannot be read and no file is
+specified by the
+.Fl c
+option.
+.Pp
+Various internal
+.Nm
+variables can be displayed and
+configuration options altered while the
+.Nm
+is running
+using the
+.Xr ntpq 1ntpqmdoc
+and
+.Xr ntpdc 1ntpdcmdoc
+utility programs.
+.Pp
+When
+.Nm
+starts it looks at the value of
+.Xr umask 2 ,
+and if zero
+.Nm
+will set the
+.Xr umask 2
+to 022.
+.Sh "OPTIONS"
+.Bl -tag
+.It Fl 4 , Fl \-ipv4
+Force IPv4 DNS name resolution.
+This option must not appear in combination with any of the following options:
+ipv6.
+.sp
+Force DNS resolution of following host names on the command line
+to the IPv4 namespace.
+.It Fl 6 , Fl \-ipv6
+Force IPv6 DNS name resolution.
+This option must not appear in combination with any of the following options:
+ipv4.
+.sp
+Force DNS resolution of following host names on the command line
+to the IPv6 namespace.
+.It Fl a , Fl \-authreq
+Require crypto authentication.
+This option must not appear in combination with any of the following options:
+authnoreq.
+.sp
+Require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is the default.
+.It Fl A , Fl \-authnoreq
+Do not require crypto authentication.
+This option must not appear in combination with any of the following options:
+authreq.
+.sp
+Do not require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is almost never a good idea.
+.It Fl b , Fl \-bcastsync
+Allow us to sync to broadcast servers.
+.sp
+.It Fl c Ar string , Fl \-configfile Ns = Ns Ar string
+configuration file name.
+.sp
+The name and path of the configuration file,
+\fI/etc/ntp.conf\fP
+by default.
+.It Fl d , Fl \-debug\-level
+Increase debug verbosity level.
+This option may appear an unlimited number of times.
+.sp
+.It Fl D Ar number , Fl \-set\-debug\-level Ns = Ns Ar number
+Set the debug verbosity level.
+This option may appear an unlimited number of times.
+This option takes an integer number as its argument.
+.sp
+.It Fl f Ar string , Fl \-driftfile Ns = Ns Ar string
+frequency drift file name.
+.sp
+The name and path of the frequency file,
+\fI/etc/ntp.drift\fP
+by default.
+This is the same operation as the
+\fBdriftfile\fP \fIdriftfile\fP
+configuration specification in the
+\fI/etc/ntp.conf\fP
+file.
+.It Fl g , Fl \-panicgate
+Allow the first adjustment to be Big.
+This option may appear an unlimited number of times.
+.sp
+Normally,
+\fBntpd\fP
+exits with a message to the system log if the offset exceeds the panic threshold, which is 1000 s by default. This option allows the time to be set to any value without restriction; however, this can happen only once. If the threshold is exceeded after that,
+\fBntpd\fP
+will exit with a message to the system log. This option can be used with the
+\fB\-q\fP
+and
+\fB\-x\fP
+options.
+See the
+\fBtinker\fP
+configuration file directive for other options.
+.It Fl i Ar string , Fl \-jaildir Ns = Ns Ar string
+Jail directory.
+.sp
+Chroot the server to the directory
+\fIjaildir\fP
+.
+This option also implies that the server attempts to drop root privileges at startup.
+You may need to also specify a
+\fB\-u\fP
+option.
+This option is only available if the OS supports adjusting the clock
+without full root privileges.
+This option is supported under NetBSD (configure with
+\fB\-\-enable\-clockctl\fP) or Linux (configure with
+\fB\-\-enable\-linuxcaps\fP) or Solaris (configure with \fB\-\-enable\-solarisprivs\fP).
+.It Fl I Ar iface , Fl \-interface Ns = Ns Ar iface
+Listen on an interface name or address.
+This option may appear an unlimited number of times.
+.sp
+Open the network address given, or all the addresses associated with the
+given interface name. This option may appear multiple times. This option
+also implies not opening other addresses, except wildcard and localhost.
+This option is deprecated. Please consider using the configuration file
+\fBinterface\fP command, which is more versatile.
+.It Fl k Ar string , Fl \-keyfile Ns = Ns Ar string
+path to symmetric keys.
+.sp
+Specify the name and path of the symmetric key file.
+\fI/etc/ntp.keys\fP
+is the default.
+This is the same operation as the
+\fBkeys\fP \fIkeyfile\fP
+configuration file directive.
+.It Fl l Ar string , Fl \-logfile Ns = Ns Ar string
+path to the log file.
+.sp
+Specify the name and path of the log file.
+The default is the system log file.
+This is the same operation as the
+\fBlogfile\fP \fIlogfile\fP
+configuration file directive.
+.It Fl L , Fl \-novirtualips
+Do not listen to virtual interfaces.
+.sp
+Do not listen to virtual interfaces, defined as those with
+names containing a colon. This option is deprecated. Please
+consider using the configuration file \fBinterface\fP command, which
+is more versatile.
+.It Fl M , Fl \-modifymmtimer
+Modify Multimedia Timer (Windows only).
+.sp
+Set the Windows Multimedia Timer to highest resolution. This
+ensures the resolution does not change while ntpd is running,
+avoiding timekeeping glitches associated with changes.
+.It Fl n , Fl \-nofork
+Do not fork.
+This option must not appear in combination with any of the following options:
+wait\-sync.
+.sp
+.It Fl N , Fl \-nice
+Run at high priority.
+.sp
+To the extent permitted by the operating system, run
+\fBntpd\fP
+at the highest priority.
+.It Fl p Ar string , Fl \-pidfile Ns = Ns Ar string
+path to the PID file.
+.sp
+Specify the name and path of the file used to record
+\fBntpd\fP's
+process ID.
+This is the same operation as the
+\fBpidfile\fP \fIpidfile\fP
+configuration file directive.
+.It Fl P Ar number , Fl \-priority Ns = Ns Ar number
+Process priority.
+This option takes an integer number as its argument.
+.sp
+To the extent permitted by the operating system, run
+\fBntpd\fP
+at the specified
+\fBsched_setscheduler(SCHED_FIFO)\fP
+priority.
+.It Fl q , Fl \-quit
+Set the time and quit.
+This option must not appear in combination with any of the following options:
+saveconfigquit, wait\-sync.
+.sp
+\fBntpd\fP
+will not daemonize and will exit after the clock is first
+synchronized. This behavior mimics that of the
+\fBntpdate\fP
+program, which will soon be replaced with a shell script.
+The
+\fB\-g\fP
+and
+\fB\-x\fP
+options can be used with this option.
+Note: The kernel time discipline is disabled with this option.
+.It Fl r Ar string , Fl \-propagationdelay Ns = Ns Ar string
+Broadcast/propagation delay.
+.sp
+Specify the default propagation delay from the broadcast/multicast server to this client. This is necessary only if the delay cannot be computed automatically by the protocol.
+.It Fl \-saveconfigquit Ns = Ns Ar string
+Save parsed configuration and quit.
+This option must not appear in combination with any of the following options:
+quit, wait\-sync.
+.sp
+Cause \fBntpd\fP to parse its startup configuration file and save an
+equivalent to the given filename and exit. This option was
+designed for automated testing.
+.It Fl s Ar string , Fl \-statsdir Ns = Ns Ar string
+Statistics file location.
+.sp
+Specify the directory path for files created by the statistics facility.
+This is the same operation as the
+\fBstatsdir\fP \fIstatsdir\fP
+configuration file directive.
+.It Fl t Ar tkey , Fl \-trustedkey Ns = Ns Ar tkey
+Trusted key number.
+This option may appear an unlimited number of times.
+.sp
+Add the specified key number to the trusted key list.
+.It Fl u Ar string , Fl \-user Ns = Ns Ar string
+Run as userid (or userid:groupid).
+.sp
+Specify a user, and optionally a group, to switch to.
+This option is only available if the OS supports adjusting the clock
+without full root privileges.
+This option is supported under NetBSD (configure with
+\fB\-\-enable\-clockctl\fP) or Linux (configure with
+\fB\-\-enable\-linuxcaps\fP) or Solaris (configure with \fB\-\-enable\-solarisprivs\fP).
+.It Fl U Ar number , Fl \-updateinterval Ns = Ns Ar number
+interval in seconds between scans for new or dropped interfaces.
+This option takes an integer number as its argument.
+.sp
+Give the time in seconds between two scans for new or dropped interfaces.
+For systems with routing socket support the scans will be performed shortly after the interface change
+has been detected by the system.
+Use 0 to disable scanning. 60 seconds is the minimum time between scans.
+.It Fl \-var Ns = Ns Ar nvar
+make ARG an ntp variable (RW).
+This option may appear an unlimited number of times.
+.sp
+.It Fl \-dvar Ns = Ns Ar ndvar
+make ARG an ntp variable (RW|DEF).
+This option may appear an unlimited number of times.
+.sp
+.It Fl w Ar number , Fl \-wait\-sync Ns = Ns Ar number
+Seconds to wait for first clock sync.
+This option must not appear in combination with any of the following options:
+nofork, quit, saveconfigquit.
+This option takes an integer number as its argument.
+.sp
+If greater than zero, alters \fBntpd\fP's behavior when forking to
+daemonize. Instead of exiting with status 0 immediately after
+the fork, the parent waits up to the specified number of
+seconds for the child to first synchronize the clock. The exit
+status is zero (success) if the clock was synchronized,
+otherwise it is \fBETIMEDOUT\fP.
+This provides the option for a script starting \fBntpd\fP to easily
+wait for the first set of the clock before proceeding.
+.It Fl x , Fl \-slew
+Slew up to 600 seconds.
+.sp
+Normally, the time is slewed if the offset is less than the step threshold, which is 128 ms by default, and stepped if above the threshold.
+This option sets the threshold to 600 s, which is well within the accuracy window to set the clock manually.
+Note: Since the slew rate of typical Unix kernels is limited to 0.5 ms/s, each second of adjustment requires an amortization interval of 2000 s.
+Thus, an adjustment as much as 600 s will take almost 14 days to complete.
+This option can be used with the
+\fB\-g\fP
+and
+\fB\-q\fP
+options.
+See the
+\fBtinker\fP
+configuration file directive for other options.
+Note: The kernel time discipline is disabled with this option.
+.It Fl \-usepcc
+Use CPU cycle counter (Windows only).
+.sp
+Attempt to substitute the CPU counter for \fBQueryPerformanceCounter\fP.
+The CPU counter and \fBQueryPerformanceCounter\fP are compared, and if
+they have the same frequency, the CPU counter (RDTSC on x86) is
+used directly, saving the overhead of a system call.
+.It Fl \-pccfreq Ns = Ns Ar string
+Force CPU cycle counter use (Windows only).
+.sp
+Force substitution the CPU counter for \fBQueryPerformanceCounter\fP.
+The CPU counter (RDTSC on x86) is used unconditionally with the
+given frequency (in Hz).
+.It Fl m , Fl \-mdns
+Register with mDNS as a NTP server.
+.sp
+Registers as an NTP server with the local mDNS server which allows
+the server to be discovered via mDNS client lookup.
+.It Fl \&? , Fl \-help
+Display usage information and exit.
+.It Fl \&! , Fl \-more\-help
+Pass the extended usage information through a pager.
+.It Fl \-version Op Brq Ar v|c|n
+Output version of program and exit. The default mode is `v', a simple
+version. The `c' mode will print copyright information and `n' will
+print the full copyright notice.
+.El
+.Sh "OPTION PRESETS"
+Any option that is not marked as \fInot presettable\fP may be preset
+by loading values from environment variables named:
+.nf
+ \fBNTPD_<option\-name>\fP or \fBNTPD\fP
+.fi
+.ad
+.Sh USAGE
+.Ss "How NTP Operates"
+The
+.Nm
+utility operates by exchanging messages with
+one or more configured servers over a range of designated poll intervals.
+When
+started, whether for the first or subsequent times, the program
+requires several exchanges from the majority of these servers so
+the signal processing and mitigation algorithms can accumulate and
+groom the data and set the clock.
+In order to protect the network
+from bursts, the initial poll interval for each server is delayed
+an interval randomized over a few seconds.
+At the default initial poll
+interval of 64s, several minutes can elapse before the clock is
+set.
+This initial delay to set the clock
+can be safely and dramatically reduced using the
+.Cm iburst
+keyword with the
+.Ic server
+configuration
+command, as described in
+.Xr ntp.conf 5 .
+.Pp
+Most operating systems and hardware of today incorporate a
+time\-of\-year (TOY) chip to maintain the time during periods when
+the power is off.
+When the machine is booted, the chip is used to
+initialize the operating system time.
+After the machine has
+synchronized to a NTP server, the operating system corrects the
+chip from time to time.
+In the default case, if
+.Nm
+detects that the time on the host
+is more than 1000s from the server time,
+.Nm
+assumes something must be terribly wrong and the only
+reliable action is for the operator to intervene and set the clock
+by hand.
+(Reasons for this include there is no TOY chip,
+or its battery is dead, or that the TOY chip is just of poor quality.)
+This causes
+.Nm
+to exit with a panic message to
+the system log.
+The
+.Fl g
+option overrides this check and the
+clock will be set to the server time regardless of the chip time
+(up to 68 years in the past or future \(em
+this is a limitation of the NTPv4 protocol).
+However, and to protect against broken hardware, such as when the
+CMOS battery fails or the clock counter becomes defective, once the
+clock has been set an error greater than 1000s will cause
+.Nm
+to exit anyway.
+.Pp
+Under ordinary conditions,
+.Nm
+adjusts the clock in
+small steps so that the timescale is effectively continuous and
+without discontinuities.
+Under conditions of extreme network
+congestion, the roundtrip delay jitter can exceed three seconds and
+the synchronization distance, which is equal to one\-half the
+roundtrip delay plus error budget terms, can become very large.
+The
+.Nm
+algorithms discard sample offsets exceeding 128 ms,
+unless the interval during which no sample offset is less than 128
+ms exceeds 900s.
+The first sample after that, no matter what the
+offset, steps the clock to the indicated time.
+In practice this
+reduces the false alarm rate where the clock is stepped in error to
+a vanishingly low incidence.
+.Pp
+As the result of this behavior, once the clock has been set it
+very rarely strays more than 128 ms even under extreme cases of
+network path congestion and jitter.
+Sometimes, in particular when
+.Nm
+is first started without a valid drift file
+on a system with a large intrinsic drift
+the error might grow to exceed 128 ms,
+which would cause the clock to be set backwards
+if the local clock time is more than 128 s
+in the future relative to the server.
+In some applications, this behavior may be unacceptable.
+There are several solutions, however.
+If the
+.Fl x
+option is included on the command line, the clock will
+never be stepped and only slew corrections will be used.
+But this choice comes with a cost that
+should be carefully explored before deciding to use
+the
+.Fl x
+option.
+The maximum slew rate possible is limited
+to 500 parts\-per\-million (PPM) as a consequence of the correctness
+principles on which the NTP protocol and algorithm design are
+based.
+As a result, the local clock can take a long time to
+converge to an acceptable offset, about 2,000 s for each second the
+clock is outside the acceptable range.
+During this interval the
+local clock will not be consistent with any other network clock and
+the system cannot be used for distributed applications that require
+correctly synchronized network time.
+.Pp
+In spite of the above precautions, sometimes when large
+frequency errors are present the resulting time offsets stray
+outside the 128\-ms range and an eventual step or slew time
+correction is required.
+If following such a correction the
+frequency error is so large that the first sample is outside the
+acceptable range,
+.Nm
+enters the same state as when the
+.Pa ntp.drift
+file is not present.
+The intent of this behavior
+is to quickly correct the frequency and restore operation to the
+normal tracking mode.
+In the most extreme cases
+(the host
+.Cm time.ien.it
+comes to mind), there may be occasional
+step/slew corrections and subsequent frequency corrections.
+It
+helps in these cases to use the
+.Cm burst
+keyword when
+configuring the server, but
+ONLY
+when you have permission to do so from the owner of the target host.
+.Pp
+Finally,
+in the past many startup scripts would run
+.Xr ntpdate 1ntpdatemdoc
+to get the system clock close to correct before starting
+.Xr ntpd 1ntpdmdoc ,
+but this was never more than a mediocre hack and is no longer needed.
+If you are following the instructions in
+.Sx "Starting NTP (Best Current Practice)"
+and you still need to set the system time before starting
+.Nm ,
+please open a bug report and document what is going on,
+and then look at using
+.Xr sntp 1sntpmdoc .
+.Pp
+There is a way to start
+.Xr ntpd 1ntpdmdoc
+that often addresses all of the problems mentioned above.
+.Ss "Starting NTP (Best Current Practice)"
+First, use the
+.Cm iburst
+option on your
+.Cm server
+entries.
+.Pp
+If you can also keep a good
+.Pa ntp.drift
+file then
+.Xr ntpd 1ntpdmdoc
+will effectively "warm\-start" and your system's clock will
+be stable in under 11 seconds' time.
+.Pp
+As soon as possible in the startup sequence, start
+.Xr ntpd 1ntpdmdoc
+with at least the
+.Fl g
+and perhaps the
+.Fl N
+options.
+Then,
+start the rest of your "normal" processes.
+This will give
+.Xr ntpd 1ntpdmdoc
+as much time as possible to get the system's clock synchronized and stable.
+.Pp
+Finally,
+if you have processes like
+.Cm dovecot
+or database servers
+that require
+monotonically\-increasing time,
+run
+.Xr ntp\-wait 1ntp\-waitmdoc
+as late as possible in the boot sequence
+(perhaps with the
+.Fl v
+flag)
+and after
+.Xr ntp\-wait 1ntp\-waitmdoc
+exits successfully
+it is as safe as it will ever be to start any process that require
+stable time.
+.Ss "Frequency Discipline"
+The
+.Nm
+behavior at startup depends on whether the
+frequency file, usually
+.Pa ntp.drift ,
+exists.
+This file
+contains the latest estimate of clock frequency error.
+When the
+.Nm
+is started and the file does not exist, the
+.Nm
+enters a special mode designed to quickly adapt to
+the particular system clock oscillator time and frequency error.
+This takes approximately 15 minutes, after which the time and
+frequency are set to nominal values and the
+.Nm
+enters
+normal mode, where the time and frequency are continuously tracked
+relative to the server.
+After one hour the frequency file is
+created and the current frequency offset written to it.
+When the
+.Nm
+is started and the file does exist, the
+.Nm
+frequency is initialized from the file and enters normal mode
+immediately.
+After that the current frequency offset is written to
+the file at hourly intervals.
+.Ss "Operating Modes"
+The
+.Nm
+utility can operate in any of several modes, including
+symmetric active/passive, client/server broadcast/multicast and
+manycast, as described in the
+.Qq Association Management
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+It normally operates continuously while
+monitoring for small changes in frequency and trimming the clock
+for the ultimate precision.
+However, it can operate in a one\-time
+mode where the time is set from an external server and frequency is
+set from a previously recorded frequency file.
+A
+broadcast/multicast or manycast client can discover remote servers,
+compute server\-client propagation delay correction factors and
+configure itself automatically.
+This makes it possible to deploy a
+fleet of workstations without specifying configuration details
+specific to the local environment.
+.Pp
+By default,
+.Nm
+runs in continuous mode where each of
+possibly several external servers is polled at intervals determined
+by an intricate state machine.
+The state machine measures the
+incidental roundtrip delay jitter and oscillator frequency wander
+and determines the best poll interval using a heuristic algorithm.
+Ordinarily, and in most operating environments, the state machine
+will start with 64s intervals and eventually increase in steps to
+1024s.
+A small amount of random variation is introduced in order to
+avoid bunching at the servers.
+In addition, should a server become
+unreachable for some time, the poll interval is increased in steps
+to 1024s in order to reduce network overhead.
+.Pp
+In some cases it may not be practical for
+.Nm
+to run continuously.
+A common workaround has been to run the
+.Xr ntpdate 1ntpdatemdoc
+or
+.Xr sntp 1sntpmdoc
+programs from a
+.Xr cron 8
+job at designated
+times.
+However, these programs do not have the crafted signal
+processing, error checking or mitigation algorithms of
+.Nm .
+The
+.Fl q
+option is intended for this purpose.
+Setting this option will cause
+.Nm
+to exit just after
+setting the clock for the first time.
+The procedure for initially
+setting the clock is the same as in continuous mode; most
+applications will probably want to specify the
+.Cm iburst
+keyword with the
+.Ic server
+configuration command.
+With this
+keyword a volley of messages are exchanged to groom the data and
+the clock is set in about 10 s.
+If nothing is heard after a
+couple of minutes, the daemon times out and exits.
+After a suitable
+period of mourning, the
+.Xr ntpdate 1ntpdatemdoc
+program will be
+retired.
+.Pp
+When kernel support is available to discipline the clock
+frequency, which is the case for stock Solaris, Tru64, Linux and
+.Fx ,
+a useful feature is available to discipline the clock
+frequency.
+First,
+.Nm
+is run in continuous mode with
+selected servers in order to measure and record the intrinsic clock
+frequency offset in the frequency file.
+It may take some hours for
+the frequency and offset to settle down.
+Then the
+.Nm
+is
+stopped and run in one\-time mode as required.
+At each startup, the
+frequency is read from the file and initializes the kernel
+frequency.
+.Ss "Poll Interval Control"
+This version of NTP includes an intricate state machine to
+reduce the network load while maintaining a quality of
+synchronization consistent with the observed jitter and wander.
+There are a number of ways to tailor the operation in order enhance
+accuracy by reducing the interval or to reduce network overhead by
+increasing it.
+However, the user is advised to carefully consider
+the consequences of changing the poll adjustment range from the
+default minimum of 64 s to the default maximum of 1,024 s.
+The
+default minimum can be changed with the
+.Ic tinker
+.Cm minpoll
+command to a value not less than 16 s.
+This value is used for all
+configured associations, unless overridden by the
+.Cm minpoll
+option on the configuration command.
+Note that most device drivers
+will not operate properly if the poll interval is less than 64 s
+and that the broadcast server and manycast client associations will
+also use the default, unless overridden.
+.Pp
+In some cases involving dial up or toll services, it may be
+useful to increase the minimum interval to a few tens of minutes
+and maximum interval to a day or so.
+Under normal operation
+conditions, once the clock discipline loop has stabilized the
+interval will be increased in steps from the minimum to the
+maximum.
+However, this assumes the intrinsic clock frequency error
+is small enough for the discipline loop correct it.
+The capture
+range of the loop is 500 PPM at an interval of 64s decreasing by a
+factor of two for each doubling of interval.
+At a minimum of 1,024
+s, for example, the capture range is only 31 PPM.
+If the intrinsic
+error is greater than this, the drift file
+.Pa ntp.drift
+will
+have to be specially tailored to reduce the residual error below
+this limit.
+Once this is done, the drift file is automatically
+updated once per hour and is available to initialize the frequency
+on subsequent daemon restarts.
+.Ss "The huff\-n'\-puff Filter"
+In scenarios where a considerable amount of data are to be
+downloaded or uploaded over telephone modems, timekeeping quality
+can be seriously degraded.
+This occurs because the differential
+delays on the two directions of transmission can be quite large.
+In
+many cases the apparent time errors are so large as to exceed the
+step threshold and a step correction can occur during and after the
+data transfer is in progress.
+.Pp
+The huff\-n'\-puff filter is designed to correct the apparent time
+offset in these cases.
+It depends on knowledge of the propagation
+delay when no other traffic is present.
+In common scenarios this
+occurs during other than work hours.
+The filter maintains a shift
+register that remembers the minimum delay over the most recent
+interval measured usually in hours.
+Under conditions of severe
+delay, the filter corrects the apparent offset using the sign of
+the offset and the difference between the apparent delay and
+minimum delay.
+The name of the filter reflects the negative (huff)
+and positive (puff) correction, which depends on the sign of the
+offset.
+.Pp
+The filter is activated by the
+.Ic tinker
+command and
+.Cm huffpuff
+keyword, as described in
+.Xr ntp.conf 5 .
+.Sh "ENVIRONMENT"
+See \fBOPTION PRESETS\fP for configuration environment variables.
+.Sh FILES
+.Bl -tag -width /etc/ntp.drift -compact
+.It Pa /etc/ntp.conf
+the default name of the configuration file
+.It Pa /etc/ntp.drift
+the default name of the drift file
+.It Pa /etc/ntp.keys
+the default name of the key file
+.El
+.Sh "EXIT STATUS"
+One of the following exit values will be returned:
+.Bl -tag
+.It 0 " (EXIT_SUCCESS)"
+Successful program execution.
+.It 1 " (EXIT_FAILURE)"
+The operation failed or the command syntax was not valid.
+.It 70 " (EX_SOFTWARE)"
+libopts had an internal operational error. Please report
+it to autogen\-users@lists.sourceforge.net. Thank you.
+.El
+.Sh "SEE ALSO"
+.Xr ntp.conf 5 ,
+.Xr ntpdate 1ntpdatemdoc ,
+.Xr ntpdc 1ntpdcmdoc ,
+.Xr ntpq 1ntpqmdoc ,
+.Xr sntp 1sntpmdoc
+.Pp
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+.Li http://www.ntp.org/ .
+A snapshot of this documentation is available in HTML format in
+.Pa /usr/share/doc/ntp .
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 1)
+.%O RFC1059
+.Re
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 2)
+.%O RFC1119
+.Re
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 3)
+.%O RFC1305
+.Re
+.Rs
+.%A David L. Mills
+.%A J. Martin, Ed.
+.%A J. Burbank
+.%A W. Kasch
+.%T Network Time Protocol Version 4: Protocol and Algorithms Specification
+.%O RFC5905
+.Re
+.Rs
+.%A David L. Mills
+.%A B. Haberman, Ed.
+.%T Network Time Protocol Version 4: Autokey Specification
+.%O RFC5906
+.Re
+.Rs
+.%A H. Gerstung
+.%A C. Elliott
+.%A B. Haberman, Ed.
+.%T Definitions of Managed Objects for Network Time Protocol Version 4: (NTPv4)
+.%O RFC5907
+.Re
+.Rs
+.%A R. Gayraud
+.%A B. Lourdelet
+.%T Network Time Protocol (NTP) Server Option for DHCPv6
+.%O RFC5908
+.Re
+.Sh "AUTHORS"
+The University of Delaware
+.Sh "COPYRIGHT"
+Copyright (C) 1970\-2014 The University of Delaware all rights reserved.
+This program is released under the terms of the NTP license, <http://ntp.org/license>.
+.Sh BUGS
+The
+.Nm
+utility has gotten rather fat.
+While not huge, it has gotten
+larger than might be desirable for an elevated\-priority
+.Nm
+running on a workstation, particularly since many of
+the fancy features which consume the space were designed more with
+a busy primary server, rather than a high stratum workstation in
+mind.
+.Pp
+Please send bug reports to: http://bugs.ntp.org, bugs@ntp.org
+.Sh NOTES
+Portions of this document came from FreeBSD.
+.Pp
+This manual page was \fIAutoGen\fP\-erated from the \fBntpd\fP
+option definitions.
diff --git a/ntpd/ntpd.c b/ntpd/ntpd.c
new file mode 100644
index 0000000..ef1baa7
--- /dev/null
+++ b/ntpd/ntpd.c
@@ -0,0 +1,1478 @@
+/*
+ * ntpd.c - main program for the fixed point NTP daemon
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntp_machine.h"
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+#include <ntp_random.h>
+
+#include "ntp_config.h"
+#include "ntp_syslog.h"
+#include "ntp_assert.h"
+#include "isc/error.h"
+#include "isc/strerror.h"
+#include "isc/formatcheck.h"
+#include "iosignal.h"
+
+#ifdef SIM
+# include "ntpsim.h"
+#endif
+
+#include "ntp_libopts.h"
+#include "ntpd-opts.h"
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#else
+# include <signal.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+#if defined(HAVE_RTPRIO)
+# ifdef HAVE_SYS_LOCK_H
+# include <sys/lock.h>
+# endif
+# include <sys/rtprio.h>
+#else
+# ifdef HAVE_PLOCK
+# ifdef HAVE_SYS_LOCK_H
+# include <sys/lock.h>
+# endif
+# endif
+#endif
+#if defined(HAVE_SCHED_SETSCHEDULER)
+# ifdef HAVE_SCHED_H
+# include <sched.h>
+# else
+# ifdef HAVE_SYS_SCHED_H
+# include <sys/sched.h>
+# endif
+# endif
+#endif
+#if defined(HAVE_SYS_MMAN_H)
+# include <sys/mman.h>
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#endif
+
+#ifdef SYS_DOMAINOS
+# include <apollo/base.h>
+#endif /* SYS_DOMAINOS */
+
+
+#include "recvbuff.h"
+#include "ntp_cmdargs.h"
+
+#if 0 /* HMS: I don't think we need this. 961223 */
+#ifdef LOCK_PROCESS
+# ifdef SYS_SOLARIS
+# include <sys/mman.h>
+# else
+# include <sys/lock.h>
+# endif
+#endif
+#endif
+
+#ifdef _AIX
+# include <ulimit.h>
+#endif /* _AIX */
+
+#ifdef SCO5_CLOCK
+# include <sys/ci/ciioctl.h>
+#endif
+
+#ifdef HAVE_DROPROOT
+# include <ctype.h>
+# include <grp.h>
+# include <pwd.h>
+#ifdef HAVE_LINUX_CAPABILITIES
+# include <sys/capability.h>
+# include <sys/prctl.h>
+#endif /* HAVE_LINUX_CAPABILITIES */
+#if defined(HAVE_PRIV_H) && defined(HAVE_SOLARIS_PRIVS)
+# include <priv.h>
+#endif /* HAVE_PRIV_H */
+#endif /* HAVE_DROPROOT */
+
+#if defined (LIBSECCOMP) && (KERN_SECCOMP)
+/* # include <sys/types.h> */
+# include <sys/resource.h>
+# include <seccomp.h>
+#endif /* LIBSECCOMP and KERN_SECCOMP */
+
+#ifdef HAVE_DNSREGISTRATION
+# include <dns_sd.h>
+DNSServiceRef mdns;
+#endif
+
+#ifdef HAVE_SETPGRP_0
+# define ntp_setpgrp(x, y) setpgrp()
+#else
+# define ntp_setpgrp(x, y) setpgrp(x, y)
+#endif
+
+#ifdef HAVE_SOLARIS_PRIVS
+# define LOWPRIVS "basic,sys_time,net_privaddr,proc_setid,!proc_info,!proc_session,!proc_exec"
+static priv_set_t *lowprivs = NULL;
+static priv_set_t *highprivs = NULL;
+#endif /* HAVE_SOLARIS_PRIVS */
+/*
+ * Scheduling priority we run at
+ */
+#define NTPD_PRIO (-12)
+
+int priority_done = 2; /* 0 - Set priority */
+ /* 1 - priority is OK where it is */
+ /* 2 - Don't set priority */
+ /* 1 and 2 are pretty much the same */
+
+int listen_to_virtual_ips = TRUE;
+
+/*
+ * No-fork flag. If set, we do not become a background daemon.
+ */
+int nofork; /* Fork by default */
+
+#ifdef HAVE_DNSREGISTRATION
+/*
+ * mDNS registration flag. If set, we attempt to register with the mDNS system, but only
+ * after we have synched the first time. If the attempt fails, then try again once per
+ * minute for up to 5 times. After all, we may be starting before mDNS.
+ */
+int mdnsreg = FALSE;
+int mdnstries = 5;
+#endif /* HAVE_DNSREGISTRATION */
+
+#ifdef HAVE_DROPROOT
+int droproot;
+int root_dropped;
+char *user; /* User to switch to */
+char *group; /* group to switch to */
+const char *chrootdir; /* directory to chroot to */
+int sw_uid;
+int sw_gid;
+char *endp;
+struct group *gr;
+struct passwd *pw;
+#endif /* HAVE_DROPROOT */
+
+#ifdef HAVE_WORKING_FORK
+int waitsync_fd_to_close = -1; /* -w/--wait-sync */
+#endif
+
+/*
+ * Initializing flag. All async routines watch this and only do their
+ * thing when it is clear.
+ */
+int initializing;
+
+/*
+ * Version declaration
+ */
+extern const char *Version;
+
+char const *progname;
+
+int was_alarmed;
+
+#ifdef DECL_SYSCALL
+/*
+ * We put this here, since the argument profile is syscall-specific
+ */
+extern int syscall (int, ...);
+#endif /* DECL_SYSCALL */
+
+
+#if !defined(SIM) && defined(SIGDIE1)
+static RETSIGTYPE finish (int);
+#endif
+
+#if !defined(SIM) && defined(HAVE_WORKING_FORK)
+static int wait_child_sync_if (int, long);
+#endif
+
+#if !defined(SIM) && !defined(SYS_WINNT)
+# ifdef DEBUG
+static RETSIGTYPE moredebug (int);
+static RETSIGTYPE lessdebug (int);
+# else /* !DEBUG follows */
+static RETSIGTYPE no_debug (int);
+# endif /* !DEBUG */
+#endif /* !SIM && !SYS_WINNT */
+
+int saved_argc;
+char ** saved_argv;
+
+#ifndef SIM
+int ntpdmain (int, char **);
+static void set_process_priority (void);
+static void assertion_failed (const char *, int,
+ isc_assertiontype_t,
+ const char *)
+ __attribute__ ((__noreturn__));
+static void library_fatal_error (const char *, int,
+ const char *, va_list)
+ ISC_FORMAT_PRINTF(3, 0);
+static void library_unexpected_error(const char *, int,
+ const char *, va_list)
+ ISC_FORMAT_PRINTF(3, 0);
+#endif /* !SIM */
+
+
+
+
+void
+parse_cmdline_opts(
+ int * pargc,
+ char ***pargv
+ )
+{
+ static int parsed;
+ static int optct;
+
+ if (!parsed)
+ optct = ntpOptionProcess(&ntpdOptions, *pargc, *pargv);
+
+ parsed = 1;
+
+ *pargc -= optct;
+ *pargv += optct;
+}
+
+
+#ifdef SIM
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ progname = argv[0];
+ parse_cmdline_opts(&argc, &argv);
+#ifdef DEBUG
+ debug = OPT_VALUE_SET_DEBUG_LEVEL;
+ DPRINTF(1, ("%s\n", Version));
+#endif
+
+ return ntpsim(argc, argv);
+}
+#else /* !SIM follows */
+#ifdef NO_MAIN_ALLOWED
+CALL(ntpd,"ntpd",ntpdmain);
+#else /* !NO_MAIN_ALLOWED follows */
+#ifndef SYS_WINNT
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ return ntpdmain(argc, argv);
+}
+#endif /* !SYS_WINNT */
+#endif /* !NO_MAIN_ALLOWED */
+#endif /* !SIM */
+
+#ifdef _AIX
+/*
+ * OK. AIX is different than solaris in how it implements plock().
+ * If you do NOT adjust the stack limit, you will get the MAXIMUM
+ * stack size allocated and PINNED with you program. To check the
+ * value, use ulimit -a.
+ *
+ * To fix this, we create an automatic variable and set our stack limit
+ * to that PLUS 32KB of extra space (we need some headroom).
+ *
+ * This subroutine gets the stack address.
+ *
+ * Grover Davidson and Matt Ladendorf
+ *
+ */
+static char *
+get_aix_stack(void)
+{
+ char ch;
+ return (&ch);
+}
+
+/*
+ * Signal handler for SIGDANGER.
+ */
+static void
+catch_danger(int signo)
+{
+ msyslog(LOG_INFO, "ntpd: setpgid(): %m");
+ /* Make the system believe we'll free something, but don't do it! */
+ return;
+}
+#endif /* _AIX */
+
+/*
+ * Set the process priority
+ */
+#ifndef SIM
+static void
+set_process_priority(void)
+{
+
+# ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_DEBUG, "set_process_priority: %s: priority_done is <%d>",
+ ((priority_done)
+ ? "Leave priority alone"
+ : "Attempt to set priority"
+ ),
+ priority_done);
+# endif /* DEBUG */
+
+# if defined(HAVE_SCHED_SETSCHEDULER)
+ if (!priority_done) {
+ extern int config_priority_override, config_priority;
+ int pmax, pmin;
+ struct sched_param sched;
+
+ pmax = sched_get_priority_max(SCHED_FIFO);
+ sched.sched_priority = pmax;
+ if ( config_priority_override ) {
+ pmin = sched_get_priority_min(SCHED_FIFO);
+ if ( config_priority > pmax )
+ sched.sched_priority = pmax;
+ else if ( config_priority < pmin )
+ sched.sched_priority = pmin;
+ else
+ sched.sched_priority = config_priority;
+ }
+ if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 )
+ msyslog(LOG_ERR, "sched_setscheduler(): %m");
+ else
+ ++priority_done;
+ }
+# endif /* HAVE_SCHED_SETSCHEDULER */
+# ifdef HAVE_RTPRIO
+# ifdef RTP_SET
+ if (!priority_done) {
+ struct rtprio srtp;
+
+ srtp.type = RTP_PRIO_REALTIME; /* was: RTP_PRIO_NORMAL */
+ srtp.prio = 0; /* 0 (hi) -> RTP_PRIO_MAX (31,lo) */
+
+ if (rtprio(RTP_SET, getpid(), &srtp) < 0)
+ msyslog(LOG_ERR, "rtprio() error: %m");
+ else
+ ++priority_done;
+ }
+# else /* !RTP_SET follows */
+ if (!priority_done) {
+ if (rtprio(0, 120) < 0)
+ msyslog(LOG_ERR, "rtprio() error: %m");
+ else
+ ++priority_done;
+ }
+# endif /* !RTP_SET */
+# endif /* HAVE_RTPRIO */
+# if defined(NTPD_PRIO) && NTPD_PRIO != 0
+# ifdef HAVE_ATT_NICE
+ if (!priority_done) {
+ errno = 0;
+ if (-1 == nice (NTPD_PRIO) && errno != 0)
+ msyslog(LOG_ERR, "nice() error: %m");
+ else
+ ++priority_done;
+ }
+# endif /* HAVE_ATT_NICE */
+# ifdef HAVE_BSD_NICE
+ if (!priority_done) {
+ if (-1 == setpriority(PRIO_PROCESS, 0, NTPD_PRIO))
+ msyslog(LOG_ERR, "setpriority() error: %m");
+ else
+ ++priority_done;
+ }
+# endif /* HAVE_BSD_NICE */
+# endif /* NTPD_PRIO && NTPD_PRIO != 0 */
+ if (!priority_done)
+ msyslog(LOG_ERR, "set_process_priority: No way found to improve our priority");
+}
+#endif /* !SIM */
+
+
+/*
+ * Main program. Initialize us, disconnect us from the tty if necessary,
+ * and loop waiting for I/O and/or timer expiries.
+ */
+#ifndef SIM
+int
+ntpdmain(
+ int argc,
+ char *argv[]
+ )
+{
+ l_fp now;
+ struct recvbuf *rbuf;
+ const char * logfilename;
+# ifdef HAVE_UMASK
+ mode_t uv;
+# endif
+# if defined(HAVE_GETUID) && !defined(MPE) /* MPE lacks the concept of root */
+ uid_t uid;
+# endif
+# if defined(HAVE_WORKING_FORK)
+ long wait_sync = 0;
+ int pipe_fds[2];
+ int rc;
+ int exit_code;
+# ifdef _AIX
+ struct sigaction sa;
+# endif
+# if !defined(HAVE_SETSID) && !defined (HAVE_SETPGID) && defined(TIOCNOTTY)
+ int fid;
+# endif
+# endif /* HAVE_WORKING_FORK*/
+# ifdef SCO5_CLOCK
+ int fd;
+ int zero;
+# endif
+
+# ifdef HAVE_UMASK
+ uv = umask(0);
+ if (uv)
+ umask(uv);
+ else
+ umask(022);
+# endif
+ saved_argc = argc;
+ saved_argv = argv;
+ progname = argv[0];
+ initializing = TRUE; /* mark that we are initializing */
+ parse_cmdline_opts(&argc, &argv);
+# ifdef DEBUG
+ debug = OPT_VALUE_SET_DEBUG_LEVEL;
+# ifdef HAVE_SETLINEBUF
+ setlinebuf(stdout);
+# endif
+# endif
+
+ if (HAVE_OPT(NOFORK) || HAVE_OPT(QUIT)
+# ifdef DEBUG
+ || debug
+# endif
+ || HAVE_OPT(SAVECONFIGQUIT))
+ nofork = TRUE;
+
+ init_logging(progname, NLOG_SYNCMASK, TRUE);
+ /* honor -l/--logfile option to log to a file */
+ if (HAVE_OPT(LOGFILE)) {
+ logfilename = OPT_ARG(LOGFILE);
+ syslogit = FALSE;
+ change_logfile(logfilename, FALSE);
+ } else {
+ logfilename = NULL;
+ if (nofork)
+ msyslog_term = TRUE;
+ if (HAVE_OPT(SAVECONFIGQUIT))
+ syslogit = FALSE;
+ }
+ msyslog(LOG_NOTICE, "%s: Starting", Version);
+
+ {
+ int i;
+ char buf[1024]; /* Secret knowledge of msyslog buf length */
+ char *cp = buf;
+
+ /* Note that every arg has an initial space character */
+ snprintf(cp, sizeof(buf), "Command line:");
+ cp += strlen(cp);
+
+ for (i = 0; i < saved_argc ; ++i) {
+ snprintf(cp, sizeof(buf) - (cp - buf),
+ " %s", saved_argv[i]);
+ cp += strlen(cp);
+ }
+ msyslog(LOG_INFO, "%s", buf);
+ }
+
+ /*
+ * Install trap handlers to log errors and assertion failures.
+ * Default handlers print to stderr which doesn't work if detached.
+ */
+ isc_assertion_setcallback(assertion_failed);
+ isc_error_setfatal(library_fatal_error);
+ isc_error_setunexpected(library_unexpected_error);
+
+ /* MPE lacks the concept of root */
+# if defined(HAVE_GETUID) && !defined(MPE)
+ uid = getuid();
+ if (uid && !HAVE_OPT( SAVECONFIGQUIT )) {
+ msyslog_term = TRUE;
+ msyslog(LOG_ERR,
+ "must be run as root, not uid %ld", (long)uid);
+ exit(1);
+ }
+# endif
+
+/*
+ * Enable the Multi-Media Timer for Windows?
+ */
+# ifdef SYS_WINNT
+ if (HAVE_OPT( MODIFYMMTIMER ))
+ set_mm_timer(MM_TIMER_HIRES);
+# endif
+
+#ifdef HAVE_DNSREGISTRATION
+/*
+ * Enable mDNS registrations?
+ */
+ if (HAVE_OPT( MDNS )) {
+ mdnsreg = TRUE;
+ }
+#endif /* HAVE_DNSREGISTRATION */
+
+ if (HAVE_OPT( NOVIRTUALIPS ))
+ listen_to_virtual_ips = 0;
+
+ /*
+ * --interface, listen on specified interfaces
+ */
+ if (HAVE_OPT( INTERFACE )) {
+ int ifacect = STACKCT_OPT( INTERFACE );
+ const char** ifaces = STACKLST_OPT( INTERFACE );
+ sockaddr_u addr;
+
+ while (ifacect-- > 0) {
+ add_nic_rule(
+ is_ip_address(*ifaces, AF_UNSPEC, &addr)
+ ? MATCH_IFADDR
+ : MATCH_IFNAME,
+ *ifaces, -1, ACTION_LISTEN);
+ ifaces++;
+ }
+ }
+
+ if (HAVE_OPT( NICE ))
+ priority_done = 0;
+
+# ifdef HAVE_SCHED_SETSCHEDULER
+ if (HAVE_OPT( PRIORITY )) {
+ config_priority = OPT_VALUE_PRIORITY;
+ config_priority_override = 1;
+ priority_done = 0;
+ }
+# endif
+
+# ifdef HAVE_WORKING_FORK
+ do { /* 'loop' once */
+ if (!HAVE_OPT( WAIT_SYNC ))
+ break;
+ wait_sync = OPT_VALUE_WAIT_SYNC;
+ if (wait_sync <= 0) {
+ wait_sync = 0;
+ break;
+ }
+ /* -w requires a fork() even with debug > 0 */
+ nofork = FALSE;
+ if (pipe(pipe_fds)) {
+ exit_code = (errno) ? errno : -1;
+ msyslog(LOG_ERR,
+ "Pipe creation failed for --wait-sync: %m");
+ exit(exit_code);
+ }
+ waitsync_fd_to_close = pipe_fds[1];
+ } while (0); /* 'loop' once */
+# endif /* HAVE_WORKING_FORK */
+
+ init_lib();
+# ifdef SYS_WINNT
+ /*
+ * Start interpolation thread, must occur before first
+ * get_systime()
+ */
+ init_winnt_time();
+# endif
+ /*
+ * Initialize random generator and public key pair
+ */
+ get_systime(&now);
+
+ ntp_srandom((int)(now.l_i * now.l_uf));
+
+ /*
+ * Detach us from the terminal. May need an #ifndef GIZMO.
+ */
+ if (!nofork) {
+
+# ifdef HAVE_WORKING_FORK
+ rc = fork();
+ if (-1 == rc) {
+ exit_code = (errno) ? errno : -1;
+ msyslog(LOG_ERR, "fork: %m");
+ exit(exit_code);
+ }
+ if (rc > 0) {
+ /* parent */
+ exit_code = wait_child_sync_if(pipe_fds[0],
+ wait_sync);
+ exit(exit_code);
+ }
+
+ /*
+ * child/daemon
+ * close all open files excepting waitsync_fd_to_close.
+ * msyslog() unreliable until after init_logging().
+ */
+ closelog();
+ if (syslog_file != NULL) {
+ fclose(syslog_file);
+ syslog_file = NULL;
+ syslogit = TRUE;
+ }
+ close_all_except(waitsync_fd_to_close);
+ INSIST(0 == open("/dev/null", 0) && 1 == dup2(0, 1) \
+ && 2 == dup2(0, 2));
+
+ init_logging(progname, 0, TRUE);
+ /* we lost our logfile (if any) daemonizing */
+ setup_logfile(logfilename);
+
+# ifdef SYS_DOMAINOS
+ {
+ uid_$t puid;
+ status_$t st;
+
+ proc2_$who_am_i(&puid);
+ proc2_$make_server(&puid, &st);
+ }
+# endif /* SYS_DOMAINOS */
+# ifdef HAVE_SETSID
+ if (setsid() == (pid_t)-1)
+ msyslog(LOG_ERR, "setsid(): %m");
+# elif defined(HAVE_SETPGID)
+ if (setpgid(0, 0) == -1)
+ msyslog(LOG_ERR, "setpgid(): %m");
+# else /* !HAVE_SETSID && !HAVE_SETPGID follows */
+# ifdef TIOCNOTTY
+ fid = open("/dev/tty", 2);
+ if (fid >= 0) {
+ ioctl(fid, (u_long)TIOCNOTTY, NULL);
+ close(fid);
+ }
+# endif /* TIOCNOTTY */
+ ntp_setpgrp(0, getpid());
+# endif /* !HAVE_SETSID && !HAVE_SETPGID */
+# ifdef _AIX
+ /* Don't get killed by low-on-memory signal. */
+ sa.sa_handler = catch_danger;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGDANGER, &sa, NULL);
+# endif /* _AIX */
+# endif /* HAVE_WORKING_FORK */
+ }
+
+# ifdef SCO5_CLOCK
+ /*
+ * SCO OpenServer's system clock offers much more precise timekeeping
+ * on the base CPU than the other CPUs (for multiprocessor systems),
+ * so we must lock to the base CPU.
+ */
+ fd = open("/dev/at1", O_RDONLY);
+ if (fd >= 0) {
+ zero = 0;
+ if (ioctl(fd, ACPU_LOCK, &zero) < 0)
+ msyslog(LOG_ERR, "cannot lock to base CPU: %m");
+ close(fd);
+ }
+# endif
+
+ /* Setup stack size in preparation for locking pages in memory. */
+# if defined(HAVE_MLOCKALL)
+# ifdef HAVE_SETRLIMIT
+ ntp_rlimit(RLIMIT_STACK, DFLT_RLIMIT_STACK * 4096, 4096, "4k");
+# ifdef RLIMIT_MEMLOCK
+ /*
+ * The default RLIMIT_MEMLOCK is very low on Linux systems.
+ * Unless we increase this limit malloc calls are likely to
+ * fail if we drop root privilege. To be useful the value
+ * has to be larger than the largest ntpd resident set size.
+ */
+ ntp_rlimit(RLIMIT_MEMLOCK, DFLT_RLIMIT_MEMLOCK * 1024 * 1024, 1024 * 1024, "MB");
+# endif /* RLIMIT_MEMLOCK */
+# endif /* HAVE_SETRLIMIT */
+# else /* !HAVE_MLOCKALL follows */
+# ifdef HAVE_PLOCK
+# ifdef PROCLOCK
+# ifdef _AIX
+ /*
+ * set the stack limit for AIX for plock().
+ * see get_aix_stack() for more info.
+ */
+ if (ulimit(SET_STACKLIM, (get_aix_stack() - 8 * 4096)) < 0)
+ msyslog(LOG_ERR,
+ "Cannot adjust stack limit for plock: %m");
+# endif /* _AIX */
+# endif /* PROCLOCK */
+# endif /* HAVE_PLOCK */
+# endif /* !HAVE_MLOCKALL */
+
+ /*
+ * Set up signals we pay attention to locally.
+ */
+# ifdef SIGDIE1
+ signal_no_reset(SIGDIE1, finish);
+ signal_no_reset(SIGDIE2, finish);
+ signal_no_reset(SIGDIE3, finish);
+ signal_no_reset(SIGDIE4, finish);
+# endif
+# ifdef SIGBUS
+ signal_no_reset(SIGBUS, finish);
+# endif
+
+# if !defined(SYS_WINNT) && !defined(VMS)
+# ifdef DEBUG
+ (void) signal_no_reset(MOREDEBUGSIG, moredebug);
+ (void) signal_no_reset(LESSDEBUGSIG, lessdebug);
+# else
+ (void) signal_no_reset(MOREDEBUGSIG, no_debug);
+ (void) signal_no_reset(LESSDEBUGSIG, no_debug);
+# endif /* DEBUG */
+# endif /* !SYS_WINNT && !VMS */
+
+ /*
+ * Set up signals we should never pay attention to.
+ */
+# ifdef SIGPIPE
+ signal_no_reset(SIGPIPE, SIG_IGN);
+# endif
+
+ /*
+ * Call the init_ routines to initialize the data structures.
+ *
+ * Exactly what command-line options are we expecting here?
+ */
+ INIT_SSL();
+ init_auth();
+ init_util();
+ init_restrict();
+ init_mon();
+ init_timer();
+ init_request();
+ init_control();
+ init_peer();
+# ifdef REFCLOCK
+ init_refclock();
+# endif
+ set_process_priority();
+ init_proto(); /* Call at high priority */
+ init_io();
+ init_loopfilter();
+ mon_start(MON_ON); /* monitor on by default now */
+ /* turn off in config if unwanted */
+
+ /*
+ * Get the configuration. This is done in a separate module
+ * since this will definitely be different for the gizmo board.
+ */
+ getconfig(argc, argv);
+
+ if (do_memlock) {
+# if defined(HAVE_MLOCKALL)
+ /*
+ * lock the process into memory
+ */
+ if (!HAVE_OPT(SAVECONFIGQUIT) &&
+ 0 != mlockall(MCL_CURRENT|MCL_FUTURE))
+ msyslog(LOG_ERR, "mlockall(): %m");
+# else /* !HAVE_MLOCKALL follows */
+# ifdef HAVE_PLOCK
+# ifdef PROCLOCK
+ /*
+ * lock the process into memory
+ */
+ if (!HAVE_OPT(SAVECONFIGQUIT) && 0 != plock(PROCLOCK))
+ msyslog(LOG_ERR, "plock(PROCLOCK): %m");
+# else /* !PROCLOCK follows */
+# ifdef TXTLOCK
+ /*
+ * Lock text into ram
+ */
+ if (!HAVE_OPT(SAVECONFIGQUIT) && 0 != plock(TXTLOCK))
+ msyslog(LOG_ERR, "plock(TXTLOCK) error: %m");
+# else /* !TXTLOCK follows */
+ msyslog(LOG_ERR, "plock() - don't know what to lock!");
+# endif /* !TXTLOCK */
+# endif /* !PROCLOCK */
+# endif /* HAVE_PLOCK */
+# endif /* !HAVE_MLOCKALL */
+ }
+
+ loop_config(LOOP_DRIFTINIT, 0);
+ report_event(EVNT_SYSRESTART, NULL, NULL);
+ initializing = FALSE;
+
+# ifdef HAVE_DROPROOT
+ if (droproot) {
+ /* Drop super-user privileges and chroot now if the OS supports this */
+
+# ifdef HAVE_LINUX_CAPABILITIES
+ /* set flag: keep privileges accross setuid() call (we only really need cap_sys_time): */
+ if (prctl( PR_SET_KEEPCAPS, 1L, 0L, 0L, 0L ) == -1) {
+ msyslog( LOG_ERR, "prctl( PR_SET_KEEPCAPS, 1L ) failed: %m" );
+ exit(-1);
+ }
+# elif HAVE_SOLARIS_PRIVS
+ /* Nothing to do here */
+# else
+ /* we need a user to switch to */
+ if (user == NULL) {
+ msyslog(LOG_ERR, "Need user name to drop root privileges (see -u flag!)" );
+ exit(-1);
+ }
+# endif /* HAVE_LINUX_CAPABILITIES || HAVE_SOLARIS_PRIVS */
+
+ if (user != NULL) {
+ if (isdigit((unsigned char)*user)) {
+ sw_uid = (uid_t)strtoul(user, &endp, 0);
+ if (*endp != '\0')
+ goto getuser;
+
+ if ((pw = getpwuid(sw_uid)) != NULL) {
+ free(user);
+ user = estrdup(pw->pw_name);
+ sw_gid = pw->pw_gid;
+ } else {
+ errno = 0;
+ msyslog(LOG_ERR, "Cannot find user ID %s", user);
+ exit (-1);
+ }
+
+ } else {
+getuser:
+ errno = 0;
+ if ((pw = getpwnam(user)) != NULL) {
+ sw_uid = pw->pw_uid;
+ sw_gid = pw->pw_gid;
+ } else {
+ if (errno)
+ msyslog(LOG_ERR, "getpwnam(%s) failed: %m", user);
+ else
+ msyslog(LOG_ERR, "Cannot find user `%s'", user);
+ exit (-1);
+ }
+ }
+ }
+ if (group != NULL) {
+ if (isdigit((unsigned char)*group)) {
+ sw_gid = (gid_t)strtoul(group, &endp, 0);
+ if (*endp != '\0')
+ goto getgroup;
+ } else {
+getgroup:
+ if ((gr = getgrnam(group)) != NULL) {
+ sw_gid = gr->gr_gid;
+ } else {
+ errno = 0;
+ msyslog(LOG_ERR, "Cannot find group `%s'", group);
+ exit (-1);
+ }
+ }
+ }
+
+ if (chrootdir ) {
+ /* make sure cwd is inside the jail: */
+ if (chdir(chrootdir)) {
+ msyslog(LOG_ERR, "Cannot chdir() to `%s': %m", chrootdir);
+ exit (-1);
+ }
+ if (chroot(chrootdir)) {
+ msyslog(LOG_ERR, "Cannot chroot() to `%s': %m", chrootdir);
+ exit (-1);
+ }
+ if (chdir("/")) {
+ msyslog(LOG_ERR, "Cannot chdir() to`root after chroot(): %m");
+ exit (-1);
+ }
+ }
+# ifdef HAVE_SOLARIS_PRIVS
+ if ((lowprivs = priv_str_to_set(LOWPRIVS, ",", NULL)) == NULL) {
+ msyslog(LOG_ERR, "priv_str_to_set() failed:%m");
+ exit(-1);
+ }
+ if ((highprivs = priv_allocset()) == NULL) {
+ msyslog(LOG_ERR, "priv_allocset() failed:%m");
+ exit(-1);
+ }
+ (void) getppriv(PRIV_PERMITTED, highprivs);
+ (void) priv_intersect(highprivs, lowprivs);
+ if (setppriv(PRIV_SET, PRIV_PERMITTED, lowprivs) == -1) {
+ msyslog(LOG_ERR, "setppriv() failed:%m");
+ exit(-1);
+ }
+# endif /* HAVE_SOLARIS_PRIVS */
+ if (user && initgroups(user, sw_gid)) {
+ msyslog(LOG_ERR, "Cannot initgroups() to user `%s': %m", user);
+ exit (-1);
+ }
+ if (group && setgid(sw_gid)) {
+ msyslog(LOG_ERR, "Cannot setgid() to group `%s': %m", group);
+ exit (-1);
+ }
+ if (group && setegid(sw_gid)) {
+ msyslog(LOG_ERR, "Cannot setegid() to group `%s': %m", group);
+ exit (-1);
+ }
+ if (user && setuid(sw_uid)) {
+ msyslog(LOG_ERR, "Cannot setuid() to user `%s': %m", user);
+ exit (-1);
+ }
+ if (user && seteuid(sw_uid)) {
+ msyslog(LOG_ERR, "Cannot seteuid() to user `%s': %m", user);
+ exit (-1);
+ }
+
+# if !defined(HAVE_LINUX_CAPABILITIES) && !defined(HAVE_SOLARIS_PRIVS)
+ /*
+ * for now assume that the privilege to bind to privileged ports
+ * is associated with running with uid 0 - should be refined on
+ * ports that allow binding to NTP_PORT with uid != 0
+ */
+ disable_dynamic_updates |= (sw_uid != 0); /* also notifies routing message listener */
+# endif /* !HAVE_LINUX_CAPABILITIES && !HAVE_SOLARIS_PRIVS */
+
+ if (disable_dynamic_updates && interface_interval) {
+ interface_interval = 0;
+ msyslog(LOG_INFO, "running as non-root disables dynamic interface tracking");
+ }
+
+# ifdef HAVE_LINUX_CAPABILITIES
+ {
+ /*
+ * We may be running under non-root uid now, but we still hold full root privileges!
+ * We drop all of them, except for the crucial one or two: cap_sys_time and
+ * cap_net_bind_service if doing dynamic interface tracking.
+ */
+ cap_t caps;
+ char *captext;
+
+ captext = (0 != interface_interval)
+ ? "cap_sys_time,cap_net_bind_service=pe"
+ : "cap_sys_time=pe";
+ caps = cap_from_text(captext);
+ if (!caps) {
+ msyslog(LOG_ERR,
+ "cap_from_text(%s) failed: %m",
+ captext);
+ exit(-1);
+ }
+ if (-1 == cap_set_proc(caps)) {
+ msyslog(LOG_ERR,
+ "cap_set_proc() failed to drop root privs: %m");
+ exit(-1);
+ }
+ cap_free(caps);
+ }
+# endif /* HAVE_LINUX_CAPABILITIES */
+# ifdef HAVE_SOLARIS_PRIVS
+ if (priv_delset(lowprivs, "proc_setid") == -1) {
+ msyslog(LOG_ERR, "priv_delset() failed:%m");
+ exit(-1);
+ }
+ if (setppriv(PRIV_SET, PRIV_PERMITTED, lowprivs) == -1) {
+ msyslog(LOG_ERR, "setppriv() failed:%m");
+ exit(-1);
+ }
+ priv_freeset(lowprivs);
+ priv_freeset(highprivs);
+# endif /* HAVE_SOLARIS_PRIVS */
+ root_dropped = TRUE;
+ fork_deferred_worker();
+ } /* if (droproot) */
+# endif /* HAVE_DROPROOT */
+
+/* libssecomp sandboxing */
+#if defined (LIBSECCOMP) && (KERN_SECCOMP)
+ scmp_filter_ctx ctx;
+
+ if ((ctx = seccomp_init(SCMP_ACT_KILL)) < 0)
+ msyslog(LOG_ERR, "%s: seccomp_init(SCMP_ACT_KILL) failed: %m", __func__);
+ else {
+ msyslog(LOG_DEBUG, "%s: seccomp_init(SCMP_ACT_KILL) succeeded", __func__);
+ }
+
+#ifdef __x86_64__
+int scmp_sc[] = {
+ SCMP_SYS(adjtimex),
+ SCMP_SYS(bind),
+ SCMP_SYS(brk),
+ SCMP_SYS(chdir),
+ SCMP_SYS(clock_gettime),
+ SCMP_SYS(clock_settime),
+ SCMP_SYS(close),
+ SCMP_SYS(connect),
+ SCMP_SYS(exit_group),
+ SCMP_SYS(fstat),
+ SCMP_SYS(fsync),
+ SCMP_SYS(futex),
+ SCMP_SYS(getitimer),
+ SCMP_SYS(getsockname),
+ SCMP_SYS(ioctl),
+ SCMP_SYS(lseek),
+ SCMP_SYS(madvise),
+ SCMP_SYS(mmap),
+ SCMP_SYS(munmap),
+ SCMP_SYS(open),
+ SCMP_SYS(poll),
+ SCMP_SYS(read),
+ SCMP_SYS(recvmsg),
+ SCMP_SYS(rename),
+ SCMP_SYS(rt_sigaction),
+ SCMP_SYS(rt_sigprocmask),
+ SCMP_SYS(rt_sigreturn),
+ SCMP_SYS(select),
+ SCMP_SYS(sendto),
+ SCMP_SYS(setitimer),
+ SCMP_SYS(setsid),
+ SCMP_SYS(socket),
+ SCMP_SYS(stat),
+ SCMP_SYS(time),
+ SCMP_SYS(write),
+};
+#endif
+#ifdef __i386__
+int scmp_sc[] = {
+ SCMP_SYS(_newselect),
+ SCMP_SYS(adjtimex),
+ SCMP_SYS(brk),
+ SCMP_SYS(chdir),
+ SCMP_SYS(clock_gettime),
+ SCMP_SYS(clock_settime),
+ SCMP_SYS(close),
+ SCMP_SYS(exit_group),
+ SCMP_SYS(fsync),
+ SCMP_SYS(futex),
+ SCMP_SYS(getitimer),
+ SCMP_SYS(madvise),
+ SCMP_SYS(mmap),
+ SCMP_SYS(mmap2),
+ SCMP_SYS(munmap),
+ SCMP_SYS(open),
+ SCMP_SYS(poll),
+ SCMP_SYS(read),
+ SCMP_SYS(rename),
+ SCMP_SYS(rt_sigaction),
+ SCMP_SYS(rt_sigprocmask),
+ SCMP_SYS(select),
+ SCMP_SYS(setitimer),
+ SCMP_SYS(setsid),
+ SCMP_SYS(sigprocmask),
+ SCMP_SYS(sigreturn),
+ SCMP_SYS(socketcall),
+ SCMP_SYS(stat64),
+ SCMP_SYS(time),
+ SCMP_SYS(write),
+};
+#endif
+ {
+ int i;
+
+ for (i = 0; i < COUNTOF(scmp_sc); i++) {
+ if (seccomp_rule_add(ctx,
+ SCMP_ACT_ALLOW, scmp_sc[i], 0) < 0) {
+ msyslog(LOG_ERR,
+ "%s: seccomp_rule_add() failed: %m",
+ __func__);
+ }
+ }
+ }
+
+ if (seccomp_load(ctx) < 0)
+ msyslog(LOG_ERR, "%s: seccomp_load() failed: %m",
+ __func__);
+ else {
+ msyslog(LOG_DEBUG, "%s: seccomp_load() succeeded", __func__);
+ }
+#endif /* LIBSECCOMP and KERN_SECCOMP */
+
+# ifdef HAVE_IO_COMPLETION_PORT
+
+ for (;;) {
+ GetReceivedBuffers();
+# else /* normal I/O */
+
+ BLOCK_IO_AND_ALARM();
+ was_alarmed = FALSE;
+
+ for (;;) {
+ if (alarm_flag) { /* alarmed? */
+ was_alarmed = TRUE;
+ alarm_flag = FALSE;
+ }
+
+ if (!was_alarmed && !has_full_recv_buffer()) {
+ /*
+ * Nothing to do. Wait for something.
+ */
+ io_handler();
+ }
+
+ if (alarm_flag) { /* alarmed? */
+ was_alarmed = TRUE;
+ alarm_flag = FALSE;
+ }
+
+ if (was_alarmed) {
+ UNBLOCK_IO_AND_ALARM();
+ /*
+ * Out here, signals are unblocked. Call timer routine
+ * to process expiry.
+ */
+ timer();
+ was_alarmed = FALSE;
+ BLOCK_IO_AND_ALARM();
+ }
+
+# endif /* !HAVE_IO_COMPLETION_PORT */
+
+# ifdef DEBUG_TIMING
+ {
+ l_fp pts;
+ l_fp tsa, tsb;
+ int bufcount = 0;
+
+ get_systime(&pts);
+ tsa = pts;
+# endif
+ rbuf = get_full_recv_buffer();
+ while (rbuf != NULL) {
+ if (alarm_flag) {
+ was_alarmed = TRUE;
+ alarm_flag = FALSE;
+ }
+ UNBLOCK_IO_AND_ALARM();
+
+ if (was_alarmed) {
+ /* avoid timer starvation during lengthy I/O handling */
+ timer();
+ was_alarmed = FALSE;
+ }
+
+ /*
+ * Call the data procedure to handle each received
+ * packet.
+ */
+ if (rbuf->receiver != NULL) {
+# ifdef DEBUG_TIMING
+ l_fp dts = pts;
+
+ L_SUB(&dts, &rbuf->recv_time);
+ DPRINTF(2, ("processing timestamp delta %s (with prec. fuzz)\n", lfptoa(&dts, 9)));
+ collect_timing(rbuf, "buffer processing delay", 1, &dts);
+ bufcount++;
+# endif
+ (*rbuf->receiver)(rbuf);
+ } else {
+ msyslog(LOG_ERR, "fatal: receive buffer callback NULL");
+ abort();
+ }
+
+ BLOCK_IO_AND_ALARM();
+ freerecvbuf(rbuf);
+ rbuf = get_full_recv_buffer();
+ }
+# ifdef DEBUG_TIMING
+ get_systime(&tsb);
+ L_SUB(&tsb, &tsa);
+ if (bufcount) {
+ collect_timing(NULL, "processing", bufcount, &tsb);
+ DPRINTF(2, ("processing time for %d buffers %s\n", bufcount, lfptoa(&tsb, 9)));
+ }
+ }
+# endif
+
+ /*
+ * Go around again
+ */
+
+# ifdef HAVE_DNSREGISTRATION
+ if (mdnsreg && (current_time - mdnsreg ) > 60 && mdnstries && sys_leap != LEAP_NOTINSYNC) {
+ mdnsreg = current_time;
+ msyslog(LOG_INFO, "Attempting to register mDNS");
+ if ( DNSServiceRegister (&mdns, 0, 0, NULL, "_ntp._udp", NULL, NULL,
+ htons(NTP_PORT), 0, NULL, NULL, NULL) != kDNSServiceErr_NoError ) {
+ if (!--mdnstries) {
+ msyslog(LOG_ERR, "Unable to register mDNS, giving up.");
+ } else {
+ msyslog(LOG_INFO, "Unable to register mDNS, will try later.");
+ }
+ } else {
+ msyslog(LOG_INFO, "mDNS service registered.");
+ mdnsreg = FALSE;
+ }
+ }
+# endif /* HAVE_DNSREGISTRATION */
+
+ }
+ UNBLOCK_IO_AND_ALARM();
+ return 1;
+}
+#endif /* !SIM */
+
+
+#if !defined(SIM) && defined(SIGDIE1)
+/*
+ * finish - exit gracefully
+ */
+static RETSIGTYPE
+finish(
+ int sig
+ )
+{
+ const char *sig_desc;
+
+ sig_desc = NULL;
+#ifdef HAVE_STRSIGNAL
+ sig_desc = strsignal(sig);
+#endif
+ if (sig_desc == NULL)
+ sig_desc = "";
+ msyslog(LOG_NOTICE, "%s exiting on signal %d (%s)", progname,
+ sig, sig_desc);
+ /* See Bug 2513 and Bug 2522 re the unlink of PIDFILE */
+# ifdef HAVE_DNSREGISTRATION
+ if (mdns != NULL)
+ DNSServiceRefDeallocate(mdns);
+# endif
+ exit(0);
+}
+#endif /* !SIM && SIGDIE1 */
+
+
+#ifndef SIM
+/*
+ * wait_child_sync_if - implements parent side of -w/--wait-sync
+ */
+# ifdef HAVE_WORKING_FORK
+static int
+wait_child_sync_if(
+ int pipe_read_fd,
+ long wait_sync
+ )
+{
+ int rc;
+ int exit_code;
+ time_t wait_end_time;
+ time_t cur_time;
+ time_t wait_rem;
+ fd_set readset;
+ struct timeval wtimeout;
+
+ if (0 == wait_sync)
+ return 0;
+
+ /* waitsync_fd_to_close used solely by child */
+ close(waitsync_fd_to_close);
+ wait_end_time = time(NULL) + wait_sync;
+ do {
+ cur_time = time(NULL);
+ wait_rem = (wait_end_time > cur_time)
+ ? (wait_end_time - cur_time)
+ : 0;
+ wtimeout.tv_sec = wait_rem;
+ wtimeout.tv_usec = 0;
+ FD_ZERO(&readset);
+ FD_SET(pipe_read_fd, &readset);
+ rc = select(pipe_read_fd + 1, &readset, NULL, NULL,
+ &wtimeout);
+ if (-1 == rc) {
+ if (EINTR == errno)
+ continue;
+ exit_code = (errno) ? errno : -1;
+ msyslog(LOG_ERR,
+ "--wait-sync select failed: %m");
+ return exit_code;
+ }
+ if (0 == rc) {
+ /*
+ * select() indicated a timeout, but in case
+ * its timeouts are affected by a step of the
+ * system clock, select() again with a zero
+ * timeout to confirm.
+ */
+ FD_ZERO(&readset);
+ FD_SET(pipe_read_fd, &readset);
+ wtimeout.tv_sec = 0;
+ wtimeout.tv_usec = 0;
+ rc = select(pipe_read_fd + 1, &readset, NULL,
+ NULL, &wtimeout);
+ if (0 == rc) /* select() timeout */
+ break;
+ else /* readable */
+ return 0;
+ } else /* readable */
+ return 0;
+ } while (wait_rem > 0);
+
+ fprintf(stderr, "%s: -w/--wait-sync %ld timed out.\n",
+ progname, wait_sync);
+ return ETIMEDOUT;
+}
+# endif /* HAVE_WORKING_FORK */
+
+
+/*
+ * assertion_failed - Redirect assertion failures to msyslog().
+ */
+static void
+assertion_failed(
+ const char *file,
+ int line,
+ isc_assertiontype_t type,
+ const char *cond
+ )
+{
+ isc_assertion_setcallback(NULL); /* Avoid recursion */
+
+ msyslog(LOG_ERR, "%s:%d: %s(%s) failed",
+ file, line, isc_assertion_typetotext(type), cond);
+ msyslog(LOG_ERR, "exiting (due to assertion failure)");
+
+#if defined(DEBUG) && defined(SYS_WINNT)
+ if (debug)
+ DebugBreak();
+#endif
+
+ abort();
+}
+
+
+/*
+ * library_fatal_error - Handle fatal errors from our libraries.
+ */
+static void
+library_fatal_error(
+ const char *file,
+ int line,
+ const char *format,
+ va_list args
+ )
+{
+ char errbuf[256];
+
+ isc_error_setfatal(NULL); /* Avoid recursion */
+
+ msyslog(LOG_ERR, "%s:%d: fatal error:", file, line);
+ vsnprintf(errbuf, sizeof(errbuf), format, args);
+ msyslog(LOG_ERR, "%s", errbuf);
+ msyslog(LOG_ERR, "exiting (due to fatal error in library)");
+
+#if defined(DEBUG) && defined(SYS_WINNT)
+ if (debug)
+ DebugBreak();
+#endif
+
+ abort();
+}
+
+
+/*
+ * library_unexpected_error - Handle non fatal errors from our libraries.
+ */
+# define MAX_UNEXPECTED_ERRORS 100
+int unexpected_error_cnt = 0;
+static void
+library_unexpected_error(
+ const char *file,
+ int line,
+ const char *format,
+ va_list args
+ )
+{
+ char errbuf[256];
+
+ if (unexpected_error_cnt >= MAX_UNEXPECTED_ERRORS)
+ return; /* avoid clutter in log */
+
+ msyslog(LOG_ERR, "%s:%d: unexpected error:", file, line);
+ vsnprintf(errbuf, sizeof(errbuf), format, args);
+ msyslog(LOG_ERR, "%s", errbuf);
+
+ if (++unexpected_error_cnt == MAX_UNEXPECTED_ERRORS)
+ msyslog(LOG_ERR, "Too many errors. Shutting up.");
+
+}
+#endif /* !SIM */
+
+#if !defined(SIM) && !defined(SYS_WINNT)
+# ifdef DEBUG
+
+/*
+ * moredebug - increase debugging verbosity
+ */
+static RETSIGTYPE
+moredebug(
+ int sig
+ )
+{
+ int saved_errno = errno;
+
+ if (debug < 255)
+ {
+ debug++;
+ msyslog(LOG_DEBUG, "debug raised to %d", debug);
+ }
+ errno = saved_errno;
+}
+
+
+/*
+ * lessdebug - decrease debugging verbosity
+ */
+static RETSIGTYPE
+lessdebug(
+ int sig
+ )
+{
+ int saved_errno = errno;
+
+ if (debug > 0)
+ {
+ debug--;
+ msyslog(LOG_DEBUG, "debug lowered to %d", debug);
+ }
+ errno = saved_errno;
+}
+
+# else /* !DEBUG follows */
+
+
+/*
+ * no_debug - We don't do the debug here.
+ */
+static RETSIGTYPE
+no_debug(
+ int sig
+ )
+{
+ int saved_errno = errno;
+
+ msyslog(LOG_DEBUG, "ntpd not compiled for debugging (signal %d)", sig);
+ errno = saved_errno;
+}
+# endif /* !DEBUG */
+#endif /* !SIM && !SYS_WINNT */
diff --git a/ntpd/ntpd.html b/ntpd/ntpd.html
new file mode 100644
index 0000000..25b6caa
--- /dev/null
+++ b/ntpd/ntpd.html
@@ -0,0 +1,995 @@
+<html lang="en">
+<head>
+<title>ntpd: Network Time Protocol (NTP) Daemon User's Manual</title>
+<meta http-equiv="Content-Type" content="text/html">
+<meta name="description" content="ntpd: Network Time Protocol (NTP) Daemon User's Manual">
+<meta name="generator" content="makeinfo 4.7">
+<link title="Top" rel="top" href="#Top">
+<link href="http://www.gnu.org/software/texinfo/" rel="generator-home" title="Texinfo Homepage">
+<meta http-equiv="Content-Style-Type" content="text/css">
+<style type="text/css"><!--
+ pre.display { font-family:inherit }
+ pre.format { font-family:inherit }
+ pre.smalldisplay { font-family:inherit; font-size:smaller }
+ pre.smallformat { font-family:inherit; font-size:smaller }
+ pre.smallexample { font-size:smaller }
+ pre.smalllisp { font-size:smaller }
+ span.sc { font-variant:small-caps }
+ span.roman { font-family: serif; font-weight: normal; }
+--></style>
+</head>
+<body>
+<h1 class="settitle">ntpd: Network Time Protocol (NTP) Daemon User's Manual</h1>
+<div class="node">
+<p><hr>
+<a name="Top"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-Description">ntpd Description</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#dir">(dir)</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#dir">(dir)</a>
+<br>
+</div>
+
+<h2 class="unnumbered">ntpd: Network Time Protocol (NTP) Daemon User Manual</h2>
+
+<p>The <code>ntpd</code> program is an operating system daemon that synchronizes the
+system clock to remote NTP time servers or local reference clocks.
+It is a complete implementation of NTP version 4 defined by RFC-5905, but
+also retains compatible with version 3 defined by RFC-1305 and versions
+1 and 2, defined by RFC-1059 and RFC-1119, respectively.
+The program can operate in any of several modes, including client/server,
+symmetric and broadcast modes, and with both symmetric-key and public-key
+cryptography.
+
+ <p>This document applies to version 4.2.7p482 of <code>ntpd</code>.
+
+<ul class="menu">
+<li><a accesskey="1" href="#ntpd-Description">ntpd Description</a>: Description
+<li><a accesskey="2" href="#ntpd-Invocation">ntpd Invocation</a>: Invoking ntpd
+<li><a accesskey="3" href="#Usage">Usage</a>: Usage
+</ul>
+
+<div class="node">
+<p><hr>
+<a name="ntpd-Description"></a>
+<br>
+</div>
+
+<!-- node-name, next, previous, up -->
+<h3 class="section">Description</h3>
+
+<p>The <code>ntpd</code> program ordinarily requires
+a configuration file described at <a href="#ntp_002econf">ntp.conf</a>.
+This configuration file contains configuration commands described on
+the pages listed above.
+However a client can discover remote servers and configure them
+automatically.
+This makes it possible to deploy a fleet of workstations without
+specifying configuration details specific to the local environment.
+
+ <p>The <code>ntpd</code> program normally operates continuously while adjusting the
+system time and frequency, but in some cases this might not be
+practical.
+With the <code>-q</code> option <code>ntpd</code> operates as in continuous mode, but
+exits just after setting the clock for the first time.
+Most applications will probably want to specify the <code>iburst</code>
+option with the <code>server</code> command.
+With this option an initial volley of messages is exchanged to
+groom the data and set the clock in about ten seconds' time.
+If nothing is heard after a few minutes' time,
+the daemon times out and exits without setting the clock.
+
+<div class="node">
+<p><hr>
+<a name="ntpd-Invocation"></a>
+<br>
+</div>
+
+<h3 class="section">Invoking ntpd</h3>
+
+<p><a name="index-ntpd-1"></a><a name="index-NTP-daemon-program-2"></a>
+
+ <p>The
+<code>ntpd</code>
+utility is an operating system daemon which sets
+and maintains the system time of day in synchronism with Internet
+standard time servers.
+It is a complete implementation of the
+Network Time Protocol (NTP) version 4, as defined by RFC-5905,
+but also retains compatibility with
+version 3, as defined by RFC-1305, and versions 1
+and 2, as defined by RFC-1059 and RFC-1119, respectively.
+
+ <p>The
+<code>ntpd</code>
+utility does most computations in 64-bit floating point
+arithmetic and does relatively clumsy 64-bit fixed point operations
+only when necessary to preserve the ultimate precision, about 232
+picoseconds.
+While the ultimate precision is not achievable with
+ordinary workstations and networks of today, it may be required
+with future gigahertz CPU clocks and gigabit LANs.
+
+ <p>Ordinarily,
+<code>ntpd</code>
+reads the
+<code>ntp.conf(5)</code>
+configuration file at startup time in order to determine the
+synchronization sources and operating modes.
+It is also possible to
+specify a working, although limited, configuration entirely on the
+command line, obviating the need for a configuration file.
+This may
+be particularly useful when the local host is to be configured as a
+broadcast/multicast client, with all peers being determined by
+listening to broadcasts at run time.
+
+ <p>If NetInfo support is built into
+<code>ntpd</code>
+then
+<code>ntpd</code>
+will attempt to read its configuration from the
+NetInfo if the default
+<code>ntp.conf(5)</code>
+file cannot be read and no file is
+specified by the
+<code>-c</code>
+option.
+
+ <p>Various internal
+<code>ntpd</code>
+variables can be displayed and
+configuration options altered while the
+<code>ntpd</code>
+is running
+using the
+<code>ntpq(1ntpqmdoc)</code>
+and
+<code>ntpdc(1ntpdcmdoc)</code>
+utility programs.
+
+ <p>When
+<code>ntpd</code>
+starts it looks at the value of
+<code>umask(2)</code>,
+and if zero
+<code>ntpd</code>
+will set the
+<code>umask(2)</code>
+to 022.
+
+ <p>This section was generated by <strong>AutoGen</strong>,
+using the <code>agtexi-cmd</code> template and the option descriptions for the <code>ntpd</code> program.
+This software is released under the NTP license, &lt;http://ntp.org/license&gt;.
+
+<ul class="menu">
+<li><a accesskey="1" href="#ntpd-usage">ntpd usage</a>: ntpd help/usage (<span class="option">--help</span>)
+<li><a accesskey="2" href="#ntpd-ipv4">ntpd ipv4</a>: ipv4 option (-4)
+<li><a accesskey="3" href="#ntpd-ipv6">ntpd ipv6</a>: ipv6 option (-6)
+<li><a accesskey="4" href="#ntpd-authreq">ntpd authreq</a>: authreq option (-a)
+<li><a accesskey="5" href="#ntpd-authnoreq">ntpd authnoreq</a>: authnoreq option (-A)
+<li><a accesskey="6" href="#ntpd-configfile">ntpd configfile</a>: configfile option (-c)
+<li><a accesskey="7" href="#ntpd-driftfile">ntpd driftfile</a>: driftfile option (-f)
+<li><a accesskey="8" href="#ntpd-panicgate">ntpd panicgate</a>: panicgate option (-g)
+<li><a accesskey="9" href="#ntpd-jaildir">ntpd jaildir</a>: jaildir option (-i)
+<li><a href="#ntpd-interface">ntpd interface</a>: interface option (-I)
+<li><a href="#ntpd-keyfile">ntpd keyfile</a>: keyfile option (-k)
+<li><a href="#ntpd-logfile">ntpd logfile</a>: logfile option (-l)
+<li><a href="#ntpd-novirtualips">ntpd novirtualips</a>: novirtualips option (-L)
+<li><a href="#ntpd-modifymmtimer">ntpd modifymmtimer</a>: modifymmtimer option (-M)
+<li><a href="#ntpd-nice">ntpd nice</a>: nice option (-N)
+<li><a href="#ntpd-pidfile">ntpd pidfile</a>: pidfile option (-p)
+<li><a href="#ntpd-priority">ntpd priority</a>: priority option (-P)
+<li><a href="#ntpd-quit">ntpd quit</a>: quit option (-q)
+<li><a href="#ntpd-propagationdelay">ntpd propagationdelay</a>: propagationdelay option (-r)
+<li><a href="#ntpd-saveconfigquit">ntpd saveconfigquit</a>: saveconfigquit option
+<li><a href="#ntpd-statsdir">ntpd statsdir</a>: statsdir option (-s)
+<li><a href="#ntpd-trustedkey">ntpd trustedkey</a>: trustedkey option (-t)
+<li><a href="#ntpd-user">ntpd user</a>: user option (-u)
+<li><a href="#ntpd-updateinterval">ntpd updateinterval</a>: updateinterval option (-U)
+<li><a href="#ntpd-wait_002dsync">ntpd wait-sync</a>: wait-sync option (-w)
+<li><a href="#ntpd-slew">ntpd slew</a>: slew option (-x)
+<li><a href="#ntpd-usepcc">ntpd usepcc</a>: usepcc option
+<li><a href="#ntpd-pccfreq">ntpd pccfreq</a>: pccfreq option
+<li><a href="#ntpd-mdns">ntpd mdns</a>: mdns option (-m)
+<li><a href="#ntpd-config">ntpd config</a>: presetting/configuring ntpd
+<li><a href="#ntpd-exit-status">ntpd exit status</a>: exit status
+<li><a href="#ntpd-Usage">ntpd Usage</a>: Usage
+<li><a href="#ntpd-Files">ntpd Files</a>: Files
+<li><a href="#ntpd-See-Also">ntpd See Also</a>: See Also
+<li><a href="#ntpd-Bugs">ntpd Bugs</a>: Bugs
+<li><a href="#ntpd-Notes">ntpd Notes</a>: Notes
+</ul>
+
+<div class="node">
+<p><hr>
+<a name="ntpd-usage"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-ipv4">ntpd ipv4</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">ntpd help/usage (<span class="option">--help</span>)</h4>
+
+<p><a name="index-ntpd-help-3"></a>
+This is the automatically generated usage text for ntpd.
+
+ <p>The text printed is the same whether selected with the <code>help</code> option
+(<span class="option">--help</span>) or the <code>more-help</code> option (<span class="option">--more-help</span>). <code>more-help</code> will print
+the usage text by passing it through a pager program.
+<code>more-help</code> is disabled on platforms without a working
+<code>fork(2)</code> function. The <code>PAGER</code> environment variable is
+used to select the program, defaulting to <span class="file">more</span>. Both will exit
+with a status code of 0.
+
+<pre class="example">ntpd - NTP daemon program - Ver. 4.2.7p481
+Usage: ntpd [ -&lt;flag&gt; [&lt;val&gt;] | --&lt;name&gt;[{=| }&lt;val&gt;] ]... \
+ [ &lt;server1&gt; ... &lt;serverN&gt; ]
+ Flg Arg Option-Name Description
+ -4 no ipv4 Force IPv4 DNS name resolution
+ - prohibits the option 'ipv6'
+ -6 no ipv6 Force IPv6 DNS name resolution
+ - prohibits the option 'ipv4'
+ -a no authreq Require crypto authentication
+ - prohibits the option 'authnoreq'
+ -A no authnoreq Do not require crypto authentication
+ - prohibits the option 'authreq'
+ -b no bcastsync Allow us to sync to broadcast servers
+ -c Str configfile configuration file name
+ -d no debug-level Increase debug verbosity level
+ - may appear multiple times
+ -D Num set-debug-level Set the debug verbosity level
+ - may appear multiple times
+ -f Str driftfile frequency drift file name
+ -g no panicgate Allow the first adjustment to be Big
+ - may appear multiple times
+ -i Str jaildir Jail directory
+ -I Str interface Listen on an interface name or address
+ - may appear multiple times
+ -k Str keyfile path to symmetric keys
+ -l Str logfile path to the log file
+ -L no novirtualips Do not listen to virtual interfaces
+ -n no nofork Do not fork
+ - prohibits the option 'wait-sync'
+ -N no nice Run at high priority
+ -p Str pidfile path to the PID file
+ -P Num priority Process priority
+ -q no quit Set the time and quit
+ - prohibits these options:
+ saveconfigquit
+ wait-sync
+ -r Str propagationdelay Broadcast/propagation delay
+ Str saveconfigquit Save parsed configuration and quit
+ - prohibits these options:
+ quit
+ wait-sync
+ -s Str statsdir Statistics file location
+ -t Str trustedkey Trusted key number
+ - may appear multiple times
+ -u Str user Run as userid (or userid:groupid)
+ -U Num updateinterval interval in seconds between scans for new or dropped interfaces
+ Str var make ARG an ntp variable (RW)
+ - may appear multiple times
+ Str dvar make ARG an ntp variable (RW|DEF)
+ - may appear multiple times
+ -w Num wait-sync Seconds to wait for first clock sync
+ - prohibits these options:
+ nofork
+ quit
+ saveconfigquit
+ -x no slew Slew up to 600 seconds
+ opt version output version information and exit
+ -? no help display extended usage information and exit
+ -! no more-help extended usage information passed thru pager
+
+Options are specified by doubled hyphens and their name or by a single
+hyphen and the flag character.
+
+
+The following option preset mechanisms are supported:
+ - examining environment variables named NTPD_*
+
+Please send bug reports to: &lt;http://bugs.ntp.org, bugs@ntp.org&gt;
+</pre>
+ <div class="node">
+<p><hr>
+<a name="ntpd-ipv4"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-ipv6">ntpd ipv6</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-usage">ntpd usage</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">ipv4 option (-4)</h4>
+
+<p><a name="index-ntpd_002dipv4-4"></a>
+This is the &ldquo;force ipv4 dns name resolution&rdquo; option.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>must not appear in combination with any of the following options:
+ipv6.
+</ul>
+
+ <p>Force DNS resolution of following host names on the command line
+to the IPv4 namespace.
+<div class="node">
+<p><hr>
+<a name="ntpd-ipv6"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-authreq">ntpd authreq</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-ipv4">ntpd ipv4</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">ipv6 option (-6)</h4>
+
+<p><a name="index-ntpd_002dipv6-5"></a>
+This is the &ldquo;force ipv6 dns name resolution&rdquo; option.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>must not appear in combination with any of the following options:
+ipv4.
+</ul>
+
+ <p>Force DNS resolution of following host names on the command line
+to the IPv6 namespace.
+<div class="node">
+<p><hr>
+<a name="ntpd-authreq"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-authnoreq">ntpd authnoreq</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-ipv6">ntpd ipv6</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">authreq option (-a)</h4>
+
+<p><a name="index-ntpd_002dauthreq-6"></a>
+This is the &ldquo;require crypto authentication&rdquo; option.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>must not appear in combination with any of the following options:
+authnoreq.
+</ul>
+
+ <p>Require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is the default.
+<div class="node">
+<p><hr>
+<a name="ntpd-authnoreq"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-configfile">ntpd configfile</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-authreq">ntpd authreq</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">authnoreq option (-A)</h4>
+
+<p><a name="index-ntpd_002dauthnoreq-7"></a>
+This is the &ldquo;do not require crypto authentication&rdquo; option.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>must not appear in combination with any of the following options:
+authreq.
+</ul>
+
+ <p>Do not require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is almost never a good idea.
+<div class="node">
+<p><hr>
+<a name="ntpd-configfile"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-driftfile">ntpd driftfile</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-authnoreq">ntpd authnoreq</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">configfile option (-c)</h4>
+
+<p><a name="index-ntpd_002dconfigfile-8"></a>
+This is the &ldquo;configuration file name&rdquo; option.
+This option takes a string argument.
+The name and path of the configuration file,
+<span class="file">/etc/ntp.conf</span>
+by default.
+<div class="node">
+<p><hr>
+<a name="ntpd-driftfile"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-panicgate">ntpd panicgate</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-configfile">ntpd configfile</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">driftfile option (-f)</h4>
+
+<p><a name="index-ntpd_002ddriftfile-9"></a>
+This is the &ldquo;frequency drift file name&rdquo; option.
+This option takes a string argument.
+The name and path of the frequency file,
+<span class="file">/etc/ntp.drift</span>
+by default.
+This is the same operation as the
+<code>driftfile</code> <kbd>driftfile</kbd>
+configuration specification in the
+<span class="file">/etc/ntp.conf</span>
+file.
+<div class="node">
+<p><hr>
+<a name="ntpd-panicgate"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-jaildir">ntpd jaildir</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-driftfile">ntpd driftfile</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">panicgate option (-g)</h4>
+
+<p><a name="index-ntpd_002dpanicgate-10"></a>
+This is the &ldquo;allow the first adjustment to be big&rdquo; option.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>may appear an unlimited number of times.
+</ul>
+
+ <p>Normally,
+<code>ntpd</code>
+exits with a message to the system log if the offset exceeds the panic threshold, which is 1000 s by default. This option allows the time to be set to any value without restriction; however, this can happen only once. If the threshold is exceeded after that,
+<code>ntpd</code>
+will exit with a message to the system log. This option can be used with the
+<code>-q</code>
+and
+<code>-x</code>
+options.
+See the
+<code>tinker</code>
+configuration file directive for other options.
+<div class="node">
+<p><hr>
+<a name="ntpd-jaildir"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-interface">ntpd interface</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-panicgate">ntpd panicgate</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">jaildir option (-i)</h4>
+
+<p><a name="index-ntpd_002djaildir-11"></a>
+This is the &ldquo;jail directory&rdquo; option.
+This option takes a string argument.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>must be compiled in by defining <code>HAVE_DROPROOT</code> during the compilation.
+</ul>
+
+ <p>Chroot the server to the directory
+<kbd>jaildir</kbd>
+.
+This option also implies that the server attempts to drop root privileges at startup.
+You may need to also specify a
+<code>-u</code>
+option.
+This option is only available if the OS supports adjusting the clock
+without full root privileges.
+This option is supported under NetBSD (configure with
+<code>--enable-clockctl</code>) or Linux (configure with
+<code>--enable-linuxcaps</code>) or Solaris (configure with <code>--enable-solarisprivs</code>).
+<div class="node">
+<p><hr>
+<a name="ntpd-interface"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-keyfile">ntpd keyfile</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-jaildir">ntpd jaildir</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">interface option (-I)</h4>
+
+<p><a name="index-ntpd_002dinterface-12"></a>
+This is the &ldquo;listen on an interface name or address&rdquo; option.
+This option takes a string argument <span class="file">iface</span>.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>may appear an unlimited number of times.
+</ul>
+
+ <p>Open the network address given, or all the addresses associated with the
+given interface name. This option may appear multiple times. This option
+also implies not opening other addresses, except wildcard and localhost.
+This option is deprecated. Please consider using the configuration file
+<code>interface</code> command, which is more versatile.
+<div class="node">
+<p><hr>
+<a name="ntpd-keyfile"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-logfile">ntpd logfile</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-interface">ntpd interface</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">keyfile option (-k)</h4>
+
+<p><a name="index-ntpd_002dkeyfile-13"></a>
+This is the &ldquo;path to symmetric keys&rdquo; option.
+This option takes a string argument.
+Specify the name and path of the symmetric key file.
+<span class="file">/etc/ntp.keys</span>
+is the default.
+This is the same operation as the
+<code>keys</code> <kbd>keyfile</kbd>
+configuration file directive.
+<div class="node">
+<p><hr>
+<a name="ntpd-logfile"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-novirtualips">ntpd novirtualips</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-keyfile">ntpd keyfile</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">logfile option (-l)</h4>
+
+<p><a name="index-ntpd_002dlogfile-14"></a>
+This is the &ldquo;path to the log file&rdquo; option.
+This option takes a string argument.
+Specify the name and path of the log file.
+The default is the system log file.
+This is the same operation as the
+<code>logfile</code> <kbd>logfile</kbd>
+configuration file directive.
+<div class="node">
+<p><hr>
+<a name="ntpd-novirtualips"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-modifymmtimer">ntpd modifymmtimer</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-logfile">ntpd logfile</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">novirtualips option (-L)</h4>
+
+<p><a name="index-ntpd_002dnovirtualips-15"></a>
+This is the &ldquo;do not listen to virtual interfaces&rdquo; option.
+Do not listen to virtual interfaces, defined as those with
+names containing a colon. This option is deprecated. Please
+consider using the configuration file <code>interface</code> command, which
+is more versatile.
+<div class="node">
+<p><hr>
+<a name="ntpd-modifymmtimer"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-nice">ntpd nice</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-novirtualips">ntpd novirtualips</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">modifymmtimer option (-M)</h4>
+
+<p><a name="index-ntpd_002dmodifymmtimer-16"></a>
+This is the &ldquo;modify multimedia timer (windows only)&rdquo; option.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>must be compiled in by defining <code>SYS_WINNT</code> during the compilation.
+</ul>
+
+ <p>Set the Windows Multimedia Timer to highest resolution. This
+ensures the resolution does not change while ntpd is running,
+avoiding timekeeping glitches associated with changes.
+<div class="node">
+<p><hr>
+<a name="ntpd-nice"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-pidfile">ntpd pidfile</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-modifymmtimer">ntpd modifymmtimer</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">nice option (-N)</h4>
+
+<p><a name="index-ntpd_002dnice-17"></a>
+This is the &ldquo;run at high priority&rdquo; option.
+To the extent permitted by the operating system, run
+<code>ntpd</code>
+at the highest priority.
+<div class="node">
+<p><hr>
+<a name="ntpd-pidfile"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-priority">ntpd priority</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-nice">ntpd nice</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">pidfile option (-p)</h4>
+
+<p><a name="index-ntpd_002dpidfile-18"></a>
+This is the &ldquo;path to the pid file&rdquo; option.
+This option takes a string argument.
+Specify the name and path of the file used to record
+<code>ntpd</code>'s
+process ID.
+This is the same operation as the
+<code>pidfile</code> <kbd>pidfile</kbd>
+configuration file directive.
+<div class="node">
+<p><hr>
+<a name="ntpd-priority"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-quit">ntpd quit</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-pidfile">ntpd pidfile</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">priority option (-P)</h4>
+
+<p><a name="index-ntpd_002dpriority-19"></a>
+This is the &ldquo;process priority&rdquo; option.
+This option takes a number argument.
+To the extent permitted by the operating system, run
+<code>ntpd</code>
+at the specified
+<code>sched_setscheduler(SCHED_FIFO)</code>
+priority.
+<div class="node">
+<p><hr>
+<a name="ntpd-quit"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-propagationdelay">ntpd propagationdelay</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-priority">ntpd priority</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">quit option (-q)</h4>
+
+<p><a name="index-ntpd_002dquit-20"></a>
+This is the &ldquo;set the time and quit&rdquo; option.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>must not appear in combination with any of the following options:
+saveconfigquit, wait-sync.
+</ul>
+
+ <p><code>ntpd</code>
+will not daemonize and will exit after the clock is first
+synchronized. This behavior mimics that of the
+<code>ntpdate</code>
+program, which will soon be replaced with a shell script.
+The
+<code>-g</code>
+and
+<code>-x</code>
+options can be used with this option.
+Note: The kernel time discipline is disabled with this option.
+<div class="node">
+<p><hr>
+<a name="ntpd-propagationdelay"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-saveconfigquit">ntpd saveconfigquit</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-quit">ntpd quit</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">propagationdelay option (-r)</h4>
+
+<p><a name="index-ntpd_002dpropagationdelay-21"></a>
+This is the &ldquo;broadcast/propagation delay&rdquo; option.
+This option takes a string argument.
+Specify the default propagation delay from the broadcast/multicast server to this client. This is necessary only if the delay cannot be computed automatically by the protocol.
+<div class="node">
+<p><hr>
+<a name="ntpd-saveconfigquit"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-statsdir">ntpd statsdir</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-propagationdelay">ntpd propagationdelay</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">saveconfigquit option</h4>
+
+<p><a name="index-ntpd_002dsaveconfigquit-22"></a>
+This is the &ldquo;save parsed configuration and quit&rdquo; option.
+This option takes a string argument.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>must be compiled in by defining <code>SAVECONFIG</code> during the compilation.
+<li>must not appear in combination with any of the following options:
+quit, wait-sync.
+</ul>
+
+ <p>Cause <code>ntpd</code> to parse its startup configuration file and save an
+equivalent to the given filename and exit. This option was
+designed for automated testing.
+<div class="node">
+<p><hr>
+<a name="ntpd-statsdir"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-trustedkey">ntpd trustedkey</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-saveconfigquit">ntpd saveconfigquit</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">statsdir option (-s)</h4>
+
+<p><a name="index-ntpd_002dstatsdir-23"></a>
+This is the &ldquo;statistics file location&rdquo; option.
+This option takes a string argument.
+Specify the directory path for files created by the statistics facility.
+This is the same operation as the
+<code>statsdir</code> <kbd>statsdir</kbd>
+configuration file directive.
+<div class="node">
+<p><hr>
+<a name="ntpd-trustedkey"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-user">ntpd user</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-statsdir">ntpd statsdir</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">trustedkey option (-t)</h4>
+
+<p><a name="index-ntpd_002dtrustedkey-24"></a>
+This is the &ldquo;trusted key number&rdquo; option.
+This option takes a string argument <span class="file">tkey</span>.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>may appear an unlimited number of times.
+</ul>
+
+ <p>Add the specified key number to the trusted key list.
+<div class="node">
+<p><hr>
+<a name="ntpd-user"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-updateinterval">ntpd updateinterval</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-trustedkey">ntpd trustedkey</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">user option (-u)</h4>
+
+<p><a name="index-ntpd_002duser-25"></a>
+This is the &ldquo;run as userid (or userid:groupid)&rdquo; option.
+This option takes a string argument.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>must be compiled in by defining <code>HAVE_DROPROOT</code> during the compilation.
+</ul>
+
+ <p>Specify a user, and optionally a group, to switch to.
+This option is only available if the OS supports adjusting the clock
+without full root privileges.
+This option is supported under NetBSD (configure with
+<code>--enable-clockctl</code>) or Linux (configure with
+<code>--enable-linuxcaps</code>) or Solaris (configure with <code>--enable-solarisprivs</code>).
+<div class="node">
+<p><hr>
+<a name="ntpd-updateinterval"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-wait_002dsync">ntpd wait-sync</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-user">ntpd user</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">updateinterval option (-U)</h4>
+
+<p><a name="index-ntpd_002dupdateinterval-26"></a>
+This is the &ldquo;interval in seconds between scans for new or dropped interfaces&rdquo; option.
+This option takes a number argument.
+Give the time in seconds between two scans for new or dropped interfaces.
+For systems with routing socket support the scans will be performed shortly after the interface change
+has been detected by the system.
+Use 0 to disable scanning. 60 seconds is the minimum time between scans.
+<div class="node">
+<p><hr>
+<a name="ntpd-wait_002dsync"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-slew">ntpd slew</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-updateinterval">ntpd updateinterval</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">wait-sync option (-w)</h4>
+
+<p><a name="index-ntpd_002dwait_002dsync-27"></a>
+This is the &ldquo;seconds to wait for first clock sync&rdquo; option.
+This option takes a number argument.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>must be compiled in by defining <code>HAVE_WORKING_FORK</code> during the compilation.
+<li>must not appear in combination with any of the following options:
+nofork, quit, saveconfigquit.
+</ul>
+
+ <p>If greater than zero, alters <code>ntpd</code>'s behavior when forking to
+daemonize. Instead of exiting with status 0 immediately after
+the fork, the parent waits up to the specified number of
+seconds for the child to first synchronize the clock. The exit
+status is zero (success) if the clock was synchronized,
+otherwise it is <code>ETIMEDOUT</code>.
+This provides the option for a script starting <code>ntpd</code> to easily
+wait for the first set of the clock before proceeding.
+<div class="node">
+<p><hr>
+<a name="ntpd-slew"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-usepcc">ntpd usepcc</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-wait_002dsync">ntpd wait-sync</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">slew option (-x)</h4>
+
+<p><a name="index-ntpd_002dslew-28"></a>
+This is the &ldquo;slew up to 600 seconds&rdquo; option.
+Normally, the time is slewed if the offset is less than the step threshold, which is 128 ms by default, and stepped if above the threshold.
+This option sets the threshold to 600 s, which is well within the accuracy window to set the clock manually.
+Note: Since the slew rate of typical Unix kernels is limited to 0.5 ms/s, each second of adjustment requires an amortization interval of 2000 s.
+Thus, an adjustment as much as 600 s will take almost 14 days to complete.
+This option can be used with the
+<code>-g</code>
+and
+<code>-q</code>
+options.
+See the
+<code>tinker</code>
+configuration file directive for other options.
+Note: The kernel time discipline is disabled with this option.
+<div class="node">
+<p><hr>
+<a name="ntpd-usepcc"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-pccfreq">ntpd pccfreq</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-slew">ntpd slew</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">usepcc option</h4>
+
+<p><a name="index-ntpd_002dusepcc-29"></a>
+This is the &ldquo;use cpu cycle counter (windows only)&rdquo; option.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>must be compiled in by defining <code>SYS_WINNT</code> during the compilation.
+</ul>
+
+ <p>Attempt to substitute the CPU counter for <code>QueryPerformanceCounter</code>.
+The CPU counter and <code>QueryPerformanceCounter</code> are compared, and if
+they have the same frequency, the CPU counter (RDTSC on x86) is
+used directly, saving the overhead of a system call.
+<div class="node">
+<p><hr>
+<a name="ntpd-pccfreq"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-mdns">ntpd mdns</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-usepcc">ntpd usepcc</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">pccfreq option</h4>
+
+<p><a name="index-ntpd_002dpccfreq-30"></a>
+This is the &ldquo;force cpu cycle counter use (windows only)&rdquo; option.
+This option takes a string argument.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>must be compiled in by defining <code>SYS_WINNT</code> during the compilation.
+</ul>
+
+ <p>Force substitution the CPU counter for <code>QueryPerformanceCounter</code>.
+The CPU counter (RDTSC on x86) is used unconditionally with the
+given frequency (in Hz).
+<div class="node">
+<p><hr>
+<a name="ntpd-mdns"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-config">ntpd config</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-pccfreq">ntpd pccfreq</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">mdns option (-m)</h4>
+
+<p><a name="index-ntpd_002dmdns-31"></a>
+This is the &ldquo;register with mdns as a ntp server&rdquo; option.
+
+<p class="noindent">This option has some usage constraints. It:
+ <ul>
+<li>must be compiled in by defining <code>HAVE_DNSREGISTRATION</code> during the compilation.
+</ul>
+
+ <p>Registers as an NTP server with the local mDNS server which allows
+the server to be discovered via mDNS client lookup.
+
+<div class="node">
+<p><hr>
+<a name="ntpd-config"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-exit-status">ntpd exit status</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-mdns">ntpd mdns</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">presetting/configuring ntpd</h4>
+
+<p>Any option that is not marked as <i>not presettable</i> may be preset by
+loading values from environment variables named <code>NTPD</code> and <code>NTPD_&lt;OPTION_NAME&gt;</code>. <code>&lt;OPTION_NAME&gt;</code> must be one of
+the options listed above in upper case and segmented with underscores.
+The <code>NTPD</code> variable will be tokenized and parsed like
+the command line. The remaining variables are tested for existence and their
+values are treated like option arguments.
+
+ <p>The command line options relating to configuration and/or usage help are:
+
+<h5 class="subsubheading">version (-)</h5>
+
+<p>Print the program version to standard out, optionally with licensing
+information, then exit 0. The optional argument specifies how much licensing
+detail to provide. The default is to print just the version. The licensing infomation may be selected with an option argument.
+Only the first letter of the argument is examined:
+
+ <dl>
+<dt><span class="samp">version</span><dd>Only print the version. This is the default.
+<br><dt><span class="samp">copyright</span><dd>Name the copyright usage licensing terms.
+<br><dt><span class="samp">verbose</span><dd>Print the full copyright usage licensing terms.
+</dl>
+
+<div class="node">
+<p><hr>
+<a name="ntpd-exit-status"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-Usage">ntpd Usage</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-config">ntpd config</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">ntpd exit status</h4>
+
+<p>One of the following exit values will be returned:
+ <dl>
+<dt><span class="samp">0 (EXIT_SUCCESS)</span><dd>Successful program execution.
+<br><dt><span class="samp">1 (EXIT_FAILURE)</span><dd>The operation failed or the command syntax was not valid.
+</dl>
+ <div class="node">
+<p><hr>
+<a name="ntpd-Usage"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-Files">ntpd Files</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-exit-status">ntpd exit status</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">ntpd Usage</h4>
+
+<div class="node">
+<p><hr>
+<a name="ntpd-Files"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-See-Also">ntpd See Also</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-Usage">ntpd Usage</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">ntpd Files</h4>
+
+<div class="node">
+<p><hr>
+<a name="ntpd-See-Also"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-Bugs">ntpd Bugs</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-Files">ntpd Files</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">ntpd See Also</h4>
+
+<div class="node">
+<p><hr>
+<a name="ntpd-Bugs"></a>Next:&nbsp;<a rel="next" accesskey="n" href="#ntpd-Notes">ntpd Notes</a>,
+Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-See-Also">ntpd See Also</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">ntpd Bugs</h4>
+
+<div class="node">
+<p><hr>
+<a name="ntpd-Notes"></a>Previous:&nbsp;<a rel="previous" accesskey="p" href="#ntpd-Bugs">ntpd Bugs</a>,
+Up:&nbsp;<a rel="up" accesskey="u" href="#ntpd-Invocation">ntpd Invocation</a>
+<br>
+</div>
+
+<h4 class="subsection">ntpd Notes</h4>
+
+<div class="node">
+<p><hr>
+<a name="Usage"></a>
+<br>
+</div>
+
+<!-- node-name, next, previous, up -->
+<h3 class="section">Usage</h3>
+
diff --git a/ntpd/ntpd.man.in b/ntpd/ntpd.man.in
new file mode 100644
index 0000000..a6824ca
--- /dev/null
+++ b/ntpd/ntpd.man.in
@@ -0,0 +1,987 @@
+.de1 NOP
+. it 1 an-trap
+. if \\n[.$] \,\\$*\/
+..
+.ie t \
+.ds B-Font [CB]
+.ds I-Font [CI]
+.ds R-Font [CR]
+.el \
+.ds B-Font B
+.ds I-Font I
+.ds R-Font R
+.TH ntpd @NTPD_MS@ "02 Dec 2014" "4.2.7p482" "User Commands"
+.\"
+.\" EDIT THIS FILE WITH CAUTION (/tmp/.ag-AtaWSL/ag-MtaORL)
+.\"
+.\" It has been AutoGen-ed December 2, 2014 at 08:56:46 AM by AutoGen 5.18.5pre4
+.\" From the definitions ntpd-opts.def
+.\" and the template file agman-cmd.tpl
+.SH NAME
+\f\*[B-Font]ntpd\fP
+\- NTP daemon program
+.SH SYNOPSIS
+\f\*[B-Font]ntpd\fP
+.\" Mixture of short (flag) options and long options
+[\f\*[B-Font]\-flags\f[]]
+[\f\*[B-Font]\-flag\f[] [\f\*[I-Font]value\f[]]]
+[\f\*[B-Font]\-\-option-name\f[][[=| ]\f\*[I-Font]value\f[]]]
+[ <server1> ... <serverN> ]
+.sp \n(Ppu
+.ne 2
+
+.SH DESCRIPTION
+The
+\f\*[B-Font]ntpd\fP
+utility is an operating system daemon which sets
+and maintains the system time of day in synchronism with Internet
+standard time servers.
+It is a complete implementation of the
+Network Time Protocol (NTP) version 4, as defined by RFC-5905,
+but also retains compatibility with
+version 3, as defined by RFC-1305, and versions 1
+and 2, as defined by RFC-1059 and RFC-1119, respectively.
+.sp \n(Ppu
+.ne 2
+
+The
+\f\*[B-Font]ntpd\fP
+utility does most computations in 64-bit floating point
+arithmetic and does relatively clumsy 64-bit fixed point operations
+only when necessary to preserve the ultimate precision, about 232
+picoseconds.
+While the ultimate precision is not achievable with
+ordinary workstations and networks of today, it may be required
+with future gigahertz CPU clocks and gigabit LANs.
+.sp \n(Ppu
+.ne 2
+
+Ordinarily,
+\f\*[B-Font]ntpd\fP
+reads the
+\fCntp.conf\fR(5)\f[]
+configuration file at startup time in order to determine the
+synchronization sources and operating modes.
+It is also possible to
+specify a working, although limited, configuration entirely on the
+command line, obviating the need for a configuration file.
+This may
+be particularly useful when the local host is to be configured as a
+broadcast/multicast client, with all peers being determined by
+listening to broadcasts at run time.
+.sp \n(Ppu
+.ne 2
+
+If NetInfo support is built into
+\f\*[B-Font]ntpd\fP,
+then
+\f\*[B-Font]ntpd\fP
+will attempt to read its configuration from the
+NetInfo if the default
+\fCntp.conf\fR(5)\f[]
+file cannot be read and no file is
+specified by the
+\f\*[B-Font]\-c\f[]
+option.
+.sp \n(Ppu
+.ne 2
+
+Various internal
+\f\*[B-Font]ntpd\fP
+variables can be displayed and
+configuration options altered while the
+\f\*[B-Font]ntpd\fP
+is running
+using the
+\fCntpq\fR(@NTPQ_MS@)\f[]
+and
+\fCntpdc\fR(@NTPDC_MS@)\f[]
+utility programs.
+.sp \n(Ppu
+.ne 2
+
+When
+\f\*[B-Font]ntpd\fP
+starts it looks at the value of
+\fCumask\fR(2)\f[],
+and if zero
+\f\*[B-Font]ntpd\fP
+will set the
+\fCumask\fR(2)\f[]
+to 022.
+.SH "OPTIONS"
+.TP
+.NOP \f\*[B-Font]\-4\f[], \f\*[B-Font]\-\-ipv4\f[]
+Force IPv4 DNS name resolution.
+This option must not appear in combination with any of the following options:
+ipv6.
+.sp
+Force DNS resolution of following host names on the command line
+to the IPv4 namespace.
+.TP
+.NOP \f\*[B-Font]\-6\f[], \f\*[B-Font]\-\-ipv6\f[]
+Force IPv6 DNS name resolution.
+This option must not appear in combination with any of the following options:
+ipv4.
+.sp
+Force DNS resolution of following host names on the command line
+to the IPv6 namespace.
+.TP
+.NOP \f\*[B-Font]\-a\f[], \f\*[B-Font]\-\-authreq\f[]
+Require crypto authentication.
+This option must not appear in combination with any of the following options:
+authnoreq.
+.sp
+Require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is the default.
+.TP
+.NOP \f\*[B-Font]\-A\f[], \f\*[B-Font]\-\-authnoreq\f[]
+Do not require crypto authentication.
+This option must not appear in combination with any of the following options:
+authreq.
+.sp
+Do not require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is almost never a good idea.
+.TP
+.NOP \f\*[B-Font]\-b\f[], \f\*[B-Font]\-\-bcastsync\f[]
+Allow us to sync to broadcast servers.
+.sp
+.TP
+.NOP \f\*[B-Font]\-c\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-configfile\f[]=\f\*[I-Font]string\f[]
+configuration file name.
+.sp
+The name and path of the configuration file,
+\fI/etc/ntp.conf\fP
+by default.
+.TP
+.NOP \f\*[B-Font]\-d\f[], \f\*[B-Font]\-\-debug\-level\f[]
+Increase debug verbosity level.
+This option may appear an unlimited number of times.
+.sp
+.TP
+.NOP \f\*[B-Font]\-D\f[] \f\*[I-Font]number\f[], \f\*[B-Font]\-\-set\-debug\-level\f[]=\f\*[I-Font]number\f[]
+Set the debug verbosity level.
+This option may appear an unlimited number of times.
+This option takes an integer number as its argument.
+.sp
+.TP
+.NOP \f\*[B-Font]\-f\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-driftfile\f[]=\f\*[I-Font]string\f[]
+frequency drift file name.
+.sp
+The name and path of the frequency file,
+\fI/etc/ntp.drift\fP
+by default.
+This is the same operation as the
+\fBdriftfile\fP \fIdriftfile\fP
+configuration specification in the
+\fI/etc/ntp.conf\fP
+file.
+.TP
+.NOP \f\*[B-Font]\-g\f[], \f\*[B-Font]\-\-panicgate\f[]
+Allow the first adjustment to be Big.
+This option may appear an unlimited number of times.
+.sp
+Normally,
+\fBntpd\fP
+exits with a message to the system log if the offset exceeds the panic threshold, which is 1000 s by default. This option allows the time to be set to any value without restriction; however, this can happen only once. If the threshold is exceeded after that,
+\fBntpd\fP
+will exit with a message to the system log. This option can be used with the
+\fB-q\fP
+and
+\fB-x\fP
+options.
+See the
+\fBtinker\fP
+configuration file directive for other options.
+.TP
+.NOP \f\*[B-Font]\-i\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-jaildir\f[]=\f\*[I-Font]string\f[]
+Jail directory.
+.sp
+Chroot the server to the directory
+\fIjaildir\fP
+.
+This option also implies that the server attempts to drop root privileges at startup.
+You may need to also specify a
+\fB-u\fP
+option.
+This option is only available if the OS supports adjusting the clock
+without full root privileges.
+This option is supported under NetBSD (configure with
+\fB--enable-clockctl\fP) or Linux (configure with
+\fB--enable-linuxcaps\fP) or Solaris (configure with \fB--enable-solarisprivs\fP).
+.TP
+.NOP \f\*[B-Font]\-I\f[] \f\*[I-Font]iface\f[], \f\*[B-Font]\-\-interface\f[]=\f\*[I-Font]iface\f[]
+Listen on an interface name or address.
+This option may appear an unlimited number of times.
+.sp
+Open the network address given, or all the addresses associated with the
+given interface name. This option may appear multiple times. This option
+also implies not opening other addresses, except wildcard and localhost.
+This option is deprecated. Please consider using the configuration file
+\fBinterface\fP command, which is more versatile.
+.TP
+.NOP \f\*[B-Font]\-k\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-keyfile\f[]=\f\*[I-Font]string\f[]
+path to symmetric keys.
+.sp
+Specify the name and path of the symmetric key file.
+\fI/etc/ntp.keys\fP
+is the default.
+This is the same operation as the
+\fBkeys\fP \fIkeyfile\fP
+configuration file directive.
+.TP
+.NOP \f\*[B-Font]\-l\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-logfile\f[]=\f\*[I-Font]string\f[]
+path to the log file.
+.sp
+Specify the name and path of the log file.
+The default is the system log file.
+This is the same operation as the
+\fBlogfile\fP \fIlogfile\fP
+configuration file directive.
+.TP
+.NOP \f\*[B-Font]\-L\f[], \f\*[B-Font]\-\-novirtualips\f[]
+Do not listen to virtual interfaces.
+.sp
+Do not listen to virtual interfaces, defined as those with
+names containing a colon. This option is deprecated. Please
+consider using the configuration file \fBinterface\fP command, which
+is more versatile.
+.TP
+.NOP \f\*[B-Font]\-M\f[], \f\*[B-Font]\-\-modifymmtimer\f[]
+Modify Multimedia Timer (Windows only).
+.sp
+Set the Windows Multimedia Timer to highest resolution. This
+ensures the resolution does not change while ntpd is running,
+avoiding timekeeping glitches associated with changes.
+.TP
+.NOP \f\*[B-Font]\-n\f[], \f\*[B-Font]\-\-nofork\f[]
+Do not fork.
+This option must not appear in combination with any of the following options:
+wait-sync.
+.sp
+.TP
+.NOP \f\*[B-Font]\-N\f[], \f\*[B-Font]\-\-nice\f[]
+Run at high priority.
+.sp
+To the extent permitted by the operating system, run
+\fBntpd\fP
+at the highest priority.
+.TP
+.NOP \f\*[B-Font]\-p\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-pidfile\f[]=\f\*[I-Font]string\f[]
+path to the PID file.
+.sp
+Specify the name and path of the file used to record
+\fBntpd\fP's
+process ID.
+This is the same operation as the
+\fBpidfile\fP \fIpidfile\fP
+configuration file directive.
+.TP
+.NOP \f\*[B-Font]\-P\f[] \f\*[I-Font]number\f[], \f\*[B-Font]\-\-priority\f[]=\f\*[I-Font]number\f[]
+Process priority.
+This option takes an integer number as its argument.
+.sp
+To the extent permitted by the operating system, run
+\fBntpd\fP
+at the specified
+\fBsched_setscheduler(SCHED_FIFO)\fP
+priority.
+.TP
+.NOP \f\*[B-Font]\-q\f[], \f\*[B-Font]\-\-quit\f[]
+Set the time and quit.
+This option must not appear in combination with any of the following options:
+saveconfigquit, wait-sync.
+.sp
+\fBntpd\fP
+will not daemonize and will exit after the clock is first
+synchronized. This behavior mimics that of the
+\fBntpdate\fP
+program, which will soon be replaced with a shell script.
+The
+\fB-g\fP
+and
+\fB-x\fP
+options can be used with this option.
+Note: The kernel time discipline is disabled with this option.
+.TP
+.NOP \f\*[B-Font]\-r\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-propagationdelay\f[]=\f\*[I-Font]string\f[]
+Broadcast/propagation delay.
+.sp
+Specify the default propagation delay from the broadcast/multicast server to this client. This is necessary only if the delay cannot be computed automatically by the protocol.
+.TP
+.NOP \f\*[B-Font]\-\-saveconfigquit\f[]=\f\*[I-Font]string\f[]
+Save parsed configuration and quit.
+This option must not appear in combination with any of the following options:
+quit, wait-sync.
+.sp
+Cause \fBntpd\fP to parse its startup configuration file and save an
+equivalent to the given filename and exit. This option was
+designed for automated testing.
+.TP
+.NOP \f\*[B-Font]\-s\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-statsdir\f[]=\f\*[I-Font]string\f[]
+Statistics file location.
+.sp
+Specify the directory path for files created by the statistics facility.
+This is the same operation as the
+\fBstatsdir\fP \fIstatsdir\fP
+configuration file directive.
+.TP
+.NOP \f\*[B-Font]\-t\f[] \f\*[I-Font]tkey\f[], \f\*[B-Font]\-\-trustedkey\f[]=\f\*[I-Font]tkey\f[]
+Trusted key number.
+This option may appear an unlimited number of times.
+.sp
+Add the specified key number to the trusted key list.
+.TP
+.NOP \f\*[B-Font]\-u\f[] \f\*[I-Font]string\f[], \f\*[B-Font]\-\-user\f[]=\f\*[I-Font]string\f[]
+Run as userid (or userid:groupid).
+.sp
+Specify a user, and optionally a group, to switch to.
+This option is only available if the OS supports adjusting the clock
+without full root privileges.
+This option is supported under NetBSD (configure with
+\fB--enable-clockctl\fP) or Linux (configure with
+\fB--enable-linuxcaps\fP) or Solaris (configure with \fB--enable-solarisprivs\fP).
+.TP
+.NOP \f\*[B-Font]\-U\f[] \f\*[I-Font]number\f[], \f\*[B-Font]\-\-updateinterval\f[]=\f\*[I-Font]number\f[]
+interval in seconds between scans for new or dropped interfaces.
+This option takes an integer number as its argument.
+.sp
+Give the time in seconds between two scans for new or dropped interfaces.
+For systems with routing socket support the scans will be performed shortly after the interface change
+has been detected by the system.
+Use 0 to disable scanning. 60 seconds is the minimum time between scans.
+.TP
+.NOP \f\*[B-Font]\-\-var\f[]=\f\*[I-Font]nvar\f[]
+make ARG an ntp variable (RW).
+This option may appear an unlimited number of times.
+.sp
+.TP
+.NOP \f\*[B-Font]\-\-dvar\f[]=\f\*[I-Font]ndvar\f[]
+make ARG an ntp variable (RW|DEF).
+This option may appear an unlimited number of times.
+.sp
+.TP
+.NOP \f\*[B-Font]\-w\f[] \f\*[I-Font]number\f[], \f\*[B-Font]\-\-wait\-sync\f[]=\f\*[I-Font]number\f[]
+Seconds to wait for first clock sync.
+This option must not appear in combination with any of the following options:
+nofork, quit, saveconfigquit.
+This option takes an integer number as its argument.
+.sp
+If greater than zero, alters \fBntpd\fP's behavior when forking to
+daemonize. Instead of exiting with status 0 immediately after
+the fork, the parent waits up to the specified number of
+seconds for the child to first synchronize the clock. The exit
+status is zero (success) if the clock was synchronized,
+otherwise it is \fBETIMEDOUT\fP.
+This provides the option for a script starting \fBntpd\fP to easily
+wait for the first set of the clock before proceeding.
+.TP
+.NOP \f\*[B-Font]\-x\f[], \f\*[B-Font]\-\-slew\f[]
+Slew up to 600 seconds.
+.sp
+Normally, the time is slewed if the offset is less than the step threshold, which is 128 ms by default, and stepped if above the threshold.
+This option sets the threshold to 600 s, which is well within the accuracy window to set the clock manually.
+Note: Since the slew rate of typical Unix kernels is limited to 0.5 ms/s, each second of adjustment requires an amortization interval of 2000 s.
+Thus, an adjustment as much as 600 s will take almost 14 days to complete.
+This option can be used with the
+\fB-g\fP
+and
+\fB-q\fP
+options.
+See the
+\fBtinker\fP
+configuration file directive for other options.
+Note: The kernel time discipline is disabled with this option.
+.TP
+.NOP \f\*[B-Font]\-\-usepcc\f[]
+Use CPU cycle counter (Windows only).
+.sp
+Attempt to substitute the CPU counter for \fBQueryPerformanceCounter\fP.
+The CPU counter and \fBQueryPerformanceCounter\fP are compared, and if
+they have the same frequency, the CPU counter (RDTSC on x86) is
+used directly, saving the overhead of a system call.
+.TP
+.NOP \f\*[B-Font]\-\-pccfreq\f[]=\f\*[I-Font]string\f[]
+Force CPU cycle counter use (Windows only).
+.sp
+Force substitution the CPU counter for \fBQueryPerformanceCounter\fP.
+The CPU counter (RDTSC on x86) is used unconditionally with the
+given frequency (in Hz).
+.TP
+.NOP \f\*[B-Font]\-m\f[], \f\*[B-Font]\-\-mdns\f[]
+Register with mDNS as a NTP server.
+.sp
+Registers as an NTP server with the local mDNS server which allows
+the server to be discovered via mDNS client lookup.
+.TP
+.NOP \f\*[B-Font]\-\&?\f[], \f\*[B-Font]\-\-help\f[]
+Display usage information and exit.
+.TP
+.NOP \f\*[B-Font]\-\&!\f[], \f\*[B-Font]\-\-more-help\f[]
+Pass the extended usage information through a pager.
+.TP
+.NOP \f\*[B-Font]\-\-version\f[] [{\f\*[I-Font]v|c|n\f[]}]
+Output version of program and exit. The default mode is `v', a simple
+version. The `c' mode will print copyright information and `n' will
+print the full copyright notice.
+.PP
+.SH "OPTION PRESETS"
+Any option that is not marked as \fInot presettable\fP may be preset
+by loading values from environment variables named:
+.nf
+ \fBNTPD_<option-name>\fP or \fBNTPD\fP
+.fi
+.ad
+.SH USAGE
+.SS "How NTP Operates"
+The
+\f\*[B-Font]ntpd\fP
+utility operates by exchanging messages with
+one or more configured servers over a range of designated poll intervals.
+When
+started, whether for the first or subsequent times, the program
+requires several exchanges from the majority of these servers so
+the signal processing and mitigation algorithms can accumulate and
+groom the data and set the clock.
+In order to protect the network
+from bursts, the initial poll interval for each server is delayed
+an interval randomized over a few seconds.
+At the default initial poll
+interval of 64s, several minutes can elapse before the clock is
+set.
+This initial delay to set the clock
+can be safely and dramatically reduced using the
+\f\*[B-Font]iburst\f[]
+keyword with the
+\f\*[B-Font]server\f[]
+configuration
+command, as described in
+\fCntp.conf\fR(5)\f[].
+.sp \n(Ppu
+.ne 2
+
+Most operating systems and hardware of today incorporate a
+time-of-year (TOY) chip to maintain the time during periods when
+the power is off.
+When the machine is booted, the chip is used to
+initialize the operating system time.
+After the machine has
+synchronized to a NTP server, the operating system corrects the
+chip from time to time.
+In the default case, if
+\f\*[B-Font]ntpd\fP
+detects that the time on the host
+is more than 1000s from the server time,
+\f\*[B-Font]ntpd\fP
+assumes something must be terribly wrong and the only
+reliable action is for the operator to intervene and set the clock
+by hand.
+(Reasons for this include there is no TOY chip,
+or its battery is dead, or that the TOY chip is just of poor quality.)
+This causes
+\f\*[B-Font]ntpd\fP
+to exit with a panic message to
+the system log.
+The
+\f\*[B-Font]\-g\f[]
+option overrides this check and the
+clock will be set to the server time regardless of the chip time
+(up to 68 years in the past or future \(em
+this is a limitation of the NTPv4 protocol).
+However, and to protect against broken hardware, such as when the
+CMOS battery fails or the clock counter becomes defective, once the
+clock has been set an error greater than 1000s will cause
+\f\*[B-Font]ntpd\fP
+to exit anyway.
+.sp \n(Ppu
+.ne 2
+
+Under ordinary conditions,
+\f\*[B-Font]ntpd\fP
+adjusts the clock in
+small steps so that the timescale is effectively continuous and
+without discontinuities.
+Under conditions of extreme network
+congestion, the roundtrip delay jitter can exceed three seconds and
+the synchronization distance, which is equal to one-half the
+roundtrip delay plus error budget terms, can become very large.
+The
+\f\*[B-Font]ntpd\fP
+algorithms discard sample offsets exceeding 128 ms,
+unless the interval during which no sample offset is less than 128
+ms exceeds 900s.
+The first sample after that, no matter what the
+offset, steps the clock to the indicated time.
+In practice this
+reduces the false alarm rate where the clock is stepped in error to
+a vanishingly low incidence.
+.sp \n(Ppu
+.ne 2
+
+As the result of this behavior, once the clock has been set it
+very rarely strays more than 128 ms even under extreme cases of
+network path congestion and jitter.
+Sometimes, in particular when
+\f\*[B-Font]ntpd\fP
+is first started without a valid drift file
+on a system with a large intrinsic drift
+the error might grow to exceed 128 ms,
+which would cause the clock to be set backwards
+if the local clock time is more than 128 s
+in the future relative to the server.
+In some applications, this behavior may be unacceptable.
+There are several solutions, however.
+If the
+\f\*[B-Font]\-x\f[]
+option is included on the command line, the clock will
+never be stepped and only slew corrections will be used.
+But this choice comes with a cost that
+should be carefully explored before deciding to use
+the
+\f\*[B-Font]\-x\f[]
+option.
+The maximum slew rate possible is limited
+to 500 parts-per-million (PPM) as a consequence of the correctness
+principles on which the NTP protocol and algorithm design are
+based.
+As a result, the local clock can take a long time to
+converge to an acceptable offset, about 2,000 s for each second the
+clock is outside the acceptable range.
+During this interval the
+local clock will not be consistent with any other network clock and
+the system cannot be used for distributed applications that require
+correctly synchronized network time.
+.sp \n(Ppu
+.ne 2
+
+In spite of the above precautions, sometimes when large
+frequency errors are present the resulting time offsets stray
+outside the 128-ms range and an eventual step or slew time
+correction is required.
+If following such a correction the
+frequency error is so large that the first sample is outside the
+acceptable range,
+\f\*[B-Font]ntpd\fP
+enters the same state as when the
+\fIntp.drift\f[]
+file is not present.
+The intent of this behavior
+is to quickly correct the frequency and restore operation to the
+normal tracking mode.
+In the most extreme cases
+(the host
+\f\*[B-Font]time.ien.it\f[]
+comes to mind), there may be occasional
+step/slew corrections and subsequent frequency corrections.
+It
+helps in these cases to use the
+\f\*[B-Font]burst\f[]
+keyword when
+configuring the server, but
+ONLY
+when you have permission to do so from the owner of the target host.
+.sp \n(Ppu
+.ne 2
+
+Finally,
+in the past many startup scripts would run
+\fCntpdate\fR(@NTPDATE_MS@)\f[]
+to get the system clock close to correct before starting
+\fCntpd\fR(@NTPD_MS@)\f[],
+but this was never more than a mediocre hack and is no longer needed.
+If you are following the instructions in
+\fIStarting NTP (Best Current Practice)\f[]
+and you still need to set the system time before starting
+\f\*[B-Font]ntpd\fP,
+please open a bug report and document what is going on,
+and then look at using
+\fCsntp\fR(@SNTP_MS@)\f[].
+.sp \n(Ppu
+.ne 2
+
+There is a way to start
+\fCntpd\fR(@NTPD_MS@)\f[]
+that often addresses all of the problems mentioned above.
+.SS "Starting NTP (Best Current Practice)"
+First, use the
+\f\*[B-Font]iburst\f[]
+option on your
+\f\*[B-Font]server\f[]
+entries.
+.sp \n(Ppu
+.ne 2
+
+If you can also keep a good
+\fIntp.drift\f[]
+file then
+\fCntpd\fR(@NTPD_MS@)\f[]
+will effectively "warm-start" and your system's clock will
+be stable in under 11 seconds' time.
+.sp \n(Ppu
+.ne 2
+
+As soon as possible in the startup sequence, start
+\fCntpd\fR(@NTPD_MS@)\f[]
+with at least the
+\f\*[B-Font]\-g\f[]
+and perhaps the
+\f\*[B-Font]\-N\f[]
+options.
+Then,
+start the rest of your "normal" processes.
+This will give
+\fCntpd\fR(@NTPD_MS@)\f[]
+as much time as possible to get the system's clock synchronized and stable.
+.sp \n(Ppu
+.ne 2
+
+Finally,
+if you have processes like
+\f\*[B-Font]dovecot\f[]
+or database servers
+that require
+monotonically-increasing time,
+run
+\fCntp-wait\fR(@NTP_WAIT_MS@)\f[]
+as late as possible in the boot sequence
+(perhaps with the
+\f\*[B-Font]\-v\f[]
+flag)
+and after
+\fCntp-wait\fR(@NTP_WAIT_MS@)\f[]
+exits successfully
+it is as safe as it will ever be to start any process that require
+stable time.
+.SS "Frequency Discipline"
+The
+\f\*[B-Font]ntpd\fP
+behavior at startup depends on whether the
+frequency file, usually
+\fIntp.drift\f[],
+exists.
+This file
+contains the latest estimate of clock frequency error.
+When the
+\f\*[B-Font]ntpd\fP
+is started and the file does not exist, the
+\f\*[B-Font]ntpd\fP
+enters a special mode designed to quickly adapt to
+the particular system clock oscillator time and frequency error.
+This takes approximately 15 minutes, after which the time and
+frequency are set to nominal values and the
+\f\*[B-Font]ntpd\fP
+enters
+normal mode, where the time and frequency are continuously tracked
+relative to the server.
+After one hour the frequency file is
+created and the current frequency offset written to it.
+When the
+\f\*[B-Font]ntpd\fP
+is started and the file does exist, the
+\f\*[B-Font]ntpd\fP
+frequency is initialized from the file and enters normal mode
+immediately.
+After that the current frequency offset is written to
+the file at hourly intervals.
+.SS "Operating Modes"
+The
+\f\*[B-Font]ntpd\fP
+utility can operate in any of several modes, including
+symmetric active/passive, client/server broadcast/multicast and
+manycast, as described in the
+"Association Management"
+page
+(available as part of the HTML documentation
+provided in
+\fI/usr/share/doc/ntp\f[]).
+It normally operates continuously while
+monitoring for small changes in frequency and trimming the clock
+for the ultimate precision.
+However, it can operate in a one-time
+mode where the time is set from an external server and frequency is
+set from a previously recorded frequency file.
+A
+broadcast/multicast or manycast client can discover remote servers,
+compute server-client propagation delay correction factors and
+configure itself automatically.
+This makes it possible to deploy a
+fleet of workstations without specifying configuration details
+specific to the local environment.
+.sp \n(Ppu
+.ne 2
+
+By default,
+\f\*[B-Font]ntpd\fP
+runs in continuous mode where each of
+possibly several external servers is polled at intervals determined
+by an intricate state machine.
+The state machine measures the
+incidental roundtrip delay jitter and oscillator frequency wander
+and determines the best poll interval using a heuristic algorithm.
+Ordinarily, and in most operating environments, the state machine
+will start with 64s intervals and eventually increase in steps to
+1024s.
+A small amount of random variation is introduced in order to
+avoid bunching at the servers.
+In addition, should a server become
+unreachable for some time, the poll interval is increased in steps
+to 1024s in order to reduce network overhead.
+.sp \n(Ppu
+.ne 2
+
+In some cases it may not be practical for
+\f\*[B-Font]ntpd\fP
+to run continuously.
+A common workaround has been to run the
+\fCntpdate\fR(@NTPDATE_MS@)\f[]
+or
+\fCsntp\fR(@SNTP_MS@)\f[]
+programs from a
+\fCcron\fR(8)\f[]
+job at designated
+times.
+However, these programs do not have the crafted signal
+processing, error checking or mitigation algorithms of
+\f\*[B-Font]ntpd\fP.
+The
+\f\*[B-Font]\-q\f[]
+option is intended for this purpose.
+Setting this option will cause
+\f\*[B-Font]ntpd\fP
+to exit just after
+setting the clock for the first time.
+The procedure for initially
+setting the clock is the same as in continuous mode; most
+applications will probably want to specify the
+\f\*[B-Font]iburst\f[]
+keyword with the
+\f\*[B-Font]server\f[]
+configuration command.
+With this
+keyword a volley of messages are exchanged to groom the data and
+the clock is set in about 10 s.
+If nothing is heard after a
+couple of minutes, the daemon times out and exits.
+After a suitable
+period of mourning, the
+\fCntpdate\fR(@NTPDATE_MS@)\f[]
+program will be
+retired.
+.sp \n(Ppu
+.ne 2
+
+When kernel support is available to discipline the clock
+frequency, which is the case for stock Solaris, Tru64, Linux and
+FreeBSD,
+a useful feature is available to discipline the clock
+frequency.
+First,
+\f\*[B-Font]ntpd\fP
+is run in continuous mode with
+selected servers in order to measure and record the intrinsic clock
+frequency offset in the frequency file.
+It may take some hours for
+the frequency and offset to settle down.
+Then the
+\f\*[B-Font]ntpd\fP
+is
+stopped and run in one-time mode as required.
+At each startup, the
+frequency is read from the file and initializes the kernel
+frequency.
+.SS "Poll Interval Control"
+This version of NTP includes an intricate state machine to
+reduce the network load while maintaining a quality of
+synchronization consistent with the observed jitter and wander.
+There are a number of ways to tailor the operation in order enhance
+accuracy by reducing the interval or to reduce network overhead by
+increasing it.
+However, the user is advised to carefully consider
+the consequences of changing the poll adjustment range from the
+default minimum of 64 s to the default maximum of 1,024 s.
+The
+default minimum can be changed with the
+\f\*[B-Font]tinker\f[]
+\f\*[B-Font]minpoll\f[]
+command to a value not less than 16 s.
+This value is used for all
+configured associations, unless overridden by the
+\f\*[B-Font]minpoll\f[]
+option on the configuration command.
+Note that most device drivers
+will not operate properly if the poll interval is less than 64 s
+and that the broadcast server and manycast client associations will
+also use the default, unless overridden.
+.sp \n(Ppu
+.ne 2
+
+In some cases involving dial up or toll services, it may be
+useful to increase the minimum interval to a few tens of minutes
+and maximum interval to a day or so.
+Under normal operation
+conditions, once the clock discipline loop has stabilized the
+interval will be increased in steps from the minimum to the
+maximum.
+However, this assumes the intrinsic clock frequency error
+is small enough for the discipline loop correct it.
+The capture
+range of the loop is 500 PPM at an interval of 64s decreasing by a
+factor of two for each doubling of interval.
+At a minimum of 1,024
+s, for example, the capture range is only 31 PPM.
+If the intrinsic
+error is greater than this, the drift file
+\fIntp.drift\f[]
+will
+have to be specially tailored to reduce the residual error below
+this limit.
+Once this is done, the drift file is automatically
+updated once per hour and is available to initialize the frequency
+on subsequent daemon restarts.
+.SS "The huff-n'-puff Filter"
+In scenarios where a considerable amount of data are to be
+downloaded or uploaded over telephone modems, timekeeping quality
+can be seriously degraded.
+This occurs because the differential
+delays on the two directions of transmission can be quite large.
+In
+many cases the apparent time errors are so large as to exceed the
+step threshold and a step correction can occur during and after the
+data transfer is in progress.
+.sp \n(Ppu
+.ne 2
+
+The huff-n'-puff filter is designed to correct the apparent time
+offset in these cases.
+It depends on knowledge of the propagation
+delay when no other traffic is present.
+In common scenarios this
+occurs during other than work hours.
+The filter maintains a shift
+register that remembers the minimum delay over the most recent
+interval measured usually in hours.
+Under conditions of severe
+delay, the filter corrects the apparent offset using the sign of
+the offset and the difference between the apparent delay and
+minimum delay.
+The name of the filter reflects the negative (huff)
+and positive (puff) correction, which depends on the sign of the
+offset.
+.sp \n(Ppu
+.ne 2
+
+The filter is activated by the
+\f\*[B-Font]tinker\f[]
+command and
+\f\*[B-Font]huffpuff\f[]
+keyword, as described in
+\fCntp.conf\fR(5)\f[].
+.SH "ENVIRONMENT"
+See \fBOPTION PRESETS\fP for configuration environment variables.
+.SH FILES
+.TP 15
+.NOP \fI/etc/ntp.conf\f[]
+the default name of the configuration file
+.br
+.ns
+.TP 15
+.NOP \fI/etc/ntp.drift\f[]
+the default name of the drift file
+.br
+.ns
+.TP 15
+.NOP \fI/etc/ntp.keys\f[]
+the default name of the key file
+.PP
+.SH "EXIT STATUS"
+One of the following exit values will be returned:
+.TP
+.NOP 0 " (EXIT_SUCCESS)"
+Successful program execution.
+.TP
+.NOP 1 " (EXIT_FAILURE)"
+The operation failed or the command syntax was not valid.
+.TP
+.NOP 70 " (EX_SOFTWARE)"
+libopts had an internal operational error. Please report
+it to autogen-users@lists.sourceforge.net. Thank you.
+.PP
+.SH "SEE ALSO"
+\fCntp.conf\fR(5)\f[],
+\fCntpdate\fR(@NTPDATE_MS@)\f[],
+\fCntpdc\fR(@NTPDC_MS@)\f[],
+\fCntpq\fR(@NTPQ_MS@)\f[],
+\fCsntp\fR(@SNTP_MS@)\f[]
+.sp \n(Ppu
+.ne 2
+
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+\f[C]http://www.ntp.org/\f[].
+A snapshot of this documentation is available in HTML format in
+\fI/usr/share/doc/ntp\f[].
+David L. Mills,
+\fINetwork Time Protocol (Version 1)\fR,
+RFC1059
+.PP
+
+David L. Mills,
+\fINetwork Time Protocol (Version 2)\fR,
+RFC1119
+.PP
+
+David L. Mills,
+\fINetwork Time Protocol (Version 3)\fR,
+RFC1305
+.PP
+
+David L. Mills and J. Martin, Ed. and J. Burbank and W. Kasch,
+\fINetwork Time Protocol Version 4: Protocol and Algorithms Specification\fR,
+RFC5905
+.PP
+
+David L. Mills and B. Haberman, Ed.,
+\fINetwork Time Protocol Version 4: Autokey Specification\fR,
+RFC5906
+.PP
+
+H. Gerstung and C. Elliott and B. Haberman, Ed.,
+\fIDefinitions of Managed Objects for Network Time Protocol Version 4: (NTPv4)\fR,
+RFC5907
+.PP
+
+R. Gayraud and B. Lourdelet,
+\fINetwork Time Protocol (NTP) Server Option for DHCPv6\fR,
+RFC5908
+.PP
+
+.SH "AUTHORS"
+The University of Delaware
+.SH "COPYRIGHT"
+Copyright (C) 1970-2014 The University of Delaware all rights reserved.
+This program is released under the terms of the NTP license, <http://ntp.org/license>.
+.SH BUGS
+The
+\f\*[B-Font]ntpd\fP
+utility has gotten rather fat.
+While not huge, it has gotten
+larger than might be desirable for an elevated-priority
+\f\*[B-Font]ntpd\fP
+running on a workstation, particularly since many of
+the fancy features which consume the space were designed more with
+a busy primary server, rather than a high stratum workstation in
+mind.
+.sp \n(Ppu
+.ne 2
+
+Please send bug reports to: http://bugs.ntp.org, bugs@ntp.org
+.SH NOTES
+Portions of this document came from FreeBSD.
+.sp \n(Ppu
+.ne 2
+
+This manual page was \fIAutoGen\fP-erated from the \fBntpd\fP
+option definitions.
diff --git a/ntpd/ntpd.mdoc.in b/ntpd/ntpd.mdoc.in
new file mode 100644
index 0000000..1c06102
--- /dev/null
+++ b/ntpd/ntpd.mdoc.in
@@ -0,0 +1,891 @@
+.Dd December 2 2014
+.Dt NTPD @NTPD_MS@ User Commands
+.Os
+.\" EDIT THIS FILE WITH CAUTION (ntpd-opts.mdoc)
+.\"
+.\" It has been AutoGen-ed December 2, 2014 at 08:57:03 AM by AutoGen 5.18.5pre4
+.\" From the definitions ntpd-opts.def
+.\" and the template file agmdoc-cmd.tpl
+.Sh NAME
+.Nm ntpd
+.Nd NTP daemon program
+.Sh SYNOPSIS
+.Nm
+.\" Mixture of short (flag) options and long options
+.Op Fl flags
+.Op Fl flag Op Ar value
+.Op Fl \-option\-name Ns Oo Oo Ns "=| " Oc Ns Ar value Oc
+[ <server1> ... <serverN> ]
+.Pp
+.Sh DESCRIPTION
+The
+.Nm
+utility is an operating system daemon which sets
+and maintains the system time of day in synchronism with Internet
+standard time servers.
+It is a complete implementation of the
+Network Time Protocol (NTP) version 4, as defined by RFC\-5905,
+but also retains compatibility with
+version 3, as defined by RFC\-1305, and versions 1
+and 2, as defined by RFC\-1059 and RFC\-1119, respectively.
+.Pp
+The
+.Nm
+utility does most computations in 64\-bit floating point
+arithmetic and does relatively clumsy 64\-bit fixed point operations
+only when necessary to preserve the ultimate precision, about 232
+picoseconds.
+While the ultimate precision is not achievable with
+ordinary workstations and networks of today, it may be required
+with future gigahertz CPU clocks and gigabit LANs.
+.Pp
+Ordinarily,
+.Nm
+reads the
+.Xr ntp.conf 5
+configuration file at startup time in order to determine the
+synchronization sources and operating modes.
+It is also possible to
+specify a working, although limited, configuration entirely on the
+command line, obviating the need for a configuration file.
+This may
+be particularly useful when the local host is to be configured as a
+broadcast/multicast client, with all peers being determined by
+listening to broadcasts at run time.
+.Pp
+If NetInfo support is built into
+.Nm ,
+then
+.Nm
+will attempt to read its configuration from the
+NetInfo if the default
+.Xr ntp.conf 5
+file cannot be read and no file is
+specified by the
+.Fl c
+option.
+.Pp
+Various internal
+.Nm
+variables can be displayed and
+configuration options altered while the
+.Nm
+is running
+using the
+.Xr ntpq @NTPQ_MS@
+and
+.Xr ntpdc @NTPDC_MS@
+utility programs.
+.Pp
+When
+.Nm
+starts it looks at the value of
+.Xr umask 2 ,
+and if zero
+.Nm
+will set the
+.Xr umask 2
+to 022.
+.Sh "OPTIONS"
+.Bl -tag
+.It Fl 4 , Fl \-ipv4
+Force IPv4 DNS name resolution.
+This option must not appear in combination with any of the following options:
+ipv6.
+.sp
+Force DNS resolution of following host names on the command line
+to the IPv4 namespace.
+.It Fl 6 , Fl \-ipv6
+Force IPv6 DNS name resolution.
+This option must not appear in combination with any of the following options:
+ipv4.
+.sp
+Force DNS resolution of following host names on the command line
+to the IPv6 namespace.
+.It Fl a , Fl \-authreq
+Require crypto authentication.
+This option must not appear in combination with any of the following options:
+authnoreq.
+.sp
+Require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is the default.
+.It Fl A , Fl \-authnoreq
+Do not require crypto authentication.
+This option must not appear in combination with any of the following options:
+authreq.
+.sp
+Do not require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is almost never a good idea.
+.It Fl b , Fl \-bcastsync
+Allow us to sync to broadcast servers.
+.sp
+.It Fl c Ar string , Fl \-configfile Ns = Ns Ar string
+configuration file name.
+.sp
+The name and path of the configuration file,
+\fI/etc/ntp.conf\fP
+by default.
+.It Fl d , Fl \-debug\-level
+Increase debug verbosity level.
+This option may appear an unlimited number of times.
+.sp
+.It Fl D Ar number , Fl \-set\-debug\-level Ns = Ns Ar number
+Set the debug verbosity level.
+This option may appear an unlimited number of times.
+This option takes an integer number as its argument.
+.sp
+.It Fl f Ar string , Fl \-driftfile Ns = Ns Ar string
+frequency drift file name.
+.sp
+The name and path of the frequency file,
+\fI/etc/ntp.drift\fP
+by default.
+This is the same operation as the
+\fBdriftfile\fP \fIdriftfile\fP
+configuration specification in the
+\fI/etc/ntp.conf\fP
+file.
+.It Fl g , Fl \-panicgate
+Allow the first adjustment to be Big.
+This option may appear an unlimited number of times.
+.sp
+Normally,
+\fBntpd\fP
+exits with a message to the system log if the offset exceeds the panic threshold, which is 1000 s by default. This option allows the time to be set to any value without restriction; however, this can happen only once. If the threshold is exceeded after that,
+\fBntpd\fP
+will exit with a message to the system log. This option can be used with the
+\fB\-q\fP
+and
+\fB\-x\fP
+options.
+See the
+\fBtinker\fP
+configuration file directive for other options.
+.It Fl i Ar string , Fl \-jaildir Ns = Ns Ar string
+Jail directory.
+.sp
+Chroot the server to the directory
+\fIjaildir\fP
+.
+This option also implies that the server attempts to drop root privileges at startup.
+You may need to also specify a
+\fB\-u\fP
+option.
+This option is only available if the OS supports adjusting the clock
+without full root privileges.
+This option is supported under NetBSD (configure with
+\fB\-\-enable\-clockctl\fP) or Linux (configure with
+\fB\-\-enable\-linuxcaps\fP) or Solaris (configure with \fB\-\-enable\-solarisprivs\fP).
+.It Fl I Ar iface , Fl \-interface Ns = Ns Ar iface
+Listen on an interface name or address.
+This option may appear an unlimited number of times.
+.sp
+Open the network address given, or all the addresses associated with the
+given interface name. This option may appear multiple times. This option
+also implies not opening other addresses, except wildcard and localhost.
+This option is deprecated. Please consider using the configuration file
+\fBinterface\fP command, which is more versatile.
+.It Fl k Ar string , Fl \-keyfile Ns = Ns Ar string
+path to symmetric keys.
+.sp
+Specify the name and path of the symmetric key file.
+\fI/etc/ntp.keys\fP
+is the default.
+This is the same operation as the
+\fBkeys\fP \fIkeyfile\fP
+configuration file directive.
+.It Fl l Ar string , Fl \-logfile Ns = Ns Ar string
+path to the log file.
+.sp
+Specify the name and path of the log file.
+The default is the system log file.
+This is the same operation as the
+\fBlogfile\fP \fIlogfile\fP
+configuration file directive.
+.It Fl L , Fl \-novirtualips
+Do not listen to virtual interfaces.
+.sp
+Do not listen to virtual interfaces, defined as those with
+names containing a colon. This option is deprecated. Please
+consider using the configuration file \fBinterface\fP command, which
+is more versatile.
+.It Fl M , Fl \-modifymmtimer
+Modify Multimedia Timer (Windows only).
+.sp
+Set the Windows Multimedia Timer to highest resolution. This
+ensures the resolution does not change while ntpd is running,
+avoiding timekeeping glitches associated with changes.
+.It Fl n , Fl \-nofork
+Do not fork.
+This option must not appear in combination with any of the following options:
+wait\-sync.
+.sp
+.It Fl N , Fl \-nice
+Run at high priority.
+.sp
+To the extent permitted by the operating system, run
+\fBntpd\fP
+at the highest priority.
+.It Fl p Ar string , Fl \-pidfile Ns = Ns Ar string
+path to the PID file.
+.sp
+Specify the name and path of the file used to record
+\fBntpd\fP's
+process ID.
+This is the same operation as the
+\fBpidfile\fP \fIpidfile\fP
+configuration file directive.
+.It Fl P Ar number , Fl \-priority Ns = Ns Ar number
+Process priority.
+This option takes an integer number as its argument.
+.sp
+To the extent permitted by the operating system, run
+\fBntpd\fP
+at the specified
+\fBsched_setscheduler(SCHED_FIFO)\fP
+priority.
+.It Fl q , Fl \-quit
+Set the time and quit.
+This option must not appear in combination with any of the following options:
+saveconfigquit, wait\-sync.
+.sp
+\fBntpd\fP
+will not daemonize and will exit after the clock is first
+synchronized. This behavior mimics that of the
+\fBntpdate\fP
+program, which will soon be replaced with a shell script.
+The
+\fB\-g\fP
+and
+\fB\-x\fP
+options can be used with this option.
+Note: The kernel time discipline is disabled with this option.
+.It Fl r Ar string , Fl \-propagationdelay Ns = Ns Ar string
+Broadcast/propagation delay.
+.sp
+Specify the default propagation delay from the broadcast/multicast server to this client. This is necessary only if the delay cannot be computed automatically by the protocol.
+.It Fl \-saveconfigquit Ns = Ns Ar string
+Save parsed configuration and quit.
+This option must not appear in combination with any of the following options:
+quit, wait\-sync.
+.sp
+Cause \fBntpd\fP to parse its startup configuration file and save an
+equivalent to the given filename and exit. This option was
+designed for automated testing.
+.It Fl s Ar string , Fl \-statsdir Ns = Ns Ar string
+Statistics file location.
+.sp
+Specify the directory path for files created by the statistics facility.
+This is the same operation as the
+\fBstatsdir\fP \fIstatsdir\fP
+configuration file directive.
+.It Fl t Ar tkey , Fl \-trustedkey Ns = Ns Ar tkey
+Trusted key number.
+This option may appear an unlimited number of times.
+.sp
+Add the specified key number to the trusted key list.
+.It Fl u Ar string , Fl \-user Ns = Ns Ar string
+Run as userid (or userid:groupid).
+.sp
+Specify a user, and optionally a group, to switch to.
+This option is only available if the OS supports adjusting the clock
+without full root privileges.
+This option is supported under NetBSD (configure with
+\fB\-\-enable\-clockctl\fP) or Linux (configure with
+\fB\-\-enable\-linuxcaps\fP) or Solaris (configure with \fB\-\-enable\-solarisprivs\fP).
+.It Fl U Ar number , Fl \-updateinterval Ns = Ns Ar number
+interval in seconds between scans for new or dropped interfaces.
+This option takes an integer number as its argument.
+.sp
+Give the time in seconds between two scans for new or dropped interfaces.
+For systems with routing socket support the scans will be performed shortly after the interface change
+has been detected by the system.
+Use 0 to disable scanning. 60 seconds is the minimum time between scans.
+.It Fl \-var Ns = Ns Ar nvar
+make ARG an ntp variable (RW).
+This option may appear an unlimited number of times.
+.sp
+.It Fl \-dvar Ns = Ns Ar ndvar
+make ARG an ntp variable (RW|DEF).
+This option may appear an unlimited number of times.
+.sp
+.It Fl w Ar number , Fl \-wait\-sync Ns = Ns Ar number
+Seconds to wait for first clock sync.
+This option must not appear in combination with any of the following options:
+nofork, quit, saveconfigquit.
+This option takes an integer number as its argument.
+.sp
+If greater than zero, alters \fBntpd\fP's behavior when forking to
+daemonize. Instead of exiting with status 0 immediately after
+the fork, the parent waits up to the specified number of
+seconds for the child to first synchronize the clock. The exit
+status is zero (success) if the clock was synchronized,
+otherwise it is \fBETIMEDOUT\fP.
+This provides the option for a script starting \fBntpd\fP to easily
+wait for the first set of the clock before proceeding.
+.It Fl x , Fl \-slew
+Slew up to 600 seconds.
+.sp
+Normally, the time is slewed if the offset is less than the step threshold, which is 128 ms by default, and stepped if above the threshold.
+This option sets the threshold to 600 s, which is well within the accuracy window to set the clock manually.
+Note: Since the slew rate of typical Unix kernels is limited to 0.5 ms/s, each second of adjustment requires an amortization interval of 2000 s.
+Thus, an adjustment as much as 600 s will take almost 14 days to complete.
+This option can be used with the
+\fB\-g\fP
+and
+\fB\-q\fP
+options.
+See the
+\fBtinker\fP
+configuration file directive for other options.
+Note: The kernel time discipline is disabled with this option.
+.It Fl \-usepcc
+Use CPU cycle counter (Windows only).
+.sp
+Attempt to substitute the CPU counter for \fBQueryPerformanceCounter\fP.
+The CPU counter and \fBQueryPerformanceCounter\fP are compared, and if
+they have the same frequency, the CPU counter (RDTSC on x86) is
+used directly, saving the overhead of a system call.
+.It Fl \-pccfreq Ns = Ns Ar string
+Force CPU cycle counter use (Windows only).
+.sp
+Force substitution the CPU counter for \fBQueryPerformanceCounter\fP.
+The CPU counter (RDTSC on x86) is used unconditionally with the
+given frequency (in Hz).
+.It Fl m , Fl \-mdns
+Register with mDNS as a NTP server.
+.sp
+Registers as an NTP server with the local mDNS server which allows
+the server to be discovered via mDNS client lookup.
+.It Fl \&? , Fl \-help
+Display usage information and exit.
+.It Fl \&! , Fl \-more\-help
+Pass the extended usage information through a pager.
+.It Fl \-version Op Brq Ar v|c|n
+Output version of program and exit. The default mode is `v', a simple
+version. The `c' mode will print copyright information and `n' will
+print the full copyright notice.
+.El
+.Sh "OPTION PRESETS"
+Any option that is not marked as \fInot presettable\fP may be preset
+by loading values from environment variables named:
+.nf
+ \fBNTPD_<option\-name>\fP or \fBNTPD\fP
+.fi
+.ad
+.Sh USAGE
+.Ss "How NTP Operates"
+The
+.Nm
+utility operates by exchanging messages with
+one or more configured servers over a range of designated poll intervals.
+When
+started, whether for the first or subsequent times, the program
+requires several exchanges from the majority of these servers so
+the signal processing and mitigation algorithms can accumulate and
+groom the data and set the clock.
+In order to protect the network
+from bursts, the initial poll interval for each server is delayed
+an interval randomized over a few seconds.
+At the default initial poll
+interval of 64s, several minutes can elapse before the clock is
+set.
+This initial delay to set the clock
+can be safely and dramatically reduced using the
+.Cm iburst
+keyword with the
+.Ic server
+configuration
+command, as described in
+.Xr ntp.conf 5 .
+.Pp
+Most operating systems and hardware of today incorporate a
+time\-of\-year (TOY) chip to maintain the time during periods when
+the power is off.
+When the machine is booted, the chip is used to
+initialize the operating system time.
+After the machine has
+synchronized to a NTP server, the operating system corrects the
+chip from time to time.
+In the default case, if
+.Nm
+detects that the time on the host
+is more than 1000s from the server time,
+.Nm
+assumes something must be terribly wrong and the only
+reliable action is for the operator to intervene and set the clock
+by hand.
+(Reasons for this include there is no TOY chip,
+or its battery is dead, or that the TOY chip is just of poor quality.)
+This causes
+.Nm
+to exit with a panic message to
+the system log.
+The
+.Fl g
+option overrides this check and the
+clock will be set to the server time regardless of the chip time
+(up to 68 years in the past or future \(em
+this is a limitation of the NTPv4 protocol).
+However, and to protect against broken hardware, such as when the
+CMOS battery fails or the clock counter becomes defective, once the
+clock has been set an error greater than 1000s will cause
+.Nm
+to exit anyway.
+.Pp
+Under ordinary conditions,
+.Nm
+adjusts the clock in
+small steps so that the timescale is effectively continuous and
+without discontinuities.
+Under conditions of extreme network
+congestion, the roundtrip delay jitter can exceed three seconds and
+the synchronization distance, which is equal to one\-half the
+roundtrip delay plus error budget terms, can become very large.
+The
+.Nm
+algorithms discard sample offsets exceeding 128 ms,
+unless the interval during which no sample offset is less than 128
+ms exceeds 900s.
+The first sample after that, no matter what the
+offset, steps the clock to the indicated time.
+In practice this
+reduces the false alarm rate where the clock is stepped in error to
+a vanishingly low incidence.
+.Pp
+As the result of this behavior, once the clock has been set it
+very rarely strays more than 128 ms even under extreme cases of
+network path congestion and jitter.
+Sometimes, in particular when
+.Nm
+is first started without a valid drift file
+on a system with a large intrinsic drift
+the error might grow to exceed 128 ms,
+which would cause the clock to be set backwards
+if the local clock time is more than 128 s
+in the future relative to the server.
+In some applications, this behavior may be unacceptable.
+There are several solutions, however.
+If the
+.Fl x
+option is included on the command line, the clock will
+never be stepped and only slew corrections will be used.
+But this choice comes with a cost that
+should be carefully explored before deciding to use
+the
+.Fl x
+option.
+The maximum slew rate possible is limited
+to 500 parts\-per\-million (PPM) as a consequence of the correctness
+principles on which the NTP protocol and algorithm design are
+based.
+As a result, the local clock can take a long time to
+converge to an acceptable offset, about 2,000 s for each second the
+clock is outside the acceptable range.
+During this interval the
+local clock will not be consistent with any other network clock and
+the system cannot be used for distributed applications that require
+correctly synchronized network time.
+.Pp
+In spite of the above precautions, sometimes when large
+frequency errors are present the resulting time offsets stray
+outside the 128\-ms range and an eventual step or slew time
+correction is required.
+If following such a correction the
+frequency error is so large that the first sample is outside the
+acceptable range,
+.Nm
+enters the same state as when the
+.Pa ntp.drift
+file is not present.
+The intent of this behavior
+is to quickly correct the frequency and restore operation to the
+normal tracking mode.
+In the most extreme cases
+(the host
+.Cm time.ien.it
+comes to mind), there may be occasional
+step/slew corrections and subsequent frequency corrections.
+It
+helps in these cases to use the
+.Cm burst
+keyword when
+configuring the server, but
+ONLY
+when you have permission to do so from the owner of the target host.
+.Pp
+Finally,
+in the past many startup scripts would run
+.Xr ntpdate @NTPDATE_MS@
+to get the system clock close to correct before starting
+.Xr ntpd @NTPD_MS@ ,
+but this was never more than a mediocre hack and is no longer needed.
+If you are following the instructions in
+.Sx "Starting NTP (Best Current Practice)"
+and you still need to set the system time before starting
+.Nm ,
+please open a bug report and document what is going on,
+and then look at using
+.Xr sntp @SNTP_MS@ .
+.Pp
+There is a way to start
+.Xr ntpd @NTPD_MS@
+that often addresses all of the problems mentioned above.
+.Ss "Starting NTP (Best Current Practice)"
+First, use the
+.Cm iburst
+option on your
+.Cm server
+entries.
+.Pp
+If you can also keep a good
+.Pa ntp.drift
+file then
+.Xr ntpd @NTPD_MS@
+will effectively "warm\-start" and your system's clock will
+be stable in under 11 seconds' time.
+.Pp
+As soon as possible in the startup sequence, start
+.Xr ntpd @NTPD_MS@
+with at least the
+.Fl g
+and perhaps the
+.Fl N
+options.
+Then,
+start the rest of your "normal" processes.
+This will give
+.Xr ntpd @NTPD_MS@
+as much time as possible to get the system's clock synchronized and stable.
+.Pp
+Finally,
+if you have processes like
+.Cm dovecot
+or database servers
+that require
+monotonically\-increasing time,
+run
+.Xr ntp\-wait 1ntp\-waitmdoc
+as late as possible in the boot sequence
+(perhaps with the
+.Fl v
+flag)
+and after
+.Xr ntp\-wait 1ntp\-waitmdoc
+exits successfully
+it is as safe as it will ever be to start any process that require
+stable time.
+.Ss "Frequency Discipline"
+The
+.Nm
+behavior at startup depends on whether the
+frequency file, usually
+.Pa ntp.drift ,
+exists.
+This file
+contains the latest estimate of clock frequency error.
+When the
+.Nm
+is started and the file does not exist, the
+.Nm
+enters a special mode designed to quickly adapt to
+the particular system clock oscillator time and frequency error.
+This takes approximately 15 minutes, after which the time and
+frequency are set to nominal values and the
+.Nm
+enters
+normal mode, where the time and frequency are continuously tracked
+relative to the server.
+After one hour the frequency file is
+created and the current frequency offset written to it.
+When the
+.Nm
+is started and the file does exist, the
+.Nm
+frequency is initialized from the file and enters normal mode
+immediately.
+After that the current frequency offset is written to
+the file at hourly intervals.
+.Ss "Operating Modes"
+The
+.Nm
+utility can operate in any of several modes, including
+symmetric active/passive, client/server broadcast/multicast and
+manycast, as described in the
+.Qq Association Management
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+It normally operates continuously while
+monitoring for small changes in frequency and trimming the clock
+for the ultimate precision.
+However, it can operate in a one\-time
+mode where the time is set from an external server and frequency is
+set from a previously recorded frequency file.
+A
+broadcast/multicast or manycast client can discover remote servers,
+compute server\-client propagation delay correction factors and
+configure itself automatically.
+This makes it possible to deploy a
+fleet of workstations without specifying configuration details
+specific to the local environment.
+.Pp
+By default,
+.Nm
+runs in continuous mode where each of
+possibly several external servers is polled at intervals determined
+by an intricate state machine.
+The state machine measures the
+incidental roundtrip delay jitter and oscillator frequency wander
+and determines the best poll interval using a heuristic algorithm.
+Ordinarily, and in most operating environments, the state machine
+will start with 64s intervals and eventually increase in steps to
+1024s.
+A small amount of random variation is introduced in order to
+avoid bunching at the servers.
+In addition, should a server become
+unreachable for some time, the poll interval is increased in steps
+to 1024s in order to reduce network overhead.
+.Pp
+In some cases it may not be practical for
+.Nm
+to run continuously.
+A common workaround has been to run the
+.Xr ntpdate @NTPDATE_MS@
+or
+.Xr sntp @SNTP_MS@
+programs from a
+.Xr cron 8
+job at designated
+times.
+However, these programs do not have the crafted signal
+processing, error checking or mitigation algorithms of
+.Nm .
+The
+.Fl q
+option is intended for this purpose.
+Setting this option will cause
+.Nm
+to exit just after
+setting the clock for the first time.
+The procedure for initially
+setting the clock is the same as in continuous mode; most
+applications will probably want to specify the
+.Cm iburst
+keyword with the
+.Ic server
+configuration command.
+With this
+keyword a volley of messages are exchanged to groom the data and
+the clock is set in about 10 s.
+If nothing is heard after a
+couple of minutes, the daemon times out and exits.
+After a suitable
+period of mourning, the
+.Xr ntpdate @NTPDATE_MS@
+program will be
+retired.
+.Pp
+When kernel support is available to discipline the clock
+frequency, which is the case for stock Solaris, Tru64, Linux and
+.Fx ,
+a useful feature is available to discipline the clock
+frequency.
+First,
+.Nm
+is run in continuous mode with
+selected servers in order to measure and record the intrinsic clock
+frequency offset in the frequency file.
+It may take some hours for
+the frequency and offset to settle down.
+Then the
+.Nm
+is
+stopped and run in one\-time mode as required.
+At each startup, the
+frequency is read from the file and initializes the kernel
+frequency.
+.Ss "Poll Interval Control"
+This version of NTP includes an intricate state machine to
+reduce the network load while maintaining a quality of
+synchronization consistent with the observed jitter and wander.
+There are a number of ways to tailor the operation in order enhance
+accuracy by reducing the interval or to reduce network overhead by
+increasing it.
+However, the user is advised to carefully consider
+the consequences of changing the poll adjustment range from the
+default minimum of 64 s to the default maximum of 1,024 s.
+The
+default minimum can be changed with the
+.Ic tinker
+.Cm minpoll
+command to a value not less than 16 s.
+This value is used for all
+configured associations, unless overridden by the
+.Cm minpoll
+option on the configuration command.
+Note that most device drivers
+will not operate properly if the poll interval is less than 64 s
+and that the broadcast server and manycast client associations will
+also use the default, unless overridden.
+.Pp
+In some cases involving dial up or toll services, it may be
+useful to increase the minimum interval to a few tens of minutes
+and maximum interval to a day or so.
+Under normal operation
+conditions, once the clock discipline loop has stabilized the
+interval will be increased in steps from the minimum to the
+maximum.
+However, this assumes the intrinsic clock frequency error
+is small enough for the discipline loop correct it.
+The capture
+range of the loop is 500 PPM at an interval of 64s decreasing by a
+factor of two for each doubling of interval.
+At a minimum of 1,024
+s, for example, the capture range is only 31 PPM.
+If the intrinsic
+error is greater than this, the drift file
+.Pa ntp.drift
+will
+have to be specially tailored to reduce the residual error below
+this limit.
+Once this is done, the drift file is automatically
+updated once per hour and is available to initialize the frequency
+on subsequent daemon restarts.
+.Ss "The huff\-n'\-puff Filter"
+In scenarios where a considerable amount of data are to be
+downloaded or uploaded over telephone modems, timekeeping quality
+can be seriously degraded.
+This occurs because the differential
+delays on the two directions of transmission can be quite large.
+In
+many cases the apparent time errors are so large as to exceed the
+step threshold and a step correction can occur during and after the
+data transfer is in progress.
+.Pp
+The huff\-n'\-puff filter is designed to correct the apparent time
+offset in these cases.
+It depends on knowledge of the propagation
+delay when no other traffic is present.
+In common scenarios this
+occurs during other than work hours.
+The filter maintains a shift
+register that remembers the minimum delay over the most recent
+interval measured usually in hours.
+Under conditions of severe
+delay, the filter corrects the apparent offset using the sign of
+the offset and the difference between the apparent delay and
+minimum delay.
+The name of the filter reflects the negative (huff)
+and positive (puff) correction, which depends on the sign of the
+offset.
+.Pp
+The filter is activated by the
+.Ic tinker
+command and
+.Cm huffpuff
+keyword, as described in
+.Xr ntp.conf 5 .
+.Sh "ENVIRONMENT"
+See \fBOPTION PRESETS\fP for configuration environment variables.
+.Sh FILES
+.Bl -tag -width /etc/ntp.drift -compact
+.It Pa /etc/ntp.conf
+the default name of the configuration file
+.It Pa /etc/ntp.drift
+the default name of the drift file
+.It Pa /etc/ntp.keys
+the default name of the key file
+.El
+.Sh "EXIT STATUS"
+One of the following exit values will be returned:
+.Bl -tag
+.It 0 " (EXIT_SUCCESS)"
+Successful program execution.
+.It 1 " (EXIT_FAILURE)"
+The operation failed or the command syntax was not valid.
+.It 70 " (EX_SOFTWARE)"
+libopts had an internal operational error. Please report
+it to autogen\-users@lists.sourceforge.net. Thank you.
+.El
+.Sh "SEE ALSO"
+.Xr ntp.conf 5 ,
+.Xr ntpdate @NTPDATE_MS@ ,
+.Xr ntpdc @NTPDC_MS@ ,
+.Xr ntpq @NTPQ_MS@ ,
+.Xr sntp @SNTP_MS@
+.Pp
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+.Li http://www.ntp.org/ .
+A snapshot of this documentation is available in HTML format in
+.Pa /usr/share/doc/ntp .
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 1)
+.%O RFC1059
+.Re
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 2)
+.%O RFC1119
+.Re
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 3)
+.%O RFC1305
+.Re
+.Rs
+.%A David L. Mills
+.%A J. Martin, Ed.
+.%A J. Burbank
+.%A W. Kasch
+.%T Network Time Protocol Version 4: Protocol and Algorithms Specification
+.%O RFC5905
+.Re
+.Rs
+.%A David L. Mills
+.%A B. Haberman, Ed.
+.%T Network Time Protocol Version 4: Autokey Specification
+.%O RFC5906
+.Re
+.Rs
+.%A H. Gerstung
+.%A C. Elliott
+.%A B. Haberman, Ed.
+.%T Definitions of Managed Objects for Network Time Protocol Version 4: (NTPv4)
+.%O RFC5907
+.Re
+.Rs
+.%A R. Gayraud
+.%A B. Lourdelet
+.%T Network Time Protocol (NTP) Server Option for DHCPv6
+.%O RFC5908
+.Re
+.Sh "AUTHORS"
+The University of Delaware
+.Sh "COPYRIGHT"
+Copyright (C) 1970\-2014 The University of Delaware all rights reserved.
+This program is released under the terms of the NTP license, <http://ntp.org/license>.
+.Sh BUGS
+The
+.Nm
+utility has gotten rather fat.
+While not huge, it has gotten
+larger than might be desirable for an elevated\-priority
+.Nm
+running on a workstation, particularly since many of
+the fancy features which consume the space were designed more with
+a busy primary server, rather than a high stratum workstation in
+mind.
+.Pp
+Please send bug reports to: http://bugs.ntp.org, bugs@ntp.org
+.Sh NOTES
+Portions of this document came from FreeBSD.
+.Pp
+This manual page was \fIAutoGen\fP\-erated from the \fBntpd\fP
+option definitions.
diff --git a/ntpd/ntpd.texi b/ntpd/ntpd.texi
new file mode 100644
index 0000000..0ac091b
--- /dev/null
+++ b/ntpd/ntpd.texi
@@ -0,0 +1,113 @@
+\input texinfo @c -*-texinfo-*-
+@c %**start of header
+@setfilename ntpd.info
+@settitle ntpd: Network Time Protocol (NTP) Daemon User's Manual
+@include ../sntp/include/version.texi
+@paragraphindent 2
+@c %**end of header
+
+@ifinfo
+This file documents the use of the NTP Project's ntpd, a program for
+controlling ntpd.
+@end ifinfo
+
+@direntry
+* ntpd: (ntpd). NTP Daemon program
+@end direntry
+
+@titlepage
+@title ntpd: Network Time Protocol (NTP) Daemon User's Manual
+@subtitle ntpd, version @value{VERSION}, @value{UPDATED}
+@c @author Max @email{foo@ntp.org}
+@end titlepage
+
+@c @page
+@c @vskip 0pt plus 1filll
+
+@node Top, ntpd Description, (dir), (dir)
+@top ntpd: Network Time Protocol (NTP) Daemon User Manual
+
+The @code{ntpd} program is an operating system daemon that synchronizes the
+system clock to remote NTP time servers or local reference clocks.
+It is a complete implementation of NTP version 4 defined by RFC-5905, but
+also retains compatible with version 3 defined by RFC-1305 and versions
+1 and 2, defined by RFC-1059 and RFC-1119, respectively.
+The program can operate in any of several modes, including client/server,
+symmetric and broadcast modes, and with both symmetric-key and public-key
+cryptography.
+
+This document applies to version @value{VERSION} of @code{ntpd}.
+
+@shortcontents
+
+@menu
+* ntpd Description:: Description
+* ntpd Invocation:: Invoking ntpd
+* Usage:: Usage
+@end menu
+
+@node ntpd Description
+@comment node-name, next, previous, up
+@section Description
+
+The @code{ntpd} program ordinarily requires
+a configuration file described at @ref{ntp.conf}.
+This configuration file contains configuration commands described on
+the pages listed above.
+However a client can discover remote servers and configure them
+automatically.
+This makes it possible to deploy a fleet of workstations without
+specifying configuration details specific to the local environment.
+
+The @code{ntpd} program normally operates continuously while adjusting the
+system time and frequency, but in some cases this might not be
+practical.
+With the @code{-q} option @code{ntpd} operates as in continuous mode, but
+exits just after setting the clock for the first time.
+Most applications will probably want to specify the @code{iburst}
+option with the @code{server} command.
+With this option an initial volley of messages is exchanged to
+groom the data and set the clock in about ten seconds' time.
+If nothing is heard after a few minutes' time,
+the daemon times out and exits without setting the clock.
+
+@include invoke-ntpd.texi
+
+@node Usage
+@comment node-name, next, previous, up
+@section Usage
+
+@multitable @columnfractions .23 .23 .05 .15
+@headitem What @tab Default @tab Flag @tab Option
+@item configuration file
+@tab @code{/etc/ntp.conf}
+@tab @code{-c}
+@tab @code{conffile}
+@item frequency file
+@tab none
+@tab @code{-f}
+@tab @code{driftfile}
+@item leapseconds file
+@tab none
+@tab
+@tab @code{leapfile}
+@item process ID file
+@tab none
+@tab @code{-p}
+@tab @code{pidfile}
+@item log file
+@tab system log
+@tab @code{-l}
+@tab @code{logfile}
+@item include file
+@tab none
+@tab none
+@tab @code{includefile}
+@item statistics path
+@tab @code{/var/NTP}
+@tab @code{-s}
+@tab @code{statsdir}
+@item keys path
+@tab @code{/usr/local/etc}
+@tab @code{-k}
+@tab @code{keysdir}
diff --git a/ntpd/ntpdbase-opts.def b/ntpd/ntpdbase-opts.def
new file mode 100644
index 0000000..110504e
--- /dev/null
+++ b/ntpd/ntpdbase-opts.def
@@ -0,0 +1,463 @@
+#include autogen-version.def
+
+include = <<- _EOF_
+ #ifdef __windows
+ extern int atoi(const char *);
+ #else
+ # include <stdlib.h>
+ #endif
+ _EOF_;
+
+flag = {
+ name = ipv4;
+ value = 4;
+ flags-cant = ipv6;
+ descrip = "Force IPv4 DNS name resolution";
+ doc = <<- _EndOfDoc_
+ Force DNS resolution of following host names on the command line
+ to the IPv4 namespace.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = ipv6;
+ value = 6;
+ flags-cant = ipv4;
+ descrip = "Force IPv6 DNS name resolution";
+ doc = <<- _EndOfDoc_
+ Force DNS resolution of following host names on the command line
+ to the IPv6 namespace.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = authreq;
+ value = a;
+ descrip = "Require crypto authentication";
+ flags-cant = authnoreq;
+ doc = <<- _EndOfDoc_
+ Require cryptographic authentication for broadcast client,
+ multicast client and symmetric passive associations.
+ This is the default.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = authnoreq;
+ value = A;
+ descrip = "Do not require crypto authentication";
+ flags-cant = authreq;
+ doc = <<- _EndOfDoc_
+ Do not require cryptographic authentication for broadcast client,
+ multicast client and symmetric passive associations.
+ This is almost never a good idea.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = bcastsync;
+ value = b;
+ descrip = "Allow us to sync to broadcast servers";
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+
+flag = {
+ name = configfile;
+ value = c;
+ arg-type = string;
+ descrip = "configuration file name";
+ doc = <<- _EndOfDoc_
+ The name and path of the configuration file,
+ @file{/etc/ntp.conf}
+ by default.
+ _EndOfDoc_;
+};
+
+#include debug-opt.def
+
+flag = {
+ name = driftfile;
+ value = f;
+ arg-type = string;
+ descrip = "frequency drift file name";
+ doc = <<- _EndOfDoc_
+ The name and path of the frequency file,
+ @file{/etc/ntp.drift}
+ by default.
+ This is the same operation as the
+ @code{driftfile} @kbd{driftfile}
+ configuration specification in the
+ @file{/etc/ntp.conf}
+ file.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = panicgate;
+ value = g;
+ max = NOLIMIT;
+ descrip = "Allow the first adjustment to be Big";
+ doc = <<- _EndOfDoc_
+ Normally,
+ @code{ntpd}
+ exits with a message to the system log if the offset exceeds the panic threshold, which is 1000 s by default. This option allows the time to be set to any value without restriction; however, this can happen only once. If the threshold is exceeded after that,
+ @code{ntpd}
+ will exit with a message to the system log. This option can be used with the
+ @code{-q}
+ and
+ @code{-x}
+ options.
+ See the
+ @code{tinker}
+ configuration file directive for other options.
+ _EndOfDoc_;
+};
+
+flag = {
+ ifdef = HAVE_DROPROOT;
+ name = jaildir;
+ value = i;
+ arg-type = string;
+ descrip = "Jail directory";
+ omitted-usage = "built without --enable-clockctl or --enable-linuxcaps or --enable-solarisprivs";
+ doc = <<- _EndOfDoc_
+ Chroot the server to the directory
+ @kbd{jaildir}
+ .
+ This option also implies that the server attempts to drop root privileges at startup.
+ You may need to also specify a
+ @code{-u}
+ option.
+ This option is only available if the OS supports adjusting the clock
+ without full root privileges.
+ This option is supported under NetBSD (configure with
+ @code{--enable-clockctl}) or Linux (configure with
+ @code{--enable-linuxcaps}) or Solaris (configure with @code{--enable-solarisprivs}).
+ _EndOfDoc_;
+};
+
+flag = {
+ name = interface;
+ value = I;
+ arg-type = string;
+ descrip = "Listen on an interface name or address";
+ max = NOLIMIT;
+ arg-name = iface;
+ stack-arg;
+ doc = <<- _EndOfDoc_
+ Open the network address given, or all the addresses associated with the
+ given interface name. This option may appear multiple times. This option
+ also implies not opening other addresses, except wildcard and localhost.
+ This option is deprecated. Please consider using the configuration file
+ @code{interface} command, which is more versatile.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = keyfile;
+ value = k;
+ arg-type = string;
+ descrip = "path to symmetric keys";
+ doc = <<- _EndOfDoc_
+ Specify the name and path of the symmetric key file.
+ @file{/etc/ntp.keys}
+ is the default.
+ This is the same operation as the
+ @code{keys} @kbd{keyfile}
+ configuration file directive.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = logfile;
+ value = l;
+ arg-type = string;
+ descrip = "path to the log file";
+ doc = <<- _EndOfDoc_
+ Specify the name and path of the log file.
+ The default is the system log file.
+ This is the same operation as the
+ @code{logfile} @kbd{logfile}
+ configuration file directive.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = novirtualips;
+ value = L;
+ descrip = "Do not listen to virtual interfaces";
+ doc = <<- _EndOfDoc_
+ Do not listen to virtual interfaces, defined as those with
+ names containing a colon. This option is deprecated. Please
+ consider using the configuration file @code{interface} command, which
+ is more versatile.
+ _EndOfDoc_;
+};
+
+flag = {
+ ifdef = SYS_WINNT;
+ name = modifymmtimer;
+ value = M;
+ descrip = "Modify Multimedia Timer (Windows only)";
+ doc = <<- _EndOfDoc_
+ Set the Windows Multimedia Timer to highest resolution. This
+ ensures the resolution does not change while ntpd is running,
+ avoiding timekeeping glitches associated with changes.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = nofork;
+ value = n;
+ flags-cant = wait-sync;
+ descrip = "Do not fork";
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+
+flag = {
+ name = nice;
+ value = N;
+ descrip = "Run at high priority";
+ doc = <<- _EndOfDoc_
+ To the extent permitted by the operating system, run
+ @code{ntpd}
+ at the highest priority.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = pidfile;
+ value = p;
+ arg-type = string;
+ descrip = "path to the PID file";
+ doc = <<- _EndOfDoc_
+ Specify the name and path of the file used to record
+ @code{ntpd}'s
+ process ID.
+ This is the same operation as the
+ @code{pidfile} @kbd{pidfile}
+ configuration file directive.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = priority;
+ value = P;
+ arg-type = number;
+ descrip = "Process priority";
+ doc = <<- _EndOfDoc_
+ To the extent permitted by the operating system, run
+ @code{ntpd}
+ at the specified
+ @code{sched_setscheduler(SCHED_FIFO)}
+ priority.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = quit;
+ value = q;
+ flags-cant = saveconfigquit;
+ flags-cant = wait-sync;
+ descrip = "Set the time and quit";
+ doc = <<- _EndOfDoc_
+ @code{ntpd}
+ will not daemonize and will exit after the clock is first
+ synchronized. This behavior mimics that of the
+ @code{ntpdate}
+ program, which will soon be replaced with a shell script.
+ The
+ @code{-g}
+ and
+ @code{-x}
+ options can be used with this option.
+ Note: The kernel time discipline is disabled with this option.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = propagationdelay;
+ value = r;
+ arg-type = string;
+ descrip = "Broadcast/propagation delay";
+ doc = <<- _EndOfDoc_
+ Specify the default propagation delay from the broadcast/multicast server to this client. This is necessary only if the delay cannot be computed automatically by the protocol.
+ _EndOfDoc_;
+};
+
+flag = {
+ ifdef = SAVECONFIG;
+ name = saveconfigquit;
+ arg-type = string;
+ flags-cant = quit;
+ flags-cant = wait-sync;
+ descrip = "Save parsed configuration and quit";
+ doc = <<- _EndOfDoc_
+ Cause @code{ntpd} to parse its startup configuration file and save an
+ equivalent to the given filename and exit. This option was
+ designed for automated testing.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = statsdir;
+ value = s;
+ arg-type = string;
+ descrip = "Statistics file location";
+ doc = <<- _EndOfDoc_
+ Specify the directory path for files created by the statistics facility.
+ This is the same operation as the
+ @code{statsdir} @kbd{statsdir}
+ configuration file directive.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = trustedkey;
+ value = t;
+ arg-type = string;
+ descrip = "Trusted key number";
+ max = NOLIMIT;
+ arg-name = tkey;
+ stack-arg;
+ doc = <<- _EndOfDoc_
+ Add the specified key number to the trusted key list.
+ _EndOfDoc_;
+};
+
+flag = {
+ ifdef = HAVE_DROPROOT;
+ name = user;
+ value = u;
+ arg-type = string;
+ descrip = "Run as userid (or userid:groupid)";
+ omitted-usage = "built without --enable-clockctl or --enable-linuxcaps or --enable-solarisprivs";
+ doc = <<- _EndOfDoc_
+ Specify a user, and optionally a group, to switch to.
+ This option is only available if the OS supports adjusting the clock
+ without full root privileges.
+ This option is supported under NetBSD (configure with
+ @code{--enable-clockctl}) or Linux (configure with
+ @code{--enable-linuxcaps}) or Solaris (configure with @code{--enable-solarisprivs}).
+ _EndOfDoc_;
+};
+
+flag = {
+ name = updateinterval;
+ value = U;
+ arg-type = number;
+ descrip = "interval in seconds between scans for new or dropped interfaces";
+ doc = <<- _EndOfDoc_
+ Give the time in seconds between two scans for new or dropped interfaces.
+ For systems with routing socket support the scans will be performed shortly after the interface change
+ has been detected by the system.
+ Use 0 to disable scanning. 60 seconds is the minimum time between scans.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = var;
+/* value = v; Bug 817 */
+ arg-type = string;
+ descrip = "make ARG an ntp variable (RW)";
+ max = NOLIMIT;
+ arg-name = nvar;
+ stack-arg;
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+
+flag = {
+ name = dvar;
+/* value = V; Bug 817 */
+ arg-type = string;
+ descrip = "make ARG an ntp variable (RW|DEF)";
+ max = NOLIMIT;
+ arg-name = ndvar;
+ stack-arg;
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+
+flag = {
+ ifdef = HAVE_WORKING_FORK;
+ name = wait-sync;
+ value = w;
+ arg-type = number;
+ flags-cant = nofork;
+ flags-cant = quit;
+ flags-cant = saveconfigquit;
+ descrip = "Seconds to wait for first clock sync";
+ doc = <<- _EndOfDoc_
+ If greater than zero, alters @code{ntpd}'s behavior when forking to
+ daemonize. Instead of exiting with status 0 immediately after
+ the fork, the parent waits up to the specified number of
+ seconds for the child to first synchronize the clock. The exit
+ status is zero (success) if the clock was synchronized,
+ otherwise it is @code{ETIMEDOUT}.
+ This provides the option for a script starting @code{ntpd} to easily
+ wait for the first set of the clock before proceeding.
+ _EndOfDoc_;
+/*
+** XXX: is it "first set" or is it more? If it's only "first set" then
+** that's not the same as SYNC.
+*/
+};
+
+flag = {
+ name = slew;
+ value = x;
+ descrip = "Slew up to 600 seconds";
+ doc = <<- _EndOfDoc_
+ Normally, the time is slewed if the offset is less than the step threshold, which is 128 ms by default, and stepped if above the threshold.
+ This option sets the threshold to 600 s, which is well within the accuracy window to set the clock manually.
+ Note: Since the slew rate of typical Unix kernels is limited to 0.5 ms/s, each second of adjustment requires an amortization interval of 2000 s.
+ Thus, an adjustment as much as 600 s will take almost 14 days to complete.
+ This option can be used with the
+ @code{-g}
+ and
+ @code{-q}
+ options.
+ See the
+ @code{tinker}
+ configuration file directive for other options.
+ Note: The kernel time discipline is disabled with this option.
+ _EndOfDoc_;
+};
+
+flag = {
+ ifdef = SYS_WINNT;
+ name = usepcc;
+ descrip = "Use CPU cycle counter (Windows only)";
+ doc = <<- _EndOfDoc_
+ Attempt to substitute the CPU counter for @code{QueryPerformanceCounter}.
+ The CPU counter and @code{QueryPerformanceCounter} are compared, and if
+ they have the same frequency, the CPU counter (RDTSC on x86) is
+ used directly, saving the overhead of a system call.
+ _EndOfDoc_;
+};
+
+flag = {
+ ifdef = SYS_WINNT;
+ name = pccfreq;
+ arg-type = string;
+ descrip = "Force CPU cycle counter use (Windows only)";
+ doc = <<- _EndOfDoc_
+ Force substitution the CPU counter for @code{QueryPerformanceCounter}.
+ The CPU counter (RDTSC on x86) is used unconditionally with the
+ given frequency (in Hz).
+ _EndOfDoc_;
+};
+
+flag = {
+ ifdef = HAVE_DNSREGISTRATION;
+ name = mdns;
+ value = m;
+ descrip = "Register with mDNS as a NTP server";
+ doc = <<- _EndOfDoc_
+ Registers as an NTP server with the local mDNS server which allows
+ the server to be discovered via mDNS client lookup.
+ _EndOfDoc_;
+};
diff --git a/ntpd/ntpsim.c b/ntpd/ntpsim.c
new file mode 100644
index 0000000..b7c3218
--- /dev/null
+++ b/ntpd/ntpsim.c
@@ -0,0 +1,657 @@
+/* ntpdsim.c
+ *
+ * The source code for the ntp discrete event simulator.
+ *
+ * Written By: Sachin Kamboj
+ * University of Delaware
+ * Newark, DE 19711
+ * Copyright (c) 2006
+ * (Some code shamelessly based on the original NTP discrete event simulator)
+ */
+
+#include <config.h>
+#ifdef SIM
+#include "ntpd.h"
+#include "ntp_config.h"
+
+/* forward prototypes */
+int determine_event_ordering(const Event *e1, const Event *e2);
+int determine_recv_buf_ordering(const struct recvbuf *b1,
+ const struct recvbuf *b2);
+void create_server_associations(void);
+void init_sim_io(void);
+
+/* Global Variable Definitions */
+sim_info simulation; /* Simulation Control Variables */
+local_clock_info simclock; /* Local Clock Variables */
+queue *event_queue; /* Event Queue */
+queue *recv_queue; /* Receive Queue */
+static double sys_residual = 0; /* adjustment residue (s) */
+
+void (*event_ptr[]) (Event *) = {
+ sim_event_beep, sim_update_clocks, sim_event_timer, sim_event_recv_packet
+}; /* Function pointer to the events */
+
+
+/*
+ * Define a function to compare two events to determine which one occurs
+ * first.
+ */
+int
+determine_event_ordering(
+ const Event *e1,
+ const Event *e2
+ )
+{
+ return (e1->time - e2->time);
+}
+
+
+/*
+ * Define a function to compare two received packets to determine which
+ * one is received first.
+ */
+int
+determine_recv_buf_ordering(
+ const struct recvbuf *b1,
+ const struct recvbuf *b2
+ )
+{
+ double recv_time1;
+ double recv_time2;
+
+ /* Simply convert the time received to double and subtract */
+ LFPTOD(&b1->recv_time, recv_time1);
+ LFPTOD(&b2->recv_time, recv_time2);
+
+ return (int)(recv_time1 - recv_time2);
+}
+
+
+/* Define a function to create the server associations */
+void create_server_associations(void)
+{
+ int i;
+
+ for (i = 0; i < simulation.num_of_servers; ++i) {
+ printf("%s\n", stoa(simulation.servers[i].addr));
+ if (peer_config(simulation.servers[i].addr,
+ NULL,
+ loopback_interface,
+ MODE_CLIENT,
+ NTP_VERSION,
+ NTP_MINDPOLL,
+ NTP_MAXDPOLL,
+ 0, /* peerflags */
+ 0, /* ttl */
+ 0, /* peerkey */
+ NULL /* group ident */) == 0) {
+ fprintf(stderr,
+ "ERROR!! Could not create association for: %s\n",
+ stoa(simulation.servers[i].addr));
+ }
+ }
+}
+
+
+/* Main Simulator Code */
+
+int
+ntpsim(
+ int argc,
+ char * argv[]
+ )
+{
+ Event * curr_event;
+ struct timeval seed;
+
+ /* Initialize the local Clock */
+ simclock.local_time = 0;
+ simclock.adj = 0;
+ simclock.slew = 500e-6;
+
+ /* Initialize the simulation */
+ simulation.num_of_servers = 0;
+ simulation.beep_delay = BEEP_DLY;
+ simulation.sim_time = 0;
+ simulation.end_time = SIM_TIME;
+
+ /* Initialize ntp modules */
+ initializing = TRUE;
+ msyslog_term = TRUE;
+ init_sim_io();
+ init_auth();
+ init_util();
+ init_restrict();
+ init_mon();
+ init_timer();
+ init_lib();
+ init_request();
+ init_control();
+ init_peer();
+ init_proto();
+ init_loopfilter();
+ mon_start(MON_OFF);
+
+ /* Call getconfig to parse the configuration file */
+ getconfig(argc, argv);
+ loop_config(LOOP_DRIFTINIT, 0);
+ initializing = FALSE;
+
+ /*
+ * Watch out here, we want the real time, not the silly stuff.
+ */
+ gettimeofday(&seed, NULL);
+ ntp_srandom(seed.tv_usec);
+
+ /* Initialize the event queue */
+ event_queue = create_priority_queue((q_order_func)
+ determine_event_ordering);
+
+ /* Initialize the receive queue */
+ recv_queue = create_priority_queue((q_order_func)
+ determine_recv_buf_ordering);
+
+ /* Push a beep and a timer on the event queue */
+ enqueue(event_queue, event(0, BEEP));
+ enqueue(event_queue, event(simulation.sim_time + 1.0, TIMER));
+
+ /*
+ * Pop the queue until nothing is left or time is exceeded
+ */
+ /* maxtime = simulation.sim_time + simulation.end_time;*/
+ while (simulation.sim_time <= simulation.end_time &&
+ (!empty(event_queue))) {
+ curr_event = dequeue(event_queue);
+ /* Update all the clocks to the time on the event */
+ sim_update_clocks(curr_event);
+
+ /* Execute the function associated with the event */
+ (*event_ptr[curr_event->function])(curr_event);
+ free_node(curr_event);
+ }
+ printf("sys_received: %lu\n", sys_received);
+ printf("sys_badlength: %lu\n", sys_badlength);
+ printf("sys_declined: %lu\n", sys_declined);
+ printf("sys_restricted: %lu\n", sys_restricted);
+ printf("sys_newversion: %lu\n", sys_newversion);
+ printf("sys_oldversion: %lu\n", sys_oldversion);
+ printf("sys_limitrejected: %lu\n", sys_limitrejected);
+ printf("sys_badauth: %lu\n", sys_badauth);
+
+ return (0);
+}
+
+
+void
+init_sim_io(void)
+{
+ loopback_interface = emalloc_zero(sizeof(*loopback_interface));
+ ep_list = loopback_interface;
+ strlcpy(loopback_interface->name, "IPv4loop",
+ sizeof(loopback_interface->name));
+ loopback_interface->flags = INT_UP | INT_LOOPBACK;
+ loopback_interface->fd = -1;
+ loopback_interface->bfd = -1;
+ loopback_interface->ifnum = 1;
+ loopback_interface->family = AF_INET;
+ AF(&loopback_interface->sin) = AF_INET;
+ SET_ADDR4(&loopback_interface->sin, LOOPBACKADR);
+ SET_PORT(&loopback_interface->sin, NTP_PORT);
+ AF(&loopback_interface->mask) = AF_INET;
+ SET_ADDR4(&loopback_interface->mask, LOOPNETMASK);
+}
+
+
+/* Define a function to create an return an Event */
+
+Event *event(double t, funcTkn f)
+{
+ Event *e;
+
+ if ((e = get_node(sizeof(*e))) == NULL)
+ abortsim("get_node failed in event");
+ e->time = t;
+ e->function = f;
+ return (e);
+}
+
+/* NTP SIMULATION FUNCTIONS */
+
+/* Define a function for processing a timer interrupt.
+ * On every timer interrupt, call the NTP timer to send packets and process
+ * the clock and then call the receive function to receive packets.
+ */
+void sim_event_timer(Event *e)
+{
+ struct recvbuf *rbuf;
+
+ /* Call the NTP timer.
+ * This will be responsible for actually "sending the packets."
+ * Since this is a simulation, the packets sent over the network
+ * will be processed by the simulate_server routine below.
+ */
+ timer();
+
+ /* Process received buffers */
+ while (!empty(recv_queue)) {
+ rbuf = (struct recvbuf *)dequeue(recv_queue);
+ (*rbuf->receiver)(rbuf);
+ free_node(rbuf);
+ }
+
+ /* Arm the next timer interrupt. */
+ enqueue(event_queue,
+ event(simulation.sim_time + (1 << EVENT_TIMEOUT), TIMER));
+}
+
+
+
+/* Define a function to simulate a server.
+ * This function processes the sent packet according to the server script,
+ * creates a reply packet and pushes the reply packet onto the event queue
+ */
+int simulate_server(
+ sockaddr_u *serv_addr, /* Address of the server */
+ endpt * inter, /* Interface on which the reply should
+ be inserted */
+ struct pkt *rpkt /* Packet sent to the server that
+ needs to be processed. */
+ )
+{
+ struct pkt xpkt; /* Packet to be transmitted back
+ to the client */
+ struct recvbuf rbuf; /* Buffer for the received packet */
+ Event *e; /* Packet receive event */
+ server_info *server; /* Pointer to the server being simulated */
+ script_info *curr_script; /* Current script being processed */
+ int i;
+ double d1, d2, d3; /* Delays while the packet is enroute */
+ double t1, t2, t3, t4; /* The four timestamps in the packet */
+ l_fp lfp_host; /* host-order l_fp */
+
+ ZERO(xpkt);
+ ZERO(rbuf);
+
+ /* Search for the server with the desired address */
+ server = NULL;
+ for (i = 0; i < simulation.num_of_servers; ++i) {
+ if (memcmp(simulation.servers[i].addr, serv_addr,
+ sizeof(*serv_addr)) == 0) {
+ server = &simulation.servers[i];
+ break;
+ }
+ }
+
+ fprintf(stderr, "Received packet from %s on %s\n",
+ stoa(serv_addr), latoa(inter));
+ if (server == NULL)
+ abortsim("Server with specified address not found!!!");
+
+ /* Get the current script for the server */
+ curr_script = server->curr_script;
+
+ /* Create a server reply packet.
+ * Masquerade the reply as a stratum-1 server with a GPS clock
+ */
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, NTP_VERSION,
+ MODE_SERVER);
+ xpkt.stratum = STRATUM_TO_PKT(((u_char)1));
+ memcpy(&xpkt.refid, "GPS", 4);
+ xpkt.ppoll = rpkt->ppoll;
+ xpkt.precision = rpkt->precision;
+ xpkt.rootdelay = 0;
+ xpkt.rootdisp = 0;
+
+ /* TIMESTAMP CALCULATIONS
+ t1 t4
+ \ /
+ d1 \ / d3
+ \ /
+ t2 ----------------- t3
+ d2
+ */
+ /* Compute the delays */
+ d1 = poisson(curr_script->prop_delay, curr_script->jitter);
+ d2 = poisson(curr_script->proc_delay, 0);
+ d3 = poisson(curr_script->prop_delay, curr_script->jitter);
+
+ /* Note: In the transmitted packet:
+ * 1. t1 and t4 are times in the client according to the local clock.
+ * 2. t2 and t3 are server times according to the simulated server.
+ * Compute t1, t2, t3 and t4
+ * Note: This function is called at time t1.
+ */
+
+ NTOHL_FP(&rpkt->xmt, &lfp_host);
+ LFPTOD(&lfp_host, t1);
+ t2 = server->server_time + d1;
+ t3 = server->server_time + d1 + d2;
+ t4 = t1 + d1 + d2 + d3;
+
+ /* Save the timestamps */
+ xpkt.org = rpkt->xmt;
+ DTOLFP(t2, &lfp_host);
+ HTONL_FP(&lfp_host, &xpkt.rec);
+ DTOLFP(t3, &lfp_host);
+ HTONL_FP(&lfp_host, &xpkt.xmt);
+ xpkt.reftime = xpkt.xmt;
+
+ /*
+ * Ok, we are done with the packet. Now initialize the receive
+ * buffer for the packet.
+ */
+ rbuf.used = 1;
+ rbuf.receiver = &receive; /* callback to process the packet */
+ rbuf.recv_length = LEN_PKT_NOMAC;
+ rbuf.recv_pkt = xpkt;
+ rbuf.dstadr = inter;
+ rbuf.fd = inter->fd;
+ memcpy(&rbuf.srcadr, serv_addr, sizeof(rbuf.srcadr));
+ memcpy(&rbuf.recv_srcadr, serv_addr, sizeof(rbuf.recv_srcadr));
+
+ /*
+ * Create a packet event and insert it onto the event_queue at the
+ * arrival time (t4) of the packet at the client
+ */
+ e = event(t4, PACKET);
+ e->rcv_buf = rbuf;
+ enqueue(event_queue, e);
+
+ /*
+ * Check if the time of the script has expired. If yes, delete it.
+ */
+ if (curr_script->duration > simulation.sim_time &&
+ NULL == HEAD_PFIFO(server->script)) {
+ printf("Hello\n");
+ /*
+ * For some reason freeing up the curr_script memory kills the
+ * simulation. Further debugging is needed to determine why.
+ * free(curr_script);
+ */
+ UNLINK_FIFO(curr_script, *server->script, link);
+ }
+
+ return (0);
+}
+
+
+/* Define a function to update all the clocks
+ * Most of the code is modified from the systime.c file by Prof. Mills
+ */
+
+void sim_update_clocks(Event *e)
+{
+ double time_gap;
+ double adj;
+ int i;
+
+ /* Compute the time between the last update event and this update */
+ time_gap = e->time - simulation.sim_time;
+
+ if (time_gap < 0)
+ printf("WARNING: e->time %.6g comes before sim_time %.6g (gap %+.6g)\n",
+ e->time, simulation.sim_time, time_gap);
+
+ /* Advance the client clock */
+ if (e->time + time_gap < simclock.local_time)
+ printf("WARNING: e->time + gap %.6g comes before local_time %.6g\n",
+ e->time + time_gap, simclock.local_time);
+ simclock.local_time = e->time + time_gap;
+
+ /* Advance the simulation time */
+ simulation.sim_time = e->time;
+
+ /* Advance the server clocks adjusted for systematic and random frequency
+ * errors. The random error is a random walk computed as the
+ * integral of samples from a Gaussian distribution.
+ */
+ for (i = 0; i < simulation.num_of_servers; ++i) {
+ simulation.servers[i].curr_script->freq_offset +=
+ gauss(0, time_gap * simulation.servers[i].curr_script->wander);
+
+ simulation.servers[i].server_time += time_gap *
+ (1 + simulation.servers[i].curr_script->freq_offset);
+ }
+
+ /* Perform the adjtime() function. If the adjustment completed
+ * in the previous interval, amortize the entire amount; if not,
+ * carry the leftover to the next interval.
+ */
+
+ adj = time_gap * simclock.slew;
+ if (adj < fabs(simclock.adj)) {
+ if (simclock.adj < 0) {
+ simclock.adj += adj;
+ simclock.local_time -= adj;
+ } else {
+ simclock.adj -= adj;
+ simclock.local_time += adj;
+ }
+ } else {
+ simclock.local_time += simclock.adj;
+ simclock.adj = 0;
+ }
+}
+
+
+/* Define a function that processes a receive packet event.
+ * This function simply inserts the packet received onto the receive queue
+ */
+
+void sim_event_recv_packet(Event *e)
+{
+ struct recvbuf *rbuf;
+
+ /* Allocate a receive buffer and copy the packet to it */
+ if ((rbuf = get_node(sizeof(*rbuf))) == NULL)
+ abortsim("get_node failed in sim_event_recv_packet");
+ memcpy(rbuf, &e->rcv_buf, sizeof(*rbuf));
+
+ /* Store the local time in the received packet */
+ DTOLFP(simclock.local_time, &rbuf->recv_time);
+
+ /* Insert the packet received onto the receive queue */
+ enqueue(recv_queue, rbuf);
+}
+
+
+
+/* Define a function to output simulation statistics on a beep event
+ */
+
+/*** TODO: Need to decide on how to output for multiple servers ***/
+void sim_event_beep(Event *e)
+{
+#if 0
+ static int first_time = 1;
+ char *dash = "-----------------";
+#endif
+
+ fprintf(stderr, "BEEP!!!\n");
+ enqueue(event_queue, event(e->time + simulation.beep_delay, BEEP));
+#if 0
+ if(simulation.beep_delay > 0) {
+ if (first_time) {
+ printf("\t%4c T %4c\t%4c T+ERR %3c\t%5cT+ERR+NTP\n",
+ ' ', ' ', ' ', ' ',' ');
+ printf("\t%s\t%s\t%s\n", dash, dash, dash);
+ first_time = 0;
+
+ printf("\t%16.6f\t%16.6f\t%16.6f\n",
+ n->time, n->clk_time, n->ntp_time);
+ return;
+ }
+ printf("\t%16.6f\t%16.6f\t%16.6f\n",
+ simclock.local_time,
+ n->time, n->clk_time, n->ntp_time);
+#endif
+
+}
+
+
+/* Define a function to abort the simulation on an error and spit out an
+ * error message
+ */
+
+void abortsim(char *errmsg)
+{
+ perror(errmsg);
+ exit(1);
+}
+
+
+
+/* CODE ORIGINALLY IN libntp/systime.c
+ * -----------------------------------
+ * This code was a part of the original NTP simulator and originally
+ * had its home in the libntp/systime.c file.
+ *
+ * It has been shamelessly moved to here and has been modified for the
+ * purposes of the current simulator.
+ */
+
+
+/*
+ * get_systime - return the system time in NTP timestamp format
+ */
+void
+get_systime(
+ l_fp *now /* current system time in l_fp */ )
+{
+ /*
+ * To fool the code that determines the local clock precision,
+ * we advance the clock a minimum of 200 nanoseconds on every
+ * clock read. This is appropriate for a typical modern machine
+ * with nanosecond clocks. Note we make no attempt here to
+ * simulate reading error, since the error is so small. This may
+ * change when the need comes to implement picosecond clocks.
+ */
+ if (simclock.local_time == simclock.last_read_time)
+ simclock.local_time += 200e-9;
+
+ simclock.last_read_time = simclock.local_time;
+ DTOLFP(simclock.local_time, now);
+/* OLD Code
+ if (ntp_node.ntp_time == ntp_node.last_time)
+ ntp_node.ntp_time += 200e-9;
+ ntp_node.last_time = ntp_node.ntp_time;
+ DTOLFP(ntp_node.ntp_time, now);
+*/
+}
+
+
+/*
+ * adj_systime - advance or retard the system clock exactly like the
+ * real thng.
+ */
+int /* always succeeds */
+adj_systime(
+ double now /* time adjustment (s) */
+ )
+{
+ struct timeval adjtv; /* new adjustment */
+ double dtemp;
+ long ticks;
+ int isneg = 0;
+
+ /*
+ * Most Unix adjtime() implementations adjust the system clock
+ * in microsecond quanta, but some adjust in 10-ms quanta. We
+ * carefully round the adjustment to the nearest quantum, then
+ * adjust in quanta and keep the residue for later.
+ */
+ dtemp = now + sys_residual;
+ if (dtemp < 0) {
+ isneg = 1;
+ dtemp = -dtemp;
+ }
+ adjtv.tv_sec = (long)dtemp;
+ dtemp -= adjtv.tv_sec;
+ ticks = (long)(dtemp / sys_tick + .5);
+ adjtv.tv_usec = (long)(ticks * sys_tick * 1e6);
+ dtemp -= adjtv.tv_usec / 1e6;
+ sys_residual = dtemp;
+
+ /*
+ * Convert to signed seconds and microseconds for the Unix
+ * adjtime() system call. Note we purposely lose the adjtime()
+ * leftover.
+ */
+ if (isneg) {
+ adjtv.tv_sec = -adjtv.tv_sec;
+ adjtv.tv_usec = -adjtv.tv_usec;
+ sys_residual = -sys_residual;
+ }
+ simclock.adj = now;
+/* ntp_node.adj = now; */
+ return (1);
+}
+
+
+/*
+ * step_systime - step the system clock. We are religious here.
+ */
+int /* always succeeds */
+step_systime(
+ double now /* step adjustment (s) */
+ )
+{
+#ifdef DEBUG
+ if (debug)
+ printf("step_systime: time %.6f adj %.6f\n",
+ simclock.local_time, now);
+#endif
+ simclock.local_time += now;
+ return (1);
+}
+
+/*
+ * gauss() - returns samples from a gaussion distribution
+ */
+double /* Gaussian sample */
+gauss(
+ double m, /* sample mean */
+ double s /* sample standard deviation (sigma) */
+ )
+{
+ double q1, q2;
+
+ /*
+ * Roll a sample from a Gaussian distribution with mean m and
+ * standard deviation s. For m = 0, s = 1, mean(y) = 0,
+ * std(y) = 1.
+ */
+ if (s == 0)
+ return (m);
+ while ((q1 = drand48()) == 0)
+ /* empty statement */;
+ q2 = drand48();
+ return (m + s * sqrt(-2. * log(q1)) * cos(2. * PI * q2));
+}
+
+
+/*
+ * poisson() - returns samples from a network delay distribution
+ */
+double /* delay sample (s) */
+poisson(
+ double m, /* fixed propagation delay (s) */
+ double s /* exponential parameter (mu) */
+ )
+{
+ double q1;
+
+ /*
+ * Roll a sample from a composite distribution with propagation
+ * delay m and exponential distribution time with parameter s.
+ * For m = 0, s = 1, mean(y) = std(y) = 1.
+ */
+ if (s == 0)
+ return (m);
+ while ((q1 = drand48()) == 0)
+ /* empty statement */;
+ return (m - s * log(q1 * s));
+}
+
+#endif
diff --git a/ntpd/ppsapi_timepps.h b/ntpd/ppsapi_timepps.h
new file mode 100644
index 0000000..8adaf62
--- /dev/null
+++ b/ntpd/ppsapi_timepps.h
@@ -0,0 +1,26 @@
+/* ppsapi_timepps.h */
+
+/*
+ * This logic first tries to get the timepps.h file from a standard
+ * location, and then from our include/ subdirectory.
+ */
+
+#ifdef HAVE_TIMEPPS_H
+# include <timepps.h>
+#else
+# ifdef HAVE_SYS_TIMEPPS_H
+# include <sys/timepps.h>
+# else
+# ifdef HAVE_CIOGETEV
+# include "timepps-SunOS.h"
+# else
+# ifdef HAVE_TIOCGPPSEV
+# include "timepps-Solaris.h"
+# else
+# ifdef TIOCDCDTIMESTAMP
+# include "timepps-SCO.h"
+# endif
+# endif
+# endif
+# endif
+#endif
diff --git a/ntpd/refclock_acts.c b/ntpd/refclock_acts.c
new file mode 100644
index 0000000..36322fd
--- /dev/null
+++ b/ntpd/refclock_acts.c
@@ -0,0 +1,911 @@
+/*
+ * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time
+ * Services
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ACTS)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_control.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#ifdef SYS_WINNT
+#undef write /* ports/winnt/include/config.h: #define write _write */
+extern int async_write(int, const void *, unsigned int);
+#define write(fd, data, octets) async_write(fd, data, octets)
+#endif
+
+/*
+ * This driver supports the US (NIST, USNO) and European (PTB, NPL,
+ * etc.) modem time services, as well as Spectracom GPS and WWVB
+ * receivers connected via a modem. The driver periodically dials a
+ * number from a telephone list, receives the timecode data and
+ * calculates the local clock correction. It is designed primarily for
+ * use as backup when neither a radio clock nor connectivity to Internet
+ * time servers is available.
+ *
+ * This driver requires a modem with a Hayes-compatible command set and
+ * control over the modem data terminal ready (DTR) control line. The
+ * modem setup string is hard-coded in the driver and may require
+ * changes for nonstandard modems or special circumstances.
+ *
+ * When enabled, the calling program dials the first number in the
+ * phones file. If that call fails, it dials the second number and
+ * so on. The phone number is specified by the Hayes ATDT prefix
+ * followed by the number itself, including the long-distance prefix
+ * and delay code, if necessary. The calling program is enabled
+ * when (a) fudge flag1 is set by ntpdc, (b) at each poll interval
+ * when no other synchronization sources are present, and (c) at each
+ * poll interval whether or not other synchronization sources are
+ * present. The calling program disconnects if (a) the called party
+ * is busy or does not answer, (b) the called party disconnects
+ * before a sufficient nuimber of timecodes have been received.
+ *
+ * The driver is transparent to each of the modem time services and
+ * Spectracom radios. It selects the parsing algorithm depending on the
+ * message length. There is some hazard should the message be corrupted.
+ * However, the data format is checked carefully and only if all checks
+ * succeed is the message accepted. Corrupted lines are discarded
+ * without complaint.
+ *
+ * Fudge controls
+ *
+ * flag1 force a call in manual mode
+ * flag2 enable port locking (not verified)
+ * flag3 not used
+ * flag4 not used
+ *
+ * time1 offset adjustment (s)
+ *
+ * Ordinarily, the serial port is connected to a modem and the phones
+ * list is defined. If no phones list is defined, the port can be
+ * connected directly to a device or another computer. In this case the
+ * driver will send a single character 'T' at each poll event. If
+ * fudge flag2 is enabled, port locking allows the modem to be shared
+ * when not in use by this driver.
+ */
+/*
+ * National Institute of Science and Technology (NIST)
+ *
+ * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii)
+ *
+ * Data Format
+ *
+ * National Institute of Standards and Technology
+ * Telephone Time Service, Generator 3B
+ * Enter question mark "?" for HELP
+ * D L D
+ * MJD YR MO DA H M S ST S UT1 msADV <OTM>
+ * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF>
+ * ...
+ *
+ * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is
+ * the on-time markers echoed by the driver and used by NIST to measure
+ * and correct for the propagation delay. Note: the ACTS timecode has
+ * recently been changed to eliminate the * on-time indicator. The
+ * reason for this and the long term implications are not clear.
+ *
+ * US Naval Observatory (USNO)
+ *
+ * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO)
+ *
+ * Data Format (two lines, repeating at one-second intervals)
+ *
+ * jjjjj nnn hhmmss UTC<CR><LF>
+ * *<CR><LF>
+ *
+ * jjjjj modified Julian day number (not used)
+ * nnn day of year
+ * hhmmss second of day
+ * * on-time marker for previous timecode
+ * ...
+ *
+ * USNO does not correct for the propagation delay. A fudge time1 of
+ * about .06 s is advisable.
+ *
+ * European Services (PTB, NPL, etc.)
+ *
+ * PTB: +49 531 512038 (Germany)
+ * NPL: 0906 851 6333 (UK only)
+ *
+ * Data format (see the documentation for phone numbers and formats.)
+ *
+ * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500<CR><LF>
+ *
+ * Spectracom GPS and WWVB Receivers
+ *
+ * If a modem is connected to a Spectracom receiver, this driver will
+ * call it up and retrieve the time in one of two formats. As this
+ * driver does not send anything, the radio will have to either be
+ * configured in continuous mode or be polled by another local driver.
+ */
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/acts%d" /* device name and unit */
+#define SPEED232 B19200 /* uart speed (19200 bps) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define LOCKFILE "/var/spool/lock/LCK..cua%d"
+#define DESCRIPTION "Automated Computer Time Service" /* WRU */
+#define REFID "NONE" /* default reference ID */
+#define MSGCNT 20 /* max message count */
+#define MAXPHONE 10 /* max number of phone numbers */
+
+/*
+ * Calling program modes (mode)
+ */
+#define MODE_BACKUP 0 /* backup mode */
+#define MODE_AUTO 1 /* automatic mode */
+#define MODE_MANUAL 2 /* manual mode */
+
+/*
+ * Service identifiers (message length)
+ */
+#define REFACTS "NIST" /* NIST reference ID */
+#define LENACTS 50 /* NIST format A */
+#define REFUSNO "USNO" /* USNO reference ID */
+#define LENUSNO 20 /* USNO */
+#define REFPTB "PTB\0" /* PTB/NPL reference ID */
+#define LENPTB 78 /* PTB/NPL format */
+#define REFWWVB "WWVB" /* WWVB reference ID */
+#define LENWWVB0 22 /* WWVB format 0 */
+#define LENWWVB2 24 /* WWVB format 2 */
+#define LF 0x0a /* ASCII LF */
+
+/*
+ * Modem setup strings. These may have to be changed for
+ * some modems.
+ *
+ * AT command prefix
+ * B1 US answer tone
+ * &C0 disable carrier detect
+ * &D2 hang up and return to command mode on DTR transition
+ * E0 modem command echo disabled
+ * L1 set modem speaker volume to low level
+ * M1 speaker enabled until carrier detect
+ * Q0 return result codes
+ * V1 return result codes as English words
+ * Y1 enable long-space disconnect
+ */
+const char def_modem_setup[] = "ATB1&C0&D2E0L1M1Q0V1Y1";
+const char *modem_setup = def_modem_setup;
+
+/*
+ * Timeouts (all in seconds)
+ */
+#define SETUP 3 /* setup timeout */
+#define REDIAL 30 /* redial timeout */
+#define ANSWER 60 /* answer timeout */
+#define TIMECODE 60 /* message timeout */
+#define MAXCODE 20 /* max timecodes */
+
+/*
+ * State machine codes
+ */
+typedef enum {
+ S_IDLE, /* wait for poll */
+ S_SETUP, /* send modem setup */
+ S_CONNECT, /* wait for answer */
+ S_MSG /* wait for timecode */
+} teModemState;
+
+/*
+ * Unit control structure
+ */
+struct actsunit {
+ int unit; /* unit number */
+ int state; /* the first one was Delaware */
+ int timer; /* timeout counter */
+ int retry; /* retry index */
+ int msgcnt; /* count of messages received */
+ l_fp tstamp; /* on-time timestamp */
+ char *bufptr; /* next incoming char stored here */
+ char buf[BMAX]; /* bufptr roams within buf[] */
+};
+
+/*
+ * Function prototypes
+ */
+static int acts_start (int, struct peer *);
+static void acts_shutdown (int, struct peer *);
+static void acts_receive (struct recvbuf *);
+static void acts_message (struct peer *, const char *);
+static void acts_timecode (struct peer *, const char *);
+static void acts_poll (int, struct peer *);
+static void acts_timeout (struct peer *, teModemState);
+static void acts_timer (int, struct peer *);
+static void acts_close (struct peer *);
+
+/*
+ * Transfer vector (conditional structure name)
+ */
+struct refclock refclock_acts = {
+ acts_start, /* start up driver */
+ acts_shutdown, /* shut down driver */
+ acts_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ acts_timer /* housekeeping timer */
+};
+
+/*
+ * Initialize data for processing
+ */
+static int
+acts_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+ const char *setup;
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(struct actsunit));
+ up->unit = unit;
+ pp = peer->procptr;
+ pp->unitptr = up;
+ pp->io.clock_recv = acts_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = -1;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy(&pp->refid, REFID, 4);
+ peer->sstclktype = CTL_SST_TS_TELEPHONE;
+ up->bufptr = up->buf;
+ if (def_modem_setup == modem_setup) {
+ setup = get_ext_sys_var("modemsetup");
+ if (setup != NULL)
+ modem_setup = estrdup(setup);
+ }
+
+ return (1);
+}
+
+
+/*
+ * acts_shutdown - shut down the clock
+ */
+static void
+acts_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Warning: do this only when a call is not in progress.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+ acts_close(peer);
+ free(up);
+}
+
+
+/*
+ * acts_receive - receive data from the serial interface
+ */
+static void
+acts_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char tbuf[sizeof(up->buf)];
+ char * tptr;
+ int octets;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp. Note
+ * we are in raw mode and victim of whatever the terminal
+ * interface kicks up; so, we have to reassemble messages from
+ * arbitrary fragments. Capture the timecode at the beginning of
+ * the message and at the '*' and '#' on-time characters.
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+ octets = sizeof(up->buf) - (up->bufptr - up->buf);
+ refclock_gtraw(rbufp, tbuf, octets, &pp->lastrec);
+ for (tptr = tbuf; *tptr != '\0'; tptr++) {
+ if (*tptr == LF) {
+ if (up->bufptr == up->buf) {
+ up->tstamp = pp->lastrec;
+ continue;
+ } else {
+ *up->bufptr = '\0';
+ up->bufptr = up->buf;
+ acts_message(peer, up->buf);
+ }
+ } else if (!iscntrl(*tptr)) {
+ *up->bufptr++ = *tptr;
+ if (*tptr == '*' || *tptr == '#') {
+ up->tstamp = pp->lastrec;
+ if (write(pp->io.fd, tptr, 1) < 0)
+ msyslog(LOG_ERR, "acts: write echo fails %m");
+ }
+ }
+ }
+}
+
+
+/*
+ * acts_message - process message
+ */
+void
+acts_message(
+ struct peer *peer,
+ const char *msg
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+ char tbuf[BMAX];
+ int dtr = TIOCM_DTR;
+
+ DPRINTF(1, ("acts: %d %s\n", (int)strlen(msg), msg));
+
+ /*
+ * What to do depends on the state and the first token in the
+ * message.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Extract the first token in the line.
+ */
+ strlcpy(tbuf, msg, sizeof(tbuf));
+ strtok(tbuf, " ");
+ switch (up->state) {
+
+ /*
+ * We are waiting for the OK response to the modem setup
+ * command. When this happens, dial the number followed.
+ * If anything other than OK is received, just ignore it
+ * and wait for timeoue.
+ */
+ case S_SETUP:
+ if (strcmp(tbuf, "OK") != 0) {
+ /*
+ * We disable echo with MODEM_SETUP's E0 but
+ * if the modem was previously E1, we will
+ * see MODEM_SETUP echoed before the OK/ERROR.
+ * Ignore it.
+ */
+ if (!strcmp(tbuf, modem_setup))
+ return;
+ break;
+ }
+
+ mprintf_event(PEVNT_CLOCK, peer, "DIAL #%d %s",
+ up->retry, sys_phone[up->retry]);
+ if (ioctl(pp->io.fd, TIOCMBIS, &dtr) < 0)
+ msyslog(LOG_ERR, "acts: ioctl(TIOCMBIS) failed: %m");
+ if (write(pp->io.fd, sys_phone[up->retry],
+ strlen(sys_phone[up->retry])) < 0)
+ msyslog(LOG_ERR, "acts: write DIAL fails %m");
+ write(pp->io.fd, "\r", 1);
+ up->retry++;
+ up->state = S_CONNECT;
+ up->timer = ANSWER;
+ return;
+
+ /*
+ * We are waiting for the CONNECT response to the dial
+ * command. When this happens, listen for timecodes. If
+ * somthing other than CONNECT is received, like BUSY
+ * or NO CARRIER, abort the call.
+ */
+ case S_CONNECT:
+ if (strcmp(tbuf, "CONNECT") != 0)
+ break;
+
+ report_event(PEVNT_CLOCK, peer, msg);
+ up->state = S_MSG;
+ up->timer = TIMECODE;
+ return;
+
+ /*
+ * We are waiting for a timecode response. Pass it to
+ * the parser. If NO CARRIER is received, save the
+ * messages and abort the call.
+ */
+ case S_MSG:
+ if (strcmp(tbuf, "NO") == 0)
+ report_event(PEVNT_CLOCK, peer, msg);
+ if (up->msgcnt < MAXCODE)
+ acts_timecode(peer, msg);
+ else
+ acts_timeout(peer, S_MSG);
+ return;
+ }
+
+ /*
+ * Other response. Tell us about it.
+ */
+ report_event(PEVNT_CLOCK, peer, msg);
+ acts_close(peer);
+}
+
+
+/*
+ * acts_timeout - called on timeout
+ */
+static void
+acts_timeout(
+ struct peer *peer,
+ teModemState dstate
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+ int fd;
+ int rc;
+ char device[20];
+ char lockfile[128], pidbuf[8];
+
+ /*
+ * The state machine is driven by messages from the modem,
+ * when first started and at timeout.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+ switch (dstate) {
+
+ /*
+ * System poll event. Lock the modem port, open the device
+ * and send the setup command.
+ */
+ case S_IDLE:
+ if (-1 != pp->io.fd)
+ return; /* port is already open */
+
+ /*
+ * Lock the modem port. If busy, retry later. Note: if
+ * something fails between here and the close, the lock
+ * file may not be removed.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG2) {
+ snprintf(lockfile, sizeof(lockfile), LOCKFILE,
+ up->unit);
+ fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL,
+ 0644);
+ if (fd < 0) {
+ report_event(PEVNT_CLOCK, peer, "acts: port busy");
+ return;
+ }
+ snprintf(pidbuf, sizeof(pidbuf), "%d\n",
+ (u_int)getpid());
+ if (write(fd, pidbuf, strlen(pidbuf)) < 0)
+ msyslog(LOG_ERR, "acts: write lock fails %m");
+ close(fd);
+ }
+
+ /*
+ * Open the device in raw mode and link the I/O.
+ */
+ snprintf(device, sizeof(device), DEVICE,
+ up->unit);
+ fd = refclock_open(device, SPEED232, LDISC_ACTS |
+ LDISC_RAW | LDISC_REMOTE);
+ if (fd <= 0) {
+ msyslog(LOG_ERR, "acts: open fails %m");
+ return;
+ }
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ msyslog(LOG_ERR, "acts: addclock fails");
+ close(fd);
+ pp->io.fd = -1;
+ return;
+ }
+ up->msgcnt = 0;
+ up->bufptr = up->buf;
+
+ /*
+ * If the port is directly connected to the device, skip
+ * the modem business and send 'T' for Spectrabum.
+ */
+ if (sys_phone[up->retry] == NULL) {
+ if (write(pp->io.fd, "T", 1) < 0)
+ msyslog(LOG_ERR, "acts: write T fails %m");
+ up->state = S_MSG;
+ up->timer = TIMECODE;
+ return;
+ }
+
+ /*
+ * Initialize the modem. This works with Hayes-
+ * compatible modems.
+ */
+ mprintf_event(PEVNT_CLOCK, peer, "SETUP %s",
+ modem_setup);
+ rc = write(pp->io.fd, modem_setup, strlen(modem_setup));
+ if (rc < 0)
+ msyslog(LOG_ERR, "acts: write SETUP fails %m");
+ write(pp->io.fd, "\r", 1);
+ up->state = S_SETUP;
+ up->timer = SETUP;
+ return;
+
+ /*
+ * In SETUP state the modem did not respond OK to setup string.
+ */
+ case S_SETUP:
+ report_event(PEVNT_CLOCK, peer, "no modem");
+ break;
+
+ /*
+ * In CONNECT state the call did not complete. Abort the call.
+ */
+ case S_CONNECT:
+ report_event(PEVNT_CLOCK, peer, "no answer");
+ break;
+
+ /*
+ * In MSG states no further timecodes are expected. If any
+ * timecodes have arrived, update the clock. In any case,
+ * terminate the call.
+ */
+ case S_MSG:
+ if (up->msgcnt == 0) {
+ report_event(PEVNT_CLOCK, peer, "no timecodes");
+ } else {
+ pp->lastref = pp->lastrec;
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+ }
+ break;
+ }
+ acts_close(peer);
+}
+
+
+/*
+ * acts_close - close and prepare for next call.
+ *
+ * In ClOSE state no further protocol actions are required
+ * other than to close and release the device and prepare to
+ * dial the next number if necessary.
+ */
+void
+acts_close(
+ struct peer *peer
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+ char lockfile[128];
+ int dtr;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (pp->io.fd != -1) {
+ report_event(PEVNT_CLOCK, peer, "close");
+ dtr = TIOCM_DTR;
+ if (ioctl(pp->io.fd, TIOCMBIC, &dtr) < 0)
+ msyslog(LOG_ERR, "acts: ioctl(TIOCMBIC) failed: %m");
+ io_closeclock(&pp->io);
+ pp->io.fd = -1;
+ }
+ if (pp->sloppyclockflag & CLK_FLAG2) {
+ snprintf(lockfile, sizeof(lockfile),
+ LOCKFILE, up->unit);
+ unlink(lockfile);
+ }
+ if (up->msgcnt == 0 && up->retry > 0) {
+ if (sys_phone[up->retry] != NULL) {
+ up->state = S_IDLE;
+ up->timer = REDIAL;
+ return;
+ }
+ }
+ up->state = S_IDLE;
+ up->timer = 0;
+}
+
+
+/*
+ * acts_poll - called by the transmit routine
+ */
+static void
+acts_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * This routine is called at every system poll. All it does is
+ * set flag1 under certain conditions. The real work is done by
+ * the timeout routine and state machine.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+ switch (peer->ttl) {
+
+ /*
+ * In manual mode the calling program is activated by the ntpdc
+ * program using the enable flag (fudge flag1), either manually
+ * or by a cron job.
+ */
+ case MODE_MANUAL:
+ return;
+
+ /*
+ * In automatic mode the calling program runs continuously at
+ * intervals determined by the poll event or specified timeout.
+ */
+ case MODE_AUTO:
+ break;
+
+ /*
+ * In backup mode the calling program runs continuously as long
+ * as either no peers are available or this peer is selected.
+ */
+ case MODE_BACKUP:
+ if (!(sys_peer == NULL || sys_peer == peer))
+ return;
+
+ break;
+ }
+ pp->polls++;
+ if (S_IDLE == up->state) {
+ up->retry = 0;
+ acts_timeout(peer, S_IDLE);
+ }
+}
+
+
+/*
+ * acts_timer - called at one-second intervals
+ */
+static void
+acts_timer(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * This routine implments a timeout which runs for a programmed
+ * interval. The counter is initialized by the state machine and
+ * counts down to zero. Upon reaching zero, the state machine is
+ * called. If flag1 is set while timer is zero, force a call.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (up->timer == 0) {
+ if (pp->sloppyclockflag & CLK_FLAG1) {
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ acts_timeout(peer, S_IDLE);
+ }
+ } else {
+ up->timer--;
+ if (up->timer == 0)
+ acts_timeout(peer, up->state);
+ }
+}
+
+/*
+ * acts_timecode - identify the service and parse the timecode message
+ */
+void
+acts_timecode(
+ struct peer * peer, /* peer structure pointer */
+ const char * str /* timecode string */
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+ int day; /* day of the month */
+ int month; /* month of the year */
+ u_long mjd; /* Modified Julian Day */
+ double dut1; /* DUT adjustment */
+
+ u_int dst; /* ACTS daylight/standard time */
+ u_int leap; /* ACTS leap indicator */
+ double msADV; /* ACTS transmit advance (ms) */
+ char utc[10]; /* ACTS timescale */
+ char flag; /* ACTS on-time character (* or #) */
+
+ char synchar; /* WWVB synchronized indicator */
+ char qualchar; /* WWVB quality indicator */
+ char leapchar; /* WWVB leap indicator */
+ char dstchar; /* WWVB daylight/savings indicator */
+ int tz; /* WWVB timezone */
+
+ int leapmonth; /* PTB/NPL month of leap */
+ char leapdir; /* PTB/NPL leap direction */
+
+ /*
+ * The parser selects the modem format based on the message
+ * length. Since the data are checked carefully, occasional
+ * errors due noise are forgivable.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+ pp->nsec = 0;
+ switch (strlen(str)) {
+
+ /*
+ * For USNO format on-time character '*', which is on a line by
+ * itself. Be sure a timecode has been received.
+ */
+ case 1:
+ if (*str == '*' && up->msgcnt > 0)
+ break;
+
+ return;
+
+ /*
+ * ACTS format A: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa
+ * UTC(NIST) *".
+ */
+ case LENACTS:
+ if (sscanf(str,
+ "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c",
+ &mjd, &pp->year, &month, &day, &pp->hour,
+ &pp->minute, &pp->second, &dst, &leap, &dut1,
+ &msADV, utc, &flag) != 13) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ pp->day = ymd2yd(pp->year, month, day);
+ pp->leap = LEAP_NOWARNING;
+ if (leap == 1)
+ pp->leap = LEAP_ADDSECOND;
+ else if (leap == 2)
+ pp->leap = LEAP_DELSECOND;
+ memcpy(&pp->refid, REFACTS, 4);
+ up->msgcnt++;
+ if (flag != '#' && up->msgcnt < 10)
+ return;
+
+ break;
+
+ /*
+ * USNO format: "jjjjj nnn hhmmss UTC"
+ */
+ case LENUSNO:
+ if (sscanf(str, "%5ld %3d %2d%2d%2d %3s",
+ &mjd, &pp->day, &pp->hour, &pp->minute,
+ &pp->second, utc) != 6) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Wait for the on-time character, which follows in a
+ * separate message. There is no provision for leap
+ * warning.
+ */
+ pp->leap = LEAP_NOWARNING;
+ memcpy(&pp->refid, REFUSNO, 4);
+ up->msgcnt++;
+ break;
+
+ /*
+ * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ"
+ */
+ case LENPTB:
+ if (sscanf(str,
+ "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c",
+ &pp->second, &pp->year, &month, &day, &pp->hour,
+ &pp->minute, &mjd, &dut1, &leapdir, &leapmonth,
+ &msADV, &flag) != 12) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ pp->leap = LEAP_NOWARNING;
+ if (leapmonth == month) {
+ if (leapdir == '+')
+ pp->leap = LEAP_ADDSECOND;
+ else if (leapdir == '-')
+ pp->leap = LEAP_DELSECOND;
+ }
+ pp->day = ymd2yd(pp->year, month, day);
+ memcpy(&pp->refid, REFPTB, 4);
+ up->msgcnt++;
+ break;
+
+
+ /*
+ * WWVB format 0: "I ddd hh:mm:ss DTZ=nn"
+ */
+ case LENWWVB0:
+ if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d",
+ &synchar, &pp->day, &pp->hour, &pp->minute,
+ &pp->second, &dstchar, &tz) != 7) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ pp->leap = LEAP_NOWARNING;
+ if (synchar != ' ')
+ pp->leap = LEAP_NOTINSYNC;
+ memcpy(&pp->refid, REFWWVB, 4);
+ up->msgcnt++;
+ break;
+
+ /*
+ * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD"
+ */
+ case LENWWVB2:
+ if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c",
+ &synchar, &qualchar, &pp->year, &pp->day,
+ &pp->hour, &pp->minute, &pp->second, &pp->nsec,
+ &dstchar, &leapchar, &dstchar) != 11) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ pp->nsec *= 1000000;
+ pp->leap = LEAP_NOWARNING;
+ if (synchar != ' ')
+ pp->leap = LEAP_NOTINSYNC;
+ else if (leapchar == 'L')
+ pp->leap = LEAP_ADDSECOND;
+ memcpy(&pp->refid, REFWWVB, 4);
+ up->msgcnt++;
+ break;
+
+ /*
+ * None of the above. Just forget about it and wait for the next
+ * message or timeout.
+ */
+ default:
+ return;
+ }
+
+ /*
+ * We have a valid timecode. The fudge time1 value is added to
+ * each sample by the main line routines. Note that in current
+ * telephone networks the propatation time can be different for
+ * each call and can reach 200 ms for some calls.
+ */
+ peer->refid = pp->refid;
+ pp->lastrec = up->tstamp;
+ if (up->msgcnt == 0)
+ return;
+
+ strlcpy(pp->a_lastcode, str, sizeof(pp->a_lastcode));
+ pp->lencode = strlen(pp->a_lastcode);
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+}
+#else
+int refclock_acts_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_arbiter.c b/ntpd/refclock_arbiter.c
new file mode 100644
index 0000000..738be50
--- /dev/null
+++ b/ntpd/refclock_arbiter.c
@@ -0,0 +1,479 @@
+/*
+ * refclock_arbiter - clock driver for Arbiter 1088A/B Satellite
+ * Controlled Clock
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ARBITER)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef SYS_WINNT
+extern int async_write(int, const void *, unsigned int);
+#undef write
+#define write(fd, data, octets) async_write(fd, data, octets)
+#endif
+
+/*
+ * This driver supports the Arbiter 1088A/B Satellite Controlled Clock.
+ * The claimed accuracy of this clock is 100 ns relative to the PPS
+ * output when receiving four or more satellites.
+ *
+ * The receiver should be configured before starting the NTP daemon, in
+ * order to establish reliable position and operating conditions. It
+ * does not initiate surveying or hold mode. For use with NTP, the
+ * daylight savings time feature should be disables (D0 command) and the
+ * broadcast mode set to operate in UTC (BU command).
+ *
+ * The timecode format supported by this driver is selected by the poll
+ * sequence "B5", which initiates a line in the following format to be
+ * repeated once per second until turned off by the "B0" poll sequence.
+ *
+ * Format B5 (24 ASCII printing characters):
+ *
+ * <cr><lf>i yy ddd hh:mm:ss.000bbb
+ *
+ * on-time = <cr>
+ * i = synchronization flag (' ' = locked, '?' = unlocked)
+ * yy = year of century
+ * ddd = day of year
+ * hh:mm:ss = hours, minutes, seconds
+ * .000 = fraction of second (not used)
+ * bbb = tailing spaces for fill
+ *
+ * The alarm condition is indicated by a '?' at i, which indicates the
+ * receiver is not synchronized. In normal operation, a line consisting
+ * of the timecode followed by the time quality character (TQ) followed
+ * by the receiver status string (SR) is written to the clockstats file.
+ * The time quality character is encoded in IEEE P1344 standard:
+ *
+ * Format TQ (IEEE P1344 estimated worst-case time quality)
+ *
+ * 0 clock locked, maximum accuracy
+ * F clock failure, time not reliable
+ * 4 clock unlocked, accuracy < 1 us
+ * 5 clock unlocked, accuracy < 10 us
+ * 6 clock unlocked, accuracy < 100 us
+ * 7 clock unlocked, accuracy < 1 ms
+ * 8 clock unlocked, accuracy < 10 ms
+ * 9 clock unlocked, accuracy < 100 ms
+ * A clock unlocked, accuracy < 1 s
+ * B clock unlocked, accuracy < 10 s
+ *
+ * The status string is encoded as follows:
+ *
+ * Format SR (25 ASCII printing characters)
+ *
+ * V=vv S=ss T=t P=pdop E=ee
+ *
+ * vv = satellites visible
+ * ss = relative signal strength
+ * t = satellites tracked
+ * pdop = position dilution of precision (meters)
+ * ee = hardware errors
+ *
+ * If flag4 is set, an additional line consisting of the receiver
+ * latitude (LA), longitude (LO), elevation (LH) (meters), and data
+ * buffer (DB) is written to this file. If channel B is enabled for
+ * deviation mode and connected to a 1-PPS signal, the last two numbers
+ * on the line are the deviation and standard deviation averaged over
+ * the last 15 seconds.
+ *
+ * PPS calibration fudge time1 .001240
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/gps%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS " /* reference ID */
+#define DESCRIPTION "Arbiter 1088A/B GPS Receiver" /* WRU */
+#define LENARB 24 /* format B5 timecode length */
+#define MAXSTA 40 /* max length of status string */
+#define MAXPOS 80 /* max length of position string */
+
+#ifdef PRE_NTP420
+#define MODE ttlmax
+#else
+#define MODE ttl
+#endif
+
+#define COMMAND_HALT_BCAST ( (peer->MODE % 2) ? "O0" : "B0" )
+#define COMMAND_START_BCAST ( (peer->MODE % 2) ? "O5" : "B5" )
+
+/*
+ * ARB unit control structure
+ */
+struct arbunit {
+ l_fp laststamp; /* last receive timestamp */
+ int tcswitch; /* timecode switch/counter */
+ char qualchar; /* IEEE P1344 quality (TQ command) */
+ char status[MAXSTA]; /* receiver status (SR command) */
+ char latlon[MAXPOS]; /* receiver position (lat/lon/alt) */
+};
+
+/*
+ * Function prototypes
+ */
+static int arb_start (int, struct peer *);
+static void arb_shutdown (int, struct peer *);
+static void arb_receive (struct recvbuf *);
+static void arb_poll (int, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_arbiter = {
+ arb_start, /* start up driver */
+ arb_shutdown, /* shut down driver */
+ arb_poll, /* transmit poll message */
+ noentry, /* not used (old arb_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old arb_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * arb_start - open the devices and initialize data for processing
+ */
+static int
+arb_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arbunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+ fd = refclock_open(device, SPEED232, LDISC_CLK);
+ if (fd <= 0)
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->io.clock_recv = arb_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ return (0);
+ }
+ pp->unitptr = up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ if (peer->MODE > 1) {
+ msyslog(LOG_NOTICE, "ARBITER: Invalid mode %d", peer->MODE);
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ return (0);
+ }
+#ifdef DEBUG
+ if(debug) { printf("arbiter: mode = %d.\n", peer->MODE); }
+#endif
+ write(pp->io.fd, COMMAND_HALT_BCAST, 2);
+ return (1);
+}
+
+
+/*
+ * arb_shutdown - shut down the clock
+ */
+static void
+arb_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arbunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ if (NULL != up)
+ free(up);
+}
+
+
+/*
+ * arb_receive - receive data from the serial interface
+ */
+static void
+arb_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct arbunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ int temp;
+ u_char syncchar; /* synch indicator */
+ char tbuf[BMAX]; /* temp buffer */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+ temp = refclock_gtlin(rbufp, tbuf, sizeof(tbuf), &trtmp);
+
+ /*
+ * Note we get a buffer and timestamp for both a <cr> and <lf>,
+ * but only the <cr> timestamp is retained. The program first
+ * sends a TQ and expects the echo followed by the time quality
+ * character. It then sends a B5 starting the timecode broadcast
+ * and expects the echo followed some time later by the on-time
+ * character <cr> and then the <lf> beginning the timecode
+ * itself. Finally, at the <cr> beginning the next timecode at
+ * the next second, the program sends a B0 shutting down the
+ * timecode broadcast.
+ *
+ * If flag4 is set, the program snatches the latitude, longitude
+ * and elevation and writes it to the clockstats file.
+ */
+ if (temp == 0)
+ return;
+
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ if (temp < 3)
+ return;
+
+ if (up->tcswitch == 0) {
+
+ /*
+ * Collect statistics. If nothing is recogized, just
+ * ignore; sometimes the clock doesn't stop spewing
+ * timecodes for awhile after the B0 command.
+ *
+ * If flag4 is not set, send TQ, SR, B5. If flag4 is
+ * sset, send TQ, SR, LA, LO, LH, DB, B5. When the
+ * median filter is full, send B0.
+ */
+ if (!strncmp(tbuf, "TQ", 2)) {
+ up->qualchar = tbuf[2];
+ write(pp->io.fd, "SR", 2);
+ return;
+
+ } else if (!strncmp(tbuf, "SR", 2)) {
+ strlcpy(up->status, tbuf + 2,
+ sizeof(up->status));
+ if (pp->sloppyclockflag & CLK_FLAG4)
+ write(pp->io.fd, "LA", 2);
+ else
+ write(pp->io.fd, COMMAND_START_BCAST, 2);
+ return;
+
+ } else if (!strncmp(tbuf, "LA", 2)) {
+ strlcpy(up->latlon, tbuf + 2, sizeof(up->latlon));
+ write(pp->io.fd, "LO", 2);
+ return;
+
+ } else if (!strncmp(tbuf, "LO", 2)) {
+ strlcat(up->latlon, " ", sizeof(up->latlon));
+ strlcat(up->latlon, tbuf + 2, sizeof(up->latlon));
+ write(pp->io.fd, "LH", 2);
+ return;
+
+ } else if (!strncmp(tbuf, "LH", 2)) {
+ strlcat(up->latlon, " ", sizeof(up->latlon));
+ strlcat(up->latlon, tbuf + 2, sizeof(up->latlon));
+ write(pp->io.fd, "DB", 2);
+ return;
+
+ } else if (!strncmp(tbuf, "DB", 2)) {
+ strlcat(up->latlon, " ", sizeof(up->latlon));
+ strlcat(up->latlon, tbuf + 2, sizeof(up->latlon));
+ record_clock_stats(&peer->srcadr, up->latlon);
+#ifdef DEBUG
+ if (debug)
+ printf("arbiter: %s\n", up->latlon);
+#endif
+ write(pp->io.fd, COMMAND_START_BCAST, 2);
+ }
+ }
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has valid length, but not in
+ * proper format, we declare bad format and exit. If the
+ * timecode has invalid length, which sometimes occurs when the
+ * B0 amputates the broadcast, we just quietly steal away. Note
+ * that the time quality character and receiver status string is
+ * tacked on the end for clockstats display.
+ */
+ up->tcswitch++;
+ if (up->tcswitch <= 1 || temp < LENARB)
+ return;
+
+ /*
+ * Timecode format B5: "i yy ddd hh:mm:ss.000 "
+ */
+ strlcpy(pp->a_lastcode, tbuf, sizeof(pp->a_lastcode));
+ pp->a_lastcode[LENARB - 2] = up->qualchar;
+ strlcat(pp->a_lastcode, up->status, sizeof(pp->a_lastcode));
+ pp->lencode = strlen(pp->a_lastcode);
+ syncchar = ' ';
+ if (sscanf(pp->a_lastcode, "%c%2d %3d %2d:%2d:%2d",
+ &syncchar, &pp->year, &pp->day, &pp->hour,
+ &pp->minute, &pp->second) != 6) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ write(pp->io.fd, COMMAND_HALT_BCAST, 2);
+ return;
+ }
+
+ /*
+ * We decode the clock dispersion from the time quality
+ * character.
+ */
+ switch (up->qualchar) {
+
+ case '0': /* locked, max accuracy */
+ pp->disp = 1e-7;
+ pp->lastref = pp->lastrec;
+ break;
+
+ case '4': /* unlock accuracy < 1 us */
+ pp->disp = 1e-6;
+ break;
+
+ case '5': /* unlock accuracy < 10 us */
+ pp->disp = 1e-5;
+ break;
+
+ case '6': /* unlock accuracy < 100 us */
+ pp->disp = 1e-4;
+ break;
+
+ case '7': /* unlock accuracy < 1 ms */
+ pp->disp = .001;
+ break;
+
+ case '8': /* unlock accuracy < 10 ms */
+ pp->disp = .01;
+ break;
+
+ case '9': /* unlock accuracy < 100 ms */
+ pp->disp = .1;
+ break;
+
+ case 'A': /* unlock accuracy < 1 s */
+ pp->disp = 1;
+ break;
+
+ case 'B': /* unlock accuracy < 10 s */
+ pp->disp = 10;
+ break;
+
+ case 'F': /* clock failure */
+ pp->disp = MAXDISPERSE;
+ refclock_report(peer, CEVNT_FAULT);
+ write(pp->io.fd, COMMAND_HALT_BCAST, 2);
+ return;
+
+ default:
+ pp->disp = MAXDISPERSE;
+ refclock_report(peer, CEVNT_BADREPLY);
+ write(pp->io.fd, COMMAND_HALT_BCAST, 2);
+ return;
+ }
+ if (syncchar != ' ')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp))
+ refclock_report(peer, CEVNT_BADTIME);
+ else if (peer->disp > MAXDISTANCE)
+ refclock_receive(peer);
+
+ /* if (up->tcswitch >= MAXSTAGE) { */
+ write(pp->io.fd, COMMAND_HALT_BCAST, 2);
+ /* } */
+}
+
+
+/*
+ * arb_poll - called by the transmit procedure
+ */
+static void
+arb_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arbunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Time to poll the clock. The Arbiter clock responds to a "B5"
+ * by returning a timecode in the format specified above.
+ * Transmission occurs once per second, unless turned off by a
+ * "B0". Note there is no checking on state, since this may not
+ * be the only customer reading the clock. Only one customer
+ * need poll the clock; all others just listen in.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+ pp->polls++;
+ up->tcswitch = 0;
+ if (write(pp->io.fd, "TQ", 2) != 2)
+ refclock_report(peer, CEVNT_FAULT);
+
+ /*
+ * Process median filter samples. If none received, declare a
+ * timeout and keep going.
+ */
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("arbiter: timecode %d %s\n",
+ pp->lencode, pp->a_lastcode);
+#endif
+}
+
+#else
+int refclock_arbiter_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_arc.c b/ntpd/refclock_arc.c
new file mode 100644
index 0000000..b2c063e
--- /dev/null
+++ b/ntpd/refclock_arc.c
@@ -0,0 +1,1587 @@
+/*
+ * refclock_arc - clock driver for ARCRON MSF/DCF/WWVB receivers
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ntp_types.h"
+
+#if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF)
+
+static const char arc_version[] = { "V1.3 2003/02/21" };
+
+/* define PRE_NTP420 for compatibility to previous versions of NTP (at least
+ to 4.1.0 */
+#undef PRE_NTP420
+
+#ifndef ARCRON_NOT_KEEN
+#define ARCRON_KEEN 1 /* Be keen, and trusting of the clock, if defined. */
+#endif
+
+#ifndef ARCRON_NOT_MULTIPLE_SAMPLES
+#define ARCRON_MULTIPLE_SAMPLES 1 /* Use all timestamp bytes as samples. */
+#endif
+
+#ifndef ARCRON_NOT_LEAPSECOND_KEEN
+#ifndef ARCRON_LEAPSECOND_KEEN
+#undef ARCRON_LEAPSECOND_KEEN /* Respond quickly to leap seconds: doesn't work yet. */
+#endif
+#endif
+
+/*
+Code by Derek Mulcahy, <derek@toybox.demon.co.uk>, 1997.
+Modifications by Damon Hart-Davis, <d@hd.org>, 1997.
+Modifications by Paul Alfille, <palfille@partners.org>, 2003.
+Modifications by Christopher Price, <cprice@cs-home.com>, 2003.
+Modifications by Nigel Roles <nigel@9fs.org>, 2003.
+
+
+THIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND. USE AT
+YOUR OWN RISK.
+
+Orginally developed and used with ntp3-5.85 by Derek Mulcahy.
+
+Built against ntp3-5.90 on Solaris 2.5 using gcc 2.7.2.
+
+This code may be freely copied and used and incorporated in other
+systems providing the disclaimer and notice of authorship are
+reproduced.
+
+-------------------------------------------------------------------------------
+
+Nigel's notes:
+
+1) Called tcgetattr() before modifying, so that fields correctly initialised
+ for all operating systems
+
+2) Altered parsing of timestamp line so that it copes with fields which are
+ not always ASCII digits (e.g. status field when battery low)
+
+-------------------------------------------------------------------------------
+
+Christopher's notes:
+
+MAJOR CHANGES SINCE V1.2
+========================
+ 1) Applied patch by Andrey Bray <abuse@madhouse.demon.co.uk>
+ 2001-02-17 comp.protocols.time.ntp
+
+ 2) Added WWVB support via clock mode command, localtime/UTC time configured
+ via flag1=(0=UTC, 1=localtime)
+
+ 3) Added ignore resync request via flag2=(0=resync, 1=ignore resync)
+
+ 4) Added simplified conversion from localtime to UTC with dst/bst translation
+
+ 5) Added average signal quality poll
+
+ 6) Fixed a badformat error when no code is available due to stripping
+ \n & \r's
+
+ 7) Fixed a badformat error when clearing lencode & memset a_lastcode in poll
+ routine
+
+ 8) Lots of code cleanup, including standardized DEBUG macros and removal
+ of unused code
+
+-------------------------------------------------------------------------------
+
+Author's original note:
+
+I enclose my ntp driver for the Galleon Systems Arc MSF receiver.
+
+It works (after a fashion) on both Solaris-1 and Solaris-2.
+
+I am currently using ntp3-5.85. I have been running the code for
+about 7 months without any problems. Even coped with the change to BST!
+
+I had to do some funky things to read from the clock because it uses the
+power from the receive lines to drive the transmit lines. This makes the
+code look a bit stupid but it works. I also had to put in some delays to
+allow for the turnaround time from receive to transmit. These delays
+are between characters when requesting a time stamp so that shouldn't affect
+the results too drastically.
+
+...
+
+The bottom line is that it works but could easily be improved. You are
+free to do what you will with the code. I haven't been able to determine
+how good the clock is. I think that this requires a known good clock
+to compare it against.
+
+-------------------------------------------------------------------------------
+
+Damon's notes for adjustments:
+
+MAJOR CHANGES SINCE V1.0
+========================
+ 1) Removal of pollcnt variable that made the clock go permanently
+ off-line once two time polls failed to gain responses.
+
+ 2) Avoiding (at least on Solaris-2) terminal becoming the controlling
+ terminal of the process when we do a low-level open().
+
+ 3) Additional logic (conditional on ARCRON_LEAPSECOND_KEEN being
+ defined) to try to resync quickly after a potential leap-second
+ insertion or deletion.
+
+ 4) Code significantly slimmer at run-time than V1.0.
+
+
+GENERAL
+=======
+
+ 1) The C preprocessor symbol to have the clock built has been changed
+ from ARC to ARCRON_MSF to CLOCK_ARCRON_MSF to minimise the
+ possiblity of clashes with other symbols in the future.
+
+ 2) PRECISION should be -4/-5 (63ms/31ms) for the following reasons:
+
+ a) The ARC documentation claims the internal clock is (only)
+ accurate to about 20ms relative to Rugby (plus there must be
+ noticable drift and delay in the ms range due to transmission
+ delays and changing atmospheric effects). This clock is not
+ designed for ms accuracy as NTP has spoilt us all to expect.
+
+ b) The clock oscillator looks like a simple uncompensated quartz
+ crystal of the sort used in digital watches (ie 32768Hz) which
+ can have large temperature coefficients and drifts; it is not
+ clear if this oscillator is properly disciplined to the MSF
+ transmission, but as the default is to resync only once per
+ *day*, we can imagine that it is not, and is free-running. We
+ can minimise drift by resyncing more often (at the cost of
+ reduced battery life), but drift/wander may still be
+ significant.
+
+ c) Note that the bit time of 3.3ms adds to the potential error in
+ the the clock timestamp, since the bit clock of the serial link
+ may effectively be free-running with respect to the host clock
+ and the MSF clock. Actually, the error is probably 1/16th of
+ the above, since the input data is probably sampled at at least
+ 16x the bit rate.
+
+ By keeping the clock marked as not very precise, it will have a
+ fairly large dispersion, and thus will tend to be used as a
+ `backup' time source and sanity checker, which this clock is
+ probably ideal for. For an isolated network without other time
+ sources, this clock can probably be expected to provide *much*
+ better than 1s accuracy, which will be fine.
+
+ By default, PRECISION is set to -4, but experience, especially at a
+ particular geographic location with a particular clock, may allow
+ this to be altered to -5. (Note that skews of +/- 10ms are to be
+ expected from the clock from time-to-time.) This improvement of
+ reported precision can be instigated by setting flag3 to 1, though
+ the PRECISION will revert to the normal value while the clock
+ signal quality is unknown whatever the flag3 setting.
+
+ IN ANY CASE, BE SURE TO SET AN APPROPRIATE FUDGE FACTOR TO REMOVE
+ ANY RESIDUAL SKEW, eg:
+
+ server 127.127.27.0 # ARCRON MSF radio clock unit 0.
+ # Fudge timestamps by about 20ms.
+ fudge 127.127.27.0 time1 0.020
+
+ You will need to observe your system's behaviour, assuming you have
+ some other NTP source to compare it with, to work out what the
+ fudge factor should be. For my Sun SS1 running SunOS 4.1.3_U1 with
+ my MSF clock with my distance from the MSF transmitter, +20ms
+ seemed about right, after some observation.
+
+ 3) REFID has been made "MSFa" to reflect the MSF time source and the
+ ARCRON receiver.
+
+ 4) DEFAULT_RESYNC_TIME is the time in seconds (by default) before
+ forcing a resync since the last attempt. This is picked to give a
+ little less than an hour between resyncs and to try to avoid
+ clashing with any regular event at a regular time-past-the-hour
+ which might cause systematic errors.
+
+ The INITIAL_RESYNC_DELAY is to avoid bothering the clock and
+ running down its batteries unnecesarily if ntpd is going to crash
+ or be killed or reconfigured quickly. If ARCRON_KEEN is defined
+ then this period is long enough for (with normal polling rates)
+ enough time samples to have been taken to allow ntpd to sync to
+ the clock before the interruption for the clock to resync to MSF.
+ This avoids ntpd syncing to another peer first and then
+ almost immediately hopping to the MSF clock.
+
+ The RETRY_RESYNC_TIME is used before rescheduling a resync after a
+ resync failed to reveal a statisfatory signal quality (too low or
+ unknown).
+
+ 5) The clock seems quite jittery, so I have increased the
+ median-filter size from the typical (previous) value of 3. I
+ discard up to half the results in the filter. It looks like maybe
+ 1 sample in 10 or so (maybe less) is a spike, so allow the median
+ filter to discard at least 10% of its entries or 1 entry, whichever
+ is greater.
+
+ 6) Sleeping *before* each character sent to the unit to allow required
+ inter-character time but without introducting jitter and delay in
+ handling the response if possible.
+
+ 7) If the flag ARCRON_KEEN is defined, take time samples whenever
+ possible, even while resyncing, etc. We rely, in this case, on the
+ clock always giving us a reasonable time or else telling us in the
+ status byte at the end of the timestamp that it failed to sync to
+ MSF---thus we should never end up syncing to completely the wrong
+ time.
+
+ 8) If the flag ARCRON_OWN_FILTER is defined, use own versions of
+ refclock median-filter routines to get round small bug in 3-5.90
+ code which does not return the median offset. XXX Removed this
+ bit due NTP Version 4 upgrade - dlm.
+
+ 9) We would appear to have a year-2000 problem with this clock since
+ it returns only the two least-significant digits of the year. But
+ ntpd ignores the year and uses the local-system year instead, so
+ this is in fact not a problem. Nevertheless, we attempt to do a
+ sensible thing with the dates, wrapping them into a 100-year
+ window.
+
+ 10)Logs stats information that can be used by Derek's Tcl/Tk utility
+ to show the status of the clock.
+
+ 11)The clock documentation insists that the number of bits per
+ character to be sent to the clock, and sent by it, is 11, including
+ one start bit and two stop bits. The data format is either 7+even
+ or 8+none.
+
+
+TO-DO LIST
+==========
+
+ * Eliminate use of scanf(), and maybe sprintf().
+
+ * Allow user setting of resync interval to trade battery life for
+ accuracy; maybe could be done via fudge factor or unit number.
+
+ * Possibly note the time since the last resync of the MSF clock to
+ MSF as the age of the last reference timestamp, ie trust the
+ clock's oscillator not very much...
+
+ * Add very slow auto-adjustment up to a value of +/- time2 to correct
+ for long-term errors in the clock value (time2 defaults to 0 so the
+ correction would be disabled by default).
+
+ * Consider trying to use the tty_clk/ppsclock support.
+
+ * Possibly use average or maximum signal quality reported during
+ resync, rather than just the last one, which may be atypical.
+
+*/
+
+
+/* Notes for HKW Elektronik GmBH Radio clock driver */
+/* Author Lyndon David, Sentinet Ltd, Feb 1997 */
+/* These notes seem also to apply usefully to the ARCRON clock. */
+
+/* The HKW clock module is a radio receiver tuned into the Rugby */
+/* MSF time signal tranmitted on 60 kHz. The clock module connects */
+/* to the computer via a serial line and transmits the time encoded */
+/* in 15 bytes at 300 baud 7 bits two stop bits even parity */
+
+/* Clock communications, from the datasheet */
+/* All characters sent to the clock are echoed back to the controlling */
+/* device. */
+/* Transmit time/date information */
+/* syntax ASCII o<cr> */
+/* Character o may be replaced if neccesary by a character whose code */
+/* contains the lowest four bits f(hex) eg */
+/* syntax binary: xxxx1111 00001101 */
+
+/* DHD note:
+You have to wait for character echo + 10ms before sending next character.
+*/
+
+/* The clock replies to this command with a sequence of 15 characters */
+/* which contain the complete time and a final <cr> making 16 characters */
+/* in total. */
+/* The RC computer clock will not reply immediately to this command because */
+/* the start bit edge of the first reply character marks the beginning of */
+/* the second. So the RC Computer Clock will reply to this command at the */
+/* start of the next second */
+/* The characters have the following meaning */
+/* 1. hours tens */
+/* 2. hours units */
+/* 3. minutes tens */
+/* 4. minutes units */
+/* 5. seconds tens */
+/* 6. seconds units */
+/* 7. day of week 1-monday 7-sunday */
+/* 8. day of month tens */
+/* 9. day of month units */
+/* 10. month tens */
+/* 11. month units */
+/* 12. year tens */
+/* 13. year units */
+/* 14. BST/UTC status */
+/* bit 7 parity */
+/* bit 6 always 0 */
+/* bit 5 always 1 */
+/* bit 4 always 1 */
+/* bit 3 always 0 */
+/* bit 2 =1 if UTC is in effect, complementary to the BST bit */
+/* bit 1 =1 if BST is in effect, according to the BST bit */
+/* bit 0 BST/UTC change impending bit=1 in case of change impending */
+/* 15. status */
+/* bit 7 parity */
+/* bit 6 always 0 */
+/* bit 5 always 1 */
+/* bit 4 always 1 */
+/* bit 3 =1 if low battery is detected */
+/* bit 2 =1 if the very last reception attempt failed and a valid */
+/* time information already exists (bit0=1) */
+/* =0 if the last reception attempt was successful */
+/* bit 1 =1 if at least one reception since 2:30 am was successful */
+/* =0 if no reception attempt since 2:30 am was successful */
+/* bit 0 =1 if the RC Computer Clock contains valid time information */
+/* This bit is zero after reset and one after the first */
+/* successful reception attempt */
+
+/* DHD note:
+Also note g<cr> command which confirms that a resync is in progress, and
+if so what signal quality (0--5) is available.
+Also note h<cr> command which starts a resync to MSF signal.
+*/
+
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+
+/*
+ * This driver supports the ARCRON MSF/DCF/WWVB Radio Controlled Clock
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/arc%d" /* Device name and unit. */
+#define SPEED B300 /* UART speed (300 baud) */
+#define PRECISION (-4) /* Precision (~63 ms). */
+#define HIGHPRECISION (-5) /* If things are going well... */
+#define REFID "MSFa" /* Reference ID. */
+#define REFID_MSF "MSF" /* Reference ID. */
+#define REFID_DCF77 "DCF" /* Reference ID. */
+#define REFID_WWVB "WWVB" /* Reference ID. */
+#define DESCRIPTION "ARCRON MSF/DCF/WWVB Receiver"
+
+#ifdef PRE_NTP420
+#define MODE ttlmax
+#else
+#define MODE ttl
+#endif
+
+#define LENARC 16 /* Format `o' timecode length. */
+
+#define BITSPERCHAR 11 /* Bits per character. */
+#define BITTIME 0x0DA740E /* Time for 1 bit at 300bps. */
+#define CHARTIME10 0x8888888 /* Time for 10-bit char at 300bps. */
+#define CHARTIME11 0x962FC96 /* Time for 11-bit char at 300bps. */
+#define CHARTIME /* Time for char at 300bps. */ \
+( (BITSPERCHAR == 11) ? CHARTIME11 : ( (BITSPERCHAR == 10) ? CHARTIME10 : \
+ (BITSPERCHAR * BITTIME) ) )
+
+ /* Allow for UART to accept char half-way through final stop bit. */
+#define INITIALOFFSET ((u_int32)(-BITTIME/2))
+
+ /*
+ charoffsets[x] is the time after the start of the second that byte
+ x (with the first byte being byte 1) is received by the UART,
+ assuming that the initial edge of the start bit of the first byte
+ is on-time. The values are represented as the fractional part of
+ an l_fp.
+
+ We store enough values to have the offset of each byte including
+ the trailing \r, on the assumption that the bytes follow one
+ another without gaps.
+ */
+ static const u_int32 charoffsets[LENARC+1] = {
+#if BITSPERCHAR == 11 /* Usual case. */
+ /* Offsets computed as accurately as possible... */
+ 0,
+ INITIALOFFSET + 0x0962fc96, /* 1 chars, 11 bits */
+ INITIALOFFSET + 0x12c5f92c, /* 2 chars, 22 bits */
+ INITIALOFFSET + 0x1c28f5c3, /* 3 chars, 33 bits */
+ INITIALOFFSET + 0x258bf259, /* 4 chars, 44 bits */
+ INITIALOFFSET + 0x2eeeeeef, /* 5 chars, 55 bits */
+ INITIALOFFSET + 0x3851eb85, /* 6 chars, 66 bits */
+ INITIALOFFSET + 0x41b4e81b, /* 7 chars, 77 bits */
+ INITIALOFFSET + 0x4b17e4b1, /* 8 chars, 88 bits */
+ INITIALOFFSET + 0x547ae148, /* 9 chars, 99 bits */
+ INITIALOFFSET + 0x5dddddde, /* 10 chars, 110 bits */
+ INITIALOFFSET + 0x6740da74, /* 11 chars, 121 bits */
+ INITIALOFFSET + 0x70a3d70a, /* 12 chars, 132 bits */
+ INITIALOFFSET + 0x7a06d3a0, /* 13 chars, 143 bits */
+ INITIALOFFSET + 0x8369d037, /* 14 chars, 154 bits */
+ INITIALOFFSET + 0x8ccccccd, /* 15 chars, 165 bits */
+ INITIALOFFSET + 0x962fc963 /* 16 chars, 176 bits */
+#else
+ /* Offsets computed with a small rounding error... */
+ 0,
+ INITIALOFFSET + 1 * CHARTIME,
+ INITIALOFFSET + 2 * CHARTIME,
+ INITIALOFFSET + 3 * CHARTIME,
+ INITIALOFFSET + 4 * CHARTIME,
+ INITIALOFFSET + 5 * CHARTIME,
+ INITIALOFFSET + 6 * CHARTIME,
+ INITIALOFFSET + 7 * CHARTIME,
+ INITIALOFFSET + 8 * CHARTIME,
+ INITIALOFFSET + 9 * CHARTIME,
+ INITIALOFFSET + 10 * CHARTIME,
+ INITIALOFFSET + 11 * CHARTIME,
+ INITIALOFFSET + 12 * CHARTIME,
+ INITIALOFFSET + 13 * CHARTIME,
+ INITIALOFFSET + 14 * CHARTIME,
+ INITIALOFFSET + 15 * CHARTIME,
+ INITIALOFFSET + 16 * CHARTIME
+#endif
+ };
+
+#define DEFAULT_RESYNC_TIME (57*60) /* Gap between resync attempts (s). */
+#define RETRY_RESYNC_TIME (27*60) /* Gap to emergency resync attempt. */
+#ifdef ARCRON_KEEN
+#define INITIAL_RESYNC_DELAY 500 /* Delay before first resync. */
+#else
+#define INITIAL_RESYNC_DELAY 50 /* Delay before first resync. */
+#endif
+
+ static const int moff[12] =
+{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+/* Flags for a raw open() of the clock serial device. */
+#ifdef O_NOCTTY /* Good, we can avoid tty becoming controlling tty. */
+#define OPEN_FLAGS (O_RDWR | O_NOCTTY)
+#else /* Oh well, it may not matter... */
+#define OPEN_FLAGS (O_RDWR)
+#endif
+
+
+/* Length of queue of command bytes to be sent. */
+#define CMDQUEUELEN 4 /* Enough for two cmds + each \r. */
+/* Queue tick time; interval in seconds between chars taken off queue. */
+/* Must be >= 2 to allow o\r response to come back uninterrupted. */
+#define QUEUETICK 2 /* Allow o\r reply to finish. */
+
+/*
+ * ARC unit control structure
+ */
+struct arcunit {
+ l_fp lastrec; /* Time tag for the receive time (system). */
+ int status; /* Clock status. */
+
+ int quality; /* Quality of reception 0--5 for unit. */
+ /* We may also use the values -1 or 6 internally. */
+ u_long quality_stamp; /* Next time to reset quality average. */
+
+ u_long next_resync; /* Next resync time (s) compared to current_time. */
+ int resyncing; /* Resync in progress if true. */
+
+ /* In the outgoing queue, cmdqueue[0] is next to be sent. */
+ char cmdqueue[CMDQUEUELEN+1]; /* Queue of outgoing commands + \0. */
+
+ u_long saved_flags; /* Saved fudge flags. */
+};
+
+#ifdef ARCRON_LEAPSECOND_KEEN
+/* The flag `possible_leap' is set non-zero when any MSF unit
+ thinks a leap-second may have happened.
+
+ Set whenever we receive a valid time sample in the first hour of
+ the first day of the first/seventh months.
+
+ Outside the special hour this value is unconditionally set
+ to zero by the receive routine.
+
+ On finding itself in this timeslot, as long as the value is
+ non-negative, the receive routine sets it to a positive value to
+ indicate a resync to MSF should be performed.
+
+ In the poll routine, if this value is positive and we are not
+ already resyncing (eg from a sync that started just before
+ midnight), start resyncing and set this value negative to
+ indicate that a leap-triggered resync has been started. Having
+ set this negative prevents the receive routine setting it
+ positive and thus prevents multiple resyncs during the witching
+ hour.
+ */
+static int possible_leap = 0; /* No resync required by default. */
+#endif
+
+#if 0
+static void dummy_event_handler (struct peer *);
+static void arc_event_handler (struct peer *);
+#endif /* 0 */
+
+#define QUALITY_UNKNOWN -1 /* Indicates unknown clock quality. */
+#define MIN_CLOCK_QUALITY 0 /* Min quality clock will return. */
+#define MIN_CLOCK_QUALITY_OK 3 /* Min quality for OK reception. */
+#define MAX_CLOCK_QUALITY 5 /* Max quality clock will return. */
+
+/*
+ * Function prototypes
+ */
+static int arc_start (int, struct peer *);
+static void arc_shutdown (int, struct peer *);
+static void arc_receive (struct recvbuf *);
+static void arc_poll (int, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_arc = {
+ arc_start, /* start up driver */
+ arc_shutdown, /* shut down driver */
+ arc_poll, /* transmit poll message */
+ noentry, /* not used (old arc_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old arc_buginfo) */
+ NOFLAGS /* not used */
+};
+
+/* Queue us up for the next tick. */
+#define ENQUEUE(up) \
+ do { \
+ peer->procptr->nextaction = current_time + QUEUETICK; \
+ } while(0)
+
+/* Placeholder event handler---does nothing safely---soaks up loose tick. */
+static void
+dummy_event_handler(
+ struct peer *peer
+ )
+{
+#ifdef DEBUG
+ if(debug) { printf("arc: dummy_event_handler() called.\n"); }
+#endif
+}
+
+/*
+Normal event handler.
+
+Take first character off queue and send to clock if not a null.
+
+Shift characters down and put a null on the end.
+
+We assume that there is no parallelism so no race condition, but even
+if there is nothing bad will happen except that we might send some bad
+data to the clock once in a while.
+*/
+static void
+arc_event_handler(
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp = peer->procptr;
+ register struct arcunit *up = pp->unitptr;
+ int i;
+ char c;
+#ifdef DEBUG
+ if(debug > 2) { printf("arc: arc_event_handler() called.\n"); }
+#endif
+
+ c = up->cmdqueue[0]; /* Next char to be sent. */
+ /* Shift down characters, shifting trailing \0 in at end. */
+ for(i = 0; i < CMDQUEUELEN; ++i)
+ { up->cmdqueue[i] = up->cmdqueue[i+1]; }
+
+ /* Don't send '\0' characters. */
+ if(c != '\0') {
+ if(write(pp->io.fd, &c, 1) != 1) {
+ msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd);
+ }
+#ifdef DEBUG
+ else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); }
+#endif
+ }
+
+ ENQUEUE(up);
+}
+
+/*
+ * arc_start - open the devices and initialize data for processing
+ */
+static int
+arc_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arcunit *up;
+ struct refclockproc *pp;
+ int temp_fd;
+ int fd;
+ char device[20];
+#ifdef HAVE_TERMIOS
+ struct termios arg;
+#endif
+
+ msyslog(LOG_NOTICE, "MSF_ARCRON %s: opening unit %d",
+ arc_version, unit);
+ DPRINTF(1, ("arc: %s: attempt to open unit %d.\n", arc_version,
+ unit));
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+ temp_fd = refclock_open(device, SPEED, LDISC_CLK);
+ if (temp_fd <= 0)
+ return 0;
+ DPRINTF(1, ("arc: unit %d using tty_open().\n", unit));
+ fd = tty_open(device, OPEN_FLAGS, 0777);
+ if (fd < 0) {
+ msyslog(LOG_ERR, "MSF_ARCRON(%d): failed second open(%s, 0777): %m.",
+ unit, device);
+ close(temp_fd);
+ return 0;
+ }
+ close(temp_fd);
+ temp_fd = -1;
+
+#ifndef SYS_WINNT
+ if (-1 == fcntl(fd, F_SETFL, 0)) /* clear the descriptor flags */
+ msyslog(LOG_ERR, "MSF_ARCRON(%d): fcntl(F_SETFL, 0): %m.",
+ unit);
+
+#endif
+ DPRINTF(1, ("arc: opened RS232 port with file descriptor %d.\n", fd));
+
+#ifdef HAVE_TERMIOS
+
+ if (tcgetattr(fd, &arg) < 0) {
+ msyslog(LOG_ERR, "MSF_ARCRON(%d): tcgetattr(%s): %m.",
+ unit, device);
+ close(fd);
+ return 0;
+ }
+
+ arg.c_iflag = IGNBRK | ISTRIP;
+ arg.c_oflag = 0;
+ arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB;
+ arg.c_lflag = 0;
+ arg.c_cc[VMIN] = 1;
+ arg.c_cc[VTIME] = 0;
+
+ if (tcsetattr(fd, TCSANOW, &arg) < 0) {
+ msyslog(LOG_ERR, "MSF_ARCRON(%d): tcsetattr(%s): %m.",
+ unit, device);
+ close(fd);
+ return 0;
+ }
+
+#else
+
+ msyslog(LOG_ERR, "ARCRON: termios required by this driver");
+ (void)close(fd);
+
+ return 0;
+
+#endif
+
+ /* Set structure to all zeros... */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->io.clock_recv = arc_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ return(0);
+ }
+ pp->unitptr = up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ peer->stratum = 2; /* Default to stratum 2 not 0. */
+ pp->clockdesc = DESCRIPTION;
+ if (peer->MODE > 3) {
+ msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", peer->MODE);
+ return 0;
+ }
+#ifdef DEBUG
+ if(debug) { printf("arc: mode = %d.\n", peer->MODE); }
+#endif
+ switch (peer->MODE) {
+ case 1:
+ memcpy((char *)&pp->refid, REFID_MSF, 4);
+ break;
+ case 2:
+ memcpy((char *)&pp->refid, REFID_DCF77, 4);
+ break;
+ case 3:
+ memcpy((char *)&pp->refid, REFID_WWVB, 4);
+ break;
+ default:
+ memcpy((char *)&pp->refid, REFID, 4);
+ break;
+ }
+ /* Spread out resyncs so that they should remain separated. */
+ up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009;
+
+#if 0 /* Not needed because of zeroing of arcunit structure... */
+ up->resyncing = 0; /* Not resyncing yet. */
+ up->saved_flags = 0; /* Default is all flags off. */
+ /* Clear send buffer out... */
+ {
+ int i;
+ for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; }
+ }
+#endif
+
+#ifdef ARCRON_KEEN
+ up->quality = QUALITY_UNKNOWN; /* Trust the clock immediately. */
+#else
+ up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */
+#endif
+
+ peer->procptr->action = arc_event_handler;
+
+ ENQUEUE(up);
+
+ return(1);
+}
+
+
+/*
+ * arc_shutdown - shut down the clock
+ */
+static void
+arc_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arcunit *up;
+ struct refclockproc *pp;
+
+ peer->procptr->action = dummy_event_handler;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ if (NULL != up)
+ free(up);
+}
+
+/*
+Compute space left in output buffer.
+*/
+static int
+space_left(
+ register struct arcunit *up
+ )
+{
+ int spaceleft;
+
+ /* Compute space left in buffer after any pending output. */
+ for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft)
+ { if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } }
+ return(spaceleft);
+}
+
+/*
+Send command by copying into command buffer as far forward as possible,
+after any pending output.
+
+Indicate an error by returning 0 if there is not space for the command.
+*/
+static int
+send_slow(
+ register struct arcunit *up,
+ int fd,
+ const char *s
+ )
+{
+ int sl = strlen(s);
+ int spaceleft = space_left(up);
+
+#ifdef DEBUG
+ if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); }
+#endif
+ if(spaceleft < sl) { /* Should not normally happen... */
+#ifdef DEBUG
+ msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)",
+ sl, spaceleft);
+#endif
+ return(0); /* FAILED! */
+ }
+
+ /* Copy in the command to be sent. */
+ while(*s && spaceleft > 0) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; }
+
+ return(1);
+}
+
+
+static int
+get2(char *p, int *val)
+{
+ if (!isdigit((int)p[0]) || !isdigit((int)p[1])) return 0;
+ *val = (p[0] - '0') * 10 + p[1] - '0';
+ return 1;
+}
+
+static int
+get1(char *p, int *val)
+{
+ if (!isdigit((int)p[0])) return 0;
+ *val = p[0] - '0';
+ return 1;
+}
+
+/* Macro indicating action we will take for different quality values. */
+#define quality_action(q) \
+(((q) == QUALITY_UNKNOWN) ? "UNKNOWN, will use clock anyway" : \
+ (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \
+ "OK, will use clock"))
+
+/*
+ * arc_receive - receive data from the serial interface
+ */
+static void
+arc_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct arcunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char c;
+ int i, n, wday, month, flags, status;
+ int arc_last_offset;
+ static int quality_average = 0;
+ static int quality_sum = 0;
+ static int quality_polls = 0;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+
+ /*
+ If the command buffer is empty, and we are resyncing, insert a
+ g\r quality request into it to poll for signal quality again.
+ */
+ if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) {
+#ifdef DEBUG
+ if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); }
+#endif
+ send_slow(up, pp->io.fd, "g\r");
+ }
+
+ /*
+ The `arc_last_offset' is the offset in lastcode[] of the last byte
+ received, and which we assume actually received the input
+ timestamp.
+
+ (When we get round to using tty_clk and it is available, we
+ assume that we will receive the whole timecode with the
+ trailing \r, and that that \r will be timestamped. But this
+ assumption also works if receive the characters one-by-one.)
+ */
+ arc_last_offset = pp->lencode+rbufp->recv_length - 1;
+
+ /*
+ We catch a timestamp iff:
+
+ * The command code is `o' for a timestamp.
+
+ * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have
+ exactly char in the buffer (the command code) so that we
+ only sample the first character of the timecode as our
+ `on-time' character.
+
+ * The first character in the buffer is not the echoed `\r'
+ from the `o` command (so if we are to timestamp an `\r' it
+ must not be first in the receive buffer with lencode==1.
+ (Even if we had other characters following it, we probably
+ would have a premature timestamp on the '\r'.)
+
+ * We have received at least one character (I cannot imagine
+ how it could be otherwise, but anyway...).
+ */
+ c = rbufp->recv_buffer[0];
+ if((pp->a_lastcode[0] == 'o') &&
+#ifndef ARCRON_MULTIPLE_SAMPLES
+ (pp->lencode == 1) &&
+#endif
+ ((pp->lencode != 1) || (c != '\r')) &&
+ (arc_last_offset >= 1)) {
+ /* Note that the timestamp should be corrected if >1 char rcvd. */
+ l_fp timestamp;
+ timestamp = rbufp->recv_time;
+#ifdef DEBUG
+ if(debug) { /* Show \r as `R', other non-printing char as `?'. */
+ printf("arc: stamp -->%c<-- (%d chars rcvd)\n",
+ ((c == '\r') ? 'R' : (isgraph((int)c) ? c : '?')),
+ rbufp->recv_length);
+ }
+#endif
+
+ /*
+ Now correct timestamp by offset of last byte received---we
+ subtract from the receive time the delay implied by the
+ extra characters received.
+
+ Reject the input if the resulting code is too long, but
+ allow for the trailing \r, normally not used but a good
+ handle for tty_clk or somesuch kernel timestamper.
+ */
+ if(arc_last_offset > LENARC) {
+#ifdef DEBUG
+ if(debug) {
+ printf("arc: input code too long (%d cf %d); rejected.\n",
+ arc_last_offset, LENARC);
+ }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ L_SUBUF(&timestamp, charoffsets[arc_last_offset]);
+#ifdef DEBUG
+ if(debug > 1) {
+ printf(
+ "arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n",
+ ((rbufp->recv_length > 1) ? "*** " : ""),
+ rbufp->recv_length,
+ arc_last_offset,
+ mfptoms((unsigned long)0,
+ charoffsets[arc_last_offset],
+ 1));
+ }
+#endif
+
+#ifdef ARCRON_MULTIPLE_SAMPLES
+ /*
+ If taking multiple samples, capture the current adjusted
+ sample iff:
+
+ * No timestamp has yet been captured (it is zero), OR
+
+ * This adjusted timestamp is earlier than the one already
+ captured, on the grounds that this one suffered less
+ delay in being delivered to us and is more accurate.
+
+ */
+ if(L_ISZERO(&(up->lastrec)) ||
+ L_ISGEQ(&(up->lastrec), &timestamp))
+#endif
+ {
+#ifdef DEBUG
+ if(debug > 1) {
+ printf("arc: system timestamp captured.\n");
+#ifdef ARCRON_MULTIPLE_SAMPLES
+ if(!L_ISZERO(&(up->lastrec))) {
+ l_fp diff;
+ diff = up->lastrec;
+ L_SUB(&diff, &timestamp);
+ printf("arc: adjusted timestamp by -%sms.\n",
+ mfptoms(diff.l_ui, diff.l_uf, 3));
+ }
+#endif
+ }
+#endif
+ up->lastrec = timestamp;
+ }
+
+ }
+
+ /* Just in case we still have lots of rubbish in the buffer... */
+ /* ...and to avoid the same timestamp being reused by mistake, */
+ /* eg on receipt of the \r coming in on its own after the */
+ /* timecode. */
+ if(pp->lencode >= LENARC) {
+#ifdef DEBUG
+ if(debug && (rbufp->recv_buffer[0] != '\r'))
+ { printf("arc: rubbish in pp->a_lastcode[].\n"); }
+#endif
+ pp->lencode = 0;
+ return;
+ }
+
+ /* Append input to code buffer, avoiding overflow. */
+ for(i = 0; i < rbufp->recv_length; i++) {
+ if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */
+ c = rbufp->recv_buffer[i];
+
+ /* Drop trailing '\r's and drop `h' command echo totally. */
+ if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; }
+
+ /*
+ If we've just put an `o' in the lastcode[0], clear the
+ timestamp in anticipation of a timecode arriving soon.
+
+ We would expect to get to process this before any of the
+ timecode arrives.
+ */
+ if((c == 'o') && (pp->lencode == 1)) {
+ L_CLR(&(up->lastrec));
+#ifdef DEBUG
+ if(debug > 1) { printf("arc: clearing timestamp.\n"); }
+#endif
+ }
+ }
+ if (pp->lencode == 0) return;
+
+ /* Handle a quality message. */
+ if(pp->a_lastcode[0] == 'g') {
+ int r, q;
+
+ if(pp->lencode < 3) { return; } /* Need more data... */
+ r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */
+ q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */
+ if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) ||
+ ((r & 0x70) != 0x30)) {
+ /* Badly formatted response. */
+#ifdef DEBUG
+ if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); }
+#endif
+ return;
+ }
+ if(r == '3') { /* Only use quality value whilst sync in progress. */
+ if (up->quality_stamp < current_time) {
+ struct calendar cal;
+ l_fp new_stamp;
+
+ get_systime (&new_stamp);
+ caljulian (new_stamp.l_ui, &cal);
+ up->quality_stamp =
+ current_time + 60 - cal.second + 5;
+ quality_sum = 0;
+ quality_polls = 0;
+ }
+ quality_sum += (q & 0xf);
+ quality_polls++;
+ quality_average = (quality_sum / quality_polls);
+#ifdef DEBUG
+ if(debug) { printf("arc: signal quality %d (%d).\n", quality_average, (q & 0xf)); }
+#endif
+ } else if( /* (r == '2') && */ up->resyncing) {
+ up->quality = quality_average;
+#ifdef DEBUG
+ if(debug)
+ {
+ printf("arc: sync finished, signal quality %d: %s\n",
+ up->quality,
+ quality_action(up->quality));
+ }
+#endif
+ msyslog(LOG_NOTICE,
+ "ARCRON: sync finished, signal quality %d: %s",
+ up->quality,
+ quality_action(up->quality));
+ up->resyncing = 0; /* Resync is over. */
+ quality_average = 0;
+ quality_sum = 0;
+ quality_polls = 0;
+
+#ifdef ARCRON_KEEN
+ /* Clock quality dubious; resync earlier than usual. */
+ if((up->quality == QUALITY_UNKNOWN) ||
+ (up->quality < MIN_CLOCK_QUALITY_OK))
+ { up->next_resync = current_time + RETRY_RESYNC_TIME; }
+#endif
+ }
+ pp->lencode = 0;
+ return;
+ }
+
+ /* Stop now if this is not a timecode message. */
+ if(pp->a_lastcode[0] != 'o') {
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /* If we don't have enough data, wait for more... */
+ if(pp->lencode < LENARC) { return; }
+
+
+ /* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */
+#ifdef DEBUG
+ if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); }
+#endif
+
+ /* But check that we actually captured a system timestamp on it. */
+ if(L_ISZERO(&(up->lastrec))) {
+#ifdef DEBUG
+ if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ /*
+ Append a mark of the clock's received signal quality for the
+ benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown'
+ quality value to `6' for his s/w) and terminate the string for
+ sure. This should not go off the buffer end.
+ */
+ pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ?
+ '6' : ('0' + up->quality));
+ pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */
+
+#ifdef PRE_NTP420
+ /* We don't use the micro-/milli- second part... */
+ pp->usec = 0;
+ pp->msec = 0;
+#else
+ /* We don't use the nano-second part... */
+ pp->nsec = 0;
+#endif
+ /* Validate format and numbers. */
+ if (pp->a_lastcode[0] != 'o'
+ || !get2(pp->a_lastcode + 1, &pp->hour)
+ || !get2(pp->a_lastcode + 3, &pp->minute)
+ || !get2(pp->a_lastcode + 5, &pp->second)
+ || !get1(pp->a_lastcode + 7, &wday)
+ || !get2(pp->a_lastcode + 8, &pp->day)
+ || !get2(pp->a_lastcode + 10, &month)
+ || !get2(pp->a_lastcode + 12, &pp->year)) {
+#ifdef DEBUG
+ /* Would expect to have caught major problems already... */
+ if(debug) { printf("arc: badly formatted data.\n"); }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ flags = pp->a_lastcode[14];
+ status = pp->a_lastcode[15];
+#ifdef DEBUG
+ if(debug) { printf("arc: status 0x%.2x flags 0x%.2x\n", flags, status); }
+#endif
+ n = 9;
+
+ /*
+ Validate received values at least enough to prevent internal
+ array-bounds problems, etc.
+ */
+ if((pp->hour < 0) || (pp->hour > 23) ||
+ (pp->minute < 0) || (pp->minute > 59) ||
+ (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
+ (wday < 1) || (wday > 7) ||
+ (pp->day < 1) || (pp->day > 31) ||
+ (month < 1) || (month > 12) ||
+ (pp->year < 0) || (pp->year > 99)) {
+ /* Data out of range. */
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+
+ if(peer->MODE == 0) { /* compatiblity to original version */
+ int bst = flags;
+ /* Check that BST/UTC bits are the complement of one another. */
+ if(!(bst & 2) == !(bst & 4)) {
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ }
+ if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); }
+
+ /* Year-2000 alert! */
+ /* Attempt to wrap 2-digit date into sensible window. */
+ if(pp->year < YEAR_PIVOT) { pp->year += 100; } /* Y2KFixes */
+ pp->year += 1900; /* use full four-digit year */ /* Y2KFixes */
+ /*
+ Attempt to do the right thing by screaming that the code will
+ soon break when we get to the end of its useful life. What a
+ hero I am... PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X!
+ */
+ if(pp->year >= YEAR_PIVOT+2000-2 ) { /* Y2KFixes */
+ /*This should get attention B^> */
+ msyslog(LOG_NOTICE,
+ "ARCRON: fix me! EITHER YOUR DATE IS BADLY WRONG or else I will break soon!");
+ }
+#ifdef DEBUG
+ if(debug) {
+ printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n",
+ n,
+ pp->hour, pp->minute, pp->second,
+ pp->day, month, pp->year, flags, status);
+ }
+#endif
+
+ /*
+ The status value tested for is not strictly supported by the
+ clock spec since the value of bit 2 (0x4) is claimed to be
+ undefined for MSF, yet does seem to indicate if the last resync
+ was successful or not.
+ */
+ pp->leap = LEAP_NOWARNING;
+ status &= 0x7;
+ if(status == 0x3) {
+ if(status != up->status)
+ { msyslog(LOG_NOTICE, "ARCRON: signal acquired"); }
+ } else {
+ if(status != up->status) {
+ msyslog(LOG_NOTICE, "ARCRON: signal lost");
+ pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */
+ up->status = status;
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ }
+ up->status = status;
+
+ if (peer->MODE == 0) { /* compatiblity to original version */
+ int bst = flags;
+
+ pp->day += moff[month - 1];
+
+ if(isleap_4(pp->year) && month > 2) { pp->day++; }/* Y2KFixes */
+
+ /* Convert to UTC if required */
+ if(bst & 2) {
+ pp->hour--;
+ if (pp->hour < 0) {
+ pp->hour = 23;
+ pp->day--;
+ /* If we try to wrap round the year
+ * (BST on 1st Jan), reject.*/
+ if(pp->day < 0) {
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ }
+ }
+ }
+
+ if(peer->MODE > 0) {
+ if(pp->sloppyclockflag & CLK_FLAG1) {
+ struct tm local;
+ struct tm *gmtp;
+ time_t unixtime;
+
+ /*
+ * Convert to GMT for sites that distribute localtime.
+ * This means we have to do Y2K conversion on the
+ * 2-digit year; otherwise, we get the time wrong.
+ */
+
+ memset(&local, 0, sizeof(local));
+
+ local.tm_year = pp->year-1900;
+ local.tm_mon = month-1;
+ local.tm_mday = pp->day;
+ local.tm_hour = pp->hour;
+ local.tm_min = pp->minute;
+ local.tm_sec = pp->second;
+ switch (peer->MODE) {
+ case 1:
+ local.tm_isdst = (flags & 2);
+ break;
+ case 2:
+ local.tm_isdst = (flags & 2);
+ break;
+ case 3:
+ switch (flags & 3) {
+ case 0: /* It is unclear exactly when the
+ Arcron changes from DST->ST and
+ ST->DST. Testing has shown this
+ to be irregular. For the time
+ being, let the OS decide. */
+ local.tm_isdst = 0;
+#ifdef DEBUG
+ if (debug)
+ printf ("arc: DST = 00 (0)\n");
+#endif
+ break;
+ case 1: /* dst->st time */
+ local.tm_isdst = -1;
+#ifdef DEBUG
+ if (debug)
+ printf ("arc: DST = 01 (1)\n");
+#endif
+ break;
+ case 2: /* st->dst time */
+ local.tm_isdst = -1;
+#ifdef DEBUG
+ if (debug)
+ printf ("arc: DST = 10 (2)\n");
+#endif
+ break;
+ case 3: /* dst time */
+ local.tm_isdst = 1;
+#ifdef DEBUG
+ if (debug)
+ printf ("arc: DST = 11 (3)\n");
+#endif
+ break;
+ }
+ break;
+ default:
+ msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d",
+ peer->MODE);
+ return;
+ break;
+ }
+ unixtime = mktime (&local);
+ if ((gmtp = gmtime (&unixtime)) == NULL)
+ {
+ pp->lencode = 0;
+ refclock_report (peer, CEVNT_FAULT);
+ return;
+ }
+ pp->year = gmtp->tm_year+1900;
+ month = gmtp->tm_mon+1;
+ pp->day = ymd2yd(pp->year,month,gmtp->tm_mday);
+ /* pp->day = gmtp->tm_yday; */
+ pp->hour = gmtp->tm_hour;
+ pp->minute = gmtp->tm_min;
+ pp->second = gmtp->tm_sec;
+#ifdef DEBUG
+ if (debug)
+ {
+ printf ("arc: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
+ pp->year,month,gmtp->tm_mday,pp->hour,pp->minute,
+ pp->second);
+ }
+#endif
+ } else
+ {
+ /*
+ * For more rational sites distributing UTC
+ */
+ pp->day = ymd2yd(pp->year,month,pp->day);
+ }
+ }
+
+ if (peer->MODE == 0) { /* compatiblity to original version */
+ /* If clock signal quality is
+ * unknown, revert to default PRECISION...*/
+ if(up->quality == QUALITY_UNKNOWN) {
+ peer->precision = PRECISION;
+ } else { /* ...else improve precision if flag3 is set... */
+ peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
+ HIGHPRECISION : PRECISION);
+ }
+ } else {
+ if ((status == 0x3) && (pp->sloppyclockflag & CLK_FLAG2)) {
+ peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
+ HIGHPRECISION : PRECISION);
+ } else if (up->quality == QUALITY_UNKNOWN) {
+ peer->precision = PRECISION;
+ } else {
+ peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
+ HIGHPRECISION : PRECISION);
+ }
+ }
+
+ /* Notice and log any change (eg from initial defaults) for flags. */
+ if(up->saved_flags != pp->sloppyclockflag) {
+#ifdef DEBUG
+ msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s",
+ ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."),
+ ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."),
+ ((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."),
+ ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : "."));
+ /* Note effects of flags changing... */
+ if(debug) {
+ printf("arc: PRECISION = %d.\n", peer->precision);
+ }
+#endif
+ up->saved_flags = pp->sloppyclockflag;
+ }
+
+ /* Note time of last believable timestamp. */
+ pp->lastrec = up->lastrec;
+
+#ifdef ARCRON_LEAPSECOND_KEEN
+ /* Find out if a leap-second might just have happened...
+ (ie is this the first hour of the first day of Jan or Jul?)
+ */
+ if((pp->hour == 0) &&
+ (pp->day == 1) &&
+ ((month == 1) || (month == 7))) {
+ if(possible_leap >= 0) {
+ /* A leap may have happened, and no resync has started yet...*/
+ possible_leap = 1;
+ }
+ } else {
+ /* Definitely not leap-second territory... */
+ possible_leap = 0;
+ }
+#endif
+
+ if (!refclock_process(pp)) {
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+}
+
+
+/* request_time() sends a time request to the clock with given peer. */
+/* This automatically reports a fault if necessary. */
+/* No data should be sent after this until arc_poll() returns. */
+static void request_time (int, struct peer *);
+static void
+request_time(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp = peer->procptr;
+ register struct arcunit *up = pp->unitptr;
+#ifdef DEBUG
+ if(debug) { printf("arc: unit %d: requesting time.\n", unit); }
+#endif
+ if (!send_slow(up, pp->io.fd, "o\r")) {
+#ifdef DEBUG
+ if (debug) {
+ printf("arc: unit %d: problem sending", unit);
+ }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ pp->polls++;
+}
+
+/*
+ * arc_poll - called by the transmit procedure
+ */
+static void
+arc_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arcunit *up;
+ struct refclockproc *pp;
+ int resync_needed; /* Should we start a resync? */
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+#if 0
+ pp->lencode = 0;
+ memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode));
+#endif
+
+#if 0
+ /* Flush input. */
+ tcflush(pp->io.fd, TCIFLUSH);
+#endif
+
+ /* Resync if our next scheduled resync time is here or has passed. */
+ resync_needed = ( !(pp->sloppyclockflag & CLK_FLAG2) &&
+ (up->next_resync <= current_time) );
+
+#ifdef ARCRON_LEAPSECOND_KEEN
+ /*
+ Try to catch a potential leap-second insertion or deletion quickly.
+
+ In addition to the normal NTP fun of clocks that don't report
+ leap-seconds spooking their hosts, this clock does not even
+ sample the radio sugnal the whole time, so may miss a
+ leap-second insertion or deletion for up to a whole sample
+ time.
+
+ To try to minimise this effect, if in the first few minutes of
+ the day immediately following a leap-second-insertion point
+ (ie in the first hour of the first day of the first and sixth
+ months), and if the last resync was in the previous day, and a
+ resync is not already in progress, resync the clock
+ immediately.
+
+ */
+ if((possible_leap > 0) && /* Must be 00:XX 01/0{1,7}/XXXX. */
+ (!up->resyncing)) { /* No resync in progress yet. */
+ resync_needed = 1;
+ possible_leap = -1; /* Prevent multiple resyncs. */
+ msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit);
+ }
+#endif
+
+ /* Do a resync if required... */
+ if(resync_needed) {
+ /* First, reset quality value to `unknown' so we can detect */
+ /* when a quality message has been responded to by this */
+ /* being set to some other value. */
+ up->quality = QUALITY_UNKNOWN;
+
+ /* Note that we are resyncing... */
+ up->resyncing = 1;
+
+ /* Now actually send the resync command and an immediate poll. */
+#ifdef DEBUG
+ if(debug) { printf("arc: sending resync command (h\\r).\n"); }
+#endif
+ msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit);
+ send_slow(up, pp->io.fd, "h\r");
+
+ /* Schedule our next resync... */
+ up->next_resync = current_time + DEFAULT_RESYNC_TIME;
+
+ /* Drop through to request time if appropriate. */
+ }
+
+ /* If clock quality is too poor to trust, indicate a fault. */
+ /* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/
+ /* we'll cross our fingers and just hope that the thing */
+ /* synced so quickly we did not catch it---we'll */
+ /* double-check the clock is OK elsewhere. */
+ if(
+#ifdef ARCRON_KEEN
+ (up->quality != QUALITY_UNKNOWN) &&
+#else
+ (up->quality == QUALITY_UNKNOWN) ||
+#endif
+ (up->quality < MIN_CLOCK_QUALITY_OK)) {
+#ifdef DEBUG
+ if(debug) {
+ printf("arc: clock quality %d too poor.\n", up->quality);
+ }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ /* This is the normal case: request a timestamp. */
+ request_time(unit, peer);
+}
+
+#else
+NONEMPTY_TRANSLATION_UNIT
+#endif
diff --git a/ntpd/refclock_as2201.c b/ntpd/refclock_as2201.c
new file mode 100644
index 0000000..1acf9b2
--- /dev/null
+++ b/ntpd/refclock_as2201.c
@@ -0,0 +1,388 @@
+/*
+ * refclock_as2201 - clock driver for the Austron 2201A GPS
+ * Timing Receiver
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_AS2201)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the Austron 2200A/2201A GPS Receiver with
+ * Buffered RS-232-C Interface Module. Note that the original 2200/2201
+ * receivers will not work reliably with this driver, since the older
+ * design cannot accept input commands at any reasonable data rate.
+ *
+ * The program sends a "*toc\r" to the radio and expects a response of
+ * the form "yy:ddd:hh:mm:ss.mmm\r" where yy = year of century, ddd =
+ * day of year, hh:mm:ss = second of day and mmm = millisecond of
+ * second. Then, it sends statistics commands to the radio and expects
+ * a multi-line reply showing the corresponding statistics or other
+ * selected data. Statistics commands are sent in order as determined by
+ * a vector of commands; these might have to be changed with different
+ * radio options. If flag4 of the fudge configuration command is set to
+ * 1, the statistics data are written to the clockstats file for later
+ * processing.
+ *
+ * In order for this code to work, the radio must be placed in non-
+ * interactive mode using the "off" command and with a single <cr>
+ * response using the "term cr" command. The setting of the "echo"
+ * and "df" commands does not matter. The radio should select UTC
+ * timescale using the "ts utc" command.
+ *
+ * There are two modes of operation for this driver. The first with
+ * default configuration is used with stock kernels and serial-line
+ * drivers and works with almost any machine. In this mode the driver
+ * assumes the radio captures a timestamp upon receipt of the "*" that
+ * begins the driver query. Accuracies in this mode are in the order of
+ * a millisecond or two and the receiver can be connected to only one
+ * host.
+ *
+ * The second mode of operation can be used for SunOS kernels that have
+ * been modified with the ppsclock streams module included in this
+ * distribution. The mode is enabled if flag3 of the fudge configuration
+ * command has been set to 1. In this mode a precise timestamp is
+ * available using a gadget box and 1-pps signal from the receiver. This
+ * improves the accuracy to the order of a few tens of microseconds. In
+ * addition, the serial output and 1-pps signal can be bussed to more
+ * than one hosts, but only one of them should be connected to the
+ * radio input data line.
+ */
+
+/*
+ * GPS Definitions
+ */
+#define SMAX 200 /* statistics buffer length */
+#define DEVICE "/dev/gps%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference ID */
+#define DESCRIPTION "Austron 2201A GPS Receiver" /* WRU */
+
+#define LENTOC 19 /* yy:ddd:hh:mm:ss.mmm timecode lngth */
+
+/*
+ * AS2201 unit control structure.
+ */
+struct as2201unit {
+ char *lastptr; /* statistics buffer pointer */
+ char stats[SMAX]; /* statistics buffer */
+ int linect; /* count of lines remaining */
+ int index; /* current statistics command */
+};
+
+/*
+ * Radio commands to extract statitistics
+ *
+ * A command consists of an ASCII string terminated by a <cr> (\r). The
+ * command list consist of a sequence of commands terminated by a null
+ * string ("\0"). One command from the list is sent immediately
+ * following each received timecode (*toc\r command) and the ASCII
+ * strings received from the radio are saved along with the timecode in
+ * the clockstats file. Subsequent commands are sent at each timecode,
+ * with the last one in the list followed by the first one. The data
+ * received from the radio consist of ASCII strings, each terminated by
+ * a <cr> (\r) character. The number of strings for each command is
+ * specified as the first line of output as an ASCII-encode number. Note
+ * that the ETF command requires the Input Buffer Module and the LORAN
+ * commands require the LORAN Assist Module. However, if these modules
+ * are not installed, the radio and this driver will continue to operate
+ * successfuly, but no data will be captured for these commands.
+ */
+static char stat_command[][30] = {
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "LORAN TDATA\r", /* LORAN signal data */
+ "ID;OPT;VER\r", /* model; options; software version */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "TRSTAT\r", /* satellite tracking status */
+ "POS;PPS;PPSOFF\r", /* position, pps source, offsets */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "LORAN TDATA\r", /* LORAN signal data */
+ "UTC\r", /* UTC leap info */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "TRSTAT\r", /* satellite tracking status */
+ "OSC;ET;TEMP\r", /* osc type; tune volts; oven temp */
+ "\0" /* end of table */
+};
+
+/*
+ * Function prototypes
+ */
+static int as2201_start (int, struct peer *);
+static void as2201_shutdown (int, struct peer *);
+static void as2201_receive (struct recvbuf *);
+static void as2201_poll (int, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_as2201 = {
+ as2201_start, /* start up driver */
+ as2201_shutdown, /* shut down driver */
+ as2201_poll, /* transmit poll message */
+ noentry, /* not used (old as2201_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old as2201_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * as2201_start - open the devices and initialize data for processing
+ */
+static int
+as2201_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char gpsdev[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit);
+ fd = refclock_open(gpsdev, SPEED232, LDISC_CLK);
+ if (fd <= 0)
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->io.clock_recv = as2201_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ return (0);
+ }
+ pp->unitptr = up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->lastptr = up->stats;
+ up->index = 0;
+ return (1);
+}
+
+
+/*
+ * as2201_shutdown - shut down the clock
+ */
+static void
+as2201_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ if (NULL != up)
+ free(up);
+}
+
+
+/*
+ * as2201__receive - receive data from the serial interface
+ */
+static void
+as2201_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ size_t octets;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+#ifdef DEBUG
+ if (debug)
+ printf("gps: timecode %d %d %s\n",
+ up->linect, pp->lencode, pp->a_lastcode);
+#endif
+ if (pp->lencode == 0)
+ return;
+
+ /*
+ * If linect is greater than zero, we must be in the middle of a
+ * statistics operation, so simply tack the received data at the
+ * end of the statistics string. If not, we could either have
+ * just received the timecode itself or a decimal number
+ * indicating the number of following lines of the statistics
+ * reply. In the former case, write the accumulated statistics
+ * data to the clockstats file and continue onward to process
+ * the timecode; in the later case, save the number of lines and
+ * quietly return.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ pp->lastrec = trtmp;
+ if (up->linect > 0) {
+ up->linect--;
+ if ((int)(up->lastptr - up->stats + pp->lencode) > SMAX - 2)
+ return;
+ *up->lastptr++ = ' ';
+ memcpy(up->lastptr, pp->a_lastcode, 1 + pp->lencode);
+ up->lastptr += pp->lencode;
+ return;
+ } else {
+ if (pp->lencode == 1) {
+ up->linect = atoi(pp->a_lastcode);
+ return;
+ } else {
+ record_clock_stats(&peer->srcadr, up->stats);
+#ifdef DEBUG
+ if (debug)
+ printf("gps: stat %s\n", up->stats);
+#endif
+ }
+ }
+ up->lastptr = up->stats;
+ *up->lastptr = '\0';
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENTOC) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format: "yy:ddd:hh:mm:ss.mmm"
+ */
+ if (sscanf(pp->a_lastcode, "%2d:%3d:%2d:%2d:%2d.%3ld", &pp->year,
+ &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->nsec)
+ != 6) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ pp->nsec *= 1000000;
+
+ /*
+ * Test for synchronization (this is a temporary crock).
+ */
+ if (pp->a_lastcode[2] != ':')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * If CLK_FLAG4 is set, initialize the statistics buffer and
+ * send the next command. If not, simply write the timecode to
+ * the clockstats file.
+ */
+ if ((int)(up->lastptr - up->stats + pp->lencode) > SMAX - 2)
+ return;
+ memcpy(up->lastptr, pp->a_lastcode, pp->lencode);
+ up->lastptr += pp->lencode;
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ octets = strlen(stat_command[up->index]);
+ if ((int)(up->lastptr - up->stats + 1 + octets) > SMAX - 2)
+ return;
+ *up->lastptr++ = ' ';
+ memcpy(up->lastptr, stat_command[up->index], octets);
+ up->lastptr += octets - 1;
+ *up->lastptr = '\0';
+ (void)write(pp->io.fd, stat_command[up->index],
+ strlen(stat_command[up->index]));
+ up->index++;
+ if (*stat_command[up->index] == '\0')
+ up->index = 0;
+ }
+}
+
+
+/*
+ * as2201_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
+as2201_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ /*
+ * Send a "\r*toc\r" to get things going. We go to great pains
+ * to avoid changing state, since there may be more than one
+ * eavesdropper watching the radio.
+ */
+ pp = peer->procptr;
+ if (write(pp->io.fd, "\r*toc\r", 6) != 6) {
+ refclock_report(peer, CEVNT_FAULT);
+ } else {
+ pp->polls++;
+ if (!(pp->sloppyclockflag & CLK_FLAG2))
+ get_systime(&pp->lastrec);
+ }
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ refclock_receive(peer);
+}
+
+#else
+int refclock_as2201_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_atom.c b/ntpd/refclock_atom.c
new file mode 100644
index 0000000..b3c0d6b
--- /dev/null
+++ b/ntpd/refclock_atom.c
@@ -0,0 +1,239 @@
+/*
+ * refclock_atom - clock driver for 1-pps signals
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver requires the PPSAPI interface (RFC 2783)
+ */
+#if defined(REFCLOCK) && defined(CLOCK_ATOM) && defined(HAVE_PPSAPI)
+#include "ppsapi_timepps.h"
+#include "refclock_atom.h"
+
+/*
+ * This driver furnishes an interface for pulse-per-second (PPS) signals
+ * produced by a cesium clock, timing receiver or related equipment. It
+ * can be used to remove accumulated jitter over a congested link and
+ * retime a server before redistributing the time to clients. It can
+ *also be used as a holdover should all other synchronization sources
+ * beconme unreachable.
+ *
+ * Before this driver becomes active, the local clock must be set to
+ * within +-0.4 s by another means, such as a radio clock or NTP
+ * itself. There are two ways to connect the PPS signal, normally at TTL
+ * levels, to the computer. One is to shift to EIA levels and connect to
+ * pin 8 (DCD) of a serial port. This requires a level converter and
+ * may require a one-shot flipflop to lengthen the pulse. The other is
+ * to connect the PPS signal directly to pin 10 (ACK) of a PC paralell
+ * port. These methods are architecture dependent.
+ *
+ * This driver requires the Pulse-per-Second API for Unix-like Operating
+ * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are
+ * available for FreeBSD, Linux, SunOS, Solaris and Tru64. However, at
+ * present only the Tru64 implementation provides the full generality of
+ * the API with multiple PPS drivers and multiple handles per driver. If
+ * the PPSAPI is normally implemented in the /usr/include/sys/timepps.h
+ * header file and kernel support specific to each operating system.
+ *
+ * This driver normally uses the PLL/FLL clock discipline implemented in
+ * the ntpd code. Ordinarily, this is the most accurate means, as the
+ * median filter in the driver interface is much larger than in the
+ * kernel. However, if the systemic clock frequency error is large (tens
+ * to hundreds of PPM), it's better to used the kernel support, if
+ * available.
+ *
+ * This deriver is subject to the mitigation rules described in the
+ * "mitigation rulse and the prefer peer" page. However, there is an
+ * important difference. If this driver becomes the PPS driver according
+ * to these rules, it is acrive only if (a) a prefer peer other than
+ * this driver is among the survivors or (b) there are no survivors and
+ * the minsane option of the tos command is zero. This is intended to
+ * support space missions where updates from other spacecraft are
+ * infrequent, but a reliable PPS signal, such as from an Ultra Stable
+ * Oscillator (USO) is available.
+ *
+ * Fudge Factors
+ *
+ * The PPS timestamp is captured on the rising (assert) edge if flag2 is
+ * dim (default) and on the falling (clear) edge if lit. If flag3 is dim
+ * (default), the kernel PPS support is disabled; if lit it is enabled.
+ * If flag4 is lit, each timesampt is copied to the clockstats file for
+ * later analysis. This can be useful when constructing Allan deviation
+ * plots. The time1 parameter can be used to compensate for
+ * miscellaneous device driver and OS delays.
+ */
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/pps%d" /* device name and unit */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "PPS\0" /* reference ID */
+#define DESCRIPTION "PPS Clock Discipline" /* WRU */
+
+/*
+ * PPS unit control structure
+ */
+struct ppsunit {
+ struct refclock_atom atom; /* atom structure pointer */
+ int fddev; /* file descriptor */
+};
+
+/*
+ * Function prototypes
+ */
+static int atom_start (int, struct peer *);
+static void atom_shutdown (int, struct peer *);
+static void atom_poll (int, struct peer *);
+static void atom_timer (int, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_atom = {
+ atom_start, /* start up driver */
+ atom_shutdown, /* shut down driver */
+ atom_poll, /* transmit poll message */
+ noentry, /* control (not used) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* buginfo (not used) */
+ atom_timer, /* called once per second */
+};
+
+
+/*
+ * atom_start - initialize data for processing
+ */
+static int
+atom_start(
+ int unit, /* unit number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct ppsunit *up;
+ char device[80];
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ pp = peer->procptr;
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ pp->stratum = STRATUM_UNSPEC;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up = emalloc(sizeof(struct ppsunit));
+ memset(up, 0, sizeof(struct ppsunit));
+ pp->unitptr = up;
+
+ /*
+ * Open PPS device. This can be any serial or parallel port and
+ * not necessarily the port used for the associated radio.
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+ up->fddev = tty_open(device, O_RDWR, 0777);
+ if (up->fddev <= 0) {
+ msyslog(LOG_ERR,
+ "refclock_atom: %s: %m", device);
+ return (0);
+ }
+
+ /*
+ * Light up the PPSAPI interface.
+ */
+ return (refclock_ppsapi(up->fddev, &up->atom));
+}
+
+
+/*
+ * atom_shutdown - shut down the clock
+ */
+static void
+atom_shutdown(
+ int unit, /* unit number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct ppsunit *up;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (up->fddev > 0)
+ close(up->fddev);
+ free(up);
+}
+
+/*
+ * atom_timer - called once per second
+ */
+void
+atom_timer(
+ int unit, /* unit pointer (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct ppsunit *up;
+ struct refclockproc *pp;
+ char tbuf[80];
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (refclock_pps(peer, &up->atom, pp->sloppyclockflag) <= 0)
+ return;
+
+ peer->flags |= FLAG_PPS;
+
+ /*
+ * If flag4 is lit, record each second offset to clockstats.
+ * That's so we can make awesome Allan deviation plots.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ snprintf(tbuf, sizeof(tbuf), "%.9f",
+ pp->filter[pp->coderecv]);
+ record_clock_stats(&peer->srcadr, tbuf);
+ }
+}
+
+
+/*
+ * atom_poll - called by the transmit procedure
+ */
+static void
+atom_poll(
+ int unit, /* unit number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+
+ /*
+ * Don't wiggle the clock until some other driver has numbered
+ * the seconds.
+ */
+ if (sys_leap == LEAP_NOTINSYNC)
+ return;
+
+ pp = peer->procptr;
+ pp->polls++;
+ if (pp->codeproc == pp->coderecv) {
+ peer->flags &= ~FLAG_PPS;
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+}
+#else
+int refclock_atom_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_bancomm.c b/ntpd/refclock_bancomm.c
new file mode 100644
index 0000000..49922e3
--- /dev/null
+++ b/ntpd/refclock_bancomm.c
@@ -0,0 +1,548 @@
+/* refclock_bancomm.c - clock driver for the Datum/Bancomm bc635VME
+ * Time and Frequency Processor. It requires the BANCOMM bc635VME/
+ * bc350VXI Time and Frequency Processor Module Driver for SunOS4.x
+ * and SunOS5.x UNIX Systems. It has been tested on a UltraSparc
+ * IIi-cEngine running Solaris 2.6.
+ *
+ * Author(s): Ganesh Ramasivan & Gary Cliff, Computing Devices Canada,
+ * Ottawa, Canada
+ *
+ * Date: July 1999
+ *
+ * Note(s): The refclock type has been defined as 16.
+ *
+ * This program has been modelled after the Bancomm driver
+ * originally written by R. Schmidt of Time Service, U.S.
+ * Naval Observatory for a HP-UX machine. Since the original
+ * authors no longer plan to maintain this code, all
+ * references to the HP-UX vme2 driver subsystem bave been
+ * removed. Functions vme_report_event(), vme_receive(),
+ * vme_control() and vme_buginfo() have been deleted because
+ * they are no longer being used.
+ *
+ * 04/28/2005 Rob Neal
+ * Modified to add support for Symmetricom bc637PCI-U Time &
+ * Frequency Processor.
+ * 2/21/2007 Ali Ghorashi
+ * Modified to add support for Symmetricom bc637PCI-U Time &
+ * Frequency Processor on Solaris.
+ * Tested on Solaris 10 with a bc635 card.
+ *
+ * Card bus type (VME/VXI or PCI) and environment are specified via the
+ * "mode" keyword on the server command in ntp.conf.
+ * server 127.127.16.u prefer mode M
+ * where u is the id (usually 0) of the entry in /dev (/dev/stfp0)
+ *
+ * and M is one of the following modes:
+ * 1 : FreeBSD PCI 635/637.
+ * 2 : Linux or Windows PCI 635/637.
+ * 3 : Solaris PCI 635/637
+ * not specified, or other number:
+ * : Assumed to be VME/VXI legacy Bancomm card on Solaris.
+ * Linux and Windows platforms require Symmetricoms' proprietary driver
+ * for the TFP card.
+ * Solaris requires Symmetricom's driver and its header file (freely distributed) to
+ * be installed and running.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_BANC)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <syslog.h>
+#include <ctype.h>
+
+struct btfp_time /* Structure for reading 5 time words */
+ /* in one ioctl(2) operation. */
+{
+ unsigned short btfp_time[5]; /* Time words 0,1,2,3, and 4. (16bit)*/
+};
+/* SunOS5 ioctl commands definitions.*/
+#define BTFPIOC ( 'b'<< 8 )
+#define IOCIO( l, n ) ( BTFPIOC | n )
+#define IOCIOR( l, n, s ) ( BTFPIOC | n )
+#define IOCIORN( l, n, s ) ( BTFPIOC | n )
+#define IOCIOWN( l, n, s ) ( BTFPIOC | n )
+
+/***** Simple ioctl commands *****/
+#define RUNLOCK IOCIOR(b, 19, int ) /* Release Capture Lockout */
+#define RCR0 IOCIOR(b, 22, int ) /* Read control register zero.*/
+#define WCR0 IOCIOWN(b, 23, int) /* Write control register zero*/
+/***** Compound ioctl commands *****/
+
+/* Read all 5 time words in one call. */
+#define READTIME IOCIORN(b, 32, sizeof( struct btfp_time ))
+
+#if defined(__FreeBSD__)
+#undef READTIME
+#define READTIME _IOR('u', 5, struct btfp_time )
+#endif
+
+/* Solaris specific section */
+struct stfp_tm {
+ int32_t tm_sec;
+ int32_t tm_min;
+ int32_t tm_hour;
+ int32_t tm_mday;
+ int32_t tm_mon;
+ int32_t tm_year;
+ int32_t tm_wday;
+ int32_t tm_yday;
+ int32_t tm_isdst;
+};
+
+struct stfp_time {
+ struct stfp_tm tm;
+ int32_t usec; /* usec 0 - 999999 */
+ int32_t hnsec; /* hnsec 0 - 9 (hundreds of nsecs) */
+ int32_t status;
+};
+
+#define SELTIMEFORMAT 2
+# define TIME_DECIMAL 0
+# define TIME_BINARY 1
+
+#if defined(__sun__)
+#undef READTIME
+#define READTIME 9
+#endif /** __sun___ **/
+/* end solaris specific section */
+
+struct vmedate { /* structure returned by get_vmetime.c */
+ unsigned short year;
+ unsigned short day;
+ unsigned short hr;
+ unsigned short mn;
+ unsigned short sec;
+ long frac;
+ unsigned short status;
+};
+
+typedef void *SYMMT_PCI_HANDLE;
+
+/*
+ * VME interface parameters.
+ */
+#define VMEPRECISION (-21) /* precision assumed (1 us) */
+#define USNOREFID "BTFP" /* or whatever */
+#define VMEREFID "BTFP" /* reference id */
+#define VMEDESCRIPTION "Bancomm bc635 TFP" /* who we are */
+#define VMEHSREFID 0x7f7f1000 /* 127.127.16.00 refid hi strata */
+/* clock type 16 is used here */
+#define GMT 0 /* hour offset from Greenwich */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time(s) */
+
+/*
+ * VME unit control structure.
+ * Changes made to vmeunit structure. Most members are now available in the
+ * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
+ */
+struct vmeunit {
+ struct vmedate vmedata; /* data returned from vme read */
+ u_long lasttime; /* last time clock heard from */
+};
+
+/*
+ * Function prototypes
+ */
+static int vme_start (int, struct peer *);
+static void vme_shutdown (int, struct peer *);
+static void vme_receive (struct recvbuf *);
+static void vme_poll (int unit, struct peer *);
+struct vmedate *get_datumtime(struct vmedate *);
+void tvme_fill(struct vmedate *, uint32_t btm[2]);
+void stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp);
+inline const char *DEVICE_NAME(int n);
+
+
+/*
+ * Define the bc*() functions as weak so we can compile/link without them.
+ * Only clients with the card will have the proprietary vendor device driver
+ * and interface library needed for use on Linux/Windows platforms.
+ */
+extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, uint32_t *, uint32_t*, uint8_t*);
+extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void);
+extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_bancomm = {
+ vme_start, /* start up driver */
+ vme_shutdown, /* shut down driver */
+ vme_poll, /* transmit poll message */
+ noentry, /* not used (old vme_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old vme_buginfo) */
+ NOFLAGS /* not used */
+};
+
+int fd_vme; /* file descriptor for ioctls */
+int regvalue;
+int tfp_type; /* mode selector, indicate platform and driver interface */
+SYMMT_PCI_HANDLE stfp_handle;
+
+/**
+ * this macro returns the device name based on
+ * the platform we are running on and the device number
+ */
+#if defined(__sun__)
+inline const char *DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/stfp%d",n);return s;}
+#else
+inline const char* DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/btfp%d",n);return s;}
+#endif /**__sun__**/
+
+/*
+ * vme_start - open the VME device and initialize data for processing
+ */
+static int
+vme_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct vmeunit *vme;
+ struct refclockproc *pp;
+ int dummy;
+ char vmedev[20];
+
+ tfp_type = (int)(peer->ttl);
+ switch (tfp_type) {
+ case 1:
+ case 3:
+ break;
+ case 2:
+ stfp_handle = bcStartPci(); /* init the card in lin/win */
+ break;
+ default:
+ break;
+ }
+ /*
+ * Open VME device
+ */
+#ifdef DEBUG
+
+ printf("Opening DATUM DEVICE %s\n",DEVICE_NAME(peer->refclkunit));
+#endif
+ if ( (fd_vme = open(DEVICE_NAME(peer->refclkunit), O_RDWR)) < 0) {
+ msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
+ return (0);
+ }
+ else {
+ switch (tfp_type) {
+ case 1: break;
+ case 2: break;
+ case 3:break;
+ default:
+ /* Release capture lockout in case it was set before. */
+ if( ioctl( fd_vme, RUNLOCK, &dummy ) )
+ msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");
+
+ regvalue = 0; /* More esoteric stuff to do... */
+ if( ioctl( fd_vme, WCR0, &regvalue ) )
+ msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
+ break;
+ }
+ }
+
+ /*
+ * Allocate unit structure
+ */
+ vme = emalloc_zero(sizeof(struct vmeunit));
+
+
+ /*
+ * Set up the structures
+ */
+ pp = peer->procptr;
+ pp->unitptr = vme;
+ pp->timestarted = current_time;
+
+ pp->io.clock_recv = vme_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd_vme;
+ /* shouldn't there be an io_addclock() call? */
+
+ /*
+ * 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 = VMEPRECISION;
+ memcpy(&pp->refid, USNOREFID,4);
+ return (1);
+}
+
+
+/*
+ * vme_shutdown - shut down a VME clock
+ */
+static void
+vme_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct vmeunit *vme;
+ struct refclockproc *pp;
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ pp = peer->procptr;
+ vme = pp->unitptr;
+ io_closeclock(&pp->io);
+ pp->unitptr = NULL;
+ if (NULL != vme)
+ free(vme);
+ if (tfp_type == 2)
+ bcStopPci(stfp_handle);
+}
+
+
+/*
+ * vme_receive - receive data from the VME device.
+ *
+ * Note: This interface would be interrupt-driven. We don't use that
+ * now, but include a dummy routine for possible future adventures.
+ */
+static void
+vme_receive(
+ struct recvbuf *rbufp
+ )
+{
+}
+
+
+/*
+ * vme_poll - called by the transmit procedure
+ */
+static void
+vme_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct vmedate *tptr;
+ struct vmeunit *vme;
+ struct refclockproc *pp;
+ time_t tloc;
+ struct tm *tadr;
+
+ pp = peer->procptr;
+ vme = pp->unitptr; /* Here is the structure */
+
+ tptr = &vme->vmedata;
+ if ((tptr = get_datumtime(tptr)) == NULL ) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ get_systime(&pp->lastrec);
+ pp->polls++;
+ vme->lasttime = current_time;
+
+ /*
+ * Get VME time and convert to timestamp format.
+ * The year must come from the system clock.
+ */
+
+ time(&tloc);
+ tadr = gmtime(&tloc);
+ tptr->year = (unsigned short)(tadr->tm_year + 1900);
+
+ snprintf(pp->a_lastcode,
+ sizeof(pp->a_lastcode),
+ "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
+ tptr->day,
+ tptr->hr,
+ tptr->mn,
+ tptr->sec,
+ tptr->frac,
+ tptr->status);
+
+ pp->lencode = (u_short) strlen(pp->a_lastcode);
+
+ pp->day = tptr->day;
+ pp->hour = tptr->hr;
+ pp->minute = tptr->mn;
+ pp->second = tptr->sec;
+ pp->nsec = tptr->frac;
+
+#ifdef DEBUG
+ if (debug)
+ printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
+ pp->day, pp->hour, pp->minute, pp->second,
+ pp->nsec, tptr->status);
+#endif
+ if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Now, compute the reference time value. Use the heavy
+ * machinery for the seconds and the millisecond field for the
+ * fraction when present. If an error in conversion to internal
+ * format is found, the program declares bad data and exits.
+ * Note that this code does not yet know how to do the years and
+ * relies on the clock-calendar chip for sanity.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+}
+
+struct vmedate *
+get_datumtime(struct vmedate *time_vme)
+{
+ char cbuf[7];
+ struct btfp_time vts;
+ uint32_t btm[2];
+ uint8_t dmy;
+ struct stfp_time stfpm;
+
+ if (time_vme == NULL)
+ time_vme = emalloc(sizeof(*time_vme));
+
+ switch (tfp_type) {
+ case 1: /* BSD, PCI, 2 32bit time words */
+ if (ioctl(fd_vme, READTIME, &btm)) {
+ msyslog(LOG_ERR, "get_bc63x error: %m");
+ return(NULL);
+ }
+ tvme_fill(time_vme, btm);
+ break;
+
+ case 2: /* Linux/Windows, PCI, 2 32bit time words */
+ if (bcReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) {
+ msyslog(LOG_ERR, "get_datumtime error: %m");
+ return(NULL);
+ }
+ tvme_fill(time_vme, btm);
+ break;
+
+ case 3: /** solaris **/
+ memset(&stfpm,0,sizeof(stfpm));
+
+ /* we need the time in decimal format */
+ /* Here we rudely assume that we are the only user of the driver.
+ * Other programs will have to set their own time format before reading
+ * the time.
+ */
+ if(ioctl (fd_vme, SELTIMEFORMAT, TIME_DECIMAL)){
+ msyslog(LOG_ERR, "Could not set time format");
+ return (NULL);
+ }
+ /* read the time */
+ if (ioctl(fd_vme, READTIME, &stfpm)) {
+ msyslog(LOG_ERR, "ioctl error: %m");
+ return(NULL);
+ }
+ stfp_time2tvme(time_vme, &stfpm);
+ break;
+
+ default: /* legacy bancomm card */
+
+ if (ioctl(fd_vme, READTIME, &vts)) {
+ msyslog(LOG_ERR,
+ "get_datumtime error: %m");
+ return(NULL);
+ }
+ /* Get day */
+ snprintf(cbuf, sizeof(cbuf), "%3.3x",
+ ((vts.btfp_time[ 0 ] & 0x000f) << 8) +
+ ((vts.btfp_time[ 1 ] & 0xff00) >> 8));
+ time_vme->day = (unsigned short)atoi(cbuf);
+
+ /* Get hour */
+ snprintf(cbuf, sizeof(cbuf), "%2.2x",
+ vts.btfp_time[ 1 ] & 0x00ff);
+ time_vme->hr = (unsigned short)atoi(cbuf);
+
+ /* Get minutes */
+ snprintf(cbuf, sizeof(cbuf), "%2.2x",
+ (vts.btfp_time[ 2 ] & 0xff00) >> 8);
+ time_vme->mn = (unsigned short)atoi(cbuf);
+
+ /* Get seconds */
+ snprintf(cbuf, sizeof(cbuf), "%2.2x",
+ vts.btfp_time[ 2 ] & 0x00ff);
+ time_vme->sec = (unsigned short)atoi(cbuf);
+
+ /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so
+ we can use the TVTOTSF function later on...*/
+
+ snprintf(cbuf, sizeof(cbuf), "%4.4x%2.2x",
+ vts.btfp_time[ 3 ],
+ vts.btfp_time[ 4 ] >> 8);
+ time_vme->frac = (u_long) atoi(cbuf);
+
+ /* Get status bit */
+ time_vme->status = (vts.btfp_time[0] & 0x0010) >> 4;
+
+ break;
+ }
+
+ if (time_vme->status)
+ return ((void *)NULL);
+ else
+ return (time_vme);
+}
+/* Assign values to time_vme struct. Mostly for readability */
+void
+tvme_fill(struct vmedate *time_vme, uint32_t btm[2])
+{
+ struct tm maj;
+ uint32_t dmaj, dmin;
+
+ dmaj = btm[1]; /* syntax sugar */
+ dmin = btm[0];
+
+ gmtime_r(&dmaj, &maj);
+ time_vme->day = maj.tm_yday+1;
+ time_vme->hr = maj.tm_hour;
+ time_vme->mn = maj.tm_min;
+ time_vme->sec = maj.tm_sec;
+ time_vme->frac = (dmin & 0x000fffff) * 1000;
+ time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100;
+ time_vme->status = (dmin & 0x01000000) >> 24;
+ return;
+}
+
+
+/* Assign values to time_vme struct. Mostly for readability */
+void
+stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp)
+{
+
+ time_vme->day = stfp->tm.tm_yday+1;
+ time_vme->hr = stfp->tm.tm_hour;
+ time_vme->mn = stfp->tm.tm_min;
+ time_vme->sec = stfp->tm.tm_sec;
+ time_vme->frac = stfp->usec*1000;
+ time_vme->frac += stfp->hnsec * 100;
+ time_vme->status = stfp->status;
+ return;
+}
+#else
+int refclock_bancomm_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_chronolog.c b/ntpd/refclock_chronolog.c
new file mode 100644
index 0000000..ee54b43
--- /dev/null
+++ b/ntpd/refclock_chronolog.c
@@ -0,0 +1,343 @@
+/*
+ * refclock_chronolog - clock driver for Chronolog K-series WWVB receiver.
+ */
+
+/*
+ * Must interpolate back to local time. Very annoying.
+ */
+#define GET_LOCALTIME
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_CHRONOLOG)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the Chronolog K-series WWVB receiver.
+ *
+ * Input format:
+ *
+ * Y YY/MM/DD<cr><lf>
+ * Z hh:mm:ss<cr><lf>
+ *
+ * YY/MM/DD -- what you'd expect. This arrives a few seconds before the
+ * timestamp.
+ * hh:mm:ss -- what you'd expect. We take time on the <cr>.
+ *
+ * Our Chronolog writes time out at 2400 bps 8/N/1, but it can be configured
+ * otherwise. The clock seems to appear every 60 seconds, which doesn't make
+ * for good statistics collection.
+ *
+ * The original source of this module was the WWVB module.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/chronolog%d" /* device name and unit */
+#define SPEED232 B2400 /* uart speed (2400 baud) */
+#define PRECISION (-13) /* precision assumed (about 100 us) */
+#define REFID "chronolog" /* reference ID */
+#define DESCRIPTION "Chrono-log K" /* WRU */
+
+#define MONLIN 15 /* number of monitoring lines */
+
+/*
+ * Chrono-log unit control structure
+ */
+struct chronolog_unit {
+ u_char tcswitch; /* timecode switch */
+ l_fp laststamp; /* last receive timestamp */
+ u_char lasthour; /* last hour (for monitor) */
+ int year; /* Y2K-adjusted year */
+ int day; /* day-of-month */
+ int month; /* month-of-year */
+};
+
+/*
+ * Function prototypes
+ */
+static int chronolog_start (int, struct peer *);
+static void chronolog_shutdown (int, struct peer *);
+static void chronolog_receive (struct recvbuf *);
+static void chronolog_poll (int, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_chronolog = {
+ chronolog_start, /* start up driver */
+ chronolog_shutdown, /* shut down driver */
+ chronolog_poll, /* poll the driver -- a nice fabrication */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * chronolog_start - open the devices and initialize data for processing
+ */
+static int
+chronolog_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct chronolog_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Don't bother with CLK line discipline, since
+ * it's not available.
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+#ifdef DEBUG
+ if (debug)
+ printf ("starting Chronolog with device %s\n",device);
+#endif
+ fd = refclock_open(device, SPEED232, 0);
+ if (fd <= 0)
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->unitptr = up;
+ pp->io.clock_recv = chronolog_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ pp->unitptr = NULL;
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * chronolog_shutdown - shut down the clock
+ */
+static void
+chronolog_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct chronolog_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ if (NULL != up)
+ free(up);
+}
+
+
+/*
+ * chronolog_receive - receive data from the serial interface
+ */
+static void
+chronolog_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct chronolog_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ l_fp trtmp; /* arrival timestamp */
+ int hours; /* hour-of-day */
+ int minutes; /* minutes-past-the-hour */
+ int seconds; /* seconds */
+ int temp; /* int temp */
+ int got_good; /* got a good time flag */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ if (temp == 0) {
+ if (up->tcswitch == 0) {
+ up->tcswitch = 1;
+ up->laststamp = trtmp;
+ } else
+ up->tcswitch = 0;
+ return;
+ }
+ pp->lencode = temp;
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ up->tcswitch = 1;
+
+#ifdef DEBUG
+ if (debug)
+ printf("chronolog: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+
+ /*
+ * We get down to business. Check the timecode format and decode
+ * its contents. This code uses the first character to see whether
+ * we're looking at a date or a time. We store data data across
+ * calls since it is transmitted a few seconds ahead of the
+ * timestamp.
+ */
+ got_good=0;
+ if (sscanf(pp->a_lastcode, "Y %d/%d/%d", &up->year,&up->month,&up->day))
+ {
+ /*
+ * Y2K convert the 2-digit year
+ */
+ up->year = up->year >= 69 ? up->year : up->year + 100;
+ return;
+ }
+ if (sscanf(pp->a_lastcode,"Z %02d:%02d:%02d",
+ &hours,&minutes,&seconds) == 3)
+ {
+#ifdef GET_LOCALTIME
+ struct tm local;
+ struct tm *gmtp;
+ time_t unixtime;
+ int adjyear;
+ int adjmon;
+
+ /*
+ * Convert to GMT for sites that distribute localtime. This
+ * means we have to do Y2K conversion on the 2-digit year;
+ * otherwise, we get the time wrong.
+ */
+
+ memset(&local, 0, sizeof(local));
+
+ local.tm_year = up->year;
+ local.tm_mon = up->month-1;
+ local.tm_mday = up->day;
+ local.tm_hour = hours;
+ local.tm_min = minutes;
+ local.tm_sec = seconds;
+ local.tm_isdst = -1;
+
+ unixtime = mktime (&local);
+ if ((gmtp = gmtime (&unixtime)) == NULL)
+ {
+ refclock_report (peer, CEVNT_FAULT);
+ return;
+ }
+ adjyear = gmtp->tm_year+1900;
+ adjmon = gmtp->tm_mon+1;
+ pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
+ pp->hour = gmtp->tm_hour;
+ pp->minute = gmtp->tm_min;
+ pp->second = gmtp->tm_sec;
+#ifdef DEBUG
+ if (debug)
+ printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
+ adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
+ pp->second);
+#endif
+
+#else
+ /*
+ * For more rational sites distributing UTC
+ */
+ pp->day = ymd2yd(year+1900,month,day);
+ pp->hour = hours;
+ pp->minute = minutes;
+ pp->second = seconds;
+
+#endif
+ got_good=1;
+ }
+
+ if (!got_good)
+ return;
+
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ up->lasthour = (u_char)pp->hour;
+}
+
+
+/*
+ * chronolog_poll - called by the transmit procedure
+ */
+static void
+chronolog_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ /*
+ * Time to poll the clock. The Chrono-log clock is supposed to
+ * respond to a 'T' by returning a timecode in the format(s)
+ * specified above. Ours does (can?) not, but this seems to be
+ * an installation-specific problem. This code is dyked out,
+ * but may be re-enabled if anyone ever finds a Chrono-log that
+ * actually listens to this command.
+ */
+#if 0
+ register struct chronolog_unit *up;
+ struct refclockproc *pp;
+ char pollchar;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (peer->burst == 0 && peer->reach == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ if (up->linect > 0)
+ pollchar = 'R';
+ else
+ pollchar = 'T';
+ if (write(pp->io.fd, &pollchar, 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+#endif
+}
+
+#else
+int refclock_chronolog_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_chu.c b/ntpd/refclock_chu.c
new file mode 100644
index 0000000..9c7093d
--- /dev/null
+++ b/ntpd/refclock_chu.c
@@ -0,0 +1,1683 @@
+/*
+ * refclock_chu - clock driver for Canadian CHU time/frequency station
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ntp_types.h"
+
+#if defined(REFCLOCK) && defined(CLOCK_CHU)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+
+#ifdef HAVE_AUDIO
+#include "audio.h"
+#endif /* HAVE_AUDIO */
+
+#define ICOM 1 /* undefine to suppress ICOM code */
+
+#ifdef ICOM
+#include "icom.h"
+#endif /* ICOM */
+/*
+ * Audio CHU demodulator/decoder
+ *
+ * This driver synchronizes the computer time using data encoded in
+ * radio transmissions from Canadian time/frequency station CHU in
+ * Ottawa, Ontario. Transmissions are made continuously on 3330 kHz,
+ * 7850 kHz and 14670 kHz in upper sideband, compatible AM mode. An
+ * ordinary shortwave receiver can be tuned manually to one of these
+ * frequencies or, in the case of ICOM receivers, the receiver can be
+ * tuned automatically as propagation conditions change throughout the
+ * day and season.
+ *
+ * The driver requires an audio codec or sound card with sampling rate 8
+ * kHz and mu-law companding. This is the same standard as used by the
+ * telephone industry and is supported by most hardware and operating
+ * systems, including Solaris, SunOS, FreeBSD, NetBSD and Linux. In this
+ * implementation, only one audio driver and codec can be supported on a
+ * single machine.
+ *
+ * The driver can be compiled to use a Bell 103 compatible modem or
+ * modem chip to receive the radio signal and demodulate the data.
+ * Alternatively, the driver can be compiled to use the audio codec of
+ * the workstation or another with compatible audio drivers. In the
+ * latter case, the driver implements the modem using DSP routines, so
+ * the radio can be connected directly to either the microphone on line
+ * input port. In either case, the driver decodes the data using a
+ * maximum-likelihood technique which exploits the considerable degree
+ * of redundancy available to maximize accuracy and minimize errors.
+ *
+ * The CHU time broadcast includes an audio signal compatible with the
+ * Bell 103 modem standard (mark = 2225 Hz, space = 2025 Hz). The signal
+ * consists of nine, ten-character bursts transmitted at 300 bps between
+ * seconds 31 and 39 of each minute. Each character consists of eight
+ * data bits plus one start bit and two stop bits to encode two hex
+ * digits. The burst data consist of five characters (ten hex digits)
+ * followed by a repeat of these characters. In format A, the characters
+ * are repeated in the same polarity; in format B, the characters are
+ * repeated in the opposite polarity.
+ *
+ * Format A bursts are sent at seconds 32 through 39 of the minute in
+ * hex digits (nibble swapped)
+ *
+ * 6dddhhmmss6dddhhmmss
+ *
+ * The first ten digits encode a frame marker (6) followed by the day
+ * (ddd), hour (hh in UTC), minute (mm) and the second (ss). Since
+ * format A bursts are sent during the third decade of seconds the tens
+ * digit of ss is always 3. The driver uses this to determine correct
+ * burst synchronization. These digits are then repeated with the same
+ * polarity.
+ *
+ * Format B bursts are sent at second 31 of the minute in hex digits
+ *
+ * xdyyyyttaaxdyyyyttaa
+ *
+ * The first ten digits encode a code (x described below) followed by
+ * the DUT1 (d in deciseconds), Gregorian year (yyyy), difference TAI -
+ * UTC (tt) and daylight time indicator (aa) peculiar to Canada. These
+ * digits are then repeated with inverted polarity.
+ *
+ * The x is coded
+ *
+ * 1 Sign of DUT (0 = +)
+ * 2 Leap second warning. One second will be added.
+ * 4 Leap second warning. One second will be subtracted.
+ * 8 Even parity bit for this nibble.
+ *
+ * By design, the last stop bit of the last character in the burst
+ * coincides with 0.5 second. Since characters have 11 bits and are
+ * transmitted at 300 bps, the last stop bit of the first character
+ * coincides with 0.5 - 9 * 11/300 = 0.170 second. Depending on the
+ * UART, character interrupts can vary somewhere between the end of bit
+ * 9 and end of bit 11. These eccentricities can be corrected along with
+ * the radio propagation delay using fudge time 1.
+ *
+ * Debugging aids
+ *
+ * The timecode format used for debugging and data recording includes
+ * data helpful in diagnosing problems with the radio signal and serial
+ * connections. With debugging enabled (-d on the ntpd command line),
+ * the driver produces one line for each burst in two formats
+ * corresponding to format A and B.Each line begins with the format code
+ * chuA or chuB followed by the status code and signal level (0-9999).
+ * The remainder of the line is as follows.
+ *
+ * Following is format A:
+ *
+ * n b f s m code
+ *
+ * where n is the number of characters in the burst (0-10), b the burst
+ * distance (0-40), f the field alignment (-1, 0, 1), s the
+ * synchronization distance (0-16), m the burst number (2-9) and code
+ * the burst characters as received. Note that the hex digits in each
+ * character are reversed, so the burst
+ *
+ * 10 38 0 16 9 06851292930685129293
+ *
+ * is interpreted as containing 10 characters with burst distance 38,
+ * field alignment 0, synchronization distance 16 and burst number 9.
+ * The nibble-swapped timecode shows day 58, hour 21, minute 29 and
+ * second 39.
+ *
+ * Following is format B:
+ *
+ * n b s code
+ *
+ * where n is the number of characters in the burst (0-10), b the burst
+ * distance (0-40), s the synchronization distance (0-40) and code the
+ * burst characters as received. Note that the hex digits in each
+ * character are reversed and the last ten digits inverted, so the burst
+ *
+ * 10 40 1091891300ef6e76ec
+ *
+ * is interpreted as containing 10 characters with burst distance 40.
+ * The nibble-swapped timecode shows DUT1 +0.1 second, year 1998 and TAI
+ * - UTC 31 seconds.
+ *
+ * Each line is preceeded by the code chuA or chuB, as appropriate. If
+ * the audio driver is compiled, the current gain (0-255) and relative
+ * signal level (0-9999) follow the code. The receiver volume control
+ * should be set so that the gain is somewhere near the middle of the
+ * range 0-255, which results in a signal level near 1000.
+ *
+ * In addition to the above, the reference timecode is updated and
+ * written to the clockstats file and debug score after the last burst
+ * received in the minute. The format is
+ *
+ * sq yyyy ddd hh:mm:ss l s dd t agc ident m b
+ *
+ * s '?' before first synchronized and ' ' after that
+ * q status code (see below)
+ * yyyy year
+ * ddd day of year
+ * hh:mm:ss time of day
+ * l leap second indicator (space, L or D)
+ * dst Canadian daylight code (opaque)
+ * t number of minutes since last synchronized
+ * agc audio gain (0 - 255)
+ * ident identifier (CHU0 3330 kHz, CHU1 7850 kHz, CHU2 14670 kHz)
+ * m signal metric (0 - 100)
+ * b number of timecodes for the previous minute (0 - 59)
+ *
+ * Fudge factors
+ *
+ * For accuracies better than the low millisceconds, fudge time1 can be
+ * set to the radio propagation delay from CHU to the receiver. This can
+ * be done conviently using the minimuf program.
+ *
+ * Fudge flag4 causes the dubugging output described above to be
+ * recorded in the clockstats file. When the audio driver is compiled,
+ * fudge flag2 selects the audio input port, where 0 is the mike port
+ * (default) and 1 is the line-in port. It does not seem useful to
+ * select the compact disc player port. Fudge flag3 enables audio
+ * monitoring of the input signal. For this purpose, the monitor gain is
+ * set to a default value.
+ *
+ * The audio codec code is normally compiled in the driver if the
+ * architecture supports it (HAVE_AUDIO defined), but is used only if
+ * the link /dev/chu_audio is defined and valid. The serial port code is
+ * always compiled in the driver, but is used only if the autdio codec
+ * is not available and the link /dev/chu%d is defined and valid.
+ *
+ * The ICOM code is normally compiled in the driver if selected (ICOM
+ * defined), but is used only if the link /dev/icom%d is defined and
+ * valid and the mode keyword on the server configuration command
+ * specifies a nonzero mode (ICOM ID select code). The C-IV speed is
+ * 9600 bps if the high order 0x80 bit of the mode is zero and 1200 bps
+ * if one. The C-IV trace is turned on if the debug level is greater
+ * than one.
+ *
+ * Alarm codes
+ *
+ * CEVNT_BADTIME invalid date or time
+ * CEVNT_PROP propagation failure - no stations heard
+ */
+/*
+ * Interface definitions
+ */
+#define SPEED232 B300 /* uart speed (300 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "CHU" /* reference ID */
+#define DEVICE "/dev/chu%d" /* device name and unit */
+#define SPEED232 B300 /* UART speed (300 baud) */
+#ifdef ICOM
+#define TUNE .001 /* offset for narrow filter (MHz) */
+#define DWELL 5 /* minutes in a dwell */
+#define NCHAN 3 /* number of channels */
+#define ISTAGE 3 /* number of integrator stages */
+#endif /* ICOM */
+
+#ifdef HAVE_AUDIO
+/*
+ * Audio demodulator definitions
+ */
+#define SECOND 8000 /* nominal sample rate (Hz) */
+#define BAUD 300 /* modulation rate (bps) */
+#define OFFSET 128 /* companded sample offset */
+#define SIZE 256 /* decompanding table size */
+#define MAXAMP 6000. /* maximum signal level */
+#define MAXCLP 100 /* max clips above reference per s */
+#define SPAN 800. /* min envelope span */
+#define LIMIT 1000. /* soft limiter threshold */
+#define AGAIN 6. /* baseband gain */
+#define LAG 10 /* discriminator lag */
+#define DEVICE_AUDIO "/dev/audio" /* device name */
+#define DESCRIPTION "CHU Audio/Modem Receiver" /* WRU */
+#define AUDIO_BUFSIZ 240 /* audio buffer size (30 ms) */
+#else
+#define DESCRIPTION "CHU Modem Receiver" /* WRU */
+#endif /* HAVE_AUDIO */
+
+/*
+ * Decoder definitions
+ */
+#define CHAR (11. / 300.) /* character time (s) */
+#define BURST 11 /* max characters per burst */
+#define MINCHARS 9 /* min characters per burst */
+#define MINDIST 28 /* min burst distance (of 40) */
+#define MINSYNC 8 /* min sync distance (of 16) */
+#define MINSTAMP 20 /* min timestamps (of 60) */
+#define MINMETRIC 50 /* min channel metric (of 160) */
+
+/*
+ * The on-time synchronization point for the driver is the last stop bit
+ * of the first character 170 ms. The modem delay is 0.8 ms, while the
+ * receiver delay is approxmately 4.7 ms at 2125 Hz. The fudge value 1.3
+ * ms due to the codec and other causes was determined by calibrating to
+ * a PPS signal from a GPS receiver. The additional propagation delay
+ * specific to each receiver location can be programmed in the fudge
+ * time1.
+ *
+ * The resulting offsets with a 2.4-GHz P4 running FreeBSD 6.1 are
+ * generally within 0.5 ms short term with 0.3 ms jitter. The long-term
+ * offsets vary up to 0.3 ms due to ionospheric layer height variations.
+ * The processor load due to the driver is 0.4 percent.
+ */
+#define PDELAY ((170 + .8 + 4.7 + 1.3) / 1000) /* system delay (s) */
+
+/*
+ * Status bits (status)
+ */
+#define RUNT 0x0001 /* runt burst */
+#define NOISE 0x0002 /* noise burst */
+#define BFRAME 0x0004 /* invalid format B frame sync */
+#define BFORMAT 0x0008 /* invalid format B data */
+#define AFRAME 0x0010 /* invalid format A frame sync */
+#define AFORMAT 0x0020 /* invalid format A data */
+#define DECODE 0x0040 /* invalid data decode */
+#define STAMP 0x0080 /* too few timestamps */
+#define AVALID 0x0100 /* valid A frame */
+#define BVALID 0x0200 /* valid B frame */
+#define INSYNC 0x0400 /* clock synchronized */
+#define METRIC 0x0800 /* one or more stations heard */
+
+/*
+ * Alarm status bits (alarm)
+ *
+ * These alarms are set at the end of a minute in which at least one
+ * burst was received. SYNERR is raised if the AFRAME or BFRAME status
+ * bits are set during the minute, FMTERR is raised if the AFORMAT or
+ * BFORMAT status bits are set, DECERR is raised if the DECODE status
+ * bit is set and TSPERR is raised if the STAMP status bit is set.
+ */
+#define SYNERR 0x01 /* frame sync error */
+#define FMTERR 0x02 /* data format error */
+#define DECERR 0x04 /* data decoding error */
+#define TSPERR 0x08 /* insufficient data */
+
+#ifdef HAVE_AUDIO
+/*
+ * Maximum-likelihood UART structure. There are eight of these
+ * corresponding to the number of phases.
+ */
+struct surv {
+ l_fp cstamp; /* last bit timestamp */
+ double shift[12]; /* sample shift register */
+ double span; /* shift register envelope span */
+ double dist; /* sample distance */
+ int uart; /* decoded character */
+};
+#endif /* HAVE_AUDIO */
+
+#ifdef ICOM
+/*
+ * CHU station structure. There are three of these corresponding to the
+ * three frequencies.
+ */
+struct xmtr {
+ double integ[ISTAGE]; /* circular integrator */
+ double metric; /* integrator sum */
+ int iptr; /* integrator pointer */
+ int probe; /* dwells since last probe */
+};
+#endif /* ICOM */
+
+/*
+ * CHU unit control structure
+ */
+struct chuunit {
+ u_char decode[20][16]; /* maximum-likelihood decoding matrix */
+ l_fp cstamp[BURST]; /* character timestamps */
+ l_fp tstamp[MAXSTAGE]; /* timestamp samples */
+ l_fp timestamp; /* current buffer timestamp */
+ l_fp laststamp; /* last buffer timestamp */
+ l_fp charstamp; /* character time as a l_fp */
+ int second; /* counts the seconds of the minute */
+ int errflg; /* error flags */
+ int status; /* status bits */
+ char ident[5]; /* station ID and channel */
+#ifdef ICOM
+ int fd_icom; /* ICOM file descriptor */
+ int chan; /* radio channel */
+ int dwell; /* dwell cycle */
+ struct xmtr xmtr[NCHAN]; /* station metric */
+#endif /* ICOM */
+
+ /*
+ * Character burst variables
+ */
+ int cbuf[BURST]; /* character buffer */
+ int ntstamp; /* number of timestamp samples */
+ int ndx; /* buffer start index */
+ int prevsec; /* previous burst second */
+ int burdist; /* burst distance */
+ int syndist; /* sync distance */
+ int burstcnt; /* format A bursts this minute */
+ double maxsignal; /* signal level (modem only) */
+ int gain; /* codec gain (modem only) */
+
+ /*
+ * Format particulars
+ */
+ int leap; /* leap/dut code */
+ int dut; /* UTC1 correction */
+ int tai; /* TAI - UTC correction */
+ int dst; /* Canadian DST code */
+
+#ifdef HAVE_AUDIO
+ /*
+ * Audio codec variables
+ */
+ int fd_audio; /* audio port file descriptor */
+ double comp[SIZE]; /* decompanding table */
+ int port; /* codec port */
+ int mongain; /* codec monitor gain */
+ int clipcnt; /* sample clip count */
+ int seccnt; /* second interval counter */
+
+ /*
+ * Modem variables
+ */
+ l_fp tick; /* audio sample increment */
+ double bpf[9]; /* IIR bandpass filter */
+ double disc[LAG]; /* discriminator shift register */
+ double lpf[27]; /* FIR lowpass filter */
+ double monitor; /* audio monitor */
+ int discptr; /* discriminator pointer */
+
+ /*
+ * Maximum-likelihood UART variables
+ */
+ double baud; /* baud interval */
+ struct surv surv[8]; /* UART survivor structures */
+ int decptr; /* decode pointer */
+ int decpha; /* decode phase */
+ int dbrk; /* holdoff counter */
+#endif /* HAVE_AUDIO */
+};
+
+/*
+ * Function prototypes
+ */
+static int chu_start (int, struct peer *);
+static void chu_shutdown (int, struct peer *);
+static void chu_receive (struct recvbuf *);
+static void chu_second (int, struct peer *);
+static void chu_poll (int, struct peer *);
+
+/*
+ * More function prototypes
+ */
+static void chu_decode (struct peer *, int, l_fp);
+static void chu_burst (struct peer *);
+static void chu_clear (struct peer *);
+static void chu_a (struct peer *, int);
+static void chu_b (struct peer *, int);
+static int chu_dist (int, int);
+static double chu_major (struct peer *);
+#ifdef HAVE_AUDIO
+static void chu_uart (struct surv *, double);
+static void chu_rf (struct peer *, double);
+static void chu_gain (struct peer *);
+static void chu_audio_receive (struct recvbuf *rbufp);
+#endif /* HAVE_AUDIO */
+#ifdef ICOM
+static int chu_newchan (struct peer *, double);
+#endif /* ICOM */
+static void chu_serial_receive (struct recvbuf *rbufp);
+
+/*
+ * Global variables
+ */
+static char hexchar[] = "0123456789abcdef_*=";
+
+#ifdef ICOM
+/*
+ * Note the tuned frequencies are 1 kHz higher than the carrier. CHU
+ * transmits on USB with carrier so we can use AM and the narrow SSB
+ * filter.
+ */
+static double qsy[NCHAN] = {3.330, 7.850, 14.670}; /* freq (MHz) */
+#endif /* ICOM */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_chu = {
+ chu_start, /* start up driver */
+ chu_shutdown, /* shut down driver */
+ chu_poll, /* transmit poll message */
+ noentry, /* not used (old chu_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old chu_buginfo) */
+ chu_second /* housekeeping timer */
+};
+
+
+/*
+ * chu_start - open the devices and initialize data for processing
+ */
+static int
+chu_start(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+ char device[20]; /* device name */
+ int fd; /* file descriptor */
+#ifdef ICOM
+ int temp;
+#endif /* ICOM */
+#ifdef HAVE_AUDIO
+ int fd_audio; /* audio port file descriptor */
+ int i; /* index */
+ double step; /* codec adjustment */
+
+ /*
+ * Open audio device. Don't complain if not there.
+ */
+ fd_audio = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit);
+
+#ifdef DEBUG
+ if (fd_audio >= 0 && debug)
+ audio_show();
+#endif
+
+ /*
+ * If audio is unavailable, Open serial port in raw mode.
+ */
+ if (fd_audio >= 0) {
+ fd = fd_audio;
+ } else {
+ snprintf(device, sizeof(device), DEVICE, unit);
+ fd = refclock_open(device, SPEED232, LDISC_RAW);
+ }
+#else /* HAVE_AUDIO */
+
+ /*
+ * Open serial port in raw mode.
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+ fd = refclock_open(device, SPEED232, LDISC_RAW);
+#endif /* HAVE_AUDIO */
+
+ if (fd < 0)
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->unitptr = up;
+ pp->io.clock_recv = chu_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ pp->unitptr = NULL;
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ strlcpy(up->ident, "CHU", sizeof(up->ident));
+ memcpy(&pp->refid, up->ident, 4);
+ DTOLFP(CHAR, &up->charstamp);
+#ifdef HAVE_AUDIO
+
+ /*
+ * The companded samples are encoded sign-magnitude. The table
+ * contains all the 256 values in the interest of speed. We do
+ * this even if the audio codec is not available. C'est la lazy.
+ */
+ up->fd_audio = fd_audio;
+ up->gain = 127;
+ up->comp[0] = up->comp[OFFSET] = 0.;
+ up->comp[1] = 1; up->comp[OFFSET + 1] = -1.;
+ up->comp[2] = 3; up->comp[OFFSET + 2] = -3.;
+ step = 2.;
+ for (i = 3; i < OFFSET; i++) {
+ up->comp[i] = up->comp[i - 1] + step;
+ up->comp[OFFSET + i] = -up->comp[i];
+ if (i % 16 == 0)
+ step *= 2.;
+ }
+ DTOLFP(1. / SECOND, &up->tick);
+#endif /* HAVE_AUDIO */
+#ifdef ICOM
+ temp = 0;
+#ifdef DEBUG
+ if (debug > 1)
+ temp = P_TRACE;
+#endif
+ if (peer->ttl > 0) {
+ if (peer->ttl & 0x80)
+ up->fd_icom = icom_init("/dev/icom", B1200,
+ temp);
+ else
+ up->fd_icom = icom_init("/dev/icom", B9600,
+ temp);
+ }
+ if (up->fd_icom > 0) {
+ if (chu_newchan(peer, 0) != 0) {
+ msyslog(LOG_NOTICE, "icom: radio not found");
+ close(up->fd_icom);
+ up->fd_icom = 0;
+ } else {
+ msyslog(LOG_NOTICE, "icom: autotune enabled");
+ }
+ }
+#endif /* ICOM */
+ return (1);
+}
+
+
+/*
+ * chu_shutdown - shut down the clock
+ */
+static void
+chu_shutdown(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (up == NULL)
+ return;
+
+ io_closeclock(&pp->io);
+#ifdef ICOM
+ if (up->fd_icom > 0)
+ close(up->fd_icom);
+#endif /* ICOM */
+ free(up);
+}
+
+
+/*
+ * chu_receive - receive data from the audio or serial device
+ */
+static void
+chu_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+#ifdef HAVE_AUDIO
+ struct chuunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * If the audio codec is warmed up, the buffer contains codec
+ * samples which need to be demodulated and decoded into CHU
+ * characters using the software UART. Otherwise, the buffer
+ * contains CHU characters from the serial port, so the software
+ * UART is bypassed. In this case the CPU will probably run a
+ * few degrees cooler.
+ */
+ if (up->fd_audio > 0)
+ chu_audio_receive(rbufp);
+ else
+ chu_serial_receive(rbufp);
+#else
+ chu_serial_receive(rbufp);
+#endif /* HAVE_AUDIO */
+}
+
+
+#ifdef HAVE_AUDIO
+/*
+ * chu_audio_receive - receive data from the audio device
+ */
+static void
+chu_audio_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ double sample; /* codec sample */
+ u_char *dpt; /* buffer pointer */
+ int bufcnt; /* buffer counter */
+ l_fp ltemp; /* l_fp temp */
+
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Main loop - read until there ain't no more. Note codec
+ * samples are bit-inverted.
+ */
+ DTOLFP((double)rbufp->recv_length / SECOND, &ltemp);
+ L_SUB(&rbufp->recv_time, &ltemp);
+ up->timestamp = rbufp->recv_time;
+ dpt = rbufp->recv_buffer;
+ for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) {
+ sample = up->comp[~*dpt++ & 0xff];
+
+ /*
+ * Clip noise spikes greater than MAXAMP. If no clips,
+ * increase the gain a tad; if the clips are too high,
+ * decrease a tad.
+ */
+ if (sample > MAXAMP) {
+ sample = MAXAMP;
+ up->clipcnt++;
+ } else if (sample < -MAXAMP) {
+ sample = -MAXAMP;
+ up->clipcnt++;
+ }
+ chu_rf(peer, sample);
+ L_ADD(&up->timestamp, &up->tick);
+
+ /*
+ * Once each second ride gain.
+ */
+ up->seccnt = (up->seccnt + 1) % SECOND;
+ if (up->seccnt == 0) {
+ chu_gain(peer);
+ }
+ }
+
+ /*
+ * Set the input port and monitor gain for the next buffer.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ up->port = 2;
+ else
+ up->port = 1;
+ if (pp->sloppyclockflag & CLK_FLAG3)
+ up->mongain = MONGAIN;
+ else
+ up->mongain = 0;
+}
+
+
+/*
+ * chu_rf - filter and demodulate the FSK signal
+ *
+ * This routine implements a 300-baud Bell 103 modem with mark 2225 Hz
+ * and space 2025 Hz. It uses a bandpass filter followed by a soft
+ * limiter, FM discriminator and lowpass filter. A maximum-likelihood
+ * decoder samples the baseband signal at eight times the baud rate and
+ * detects the start bit of each character.
+ *
+ * The filters are built for speed, which explains the rather clumsy
+ * code. Hopefully, the compiler will efficiently implement the move-
+ * and-muiltiply-and-add operations.
+ */
+static void
+chu_rf(
+ struct peer *peer, /* peer structure pointer */
+ double sample /* analog sample */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+ struct surv *sp;
+
+ /*
+ * Local variables
+ */
+ double signal; /* bandpass signal */
+ double limit; /* limiter signal */
+ double disc; /* discriminator signal */
+ double lpf; /* lowpass signal */
+ double dist; /* UART signal distance */
+ int i, j;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Bandpass filter. 4th-order elliptic, 500-Hz bandpass centered
+ * at 2125 Hz. Passband ripple 0.3 dB, stopband ripple 50 dB,
+ * phase delay 0.24 ms.
+ */
+ signal = (up->bpf[8] = up->bpf[7]) * 5.844676e-01;
+ signal += (up->bpf[7] = up->bpf[6]) * 4.884860e-01;
+ signal += (up->bpf[6] = up->bpf[5]) * 2.704384e+00;
+ signal += (up->bpf[5] = up->bpf[4]) * 1.645032e+00;
+ signal += (up->bpf[4] = up->bpf[3]) * 4.644557e+00;
+ signal += (up->bpf[3] = up->bpf[2]) * 1.879165e+00;
+ signal += (up->bpf[2] = up->bpf[1]) * 3.522634e+00;
+ signal += (up->bpf[1] = up->bpf[0]) * 7.315738e-01;
+ up->bpf[0] = sample - signal;
+ signal = up->bpf[0] * 6.176213e-03
+ + up->bpf[1] * 3.156599e-03
+ + up->bpf[2] * 7.567487e-03
+ + up->bpf[3] * 4.344580e-03
+ + up->bpf[4] * 1.190128e-02
+ + up->bpf[5] * 4.344580e-03
+ + up->bpf[6] * 7.567487e-03
+ + up->bpf[7] * 3.156599e-03
+ + up->bpf[8] * 6.176213e-03;
+
+ up->monitor = signal / 4.; /* note monitor after filter */
+
+ /*
+ * Soft limiter/discriminator. The 11-sample discriminator lag
+ * interval corresponds to three cycles of 2125 Hz, which
+ * requires the sample frequency to be 2125 * 11 / 3 = 7791.7
+ * Hz. The discriminator output varies +-0.5 interval for input
+ * frequency 2025-2225 Hz. However, we don't get to sample at
+ * this frequency, so the discriminator output is biased. Life
+ * at 8000 Hz sucks.
+ */
+ limit = signal;
+ if (limit > LIMIT)
+ limit = LIMIT;
+ else if (limit < -LIMIT)
+ limit = -LIMIT;
+ disc = up->disc[up->discptr] * -limit;
+ up->disc[up->discptr] = limit;
+ up->discptr = (up->discptr + 1 ) % LAG;
+ if (disc >= 0)
+ disc = SQRT(disc);
+ else
+ disc = -SQRT(-disc);
+
+ /*
+ * Lowpass filter. Raised cosine FIR, Ts = 1 / 300, beta = 0.1.
+ */
+ lpf = (up->lpf[26] = up->lpf[25]) * 2.538771e-02;
+ lpf += (up->lpf[25] = up->lpf[24]) * 1.084671e-01;
+ lpf += (up->lpf[24] = up->lpf[23]) * 2.003159e-01;
+ lpf += (up->lpf[23] = up->lpf[22]) * 2.985303e-01;
+ lpf += (up->lpf[22] = up->lpf[21]) * 4.003697e-01;
+ lpf += (up->lpf[21] = up->lpf[20]) * 5.028552e-01;
+ lpf += (up->lpf[20] = up->lpf[19]) * 6.028795e-01;
+ lpf += (up->lpf[19] = up->lpf[18]) * 6.973249e-01;
+ lpf += (up->lpf[18] = up->lpf[17]) * 7.831828e-01;
+ lpf += (up->lpf[17] = up->lpf[16]) * 8.576717e-01;
+ lpf += (up->lpf[16] = up->lpf[15]) * 9.183463e-01;
+ lpf += (up->lpf[15] = up->lpf[14]) * 9.631951e-01;
+ lpf += (up->lpf[14] = up->lpf[13]) * 9.907208e-01;
+ lpf += (up->lpf[13] = up->lpf[12]) * 1.000000e+00;
+ lpf += (up->lpf[12] = up->lpf[11]) * 9.907208e-01;
+ lpf += (up->lpf[11] = up->lpf[10]) * 9.631951e-01;
+ lpf += (up->lpf[10] = up->lpf[9]) * 9.183463e-01;
+ lpf += (up->lpf[9] = up->lpf[8]) * 8.576717e-01;
+ lpf += (up->lpf[8] = up->lpf[7]) * 7.831828e-01;
+ lpf += (up->lpf[7] = up->lpf[6]) * 6.973249e-01;
+ lpf += (up->lpf[6] = up->lpf[5]) * 6.028795e-01;
+ lpf += (up->lpf[5] = up->lpf[4]) * 5.028552e-01;
+ lpf += (up->lpf[4] = up->lpf[3]) * 4.003697e-01;
+ lpf += (up->lpf[3] = up->lpf[2]) * 2.985303e-01;
+ lpf += (up->lpf[2] = up->lpf[1]) * 2.003159e-01;
+ lpf += (up->lpf[1] = up->lpf[0]) * 1.084671e-01;
+ lpf += up->lpf[0] = disc * 2.538771e-02;
+
+ /*
+ * Maximum-likelihood decoder. The UART updates each of the
+ * eight survivors and determines the span, slice level and
+ * tentative decoded character. Valid 11-bit characters are
+ * framed so that bit 10 and bit 11 (stop bits) are mark and bit
+ * 1 (start bit) is space. When a valid character is found, the
+ * survivor with maximum distance determines the final decoded
+ * character.
+ */
+ up->baud += 1. / SECOND;
+ if (up->baud > 1. / (BAUD * 8.)) {
+ up->baud -= 1. / (BAUD * 8.);
+ up->decptr = (up->decptr + 1) % 8;
+ sp = &up->surv[up->decptr];
+ sp->cstamp = up->timestamp;
+ chu_uart(sp, -lpf * AGAIN);
+ if (up->dbrk > 0) {
+ up->dbrk--;
+ if (up->dbrk > 0)
+ return;
+
+ up->decpha = up->decptr;
+ }
+ if (up->decptr != up->decpha)
+ return;
+
+ dist = 0;
+ j = -1;
+ for (i = 0; i < 8; i++) {
+
+ /*
+ * The timestamp is taken at the last bit, so
+ * for correct decoding we reqire sufficient
+ * span and correct start bit and two stop bits.
+ */
+ if ((up->surv[i].uart & 0x601) != 0x600 ||
+ up->surv[i].span < SPAN)
+ continue;
+
+ if (up->surv[i].dist > dist) {
+ dist = up->surv[i].dist;
+ j = i;
+ }
+ }
+ if (j < 0)
+ return;
+
+ /*
+ * Process the character, then blank the decoder until
+ * the end of the next character.This sets the decoding
+ * phase of the entire burst from the phase of the first
+ * character.
+ */
+ up->maxsignal = up->surv[j].span;
+ chu_decode(peer, (up->surv[j].uart >> 1) & 0xff,
+ up->surv[j].cstamp);
+ up->dbrk = 88;
+ }
+}
+
+
+/*
+ * chu_uart - maximum-likelihood UART
+ *
+ * This routine updates a shift register holding the last 11 envelope
+ * samples. It then computes the slice level and span over these samples
+ * and determines the tentative data bits and distance. The calling
+ * program selects over the last eight survivors the one with maximum
+ * distance to determine the decoded character.
+ */
+static void
+chu_uart(
+ struct surv *sp, /* survivor structure pointer */
+ double sample /* baseband signal */
+ )
+{
+ double es_max, es_min; /* max/min envelope */
+ double slice; /* slice level */
+ double dist; /* distance */
+ double dtemp;
+ int i;
+
+ /*
+ * Save the sample and shift right. At the same time, measure
+ * the maximum and minimum over all eleven samples.
+ */
+ es_max = -1e6;
+ es_min = 1e6;
+ sp->shift[0] = sample;
+ for (i = 11; i > 0; i--) {
+ sp->shift[i] = sp->shift[i - 1];
+ if (sp->shift[i] > es_max)
+ es_max = sp->shift[i];
+ if (sp->shift[i] < es_min)
+ es_min = sp->shift[i];
+ }
+
+ /*
+ * Determine the span as the maximum less the minimum and the
+ * slice level as the minimum plus a fraction of the span. Note
+ * the slight bias toward mark to correct for the modem tendency
+ * to make more mark than space errors. Compute the distance on
+ * the assumption the last two bits must be mark, the first
+ * space and the rest either mark or space.
+ */
+ sp->span = es_max - es_min;
+ slice = es_min + .45 * sp->span;
+ dist = 0;
+ sp->uart = 0;
+ for (i = 1; i < 12; i++) {
+ sp->uart <<= 1;
+ dtemp = sp->shift[i];
+ if (dtemp > slice)
+ sp->uart |= 0x1;
+ if (i == 1 || i == 2) {
+ dist += dtemp - es_min;
+ } else if (i == 11) {
+ dist += es_max - dtemp;
+ } else {
+ if (dtemp > slice)
+ dist += dtemp - es_min;
+ else
+ dist += es_max - dtemp;
+ }
+ }
+ sp->dist = dist / (11 * sp->span);
+}
+#endif /* HAVE_AUDIO */
+
+
+/*
+ * chu_serial_receive - receive data from the serial device
+ */
+static void
+chu_serial_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ u_char *dpt; /* receive buffer pointer */
+
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ dpt = (u_char *)&rbufp->recv_space;
+ chu_decode(peer, *dpt, rbufp->recv_time);
+}
+
+
+/*
+ * chu_decode - decode the character data
+ */
+static void
+chu_decode(
+ struct peer *peer, /* peer structure pointer */
+ int hexhex, /* data character */
+ l_fp cstamp /* data character timestamp */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ l_fp tstmp; /* timestamp temp */
+ double dtemp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * If the interval since the last character is greater than the
+ * longest burst, process the last burst and start a new one. If
+ * the interval is less than this but greater than two
+ * characters, consider this a noise burst and reject it.
+ */
+ tstmp = up->timestamp;
+ if (L_ISZERO(&up->laststamp))
+ up->laststamp = up->timestamp;
+ L_SUB(&tstmp, &up->laststamp);
+ up->laststamp = up->timestamp;
+ LFPTOD(&tstmp, dtemp);
+ if (dtemp > BURST * CHAR) {
+ chu_burst(peer);
+ up->ndx = 0;
+ } else if (dtemp > 2.5 * CHAR) {
+ up->ndx = 0;
+ }
+
+ /*
+ * Append the character to the current burst and append the
+ * character timestamp to the timestamp list.
+ */
+ if (up->ndx < BURST) {
+ up->cbuf[up->ndx] = hexhex & 0xff;
+ up->cstamp[up->ndx] = cstamp;
+ up->ndx++;
+
+ }
+}
+
+
+/*
+ * chu_burst - search for valid burst format
+ */
+static void
+chu_burst(
+ struct peer *peer
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+
+ int i;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Correlate a block of five characters with the next block of
+ * five characters. The burst distance is defined as the number
+ * of bits that match in the two blocks for format A and that
+ * match the inverse for format B.
+ */
+ if (up->ndx < MINCHARS) {
+ up->status |= RUNT;
+ return;
+ }
+ up->burdist = 0;
+ for (i = 0; i < 5 && i < up->ndx - 5; i++)
+ up->burdist += chu_dist(up->cbuf[i], up->cbuf[i + 5]);
+
+ /*
+ * If the burst distance is at least MINDIST, this must be a
+ * format A burst; if the value is not greater than -MINDIST, it
+ * must be a format B burst. If the B burst is perfect, we
+ * believe it; otherwise, it is a noise burst and of no use to
+ * anybody.
+ */
+ if (up->burdist >= MINDIST) {
+ chu_a(peer, up->ndx);
+ } else if (up->burdist <= -MINDIST) {
+ chu_b(peer, up->ndx);
+ } else {
+ up->status |= NOISE;
+ return;
+ }
+
+ /*
+ * If this is a valid burst, wait a guard time of ten seconds to
+ * allow for more bursts, then arm the poll update routine to
+ * process the minute. Don't do this if this is called from the
+ * timer interrupt routine.
+ */
+ if (peer->outdate != current_time)
+ peer->nextdate = current_time + 10;
+}
+
+
+/*
+ * chu_b - decode format B burst
+ */
+static void
+chu_b(
+ struct peer *peer,
+ int nchar
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ u_char code[11]; /* decoded timecode */
+ char tbuf[80]; /* trace buffer */
+ char * p;
+ size_t chars;
+ size_t cb;
+ int i;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * In a format B burst, a character is considered valid only if
+ * the first occurence matches the last occurence. The burst is
+ * considered valid only if all characters are valid; that is,
+ * only if the distance is 40. Note that once a valid frame has
+ * been found errors are ignored.
+ */
+ snprintf(tbuf, sizeof(tbuf), "chuB %04x %4.0f %2d %2d ",
+ up->status, up->maxsignal, nchar, -up->burdist);
+ cb = sizeof(tbuf);
+ p = tbuf;
+ for (i = 0; i < nchar; i++) {
+ chars = strlen(p);
+ if (cb < chars + 1) {
+ msyslog(LOG_ERR, "chu_b() fatal out buffer");
+ exit(1);
+ }
+ cb -= chars;
+ p += chars;
+ snprintf(p, cb, "%02x", up->cbuf[i]);
+ }
+ if (pp->sloppyclockflag & CLK_FLAG4)
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif
+ if (up->burdist > -40) {
+ up->status |= BFRAME;
+ return;
+ }
+
+ /*
+ * Convert the burst data to internal format. Don't bother with
+ * the timestamps.
+ */
+ for (i = 0; i < 5; i++) {
+ code[2 * i] = hexchar[up->cbuf[i] & 0xf];
+ code[2 * i + 1] = hexchar[(up->cbuf[i] >>
+ 4) & 0xf];
+ }
+ if (sscanf((char *)code, "%1x%1d%4d%2d%2x", &up->leap, &up->dut,
+ &pp->year, &up->tai, &up->dst) != 5) {
+ up->status |= BFORMAT;
+ return;
+ }
+ up->status |= BVALID;
+ if (up->leap & 0x8)
+ up->dut = -up->dut;
+}
+
+
+/*
+ * chu_a - decode format A burst
+ */
+static void
+chu_a(
+ struct peer *peer,
+ int nchar
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ char tbuf[80]; /* trace buffer */
+ char * p;
+ size_t chars;
+ size_t cb;
+ l_fp offset; /* timestamp offset */
+ int val; /* distance */
+ int temp;
+ int i, j, k;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Determine correct burst phase. There are three cases
+ * corresponding to in-phase, one character early or one
+ * character late. These cases are distinguished by the position
+ * of the framing digits 0x6 at positions 0 and 5 and 0x3 at
+ * positions 4 and 9. The correct phase is when the distance
+ * relative to the framing digits is maximum. The burst is valid
+ * only if the maximum distance is at least MINSYNC.
+ */
+ up->syndist = k = 0;
+ val = -16;
+ for (i = -1; i < 2; i++) {
+ temp = up->cbuf[i + 4] & 0xf;
+ if (i >= 0)
+ temp |= (up->cbuf[i] & 0xf) << 4;
+ val = chu_dist(temp, 0x63);
+ temp = (up->cbuf[i + 5] & 0xf) << 4;
+ if (i + 9 < nchar)
+ temp |= up->cbuf[i + 9] & 0xf;
+ val += chu_dist(temp, 0x63);
+ if (val > up->syndist) {
+ up->syndist = val;
+ k = i;
+ }
+ }
+
+ /*
+ * Extract the second number; it must be in the range 2 through
+ * 9 and the two repititions must be the same.
+ */
+ temp = (up->cbuf[k + 4] >> 4) & 0xf;
+ if (temp < 2 || temp > 9 || k + 9 >= nchar || temp !=
+ ((up->cbuf[k + 9] >> 4) & 0xf))
+ temp = 0;
+ snprintf(tbuf, sizeof(tbuf),
+ "chuA %04x %4.0f %2d %2d %2d %2d %1d ", up->status,
+ up->maxsignal, nchar, up->burdist, k, up->syndist,
+ temp);
+ cb = sizeof(tbuf);
+ p = tbuf;
+ for (i = 0; i < nchar; i++) {
+ chars = strlen(p);
+ if (cb < chars + 1) {
+ msyslog(LOG_ERR, "chu_a() fatal out buffer");
+ exit(1);
+ }
+ cb -= chars;
+ p += chars;
+ snprintf(p, cb, "%02x", up->cbuf[i]);
+ }
+ if (pp->sloppyclockflag & CLK_FLAG4)
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif
+ if (up->syndist < MINSYNC) {
+ up->status |= AFRAME;
+ return;
+ }
+
+ /*
+ * A valid burst requires the first seconds number to match the
+ * last seconds number. If so, the burst timestamps are
+ * corrected to the current minute and saved for later
+ * processing. In addition, the seconds decode is advanced from
+ * the previous burst to the current one.
+ */
+ if (temp == 0) {
+ up->status |= AFORMAT;
+ } else {
+ up->status |= AVALID;
+ up->second = pp->second = 30 + temp;
+ offset.l_ui = 30 + temp;
+ offset.l_uf = 0;
+ i = 0;
+ if (k < 0)
+ offset = up->charstamp;
+ else if (k > 0)
+ i = 1;
+ for (; i < nchar && i < k + 10; i++) {
+ up->tstamp[up->ntstamp] = up->cstamp[i];
+ L_SUB(&up->tstamp[up->ntstamp], &offset);
+ L_ADD(&offset, &up->charstamp);
+ if (up->ntstamp < MAXSTAGE - 1)
+ up->ntstamp++;
+ }
+ while (temp > up->prevsec) {
+ for (j = 15; j > 0; j--) {
+ up->decode[9][j] = up->decode[9][j - 1];
+ up->decode[19][j] =
+ up->decode[19][j - 1];
+ }
+ up->decode[9][j] = up->decode[19][j] = 0;
+ up->prevsec++;
+ }
+ }
+
+ /*
+ * Stash the data in the decoding matrix.
+ */
+ i = -(2 * k);
+ for (j = 0; j < nchar; j++) {
+ if (i < 0 || i > 18) {
+ i += 2;
+ continue;
+ }
+ up->decode[i][up->cbuf[j] & 0xf]++;
+ i++;
+ up->decode[i][(up->cbuf[j] >> 4) & 0xf]++;
+ i++;
+ }
+ up->burstcnt++;
+}
+
+
+/*
+ * chu_poll - called by the transmit procedure
+ */
+static void
+chu_poll(
+ int unit,
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ pp->polls++;
+}
+
+
+/*
+ * chu_second - process minute data
+ */
+static void
+chu_second(
+ int unit,
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+ l_fp offset;
+ char synchar, qual, leapchar;
+ int minset, i;
+ double dtemp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * This routine is called once per minute to process the
+ * accumulated burst data. We do a bit of fancy footwork so that
+ * this doesn't run while burst data are being accumulated.
+ */
+ up->second = (up->second + 1) % 60;
+ if (up->second != 0)
+ return;
+
+ /*
+ * Process the last burst, if still in the burst buffer.
+ * If the minute contains a valid B frame with sufficient A
+ * frame metric, it is considered valid. However, the timecode
+ * is sent to clockstats even if invalid.
+ */
+ chu_burst(peer);
+ minset = ((current_time - peer->update) + 30) / 60;
+ dtemp = chu_major(peer);
+ qual = 0;
+ if (up->status & (BFRAME | AFRAME))
+ qual |= SYNERR;
+ if (up->status & (BFORMAT | AFORMAT))
+ qual |= FMTERR;
+ if (up->status & DECODE)
+ qual |= DECERR;
+ if (up->status & STAMP)
+ qual |= TSPERR;
+ if (up->status & BVALID && dtemp >= MINMETRIC)
+ up->status |= INSYNC;
+ synchar = leapchar = ' ';
+ if (!(up->status & INSYNC)) {
+ pp->leap = LEAP_NOTINSYNC;
+ synchar = '?';
+ } else if (up->leap & 0x2) {
+ pp->leap = LEAP_ADDSECOND;
+ leapchar = 'L';
+ } else if (up->leap & 0x4) {
+ pp->leap = LEAP_DELSECOND;
+ leapchar = 'l';
+ } else {
+ pp->leap = LEAP_NOWARNING;
+ }
+ snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
+ "%c%1X %04d %03d %02d:%02d:%02d %c%x %+d %d %d %s %.0f %d",
+ synchar, qual, pp->year, pp->day, pp->hour, pp->minute,
+ pp->second, leapchar, up->dst, up->dut, minset, up->gain,
+ up->ident, dtemp, up->ntstamp);
+ pp->lencode = strlen(pp->a_lastcode);
+
+ /*
+ * If in sync and the signal metric is above threshold, the
+ * timecode is ipso fatso valid and can be selected to
+ * discipline the clock.
+ */
+ if (up->status & INSYNC && !(up->status & (DECODE | STAMP)) &&
+ dtemp > MINMETRIC) {
+ if (!clocktime(pp->day, pp->hour, pp->minute, 0, GMT,
+ up->tstamp[0].l_ui, &pp->yearstart, &offset.l_ui)) {
+ up->errflg = CEVNT_BADTIME;
+ } else {
+ offset.l_uf = 0;
+ for (i = 0; i < up->ntstamp; i++)
+ refclock_process_offset(pp, offset,
+ up->tstamp[i], PDELAY +
+ pp->fudgetime1);
+ pp->lastref = up->timestamp;
+ refclock_receive(peer);
+ }
+ }
+ if (dtemp > 0)
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("chu: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+#ifdef ICOM
+ chu_newchan(peer, dtemp);
+#endif /* ICOM */
+ chu_clear(peer);
+ if (up->errflg)
+ refclock_report(peer, up->errflg);
+ up->errflg = 0;
+}
+
+
+/*
+ * chu_major - majority decoder
+ */
+static double
+chu_major(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ u_char code[11]; /* decoded timecode */
+ int metric; /* distance metric */
+ int val1; /* maximum distance */
+ int synchar; /* stray cat */
+ int temp;
+ int i, j, k;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Majority decoder. Each burst encodes two replications at each
+ * digit position in the timecode. Each row of the decoding
+ * matrix encodes the number of occurences of each digit found
+ * at the corresponding position. The maximum over all
+ * occurrences at each position is the distance for this
+ * position and the corresponding digit is the maximum-
+ * likelihood candidate. If the distance is not more than half
+ * the total number of occurences, a majority has not been found
+ * and the data are discarded. The decoding distance is defined
+ * as the sum of the distances over the first nine digits. The
+ * tenth digit varies over the seconds, so we don't count it.
+ */
+ metric = 0;
+ for (i = 0; i < 9; i++) {
+ val1 = 0;
+ k = 0;
+ for (j = 0; j < 16; j++) {
+ temp = up->decode[i][j] + up->decode[i + 10][j];
+ if (temp > val1) {
+ val1 = temp;
+ k = j;
+ }
+ }
+ if (val1 <= up->burstcnt)
+ up->status |= DECODE;
+ metric += val1;
+ code[i] = hexchar[k];
+ }
+
+ /*
+ * Compute the timecode timestamp from the days, hours and
+ * minutes of the timecode. Use clocktime() for the aggregate
+ * minutes and the minute offset computed from the burst
+ * seconds. Note that this code relies on the filesystem time
+ * for the years and does not use the years of the timecode.
+ */
+ if (sscanf((char *)code, "%1x%3d%2d%2d", &synchar, &pp->day,
+ &pp->hour, &pp->minute) != 4)
+ up->status |= DECODE;
+ if (up->ntstamp < MINSTAMP)
+ up->status |= STAMP;
+ return (metric);
+}
+
+
+/*
+ * chu_clear - clear decoding matrix
+ */
+static void
+chu_clear(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+ int i, j;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Clear stuff for the minute.
+ */
+ up->ndx = up->prevsec = 0;
+ up->burstcnt = up->ntstamp = 0;
+ up->status &= INSYNC | METRIC;
+ for (i = 0; i < 20; i++) {
+ for (j = 0; j < 16; j++)
+ up->decode[i][j] = 0;
+ }
+}
+
+#ifdef ICOM
+/*
+ * chu_newchan - called once per minute to find the best channel;
+ * returns zero on success, nonzero if ICOM error.
+ */
+static int
+chu_newchan(
+ struct peer *peer,
+ double met
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+ struct xmtr *sp;
+ int rval;
+ double metric;
+ int i;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * The radio can be tuned to three channels: 0 (3330 kHz), 1
+ * (7850 kHz) and 2 (14670 kHz). There are five one-minute
+ * dwells in each cycle. During the first dwell the radio is
+ * tuned to one of the three channels to measure the channel
+ * metric. The channel is selected as the one least recently
+ * measured. During the remaining four dwells the radio is tuned
+ * to the channel with the highest channel metric.
+ */
+ if (up->fd_icom <= 0)
+ return (0);
+
+ /*
+ * Update the current channel metric and age of all channels.
+ * Scan all channels for the highest metric.
+ */
+ sp = &up->xmtr[up->chan];
+ sp->metric -= sp->integ[sp->iptr];
+ sp->integ[sp->iptr] = met;
+ sp->metric += sp->integ[sp->iptr];
+ sp->probe = 0;
+ sp->iptr = (sp->iptr + 1) % ISTAGE;
+ metric = 0;
+ for (i = 0; i < NCHAN; i++) {
+ up->xmtr[i].probe++;
+ if (up->xmtr[i].metric > metric) {
+ up->status |= METRIC;
+ metric = up->xmtr[i].metric;
+ up->chan = i;
+ }
+ }
+
+ /*
+ * Start the next dwell. If the first dwell or no stations have
+ * been heard, continue round-robin scan.
+ */
+ up->dwell = (up->dwell + 1) % DWELL;
+ if (up->dwell == 0 || metric == 0) {
+ rval = 0;
+ for (i = 0; i < NCHAN; i++) {
+ if (up->xmtr[i].probe > rval) {
+ rval = up->xmtr[i].probe;
+ up->chan = i;
+ }
+ }
+ }
+
+ /* Retune the radio at each dwell in case somebody nudges the
+ * tuning knob.
+ */
+ rval = icom_freq(up->fd_icom, peer->ttl & 0x7f, qsy[up->chan] +
+ TUNE);
+ snprintf(up->ident, sizeof(up->ident), "CHU%d", up->chan);
+ memcpy(&pp->refid, up->ident, 4);
+ memcpy(&peer->refid, up->ident, 4);
+ if (metric == 0 && up->status & METRIC) {
+ up->status &= ~METRIC;
+ refclock_report(peer, CEVNT_PROP);
+ }
+ return (rval);
+}
+#endif /* ICOM */
+
+
+/*
+ * chu_dist - determine the distance of two octet arguments
+ */
+static int
+chu_dist(
+ int x, /* an octet of bits */
+ int y /* another octet of bits */
+ )
+{
+ int val; /* bit count */
+ int temp;
+ int i;
+
+ /*
+ * The distance is determined as the weight of the exclusive OR
+ * of the two arguments. The weight is determined by the number
+ * of one bits in the result. Each one bit increases the weight,
+ * while each zero bit decreases it.
+ */
+ temp = x ^ y;
+ val = 0;
+ for (i = 0; i < 8; i++) {
+ if ((temp & 0x1) == 0)
+ val++;
+ else
+ val--;
+ temp >>= 1;
+ }
+ return (val);
+}
+
+
+#ifdef HAVE_AUDIO
+/*
+ * chu_gain - adjust codec gain
+ *
+ * This routine is called at the end of each second. During the second
+ * the number of signal clips above the MAXAMP threshold (6000). If
+ * there are no clips, the gain is bumped up; if there are more than
+ * MAXCLP clips (100), it is bumped down. The decoder is relatively
+ * insensitive to amplitude, so this crudity works just peachy. The
+ * routine also jiggles the input port and selectively mutes the
+ */
+static void
+chu_gain(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Apparently, the codec uses only the high order bits of the
+ * gain control field. Thus, it may take awhile for changes to
+ * wiggle the hardware bits.
+ */
+ if (up->clipcnt == 0) {
+ up->gain += 4;
+ if (up->gain > MAXGAIN)
+ up->gain = MAXGAIN;
+ } else if (up->clipcnt > MAXCLP) {
+ up->gain -= 4;
+ if (up->gain < 0)
+ up->gain = 0;
+ }
+ audio_gain(up->gain, up->mongain, up->port);
+ up->clipcnt = 0;
+}
+#endif /* HAVE_AUDIO */
+
+
+#else
+NONEMPTY_TRANSLATION_UNIT
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_conf.c b/ntpd/refclock_conf.c
new file mode 100644
index 0000000..30cc632
--- /dev/null
+++ b/ntpd/refclock_conf.c
@@ -0,0 +1,326 @@
+/*
+ * refclock_conf.c - reference clock configuration
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#ifdef REFCLOCK
+
+static struct refclock refclock_none = {
+ noentry, noentry, noentry, noentry, noentry, noentry, NOFLAGS
+};
+
+#ifdef CLOCK_LOCAL
+extern struct refclock refclock_local;
+#else
+#define refclock_local refclock_none
+#endif
+
+#ifdef CLOCK_PST
+extern struct refclock refclock_pst;
+#else
+#define refclock_pst refclock_none
+#endif
+
+#ifdef CLOCK_CHU
+extern struct refclock refclock_chu;
+#else
+#define refclock_chu refclock_none
+#endif
+
+#ifdef CLOCK_WWV
+extern struct refclock refclock_wwv;
+#else
+#define refclock_wwv refclock_none
+#endif
+
+#ifdef CLOCK_SPECTRACOM
+extern struct refclock refclock_wwvb;
+#else
+#define refclock_wwvb refclock_none
+#endif
+
+#ifdef CLOCK_PARSE
+extern struct refclock refclock_parse;
+#else
+#define refclock_parse refclock_none
+#endif
+
+#if defined(CLOCK_MX4200) && defined(HAVE_PPSAPI)
+extern struct refclock refclock_mx4200;
+#else
+#define refclock_mx4200 refclock_none
+#endif
+
+#ifdef CLOCK_AS2201
+extern struct refclock refclock_as2201;
+#else
+#define refclock_as2201 refclock_none
+#endif
+
+#ifdef CLOCK_ARBITER
+extern struct refclock refclock_arbiter;
+#else
+#define refclock_arbiter refclock_none
+#endif
+
+#ifdef CLOCK_TPRO
+extern struct refclock refclock_tpro;
+#else
+#define refclock_tpro refclock_none
+#endif
+
+#ifdef CLOCK_LEITCH
+extern struct refclock refclock_leitch;
+#else
+#define refclock_leitch refclock_none
+#endif
+
+#ifdef CLOCK_IRIG
+extern struct refclock refclock_irig;
+#else
+#define refclock_irig refclock_none
+#endif
+
+#if 0 && defined(CLOCK_MSFEES) && defined(PPS)
+extern struct refclock refclock_msfees;
+#else
+#define refclock_msfees refclock_none
+#endif
+
+#ifdef CLOCK_BANC
+extern struct refclock refclock_bancomm;
+#else
+#define refclock_bancomm refclock_none
+#endif
+
+#ifdef CLOCK_TRUETIME
+extern struct refclock refclock_true;
+#else
+#define refclock_true refclock_none
+#endif
+
+#ifdef CLOCK_DATUM
+extern struct refclock refclock_datum;
+#else
+#define refclock_datum refclock_none
+#endif
+
+#ifdef CLOCK_ACTS
+extern struct refclock refclock_acts;
+#else
+#define refclock_acts refclock_none
+#endif
+
+#ifdef CLOCK_HEATH
+extern struct refclock refclock_heath;
+#else
+#define refclock_heath refclock_none
+#endif
+
+#ifdef CLOCK_NMEA
+extern struct refclock refclock_nmea;
+#else
+#define refclock_nmea refclock_none
+#endif
+
+#if defined (CLOCK_ATOM) && defined(HAVE_PPSAPI)
+extern struct refclock refclock_atom;
+#else
+#define refclock_atom refclock_none
+#endif
+
+#ifdef CLOCK_HPGPS
+extern struct refclock refclock_hpgps;
+#else
+#define refclock_hpgps refclock_none
+#endif
+
+#ifdef CLOCK_GPSVME
+extern struct refclock refclock_gpsvme;
+#else
+#define refclock_gpsvme refclock_none
+#endif
+
+#ifdef CLOCK_ARCRON_MSF
+extern struct refclock refclock_arc;
+#else
+#define refclock_arc refclock_none
+#endif
+
+#ifdef CLOCK_SHM
+extern struct refclock refclock_shm;
+#else
+#define refclock_shm refclock_none
+#endif
+
+#ifdef CLOCK_PALISADE
+extern struct refclock refclock_palisade;
+#else
+#define refclock_palisade refclock_none
+#endif
+
+#if defined(CLOCK_ONCORE)
+extern struct refclock refclock_oncore;
+#else
+#define refclock_oncore refclock_none
+#endif
+
+#if defined(CLOCK_JUPITER) && defined(HAVE_PPSAPI)
+extern struct refclock refclock_jupiter;
+#else
+#define refclock_jupiter refclock_none
+#endif
+
+#if defined(CLOCK_CHRONOLOG)
+extern struct refclock refclock_chronolog;
+#else
+#define refclock_chronolog refclock_none
+#endif
+
+#if defined(CLOCK_DUMBCLOCK)
+extern struct refclock refclock_dumbclock;
+#else
+#define refclock_dumbclock refclock_none
+#endif
+
+#ifdef CLOCK_ULINK
+extern struct refclock refclock_ulink;
+#else
+#define refclock_ulink refclock_none
+#endif
+
+#ifdef CLOCK_PCF
+extern struct refclock refclock_pcf;
+#else
+#define refclock_pcf refclock_none
+#endif
+
+#ifdef CLOCK_FG
+extern struct refclock refclock_fg;
+#else
+#define refclock_fg refclock_none
+#endif
+
+#ifdef CLOCK_HOPF_SERIAL
+extern struct refclock refclock_hopfser;
+#else
+#define refclock_hopfser refclock_none
+#endif
+
+#ifdef CLOCK_HOPF_PCI
+extern struct refclock refclock_hopfpci;
+#else
+#define refclock_hopfpci refclock_none
+#endif
+
+#ifdef CLOCK_JJY
+extern struct refclock refclock_jjy;
+#else
+#define refclock_jjy refclock_none
+#endif
+
+#ifdef CLOCK_TT560
+extern struct refclock refclock_tt560;
+#else
+#define refclock_tt560 refclock_none
+#endif
+
+#ifdef CLOCK_ZYFER
+extern struct refclock refclock_zyfer;
+#else
+#define refclock_zyfer refclock_none
+#endif
+
+#ifdef CLOCK_RIPENCC
+extern struct refclock refclock_ripencc;
+#else
+#define refclock_ripencc refclock_none
+#endif
+
+#ifdef CLOCK_NEOCLOCK4X
+extern struct refclock refclock_neoclock4x;
+#else
+#define refclock_neoclock4x refclock_none
+#endif
+
+#ifdef CLOCK_TSYNCPCI
+extern struct refclock refclock_tsyncpci;
+#else
+#define refclock_tsyncpci refclock_none
+#endif
+
+#if defined(CLOCK_GPSDJSON) && !defined(SYS_WINNT)
+extern struct refclock refclock_gpsdjson;
+#else
+#define refclock_gpsdjson refclock_none
+#endif
+/*
+ * Order is clock_start(), clock_shutdown(), clock_poll(),
+ * clock_control(), clock_init(), clock_buginfo, clock_flags;
+ *
+ * Types are defined in ntp.h. The index must match this.
+ */
+struct refclock * const refclock_conf[] = {
+ &refclock_none, /* 0 REFCLK_NONE */
+ &refclock_local, /* 1 REFCLK_LOCAL */
+ &refclock_none, /* 2 deprecated: REFCLK_GPS_TRAK */
+ &refclock_pst, /* 3 REFCLK_WWV_PST */
+ &refclock_wwvb, /* 4 REFCLK_SPECTRACOM */
+ &refclock_true, /* 5 REFCLK_TRUETIME */
+ &refclock_irig, /* 6 REFCLK_IRIG_AUDIO */
+ &refclock_chu, /* 7 REFCLK_CHU_AUDIO */
+ &refclock_parse, /* 8 REFCLK_PARSE */
+ &refclock_mx4200, /* 9 REFCLK_GPS_MX4200 */
+ &refclock_as2201, /* 10 REFCLK_GPS_AS2201 */
+ &refclock_arbiter, /* 11 REFCLK_GPS_ARBITER */
+ &refclock_tpro, /* 12 REFCLK_IRIG_TPRO */
+ &refclock_leitch, /* 13 REFCLK_ATOM_LEITCH */
+ &refclock_none, /* 14 deprecated: REFCLK_MSF_EES */
+ &refclock_none, /* 15 not used */
+ &refclock_bancomm, /* 16 REFCLK_IRIG_BANCOMM */
+ &refclock_datum, /* 17 REFCLK_GPS_DATUM */
+ &refclock_acts, /* 18 REFCLK_ACTS */
+ &refclock_heath, /* 19 REFCLK_WWV_HEATH */
+ &refclock_nmea, /* 20 REFCLK_GPS_NMEA */
+ &refclock_gpsvme, /* 21 REFCLK_GPS_VME */
+ &refclock_atom, /* 22 REFCLK_ATOM_PPS */
+ &refclock_none, /* 23 not used */
+ &refclock_none, /* 24 not used */
+ &refclock_none, /* 25 not used */
+ &refclock_hpgps, /* 26 REFCLK_GPS_HP */
+ &refclock_arc, /* 27 REFCLK_ARCRON_MSF */
+ &refclock_shm, /* 28 REFCLK_SHM */
+ &refclock_palisade, /* 29 REFCLK_PALISADE */
+ &refclock_oncore, /* 30 REFCLK_ONCORE */
+ &refclock_jupiter, /* 31 REFCLK_GPS_JUPITER */
+ &refclock_chronolog, /* 32 REFCLK_CHRONOLOG */
+ &refclock_dumbclock, /* 33 REFCLK_DUMBCLOCK */
+ &refclock_ulink, /* 34 REFCLOCK_ULINK */
+ &refclock_pcf, /* 35 REFCLOCK_PCF */
+ &refclock_wwv, /* 36 REFCLOCK_WWV_AUDIO */
+ &refclock_fg, /* 37 REFCLOCK_FG */
+ &refclock_hopfser, /* 38 REFCLK_HOPF_SERIAL */
+ &refclock_hopfpci, /* 39 REFCLK_HOPF_PCI */
+ &refclock_jjy, /* 40 REFCLK_JJY */
+ &refclock_tt560, /* 41 REFCLK_TT560 */
+ &refclock_zyfer, /* 42 REFCLK_ZYFER */
+ &refclock_ripencc, /* 43 REFCLK_RIPENCC */
+ &refclock_neoclock4x, /* 44 REFCLK_NEOCLOCK4X */
+ &refclock_tsyncpci, /* 45 REFCLK_TSYNCPCI */
+ &refclock_gpsdjson /* 46 REFCLK_GPSDJSON */
+};
+
+u_char num_refclock_conf = sizeof(refclock_conf)/sizeof(struct refclock *);
+
+#else
+int refclock_conf_bs;
+#endif
diff --git a/ntpd/refclock_datum.c b/ntpd/refclock_datum.c
new file mode 100644
index 0000000..95f13a8
--- /dev/null
+++ b/ntpd/refclock_datum.c
@@ -0,0 +1,782 @@
+/*
+** refclock_datum - clock driver for the Datum Programmable Time Server
+**
+** Important note: This driver assumes that you have termios. If you have
+** a system that does not have termios, you will have to modify this driver.
+**
+** Sorry, I have only tested this driver on SUN and HP platforms.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntp_types.h"
+
+#if defined(REFCLOCK) && defined(CLOCK_DATUM)
+
+/*
+** Include Files
+*/
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_tty.h"
+#include "ntp_refclock.h"
+#include "timevalops.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#if defined(STREAM)
+#include <stropts.h>
+#endif /* STREAM */
+
+#include "ntp_stdlib.h"
+
+/*
+** This driver supports the Datum Programmable Time System (PTS) clock.
+** The clock works in very straight forward manner. When it receives a
+** time code request (e.g., the ascii string "//k/mn"), it responds with
+** a seven byte BCD time code. This clock only responds with a
+** time code after it first receives the "//k/mn" message. It does not
+** periodically send time codes back at some rate once it is started.
+** the returned time code can be broken down into the following fields.
+**
+** _______________________________
+** Bit Index | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+** ===============================
+** byte 0: | - - - - | H D |
+** ===============================
+** byte 1: | T D | U D |
+** ===============================
+** byte 2: | - - | T H | U H |
+** ===============================
+** byte 3: | - | T M | U M |
+** ===============================
+** byte 4: | - | T S | U S |
+** ===============================
+** byte 5: | t S | h S |
+** ===============================
+** byte 6: | m S | - - - - |
+** ===============================
+**
+** In the table above:
+**
+** "-" means don't care
+** "H D", "T D", and "U D" means Hundreds, Tens, and Units of Days
+** "T H", and "UH" means Tens and Units of Hours
+** "T M", and "U M" means Tens and Units of Minutes
+** "T S", and "U S" means Tens and Units of Seconds
+** "t S", "h S", and "m S" means tenths, hundredths, and thousandths
+** of seconds
+**
+** The Datum PTS communicates throught the RS232 port on your machine.
+** Right now, it assumes that you have termios. This driver has been tested
+** on SUN and HP workstations. The Datum PTS supports various IRIG and
+** NASA input codes. This driver assumes that the name of the device is
+** /dev/datum. You will need to make a soft link to your RS232 device or
+** create a new driver to use this refclock.
+*/
+
+/*
+** Datum PTS defines
+*/
+
+/*
+** Note that if GMT is defined, then the Datum PTS must use Greenwich
+** time. Otherwise, this driver allows the Datum PTS to use the current
+** wall clock for its time. It determines the time zone offset by minimizing
+** the error after trying several time zone offsets. If the Datum PTS
+** time is Greenwich time and GMT is not defined, everything should still
+** work since the time zone will be found to be 0. What this really means
+** is that your system time (at least to start with) must be within the
+** correct time by less than +- 30 minutes. The default is for GMT to not
+** defined. If you really want to force GMT without the funny +- 30 minute
+** stuff then you must define (uncomment) GMT below.
+*/
+
+/*
+#define GMT
+#define DEBUG_DATUM_PTC
+#define LOG_TIME_ERRORS
+*/
+
+
+#define PRECISION (-10) /* precision assumed 1/1024 ms */
+#define REFID "DATM" /* reference id */
+#define DATUM_DISPERSION 0 /* fixed dispersion = 0 ms */
+#define DATUM_MAX_ERROR 0.100 /* limits on sigma squared */
+#define DATUM_DEV "/dev/datum" /* device name */
+
+#define DATUM_MAX_ERROR2 (DATUM_MAX_ERROR*DATUM_MAX_ERROR)
+
+/*
+** The Datum PTS structure
+*/
+
+/*
+** I don't use a fixed array of MAXUNITS like everyone else just because
+** I don't like to program that way. Sorry if this bothers anyone. I assume
+** that you can use any id for your unit and I will search for it in a
+** dynamic array of units until I find it. I was worried that users might
+** enter a bad id in their configuration file (larger than MAXUNITS) and
+** besides, it is just cleaner not to have to assume that you have a fixed
+** number of anything in a program.
+*/
+
+struct datum_pts_unit {
+ struct peer *peer; /* peer used by ntp */
+ int PTS_fd; /* file descriptor for PTS */
+ u_int unit; /* id for unit */
+ u_long timestarted; /* time started */
+ l_fp lastrec; /* time tag for the receive time (system) */
+ l_fp lastref; /* reference time (Datum time) */
+ u_long yearstart; /* the year that this clock started */
+ int coderecv; /* number of time codes received */
+ int day; /* day */
+ int hour; /* hour */
+ int minute; /* minutes */
+ int second; /* seconds */
+ int msec; /* miliseconds */
+ int usec; /* miliseconds */
+ u_char leap; /* funny leap character code */
+ char retbuf[8]; /* returned time from the datum pts */
+ char nbytes; /* number of bytes received from datum pts */
+ double sigma2; /* average squared error (roughly) */
+ int tzoff; /* time zone offest from GMT */
+};
+
+/*
+** PTS static constant variables for internal use
+*/
+
+static char TIME_REQUEST[6]; /* request message sent to datum for time */
+static int nunits; /* number of active units */
+
+/*
+** Callback function prototypes that ntpd needs to know about.
+*/
+
+static int datum_pts_start (int, struct peer *);
+static void datum_pts_shutdown (int, struct peer *);
+static void datum_pts_poll (int, struct peer *);
+static void datum_pts_control (int, const struct refclockstat *,
+ struct refclockstat *, struct peer *);
+static void datum_pts_init (void);
+static void datum_pts_buginfo (int, struct refclockbug *, struct peer *);
+
+/*
+** This is the call back function structure that ntpd actually uses for
+** this refclock.
+*/
+
+struct refclock refclock_datum = {
+ datum_pts_start, /* start up a new Datum refclock */
+ datum_pts_shutdown, /* shutdown a Datum refclock */
+ datum_pts_poll, /* sends out the time request */
+ datum_pts_control, /* not used */
+ datum_pts_init, /* initialization (called first) */
+ datum_pts_buginfo, /* not used */
+ NOFLAGS /* we are not setting any special flags */
+};
+
+/*
+** The datum_pts_receive callback function is handled differently from the
+** rest. It is passed to the ntpd io data structure. Basically, every
+** 64 seconds, the datum_pts_poll() routine is called. It sends out the time
+** request message to the Datum Programmable Time System. Then, ntpd
+** waits on a select() call to receive data back. The datum_pts_receive()
+** function is called as data comes back. We expect a seven byte time
+** code to be returned but the datum_pts_receive() function may only get
+** a few bytes passed to it at a time. In other words, this routine may
+** get called by the io stuff in ntpd a few times before we get all seven
+** bytes. Once the last byte is received, we process it and then pass the
+** new time measurement to ntpd for updating the system time. For now,
+** there is no 3 state filtering done on the time measurements. The
+** jitter may be a little high but at least for its current use, it is not
+** a problem. We have tried to keep things as simple as possible. This
+** clock should not jitter more than 1 or 2 mseconds at the most once
+** things settle down. It is important to get the right drift calibrated
+** in the ntpd.drift file as well as getting the right tick set up right
+** using tickadj for SUNs. Tickadj is not used for the HP but you need to
+** remember to bring up the adjtime daemon because HP does not support
+** the adjtime() call.
+*/
+
+static void datum_pts_receive (struct recvbuf *);
+
+/*......................................................................*/
+/* datum_pts_start - start up the datum PTS. This means open the */
+/* RS232 device and set up the data structure for my unit. */
+/*......................................................................*/
+
+static int
+datum_pts_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct datum_pts_unit *datum_pts;
+ int fd;
+#ifdef HAVE_TERMIOS
+ int rc;
+ struct termios arg;
+#endif
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Starting Datum PTS unit %d\n", unit);
+#endif
+
+ /*
+ ** Open the Datum PTS device
+ */
+ fd = open(DATUM_DEV, O_RDWR);
+
+ if (fd < 0) {
+ msyslog(LOG_ERR, "Datum_PTS: open(\"%s\", O_RDWR) failed: %m", DATUM_DEV);
+ return 0;
+ }
+
+ /*
+ ** Create the memory for the new unit
+ */
+ datum_pts = emalloc_zero(sizeof(*datum_pts));
+ datum_pts->unit = unit; /* set my unit id */
+ datum_pts->yearstart = 0; /* initialize the yearstart to 0 */
+ datum_pts->sigma2 = 0.0; /* initialize the sigma2 to 0 */
+
+ datum_pts->PTS_fd = fd;
+
+ if (-1 == fcntl(datum_pts->PTS_fd, F_SETFL, 0)) /* clear the descriptor flags */
+ msyslog(LOG_ERR, "MSF_ARCRON(%d): fcntl(F_SETFL, 0): %m.",
+ unit);
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Opening RS232 port with file descriptor %d\n",
+ datum_pts->PTS_fd);
+#endif
+
+ /*
+ ** Set up the RS232 terminal device information. Note that we assume that
+ ** we have termios. This code has only been tested on SUNs and HPs. If your
+ ** machine does not have termios this driver cannot be initialized. You can change this
+ ** if you want by editing this source. Please give the changes back to the
+ ** ntp folks so that it can become part of their regular distribution.
+ */
+
+ memset(&arg, 0, sizeof(arg));
+
+ arg.c_iflag = IGNBRK;
+ arg.c_oflag = 0;
+ arg.c_cflag = B9600 | CS8 | CREAD | PARENB | CLOCAL;
+ arg.c_lflag = 0;
+ arg.c_cc[VMIN] = 0; /* start timeout timer right away (not used) */
+ arg.c_cc[VTIME] = 30; /* 3 second timout on reads (not used) */
+
+ rc = tcsetattr(datum_pts->PTS_fd, TCSANOW, &arg);
+ if (rc < 0) {
+ msyslog(LOG_ERR, "Datum_PTS: tcsetattr(\"%s\") failed: %m", DATUM_DEV);
+ close(datum_pts->PTS_fd);
+ free(datum_pts);
+ return 0;
+ }
+
+ /*
+ ** Initialize the ntpd IO structure
+ */
+
+ datum_pts->peer = peer;
+ pp = peer->procptr;
+ pp->io.clock_recv = datum_pts_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = datum_pts->PTS_fd;
+
+ if (!io_addclock(&pp->io)) {
+ pp->io.fd = -1;
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Problem adding clock\n");
+#endif
+
+ msyslog(LOG_ERR, "Datum_PTS: Problem adding clock");
+ close(datum_pts->PTS_fd);
+ free(datum_pts);
+
+ return 0;
+ }
+ peer->procptr->unitptr = datum_pts;
+
+ /*
+ ** Now add one to the number of units and return a successful code
+ */
+
+ nunits++;
+ return 1;
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_shutdown - this routine shuts doen the device and */
+/* removes the memory for the unit. */
+/*......................................................................*/
+
+static void
+datum_pts_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct datum_pts_unit *datum_pts;
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Shutdown Datum PTS\n");
+#endif
+
+ msyslog(LOG_ERR, "Datum_PTS: Shutdown Datum PTS");
+
+ /*
+ ** We found the unit so close the file descriptor and free up the memory used
+ ** by the structure.
+ */
+ pp = peer->procptr;
+ datum_pts = pp->unitptr;
+ if (NULL != datum_pts) {
+ io_closeclock(&pp->io);
+ free(datum_pts);
+ }
+}
+
+
+/*......................................................................*/
+/* datum_pts_poll - this routine sends out the time request to the */
+/* Datum PTS device. The time will be passed back in the */
+/* datum_pts_receive() routine. */
+/*......................................................................*/
+
+static void
+datum_pts_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ int error_code;
+ struct datum_pts_unit *datum_pts;
+
+ datum_pts = peer->procptr->unitptr;
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Poll Datum PTS\n");
+#endif
+
+ /*
+ ** Find the right unit and send out a time request once it is found.
+ */
+ error_code = write(datum_pts->PTS_fd, TIME_REQUEST, 6);
+ if (error_code != 6)
+ perror("TIME_REQUEST");
+ datum_pts->nbytes = 0;
+}
+
+
+/*......................................................................*/
+/* datum_pts_control - not used */
+/*......................................................................*/
+
+static void
+datum_pts_control(
+ int unit,
+ const struct refclockstat *in,
+ struct refclockstat *out,
+ struct peer *peer
+ )
+{
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Control Datum PTS\n");
+#endif
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_init - initializes things for all possible Datum */
+/* time code generators that might be used. In practice, this is */
+/* only called once at the beginning before anything else is */
+/* called. */
+/*......................................................................*/
+
+static void
+datum_pts_init(void)
+{
+
+ /* */
+ /*...... open up the log file if we are debugging ......................*/
+ /* */
+
+ /*
+ ** Open up the log file if we are debugging. For now, send data out to the
+ ** screen (stdout).
+ */
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Init Datum PTS\n");
+#endif
+
+ /*
+ ** Initialize the time request command string. This is the only message
+ ** that we ever have to send to the Datum PTS (although others are defined).
+ */
+
+ memcpy(TIME_REQUEST, "//k/mn",6);
+
+ /*
+ ** Initialize the number of units to 0 and set the dynamic array of units to
+ ** NULL since there are no units defined yet.
+ */
+
+ nunits = 0;
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_buginfo - not used */
+/*......................................................................*/
+
+static void
+datum_pts_buginfo(
+ int unit,
+ register struct refclockbug *bug,
+ register struct peer *peer
+ )
+{
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Buginfo Datum PTS\n");
+#endif
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_receive - receive the time buffer that was read in */
+/* by the ntpd io handling routines. When 7 bytes have been */
+/* received (it may take several tries before all 7 bytes are */
+/* received), then the time code must be unpacked and sent to */
+/* the ntpd clock_receive() routine which causes the systems */
+/* clock to be updated (several layers down). */
+/*......................................................................*/
+
+static void
+datum_pts_receive(
+ struct recvbuf *rbufp
+ )
+{
+ int i;
+ l_fp tstmp;
+ struct peer *p;
+ struct datum_pts_unit *datum_pts;
+ char *dpt;
+ int dpend;
+ int tzoff;
+ int timerr;
+ double ftimerr, abserr;
+#ifdef DEBUG_DATUM_PTC
+ double dispersion;
+#endif
+ int goodtime;
+ /*double doffset;*/
+
+ /*
+ ** Get the time code (maybe partial) message out of the rbufp buffer.
+ */
+
+ p = rbufp->recv_peer;
+ datum_pts = p->procptr->unitptr;
+ dpt = (char *)&rbufp->recv_space;
+ dpend = rbufp->recv_length;
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Receive Datum PTS: %d bytes\n", dpend);
+#endif
+
+ /* */
+ /*...... save the ntp system time when the first byte is received ......*/
+ /* */
+
+ /*
+ ** Save the ntp system time when the first byte is received. Note that
+ ** because it may take several calls to this routine before all seven
+ ** bytes of our return message are finally received by the io handlers in
+ ** ntpd, we really do want to use the time tag when the first byte is
+ ** received to reduce the jitter.
+ */
+
+ if (datum_pts->nbytes == 0) {
+ datum_pts->lastrec = rbufp->recv_time;
+ }
+
+ /*
+ ** Increment our count to the number of bytes received so far. Return if we
+ ** haven't gotten all seven bytes yet.
+ */
+
+ for (i=0; i<dpend; i++) {
+ datum_pts->retbuf[datum_pts->nbytes+i] = dpt[i];
+ }
+
+ datum_pts->nbytes += dpend;
+
+ if (datum_pts->nbytes != 7) {
+ return;
+ }
+
+ /*
+ ** Convert the seven bytes received in our time buffer to day, hour, minute,
+ ** second, and msecond values. The usec value is not used for anything
+ ** currently. It is just the fractional part of the time stored in units
+ ** of microseconds.
+ */
+
+ datum_pts->day = 100*(datum_pts->retbuf[0] & 0x0f) +
+ 10*((datum_pts->retbuf[1] & 0xf0)>>4) +
+ (datum_pts->retbuf[1] & 0x0f);
+
+ datum_pts->hour = 10*((datum_pts->retbuf[2] & 0x30)>>4) +
+ (datum_pts->retbuf[2] & 0x0f);
+
+ datum_pts->minute = 10*((datum_pts->retbuf[3] & 0x70)>>4) +
+ (datum_pts->retbuf[3] & 0x0f);
+
+ datum_pts->second = 10*((datum_pts->retbuf[4] & 0x70)>>4) +
+ (datum_pts->retbuf[4] & 0x0f);
+
+ datum_pts->msec = 100*((datum_pts->retbuf[5] & 0xf0) >> 4) +
+ 10*(datum_pts->retbuf[5] & 0x0f) +
+ ((datum_pts->retbuf[6] & 0xf0)>>4);
+
+ datum_pts->usec = 1000*datum_pts->msec;
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("day %d, hour %d, minute %d, second %d, msec %d\n",
+ datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ datum_pts->msec);
+#endif
+
+ /*
+ ** Get the GMT time zone offset. Note that GMT should be zero if the Datum
+ ** reference time is using GMT as its time base. Otherwise we have to
+ ** determine the offset if the Datum PTS is using time of day as its time
+ ** base.
+ */
+
+ goodtime = 0; /* We are not sure about the time and offset yet */
+
+#ifdef GMT
+
+ /*
+ ** This is the case where the Datum PTS is using GMT so there is no time
+ ** zone offset.
+ */
+
+ tzoff = 0; /* set time zone offset to 0 */
+
+#else
+
+ /*
+ ** This is the case where the Datum PTS is using regular time of day for its
+ ** time so we must compute the time zone offset. The way we do it is kind of
+ ** funny but it works. We loop through different time zones (0 to 24) and
+ ** pick the one that gives the smallest error (+- one half hour). The time
+ ** zone offset is stored in the datum_pts structure for future use. Normally,
+ ** the clocktime() routine is only called once (unless the time zone offset
+ ** changes due to daylight savings) since the goodtime flag is set when a
+ ** good time is found (with a good offset). Note that even if the Datum
+ ** PTS is using GMT, this mechanism will still work since it should come up
+ ** with a value for tzoff = 0 (assuming that your system clock is within
+ ** a half hour of the Datum time (even with time zone differences).
+ */
+
+ for (tzoff=0; tzoff<24; tzoff++) {
+ if (clocktime( datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ (tzoff + datum_pts->tzoff) % 24,
+ datum_pts->lastrec.l_ui,
+ &datum_pts->yearstart,
+ &datum_pts->lastref.l_ui) ) {
+
+ datum_pts->lastref.l_uf = 0;
+ error = datum_pts->lastref.l_ui - datum_pts->lastrec.l_ui;
+
+#ifdef DEBUG_DATUM_PTC
+ printf("Time Zone (clocktime method) = %d, error = %d\n", tzoff, error);
+#endif
+
+ if ((error < 1799) && (error > -1799)) {
+ tzoff = (tzoff + datum_pts->tzoff) % 24;
+ datum_pts->tzoff = tzoff;
+ goodtime = 1;
+
+#ifdef DEBUG_DATUM_PTC
+ printf("Time Zone found (clocktime method) = %d\n",tzoff);
+#endif
+
+ break;
+ }
+
+ }
+ }
+
+#endif
+
+ /*
+ ** Make sure that we have a good time from the Datum PTS. Clocktime() also
+ ** sets yearstart and lastref.l_ui. We will have to set astref.l_uf (i.e.,
+ ** the fraction of a second) stuff later.
+ */
+
+ if (!goodtime) {
+
+ if (!clocktime( datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ tzoff,
+ datum_pts->lastrec.l_ui,
+ &datum_pts->yearstart,
+ &datum_pts->lastref.l_ui) ) {
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ {
+ printf("Error: bad clocktime\n");
+ printf("GMT %d, lastrec %d, yearstart %d, lastref %d\n",
+ tzoff,
+ datum_pts->lastrec.l_ui,
+ datum_pts->yearstart,
+ datum_pts->lastref.l_ui);
+ }
+#endif
+
+ msyslog(LOG_ERR, "Datum_PTS: Bad clocktime");
+
+ return;
+
+ }else{
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Good clocktime\n");
+#endif
+
+ }
+
+ }
+
+ /*
+ ** We have datum_pts->lastref.l_ui set (which is the integer part of the
+ ** time. Now set the microseconds field.
+ */
+
+ TVUTOTSF(datum_pts->usec, datum_pts->lastref.l_uf);
+
+ /*
+ ** Compute the time correction as the difference between the reference
+ ** time (i.e., the Datum time) minus the receive time (system time).
+ */
+
+ tstmp = datum_pts->lastref; /* tstmp is the datum ntp time */
+ L_SUB(&tstmp, &datum_pts->lastrec); /* tstmp is now the correction */
+ datum_pts->coderecv++; /* increment a counter */
+
+#ifdef DEBUG_DATUM_PTC
+ dispersion = DATUM_DISPERSION; /* set the dispersion to 0 */
+ ftimerr = dispersion;
+ ftimerr /= (1024.0 * 64.0);
+ if (debug)
+ printf("dispersion = %d, %f\n", dispersion, ftimerr);
+#endif
+
+ /*
+ ** Pass the new time to ntpd through the refclock_receive function. Note
+ ** that we are not trying to make any corrections due to the time it takes
+ ** for the Datum PTS to send the message back. I am (erroneously) assuming
+ ** that the time for the Datum PTS to send the time back to us is negligable.
+ ** I suspect that this time delay may be as much as 15 ms or so (but probably
+ ** less). For our needs at JPL, this kind of error is ok so it is not
+ ** necessary to use fudge factors in the ntp.conf file. Maybe later we will.
+ */
+ /*LFPTOD(&tstmp, doffset);*/
+ datum_pts->lastref = datum_pts->lastrec;
+ refclock_receive(datum_pts->peer);
+
+ /*
+ ** Compute sigma squared (not used currently). Maybe later, this could be
+ ** used for the dispersion estimate. The problem is that ntpd does not link
+ ** in the math library so sqrt() is not available. Anyway, this is useful
+ ** for debugging. Maybe later I will just use absolute values for the time
+ ** error to come up with my dispersion estimate. Anyway, for now my dispersion
+ ** is set to 0.
+ */
+
+ timerr = tstmp.l_ui<<20;
+ timerr |= (tstmp.l_uf>>12) & 0x000fffff;
+ ftimerr = timerr;
+ ftimerr /= 1024*1024;
+ abserr = ftimerr;
+ if (ftimerr < 0.0) abserr = -ftimerr;
+
+ if (datum_pts->sigma2 == 0.0) {
+ if (abserr < DATUM_MAX_ERROR) {
+ datum_pts->sigma2 = abserr*abserr;
+ }else{
+ datum_pts->sigma2 = DATUM_MAX_ERROR2;
+ }
+ }else{
+ if (abserr < DATUM_MAX_ERROR) {
+ datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*abserr*abserr;
+ }else{
+ datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*DATUM_MAX_ERROR2;
+ }
+ }
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Time error = %f seconds\n", ftimerr);
+#endif
+
+#if defined(DEBUG_DATUM_PTC) || defined(LOG_TIME_ERRORS)
+ if (debug)
+ printf("PTS: day %d, hour %d, minute %d, second %d, msec %d, Time Error %f\n",
+ datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ datum_pts->msec,
+ ftimerr);
+#endif
+
+}
+#else
+NONEMPTY_TRANSLATION_UNIT
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_dumbclock.c b/ntpd/refclock_dumbclock.c
new file mode 100644
index 0000000..89f0f47
--- /dev/null
+++ b/ntpd/refclock_dumbclock.c
@@ -0,0 +1,383 @@
+/*
+ * refclock_dumbclock - clock driver for a unknown time distribution system
+ * that only provides hh:mm:ss (in local time, yet!).
+ */
+
+/*
+ * Must interpolate back to local time. Very annoying.
+ */
+#define GET_LOCALTIME
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef SYS_WINNT
+extern int async_write(int, const void *, unsigned int);
+#undef write
+#define write(fd, data, octets) async_write(fd, data, octets)
+#endif
+
+/*
+ * This driver supports a generic dumb clock that only outputs hh:mm:ss,
+ * in local time, no less.
+ *
+ * Input format:
+ *
+ * hh:mm:ss <cr>
+ *
+ * hh:mm:ss -- what you'd expect, with a 24 hour clock. (Heck, that's the only
+ * way it could get stupider.) We take time on the <cr>.
+ *
+ * The original source of this module was the WWVB module.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/dumbclock%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-13) /* precision assumed (about 100 us) */
+#define REFID "dumbclock" /* reference ID */
+#define DESCRIPTION "Dumb clock" /* WRU */
+
+
+/*
+ * Insanity check. Since the time is local, we need to make sure that during midnight
+ * transitions, we can convert back to Unix time. If the conversion results in some number
+ * worse than this number of seconds away, assume the next day and retry.
+ */
+#define INSANE_SECONDS 3600
+
+/*
+ * Dumb clock control structure
+ */
+struct dumbclock_unit {
+ u_char tcswitch; /* timecode switch */
+ l_fp laststamp; /* last receive timestamp */
+ u_char lasthour; /* last hour (for monitor) */
+ u_char linect; /* count ignored lines (for monitor */
+ struct tm ymd; /* struct tm for y/m/d only */
+};
+
+/*
+ * Function prototypes
+ */
+static int dumbclock_start (int, struct peer *);
+static void dumbclock_shutdown (int, struct peer *);
+static void dumbclock_receive (struct recvbuf *);
+#if 0
+static void dumbclock_poll (int, struct peer *);
+#endif
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_dumbclock = {
+ dumbclock_start, /* start up driver */
+ dumbclock_shutdown, /* shut down driver */
+ noentry, /* poll the driver -- a nice fabrication */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * dumbclock_start - open the devices and initialize data for processing
+ */
+static int
+dumbclock_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct dumbclock_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+ struct tm *tm_time_p;
+ time_t now;
+
+ /*
+ * Open serial port. Don't bother with CLK line discipline, since
+ * it's not available.
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+#ifdef DEBUG
+ if (debug)
+ printf ("starting Dumbclock with device %s\n",device);
+#endif
+ fd = refclock_open(device, SPEED232, 0);
+ if (fd <= 0)
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->unitptr = up;
+ pp->io.clock_recv = dumbclock_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ pp->unitptr = NULL;
+ return (0);
+ }
+
+
+ time(&now);
+#ifdef GET_LOCALTIME
+ tm_time_p = localtime(&now);
+#else
+ tm_time_p = gmtime(&now);
+#endif
+ if (tm_time_p)
+ up->ymd = *tm_time_p;
+ else
+ return 0;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * dumbclock_shutdown - shut down the clock
+ */
+static void
+dumbclock_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct dumbclock_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ if (NULL != up)
+ free(up);
+}
+
+
+/*
+ * dumbclock_receive - receive data from the serial interface
+ */
+static void
+dumbclock_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct dumbclock_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ l_fp trtmp; /* arrival timestamp */
+ int hours; /* hour-of-day */
+ int minutes; /* minutes-past-the-hour */
+ int seconds; /* seconds */
+ int temp; /* int temp */
+ int got_good; /* got a good time flag */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ if (temp == 0) {
+ if (up->tcswitch == 0) {
+ up->tcswitch = 1;
+ up->laststamp = trtmp;
+ } else
+ up->tcswitch = 0;
+ return;
+ }
+ pp->lencode = (u_short)temp;
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ up->tcswitch = 1;
+
+#ifdef DEBUG
+ if (debug)
+ printf("dumbclock: timecode %d %s\n",
+ pp->lencode, pp->a_lastcode);
+#endif
+
+ /*
+ * We get down to business. Check the timecode format...
+ */
+ got_good=0;
+ if (sscanf(pp->a_lastcode,"%02d:%02d:%02d",
+ &hours,&minutes,&seconds) == 3)
+ {
+ struct tm *gmtp;
+ struct tm *lt_p;
+ time_t asserted_time; /* the SPM time based on the composite time+date */
+ struct tm asserted_tm; /* the struct tm of the same */
+ int adjyear;
+ int adjmon;
+ time_t reality_delta;
+ time_t now;
+
+
+ /*
+ * Convert to GMT for sites that distribute localtime. This
+ * means we have to figure out what day it is. Easier said
+ * than done...
+ */
+
+ memset(&asserted_tm, 0, sizeof(asserted_tm));
+
+ asserted_tm.tm_year = up->ymd.tm_year;
+ asserted_tm.tm_mon = up->ymd.tm_mon;
+ asserted_tm.tm_mday = up->ymd.tm_mday;
+ asserted_tm.tm_hour = hours;
+ asserted_tm.tm_min = minutes;
+ asserted_tm.tm_sec = seconds;
+ asserted_tm.tm_isdst = -1;
+
+#ifdef GET_LOCALTIME
+ asserted_time = mktime (&asserted_tm);
+ time(&now);
+#else
+#include "GMT unsupported for dumbclock!"
+#endif
+ reality_delta = asserted_time - now;
+
+ /*
+ * We assume that if the time is grossly wrong, it's because we got the
+ * year/month/day wrong.
+ */
+ if (reality_delta > INSANE_SECONDS)
+ {
+ asserted_time -= SECSPERDAY; /* local clock behind real time */
+ }
+ else if (-reality_delta > INSANE_SECONDS)
+ {
+ asserted_time += SECSPERDAY; /* local clock ahead of real time */
+ }
+ lt_p = localtime(&asserted_time);
+ if (lt_p)
+ {
+ up->ymd = *lt_p;
+ }
+ else
+ {
+ refclock_report (peer, CEVNT_FAULT);
+ return;
+ }
+
+ if ((gmtp = gmtime (&asserted_time)) == NULL)
+ {
+ refclock_report (peer, CEVNT_FAULT);
+ return;
+ }
+ adjyear = gmtp->tm_year+1900;
+ adjmon = gmtp->tm_mon+1;
+ pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
+ pp->hour = gmtp->tm_hour;
+ pp->minute = gmtp->tm_min;
+ pp->second = gmtp->tm_sec;
+#ifdef DEBUG
+ if (debug)
+ printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
+ adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
+ pp->second);
+#endif
+
+ got_good=1;
+ }
+
+ if (!got_good)
+ {
+ if (up->linect > 0)
+ up->linect--;
+ else
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ up->lasthour = (u_char)pp->hour;
+}
+
+#if 0
+/*
+ * dumbclock_poll - called by the transmit procedure
+ */
+static void
+dumbclock_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct dumbclock_unit *up;
+ struct refclockproc *pp;
+ char pollchar;
+
+ /*
+ * Time to poll the clock. The Chrono-log clock is supposed to
+ * respond to a 'T' by returning a timecode in the format(s)
+ * specified above. Ours does (can?) not, but this seems to be
+ * an installation-specific problem. This code is dyked out,
+ * but may be re-enabled if anyone ever finds a Chrono-log that
+ * actually listens to this command.
+ */
+#if 0
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (peer->reach == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ if (up->linect > 0)
+ pollchar = 'R';
+ else
+ pollchar = 'T';
+ if (write(pp->io.fd, &pollchar, 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+#endif
+}
+#endif
+
+#else
+int refclock_dumbclock_bs;
+#endif /* defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK) */
diff --git a/ntpd/refclock_fg.c b/ntpd/refclock_fg.c
new file mode 100644
index 0000000..d5915da
--- /dev/null
+++ b/ntpd/refclock_fg.c
@@ -0,0 +1,335 @@
+/*
+ * refclock_fg - clock driver for the Forum Graphic GPS datating station
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_FG)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the Forum Graphic GPS dating station.
+ * More information about FG GPS is available on http://www.forumgraphic.com
+ * Contact das@amt.ru for any question about this driver.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/fgclock%d"
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "GPS"
+#define DESCRIPTION "Forum Graphic GPS dating station"
+#define LENFG 26 /* timecode length */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+
+/*
+ * Function prototypes
+ */
+static int fg_init (int);
+static int fg_start (int, struct peer *);
+static void fg_shutdown (int, struct peer *);
+static void fg_poll (int, struct peer *);
+static void fg_receive (struct recvbuf *);
+
+/*
+ * Forum Graphic unit control structure
+ */
+
+struct fgunit {
+ int pollnum; /* Use peer.poll instead? */
+ int status; /* Hug to check status information on GPS */
+ int y2kwarn; /* Y2K bug */
+};
+
+/*
+ * Queries definition
+ */
+static char fginit[] = { 0x10, 0x48, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static char fgdate[] = { 0x10, 0x44, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_fg = {
+ fg_start, /* start up driver */
+ fg_shutdown, /* shut down driver */
+ fg_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * fg_init - Initialization of FG GPS.
+ */
+
+static int
+fg_init(
+ int fd
+ )
+{
+ if (write(fd, fginit, LENFG) != LENFG)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * fg_start - open the device and initialize data for processing
+ */
+static int
+fg_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct fgunit *up;
+ int fd;
+ char device[20];
+
+
+ /*
+ * Open device file for reading.
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+
+ DPRINTF(1, ("starting FG with device %s\n",device));
+
+ fd = refclock_open(device, SPEED232, LDISC_CLK);
+ if (fd <= 0)
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+
+ up = emalloc(sizeof(struct fgunit));
+ memset(up, 0, sizeof(struct fgunit));
+ pp = peer->procptr;
+ pp->unitptr = up;
+ pp->io.clock_recv = fg_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ return 0;
+ }
+
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy(&pp->refid, REFID, 3);
+ up->pollnum = 0;
+
+ /*
+ * Setup dating station to use GPS receiver.
+ * GPS receiver should work before this operation.
+ */
+ if(!fg_init(pp->io.fd))
+ refclock_report(peer, CEVNT_FAULT);
+
+ return (1);
+}
+
+
+/*
+ * fg_shutdown - shut down the clock
+ */
+static void
+fg_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct fgunit *up;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (pp->io.fd != -1)
+ io_closeclock(&pp->io);
+ if (up != NULL)
+ free(up);
+}
+
+
+/*
+ * fg_poll - called by the transmit procedure
+ */
+static void
+fg_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ /*
+ * Time to poll the clock. The FG clock responds to a
+ * "<DLE>D<DLE><CR>" by returning a timecode in the format specified
+ * above. If nothing is heard from the clock for two polls,
+ * declare a timeout and keep going.
+ */
+
+ if (write(pp->io.fd, fgdate, LENFG) != LENFG)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+
+ /*
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ */
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+ return;
+
+}
+
+/*
+ * fg_receive - receive data from the serial interface
+ */
+static void
+fg_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct refclockproc *pp;
+ struct fgunit *up;
+ struct peer *peer;
+ char *bpt;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ * We can't use gtlin function because we need bynary data in buf */
+
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Below hug to implement receiving of status information
+ */
+ if(!up->pollnum) {
+ up->pollnum++;
+ return;
+ }
+
+
+ if (rbufp->recv_length < (LENFG - 2)) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return; /* The reply is invalid discard it. */
+ }
+
+ /* Below I trying to find a correct reply in buffer.
+ * Sometime GPS reply located in the beginnig of buffer,
+ * sometime you can find it with some offset.
+ */
+
+ bpt = (char *)rbufp->recv_space.X_recv_buffer;
+ while (*bpt != '\x10')
+ bpt++;
+
+#define BP2(x) ( bpt[x] & 15 )
+#define BP1(x) (( bpt[x] & 240 ) >> 4)
+
+ pp->year = BP1(2) * 10 + BP2(2);
+
+ if (pp->year == 94) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ if (!fg_init(pp->io.fd))
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ /* GPS is just powered up. The date is invalid -
+ discarding it. Initilize GPS one more time */
+ /* Sorry - this driver will broken in 2094 ;) */
+ }
+
+ if (pp->year < 99)
+ pp->year += 100;
+
+ pp->year += 1900;
+ pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4);
+
+/*
+ After Jan, 10 2000 Forum Graphic GPS receiver had a very strange
+ benahour. It doubles day number for an hours in replys after 10:10:10 UTC
+ and doubles min every hour at HH:10:ss for a minute.
+ Hope it is a problem of my unit only and not a Y2K problem of FG GPS.
+ Below small code to avoid such situation.
+*/
+ if (up->y2kwarn > 10)
+ pp->hour = BP1(6)*10 + BP2(6);
+ else
+ pp->hour = BP1(5)*10 + BP2(5);
+
+ if ((up->y2kwarn > 10) && (pp->hour == 10)) {
+ pp->minute = BP1(7)*10 + BP2(7);
+ pp->second = BP1(8)*10 + BP2(8);
+ pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000;
+ pp->nsec += BP1(10) * 1000;
+ } else {
+ pp->hour = BP1(5)*10 + BP2(5);
+ pp->minute = BP1(6)*10 + BP2(6);
+ pp->second = BP1(7)*10 + BP2(7);
+ pp->nsec = (BP1(8)*10 + BP2(8)) * 1000000;
+ pp->nsec += BP1(9) * 1000;
+ }
+
+ if ((pp->hour == 10) && (pp->minute == 10)) {
+ up->y2kwarn++;
+ }
+
+ snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
+ "%d %d %d %d %d", pp->year, pp->day, pp->hour,
+ pp->minute, pp->second);
+ pp->lencode = strlen(pp->a_lastcode);
+ /*get_systime(&pp->lastrec);*/
+
+#ifdef DEBUG
+ if (debug)
+ printf("fg: time is %04d/%03d %02d:%02d:%02d UTC\n",
+ pp->year, pp->day, pp->hour, pp->minute, pp->second);
+#endif
+ pp->disp = (10e-6);
+ pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */
+ /* pp->leap = LEAP_NOWARNING; */
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+
+ if (!refclock_process(pp))
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ return;
+}
+
+
+#else
+int refclock_fg_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_gpsdjson.c b/ntpd/refclock_gpsdjson.c
new file mode 100644
index 0000000..6150982
--- /dev/null
+++ b/ntpd/refclock_gpsdjson.c
@@ -0,0 +1,1341 @@
+/*
+ * refclock_gpsdjson.c - clock driver as GPSD JSON client
+ * Juergen Perlinger (perlinger@ntp.org)
+ * Feb 11, 2014 for the NTP project.
+ * The contents of 'html/copyright.html' apply.
+ *
+ * Heavily inspired by refclock_nmea.c
+ *
+ * Note: This will currently NOT work with Windows due to some
+ * limitations:
+ *
+ * - There is no GPSD for Windows. (There is an unofficial port to
+ * cygwin, but Windows is not officially supported.)
+ *
+ * - To work properly, this driver needs PPS and TPV sentences from
+ * GPSD. I don't see how the cygwin port should deal with that.
+ *
+ * - The device name matching must be done in a different way for
+ * Windows. (Can be done with COMxx matching, as done for NMEA.)
+ *
+ * Apart from those minor hickups, once GPSD has been fully ported to
+ * Windows, there's no reason why this should not work there ;-)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ntp_types.h"
+
+#if defined(REFCLOCK) && defined(CLOCK_GPSDJSON) && !defined(SYS_WINNT)
+
+/* =====================================================================
+ * get the little JSMN library directly into our guts
+ */
+#include "../libjsmn/jsmn.c"
+
+/* =====================================================================
+ * header stuff we need
+ */
+
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/tcp.h>
+
+#if defined(HAVE_SYS_POLL_H)
+# include <sys/poll.h>
+#elif defined(HAVE_SYS_SLECET_H)
+# include <sys/select.h>
+#else
+# error need poll() or select()
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_calendar.h"
+#include "timespecops.h"
+
+#define PRECISION (-9) /* precision assumed (about 2 ms) */
+#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPSD" /* reference id */
+#define DESCRIPTION "GPSD JSON client clock" /* who we are */
+
+#define MAX_PDU_LEN 1600
+#define TICKOVER_LOW 10
+#define TICKOVER_HIGH 120
+#define LOGTHROTTLE 3600
+
+#define PPS_MAXCOUNT 30
+#define PPS_HIWAT 20
+#define PPS_LOWAT 10
+
+#ifndef BOOL
+# define BOOL int
+#endif
+#ifndef TRUE
+# define TRUE 1
+#endif
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+/* some local typedefs : The NTPD formatting style cries for short type
+ * names, and we provide them locally. Note:the suffix '_t' is reserved
+ * for the standard; I use a capital T instead.
+ */
+typedef struct peer peerT;
+typedef struct refclockproc clockprocT;
+typedef struct addrinfo addrinfoT;
+
+/* =====================================================================
+ * We use the same device name scheme as does the NMEA driver; since
+ * GPSD supports the same links, we can select devices by a fixed name.
+ */
+static const char * s_dev_stem = "/dev/gps";
+
+/* =====================================================================
+ * forward declarations for transfer vector and the vector itself
+ */
+
+static void gpsd_init (void);
+static int gpsd_start (int, peerT *);
+static void gpsd_shutdown (int, peerT *);
+static void gpsd_receive (struct recvbuf *);
+static void gpsd_poll (int, peerT *);
+static void gpsd_control (int, const struct refclockstat *,
+ struct refclockstat *, peerT *);
+static void gpsd_timer (int, peerT *);
+static void gpsd_clockstats (int, peerT *);
+
+static int myasprintf(char**, char const*, ...);
+
+struct refclock refclock_gpsdjson = {
+ gpsd_start, /* start up driver */
+ gpsd_shutdown, /* shut down driver */
+ gpsd_poll, /* transmit poll message */
+ gpsd_control, /* fudge control */
+ gpsd_init, /* initialize driver */
+ noentry, /* buginfo */
+ gpsd_timer /* called once per second */
+};
+
+/* =====================================================================
+ * our local clock unit and data
+ */
+typedef struct gpsd_unit {
+ int unit;
+ /* current line protocol version */
+ uint16_t proto_major;
+ uint16_t proto_minor;
+
+ /* PPS time stamps */
+ l_fp pps_local; /* when we received the PPS message */
+ l_fp pps_stamp; /* related reference time */
+ l_fp pps_recvt; /* when GPSD detected the pulse */
+
+ /* TPV (GPS data) time stamps */
+ l_fp tpv_local; /* when we received the TPV message */
+ l_fp tpv_stamp; /* effective GPS time stamp */
+ l_fp tpv_recvt; /* when GPSD got the fix */
+
+ /* fudge values for correction, mirrored as 'l_fp' */
+ l_fp pps_fudge;
+ l_fp tpv_fudge;
+
+ /* Flags to indicate available data */
+ int fl_tpv : 1; /* valid TPV seen (have time) */
+ int fl_pps : 1; /* valid pulse seen */
+ int fl_vers : 1; /* have protocol version */
+ int fl_watch : 1; /* watch reply seen */
+ int fl_nsec : 1; /* have nanosec PPS info */
+
+ /* admin stuff for sockets and device selection */
+ int fdt; /* current connecting socket */
+ addrinfoT * addr; /* next address to try */
+ u_int tickover; /* timeout countdown */
+ u_int tickpres; /* timeout preset */
+ u_int ppscount; /* PPS mode up/down count */
+ char * device; /* device name of unit */
+
+ /* tallies for the various events */
+ u_int tc_good; /* good samples received */
+ u_int tc_btime; /* bad time stamps */
+ u_int tc_bdate; /* bad date strings */
+ u_int tc_breply; /* bad replies */
+ u_int tc_recv; /* received known records */
+
+ /* log bloat throttle */
+ u_int logthrottle;/* seconds to next log slot */
+
+ /* record assemby buffer and saved length */
+ int buflen;
+ char buffer[MAX_PDU_LEN];
+} gpsd_unitT;
+
+/* =====================================================================
+ * static local helpers forward decls
+ */
+static void gpsd_init_socket(peerT * const peer);
+static void gpsd_test_socket(peerT * const peer);
+static void gpsd_stop_socket(peerT * const peer);
+
+static void gpsd_parse(peerT * const peer,
+ const l_fp * const rtime);
+static BOOL convert_ascii_time(l_fp * fp, const char * gps_time);
+static void save_ltc(clockprocT * const pp, const char * const tc);
+static int syslogok(clockprocT * const pp, gpsd_unitT * const up);
+
+/* =====================================================================
+ * local / static stuff
+ */
+
+/* The logon string is actually the ?WATCH command of GPSD, using JSON
+ * data and selecting the GPS device name we created from our unit
+ * number. [Note: This is a format string!]
+ */
+static const char * s_logon =
+ "?WATCH={\"enable\":true,\"json\":true,\"device\":\"%s\"};\r\n";
+
+/* We keep a static list of network addresses for 'localhost:gpsd', and
+ * we try to connect to them in round-robin fashion.
+ */
+static addrinfoT * s_gpsd_addr;
+
+/* =====================================================================
+ * log throttling
+ */
+static int/*BOOL*/
+syslogok(
+ clockprocT * const pp,
+ gpsd_unitT * const up)
+{
+ int res = (0 != (pp->sloppyclockflag & CLK_FLAG3))
+ || (0 == up->logthrottle )
+ || (LOGTHROTTLE == up->logthrottle );
+ if (res)
+ up->logthrottle = LOGTHROTTLE;
+ return res;
+}
+
+/* =====================================================================
+ * the clock functions
+ */
+
+/* ---------------------------------------------------------------------
+ * Init: This currently just gets the socket address for the GPS daemon
+ */
+static void
+gpsd_init(void)
+{
+ addrinfoT hints;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_socktype = SOCK_STREAM;
+
+ /* just take the first configured address of localhost... */
+ if (getaddrinfo("localhost", "gpsd", &hints, &s_gpsd_addr))
+ s_gpsd_addr = NULL;
+}
+
+/* ---------------------------------------------------------------------
+ * Start: allocate a unit pointer and set up the runtime data
+ */
+
+static int
+gpsd_start(
+ int unit,
+ peerT * peer)
+{
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = emalloc_zero(sizeof(*up));
+
+ struct stat sb;
+
+ /* initialize the unit structure */
+ up->fdt = -1;
+ up->addr = s_gpsd_addr;
+ up->tickpres = TICKOVER_LOW;
+
+ /* setup refclock processing */
+ up->unit = unit;
+ pp->unitptr = (caddr_t)up;
+ pp->io.fd = -1;
+ pp->io.clock_recv = gpsd_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->a_lastcode[0] = '\0';
+ pp->lencode = 0;
+ pp->clockdesc = DESCRIPTION;
+ memcpy(&pp->refid, REFID, 4);
+
+ /* Initialize miscellaneous variables */
+ peer->precision = PRECISION;
+
+ /* Create the device name and check for a Character Device. It's
+ * assumed that GPSD was started with the same link, so the
+ * names match. (If this is not practicable, we will have to
+ * read the symlink, if any, so we can get the true device
+ * file.)
+ */
+ if (-1 == myasprintf(&up->device, "%s%u", s_dev_stem, unit)) {
+ msyslog(LOG_ERR, "%s clock device name too long",
+ refnumtoa(&peer->srcadr));
+ goto dev_fail;
+ }
+ if (-1 == stat(up->device, &sb) || !S_ISCHR(sb.st_mode)) {
+ msyslog(LOG_ERR, "%s: '%s' is not a character device",
+ refnumtoa(&peer->srcadr), up->device);
+ goto dev_fail;
+ }
+ LOGIF(CLOCKINFO,
+ (LOG_NOTICE, "%s: startup, device is '%s'",
+ refnumtoa(&peer->srcadr), up->device));
+ return TRUE;
+
+dev_fail:
+ /* On failure, remove all UNIT ressources and declare defeat. */
+
+ INSIST (up);
+ free(up->device);
+ free(up);
+
+ pp->unitptr = (caddr_t)NULL;
+ return FALSE;
+}
+
+/* ------------------------------------------------------------------ */
+
+static void
+gpsd_shutdown(
+ int unit,
+ peerT * peer)
+{
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
+
+ UNUSED_ARG(unit);
+
+ if (up) {
+ free(up->device);
+ free(up);
+ }
+ pp->unitptr = (caddr_t)NULL;
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ pp->io.fd = -1;
+ LOGIF(CLOCKINFO,
+ (LOG_NOTICE, "%s: shutdown", refnumtoa(&peer->srcadr)));
+}
+
+/* ------------------------------------------------------------------ */
+
+static void
+gpsd_receive(
+ struct recvbuf * rbufp)
+{
+ /* declare & init control structure ptrs */
+ peerT * const peer = rbufp->recv_peer;
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
+
+ const char *psrc, *esrc;
+ char *pdst, *edst, ch;
+
+ /* Since we're getting a raw stream data, we must assemble lines
+ * in our receive buffer. We can't use neither 'refclock_gtraw'
+ * not 'refclock_gtlin' here... We process chars until we reach
+ * an EoL (that is, line feed) but we truncate the message if it
+ * does not fit the buffer. GPSD might truncate messages, too,
+ * so dealing with truncated buffers is necessary anyway.
+ */
+ psrc = (const char*)rbufp->recv_buffer;
+ esrc = psrc + rbufp->recv_length;
+
+ pdst = up->buffer + up->buflen;
+ edst = pdst + sizeof(up->buffer) - 1; /* for trailing NUL */
+
+ while (psrc != esrc) {
+ ch = *psrc++;
+ if (ch == '\n') {
+ /* trim trailing whitespace & terminate buffer */
+ while (pdst != up->buffer && pdst[-1] <= ' ')
+ --pdst;
+ *pdst = '\0';
+ /* process data and reset buffer */
+ gpsd_parse(peer, &rbufp->recv_time);
+ pdst = up->buffer;
+ } else if (pdst != edst) {
+ /* add next char, ignoring leading whitespace */
+ if (ch > ' ' || pdst != up->buffer)
+ *pdst++ = ch;
+ }
+ }
+ up->buflen = pdst - up->buffer;
+ up->tickover = TICKOVER_LOW;
+}
+
+/* ------------------------------------------------------------------ */
+
+static void
+gpsd_poll(
+ int unit,
+ peerT * peer)
+{
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
+ u_int tc_max;
+
+ ++pp->polls;
+
+ /* find the dominant error */
+ tc_max = max(up->tc_btime, up->tc_bdate);
+ tc_max = max(tc_max, up->tc_breply);
+
+ if (pp->coderecv != pp->codeproc) {
+ /* all is well */
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ } else {
+ /* not working properly, admit to it */
+ peer->flags &= ~FLAG_PPS;
+ peer->precision = PRECISION;
+
+ if (-1 == pp->io.fd) {
+ /* not connected to GPSD: clearly not working! */
+ refclock_report(peer, CEVNT_FAULT);
+ } else if (tc_max == up->tc_breply) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ } else if (tc_max == up->tc_btime) {
+ refclock_report(peer, CEVNT_BADTIME);
+ } else if (tc_max == up->tc_bdate) {
+ refclock_report(peer, CEVNT_BADDATE);
+ } else {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ }
+ }
+
+ if (pp->sloppyclockflag & CLK_FLAG4)
+ gpsd_clockstats(unit, peer);
+
+ /* clear tallies for next round */
+ up->tc_good = up->tc_btime = up->tc_bdate =
+ up->tc_breply = up->tc_recv = 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static void
+gpsd_control(
+ int unit,
+ const struct refclockstat * in_st,
+ struct refclockstat * out_st,
+ peerT * peer )
+{
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
+
+ /* save preprocessed fudge times */
+ DTOLFP(pp->fudgetime1, &up->pps_fudge);
+ DTOLFP(pp->fudgetime2, &up->tpv_fudge);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void
+gpsd_timer(
+ int unit,
+ peerT * peer)
+{
+ static const char query[] = "?VERSION;";
+
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
+ int rc;
+
+ /* This is used for timeout handling. Nothing that needs
+ * sub-second precison happens here, so receive/connect/retry
+ * timeouts are simply handled by a count down, and then we
+ * decide what to do by the socket values.
+ *
+ * Note that the timer stays at zero here, unless some of the
+ * functions set it to another value.
+ */
+ if (up->logthrottle)
+ --up->logthrottle;
+ if (up->tickover)
+ --up->tickover;
+ switch (up->tickover) {
+ case 4:
+ /* try to get a live signal
+ * If the device is not yet present, we will most likely
+ * get an error. We put out a new version request,
+ * because the reply will initiate a new watch request
+ * cycle.
+ */
+ if (-1 != pp->io.fd) {
+ if ( ! up->fl_watch) {
+ DPRINTF(2, ("GPSD_JSON(%d): timer livecheck: '%s'\n",
+ up->unit, query));
+ rc = write(pp->io.fd,
+ query, sizeof(query));
+ (void)rc;
+ }
+ } else if (-1 != up->fdt) {
+ gpsd_test_socket(peer);
+ }
+ break;
+
+ case 0:
+ if (-1 != pp->io.fd)
+ gpsd_stop_socket(peer);
+ else if (-1 != up->fdt)
+ gpsd_test_socket(peer);
+ else if (NULL != s_gpsd_addr)
+ gpsd_init_socket(peer);
+ break;
+
+ default:
+ if (-1 == pp->io.fd && -1 != up->fdt)
+ gpsd_test_socket(peer);
+ }
+
+ if (up->ppscount > PPS_HIWAT && !(peer->flags & FLAG_PPS))
+ peer->flags |= FLAG_PPS;
+ if (up->ppscount < PPS_LOWAT && (peer->flags & FLAG_PPS))
+ peer->flags &= ~FLAG_PPS;
+}
+
+/* =====================================================================
+ * JSON parsing stuff
+ */
+
+#define JSMN_MAXTOK 100
+#define INVALID_TOKEN (-1)
+
+typedef struct json_ctx {
+ char * buf;
+ int ntok;
+ jsmntok_t tok[JSMN_MAXTOK];
+} json_ctx;
+
+typedef int tok_ref;
+
+#ifdef HAVE_LONG_LONG
+typedef long long json_int;
+ #define JSON_STRING_TO_INT strtoll
+#else
+typedef long json_int;
+ #define JSON_STRING_TO_INT strtol
+#endif
+
+/* ------------------------------------------------------------------ */
+
+static tok_ref
+json_token_skip(
+ const json_ctx * ctx,
+ tok_ref tid)
+{
+ int len;
+ len = ctx->tok[tid].size;
+ for (++tid; len; --len)
+ if (tid < ctx->ntok)
+ tid = json_token_skip(ctx, tid);
+ else
+ break;
+ if (tid > ctx->ntok)
+ tid = ctx->ntok;
+ return tid;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int
+json_object_lookup(
+ const json_ctx * ctx,
+ tok_ref tid,
+ const char * key)
+{
+ int len;
+
+ if (tid >= ctx->ntok || ctx->tok[tid].type != JSMN_OBJECT)
+ return INVALID_TOKEN;
+ len = ctx->ntok - tid - 1;
+ if (len > ctx->tok[tid].size)
+ len = ctx->tok[tid].size;
+ for (tid += 1; len > 1; len-=2) {
+ if (ctx->tok[tid].type != JSMN_STRING)
+ continue; /* hmmm... that's an error, strictly speaking */
+ if (!strcmp(key, ctx->buf + ctx->tok[tid].start))
+ return tid + 1;
+ tid = json_token_skip(ctx, tid + 1);
+ }
+ return INVALID_TOKEN;
+}
+
+/* ------------------------------------------------------------------ */
+
+#if 0 /* currently unused */
+static const char*
+json_object_lookup_string(
+ const json_ctx * ctx,
+ tok_ref tid,
+ const char * key)
+{
+ tok_ref val_ref;
+ val_ref = json_object_lookup(ctx, tid, key);
+ if (INVALID_TOKEN == val_ref ||
+ JSMN_STRING != ctx->tok[val_ref].type )
+ goto cvt_error;
+ return ctx->buf + ctx->tok[val_ref].start;
+
+ cvt_error:
+ errno = EINVAL;
+ return NULL;
+}
+#endif
+
+static const char*
+json_object_lookup_string_default(
+ const json_ctx * ctx,
+ tok_ref tid,
+ const char * key,
+ const char * def)
+{
+ tok_ref val_ref;
+ val_ref = json_object_lookup(ctx, tid, key);
+ if (INVALID_TOKEN == val_ref ||
+ JSMN_STRING != ctx->tok[val_ref].type )
+ return def;
+ return ctx->buf + ctx->tok[val_ref].start;
+}
+
+/* ------------------------------------------------------------------ */
+
+static json_int
+json_object_lookup_int(
+ const json_ctx * ctx,
+ tok_ref tid,
+ const char * key)
+{
+ json_int ret;
+ tok_ref val_ref;
+ char * ep;
+
+ val_ref = json_object_lookup(ctx, tid, key);
+ if (INVALID_TOKEN == val_ref ||
+ JSMN_PRIMITIVE != ctx->tok[val_ref].type )
+ goto cvt_error;
+ ret = JSON_STRING_TO_INT(
+ ctx->buf + ctx->tok[val_ref].start, &ep, 10);
+ if (*ep)
+ goto cvt_error;
+ return ret;
+
+ cvt_error:
+ errno = EINVAL;
+ return 0;
+}
+
+static json_int
+json_object_lookup_int_default(
+ const json_ctx * ctx,
+ tok_ref tid,
+ const char * key,
+ json_int def)
+{
+ json_int retv;
+ int esave;
+
+ esave = errno;
+ errno = 0;
+ retv = json_object_lookup_int(ctx, tid, key);
+ if (0 != errno)
+ retv = def;
+ errno = esave;
+ return retv;
+}
+
+/* ------------------------------------------------------------------ */
+
+static double
+json_object_lookup_float(
+ const json_ctx * ctx,
+ tok_ref tid,
+ const char * key)
+{
+ double ret;
+ tok_ref val_ref;
+ char * ep;
+
+ val_ref = json_object_lookup(ctx, tid, key);
+ if (INVALID_TOKEN == val_ref ||
+ JSMN_PRIMITIVE != ctx->tok[val_ref].type )
+ goto cvt_error;
+ ret = strtod(ctx->buf + ctx->tok[val_ref].start, &ep);
+ if (*ep)
+ goto cvt_error;
+ return ret;
+
+ cvt_error:
+ errno = EINVAL;
+ return 0.0;
+}
+
+static double
+json_object_lookup_float_default(
+ const json_ctx * ctx,
+ tok_ref tid,
+ const char * key,
+ double def)
+{
+ double retv;
+ int esave;
+
+ esave = errno;
+ errno = 0;
+ retv = json_object_lookup_float(ctx, tid, key);
+ if (0 != errno)
+ retv = def;
+ errno = esave;
+ return retv;
+}
+
+/* ------------------------------------------------------------------ */
+
+static BOOL
+json_parse_record(
+ json_ctx * ctx,
+ char * buf)
+{
+ jsmn_parser jsm;
+ int idx, rc;
+
+ jsmn_init(&jsm);
+ rc = jsmn_parse(&jsm, buf, ctx->tok, JSMN_MAXTOK);
+ ctx->buf = buf;
+ ctx->ntok = jsm.toknext;
+
+ /* Make all tokens NUL terminated by overwriting the
+ * terminator symbol
+ */
+ for (idx = 0; idx < jsm.toknext; ++idx)
+ if (ctx->tok[idx].end > ctx->tok[idx].start)
+ ctx->buf[ctx->tok[idx].end] = '\0';
+
+ if (JSMN_ERROR_PART != rc &&
+ JSMN_ERROR_NOMEM != rc &&
+ JSMN_SUCCESS != rc )
+ return FALSE; /* not parseable - bail out */
+
+ if (0 >= jsm.toknext || JSMN_OBJECT != ctx->tok[0].type)
+ return FALSE; /* not object or no data!?! */
+
+ return TRUE;
+}
+
+
+/* =====================================================================
+ * static local helpers
+ */
+
+/* ------------------------------------------------------------------ */
+/* Process a WATCH record
+ *
+ * Currently this is only used to recognise that the device is present
+ * and that we're listed subscribers.
+ */
+static void
+process_watch(
+ peerT * const peer ,
+ json_ctx * const jctx ,
+ const l_fp * const rtime)
+{
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
+
+ up->fl_watch = -1;
+}
+
+/* ------------------------------------------------------------------ */
+
+static void
+process_version(
+ peerT * const peer ,
+ json_ctx * const jctx ,
+ const l_fp * const rtime)
+{
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
+
+ int len;
+ char * buf;
+ const char *revision;
+ const char *release;
+
+ /* get protocol version number */
+ revision = json_object_lookup_string_default(
+ jctx, 0, "rev", "(unknown)");
+ release = json_object_lookup_string_default(
+ jctx, 0, "release", "(unknown)");
+ errno = 0;
+ up->proto_major = (uint16_t)json_object_lookup_int(
+ jctx, 0, "proto_major");
+ up->proto_minor = (uint16_t)json_object_lookup_int(
+ jctx, 0, "proto_minor");
+ if (0 == errno) {
+ up->fl_vers = -1;
+ if (syslogok(pp, up))
+ msyslog(LOG_INFO,
+ "%s: GPSD revision=%s release=%s protocol=%u.%u",
+ refnumtoa(&peer->srcadr),
+ revision, release,
+ up->proto_major, up->proto_minor);
+ }
+
+ /* With the 3.9 GPSD protocol, '*_musec' vanished and was
+ * replace by '*_nsec'. Dispatch properly.
+ */
+ if ( up->proto_major > 3 ||
+ (up->proto_major == 3 && up->proto_minor >= 9))
+ up->fl_nsec = -1;
+ else
+ up->fl_nsec = 0;
+
+ /*TODO: validate protocol version! */
+
+ /* request watch for our GPS device
+ * Reuse the input buffer, which is no longer needed in the
+ * current cycle. Also assume that we can write the watch
+ * request in one sweep into the socket; since we do not do
+ * output otherwise, this should always work. (Unless the
+ * TCP/IP window size gets lower than the length of the
+ * request. We handle that when it happens.)
+ */
+ snprintf(up->buffer, sizeof(up->buffer),
+ s_logon, up->device);
+ buf = up->buffer;
+ len = strlen(buf);
+ if (len != write(pp->io.fd, buf, len)) {
+ /*Note: if the server fails to read our request, the
+ * resulting data timeout will take care of the
+ * connection!
+ */
+ if (syslogok(pp, up))
+ msyslog(LOG_ERR,
+ "%s: failed to write watch request (%m)",
+ refnumtoa(&peer->srcadr));
+ }
+}
+
+/* ------------------------------------------------------------------ */
+
+static void
+process_tpv(
+ peerT * const peer ,
+ json_ctx * const jctx ,
+ const l_fp * const rtime)
+{
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
+
+ const char * gps_time;
+ int gps_mode;
+ double ept, epp, epx, epy, epv;
+ int log2;
+
+ gps_mode = (int)json_object_lookup_int_default(
+ jctx, 0, "mode", 0);
+
+ gps_time = json_object_lookup_string_default(
+ jctx, 0, "time", NULL);
+
+ if (gps_mode < 1 || NULL == gps_time) {
+ /* receiver has no fix; tell about and avoid stale data */
+ up->tc_breply += 1;
+ up->fl_tpv = 0;
+ up->fl_pps = 0;
+ return;
+ }
+
+ /* save last time code to clock data */
+ save_ltc(pp, gps_time);
+
+ /* convert clock and set resulting ref time */
+ if (convert_ascii_time(&up->tpv_stamp, gps_time)) {
+ DPRINTF(2, ("GPSD_JSON(%d): process_tpv, stamp='%s', recvt='%s' mode=%u\n",
+ up->unit,
+ gmprettydate(&up->tpv_stamp),
+ gmprettydate(&up->tpv_recvt),
+ gps_mode));
+
+ up->tpv_local = *rtime;
+ up->tpv_recvt = *rtime;/*TODO: hack until we get it remote from GPSD */
+ L_SUB(&up->tpv_recvt, &up->tpv_fudge);
+ up->fl_tpv = -1;
+ } else {
+ up->tc_btime += 1;
+ up->fl_tpv = 0;
+ }
+
+ /* Set the precision from the GPSD data
+ *
+ * Since EPT has some issues, we use EPT and a home-brewed error
+ * estimation base on a sphere derived from EPX/Y/V and the
+ * speed of light. Use the better one of those two.
+ */
+ ept = json_object_lookup_float_default(jctx, 0, "ept", 1.0);
+ epx = json_object_lookup_float_default(jctx, 0, "epx", 1000.0);
+ epy = json_object_lookup_float_default(jctx, 0, "epy", 1000.0);
+ if (1 == gps_mode) {
+ /* 2d-fix: extend bounding rectangle to cuboid */
+ epv = max(epx, epy);
+ } else {
+ /* 3d-fix: get bounding cuboid */
+ epv = json_object_lookup_float_default(
+ jctx, 0, "epv", 1000.0);
+ }
+
+ /* get diameter of enclosing sphere of bounding cuboid as spatial
+ * error, then divide spatial error by speed of light to get
+ * another time error estimate. Add extra 100 meters as
+ * optimistic lower bound. Then use the better one of the two
+ * estimations.
+ */
+ epp = 2.0 * sqrt(epx*epx + epy*epy + epv*epv);
+ epp = (epp + 100.0) / 299792458.0;
+
+ ept = min(ept, epp );
+ ept = min(ept, 0.5 );
+ ept = max(ept, 1.0-9);
+ ept = frexp(ept, &log2);
+
+ peer->precision = log2;
+}
+
+/* ------------------------------------------------------------------ */
+
+static void
+process_pps(
+ peerT * const peer ,
+ json_ctx * const jctx ,
+ const l_fp * const rtime)
+{
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
+
+ struct timespec ts;
+
+ errno = 0;
+ ts.tv_sec = (time_t)json_object_lookup_int(
+ jctx, 0, "clock_sec");
+ if (up->fl_nsec)
+ ts.tv_nsec = json_object_lookup_int(
+ jctx, 0, "clock_nsec");
+ else
+ ts.tv_nsec = json_object_lookup_int(
+ jctx, 0, "clock_musec") * 1000;
+
+ if (0 != errno)
+ goto fail;
+
+ up->pps_local = *rtime;
+ /* get fudged receive time */
+ up->pps_recvt = tspec_stamp_to_lfp(ts);
+ L_SUB(&up->pps_recvt, &up->pps_fudge);
+
+ /* map to next full second as reference time stamp */
+ up->pps_stamp = up->pps_recvt;
+ L_ADDUF(&up->pps_stamp, 0x80000000u);
+ up->pps_stamp.l_uf = 0;
+
+ pp->lastrec = up->pps_stamp;
+
+ DPRINTF(2, ("GPSD_JSON(%d): process_pps, stamp='%s', recvt='%s'\n",
+ up->unit,
+ gmprettydate(&up->pps_stamp),
+ gmprettydate(&up->pps_recvt)));
+
+ /* When we have a time pulse, clear the TPV flag: the
+ * PPS is only valid for the >NEXT< TPV value!
+ */
+ up->fl_pps = -1;
+ up->fl_tpv = 0;
+ return;
+
+ fail:
+ DPRINTF(2, ("GPSD_JSON(%d): process_pps FAILED, nsec=%d stamp='%s', recvt='%s'\n",
+ up->unit, up->fl_nsec,
+ gmprettydate(&up->pps_stamp),
+ gmprettydate(&up->pps_recvt)));
+ up->tc_breply += 1;
+}
+
+/* ------------------------------------------------------------------ */
+
+static void
+gpsd_parse(
+ peerT * const peer ,
+ const l_fp * const rtime)
+{
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
+
+ json_ctx jctx;
+ const char * clsid;
+ l_fp tmpfp;
+
+ DPRINTF(2, ("GPSD_JSON(%d): gpsd_parse: time %s '%s'\n",
+ up->unit, ulfptoa(rtime, 6), up->buffer));
+
+ /* See if we can grab anything potentially useful */
+ if (!json_parse_record(&jctx, up->buffer))
+ return;
+
+ /* Now dispatch over the objects we know */
+ clsid = json_object_lookup_string_default(
+ &jctx, 0, "class", "-bad-repy-");
+
+ up->tc_recv += 1;
+ if (!strcmp("VERSION", clsid))
+ process_version(peer, &jctx, rtime);
+ else if (!strcmp("TPV", clsid))
+ process_tpv(peer, &jctx, rtime);
+ else if (!strcmp("PPS", clsid))
+ process_pps(peer, &jctx, rtime);
+ else if (!strcmp("WATCH", clsid))
+ process_watch(peer, &jctx, rtime);
+ else
+ return; /* nothing we know about... */
+
+ /* now aggregate TPV and PPS -- no PPS? just use TPV...*/
+ if (up->fl_tpv) {
+ /* TODO: also check remote receive time stamps */
+ tmpfp = up->tpv_local;
+ L_SUB(&tmpfp, &up->pps_local);
+
+ if (up->fl_pps && 0 == tmpfp.l_ui) {
+ refclock_process_offset(
+ pp, up->tpv_stamp, up->pps_recvt, 0.0);
+ if (up->ppscount < PPS_MAXCOUNT)
+ up->ppscount += 1;
+ } else {
+ refclock_process_offset(
+ pp, up->tpv_stamp, up->tpv_recvt, 0.0);
+ if (up->ppscount > 0)
+ up->ppscount -= 1;
+ }
+ up->fl_pps = 0;
+ up->fl_tpv = 0;
+ up->tc_good += 1;
+ }
+}
+
+/* ------------------------------------------------------------------ */
+
+static void
+gpsd_stop_socket(
+ peerT * const peer)
+{
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
+
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ pp->io.fd = -1;
+ if (syslogok(pp, up))
+ msyslog(LOG_INFO,
+ "%s: closing socket to GPSD",
+ refnumtoa(&peer->srcadr));
+ up->tickover = up->tickpres;
+ up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH);
+ up->fl_vers = 0;
+ up->fl_tpv = 0;
+ up->fl_pps = 0;
+ up->fl_watch = 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static void
+gpsd_init_socket(
+ peerT * const peer)
+{
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
+ addrinfoT * ai;
+ int rc;
+ int ov;
+
+ /* draw next address to try */
+ if (NULL == up->addr)
+ up->addr = s_gpsd_addr;
+ ai = up->addr;
+ up->addr = ai->ai_next;
+
+ /* try to create a matching socket */
+ up->fdt = socket(
+ ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (-1 == up->fdt) {
+ if (syslogok(pp, up))
+ msyslog(LOG_ERR,
+ "%s: cannot create GPSD socket: %m",
+ refnumtoa(&peer->srcadr));
+ goto no_socket;
+ }
+
+ /* make sure the socket is non-blocking */
+ rc = fcntl(up->fdt, F_SETFL, O_NONBLOCK, 1);
+ if (-1 == rc) {
+ if (syslogok(pp, up))
+ msyslog(LOG_ERR,
+ "%s: cannot set GPSD socket to non-blocking: %m",
+ refnumtoa(&peer->srcadr));
+ goto no_socket;
+ }
+ /* disable nagling */
+ ov = 1;
+ rc = setsockopt(up->fdt, IPPROTO_TCP, TCP_NODELAY,
+ (char*)&ov, sizeof(ov));
+ if (-1 == rc) {
+ if (syslogok(pp, up))
+ msyslog(LOG_INFO,
+ "%s: cannot disable TCP nagle: %m",
+ refnumtoa(&peer->srcadr));
+ }
+
+ /* start a non-blocking connect */
+ rc = connect(up->fdt, ai->ai_addr, ai->ai_addrlen);
+ if (-1 == rc && errno != EINPROGRESS) {
+ if (syslogok(pp, up))
+ msyslog(LOG_ERR,
+ "%s: cannot connect GPSD socket: %m",
+ refnumtoa(&peer->srcadr));
+ goto no_socket;
+ }
+
+ return;
+
+ no_socket:
+ if (-1 != up->fdt)
+ close(up->fdt);
+ up->fdt = -1;
+ up->tickover = up->tickpres;
+ up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void
+gpsd_test_socket(
+ peerT * const peer)
+{
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
+
+ int ec, rc;
+ socklen_t lc;
+
+ /* Check if the non-blocking connect was finished by testing the
+ * socket for writeability. Use the 'poll()' API if available
+ * and 'select()' otherwise.
+ */
+ DPRINTF(2, ("GPSD_JSON(%d): check connect, fd=%d\n",
+ up->unit, up->fdt));
+
+#if defined(HAVE_SYS_POLL_H)
+ {
+ struct pollfd pfd;
+
+ pfd.events = POLLOUT;
+ pfd.fd = up->fdt;
+ rc = poll(&pfd, 1, 0);
+ if (1 != rc || !(pfd.revents & POLLOUT))
+ return;
+ }
+#elif defined(HAVE_SYS_SELECT_H)
+ {
+ struct timeval tout;
+ fd_set wset;
+
+ memset(&tout, 0, sizeof(tout));
+ FD_ZERO(&wset);
+ FD_SET(up->fdt, &wset);
+ rc = select(up->fdt+1, NULL, &wset, NULL, &tout);
+ if (0 == rc || !(FD_ISSET(up->fdt, &wset)))
+ return;
+ }
+#else
+# error Blooper! That should have been found earlier!
+#endif
+
+ /* next timeout is a full one... */
+ up->tickover = TICKOVER_LOW;
+
+ /* check for socket error */
+ ec = 0;
+ lc = sizeof(ec);
+ rc = getsockopt(up->fdt, SOL_SOCKET, SO_ERROR, &ec, &lc);
+ DPRINTF(1, ("GPSD_JSON(%d): connect finshed, fd=%d, ec=%d(%s)\n",
+ up->unit, up->fdt, ec, strerror(ec)));
+ if (-1 == rc || 0 != ec) {
+ errno = ec;
+ if (syslogok(pp, up))
+ msyslog(LOG_ERR,
+ "%s: (async)cannot connect GPSD socket: %m",
+ refnumtoa(&peer->srcadr));
+ goto no_socket;
+ }
+ /* swap socket FDs, and make sure the clock was added */
+ pp->io.fd = up->fdt;
+ up->fdt = -1;
+ if (0 == io_addclock(&pp->io)) {
+ if (syslogok(pp, up))
+ msyslog(LOG_ERR,
+ "%s: failed to register with I/O engine",
+ refnumtoa(&peer->srcadr));
+ goto no_socket;
+ }
+ return;
+
+ no_socket:
+ if (-1 != up->fdt)
+ close(up->fdt);
+ up->fdt = -1;
+ up->tickover = up->tickpres;
+ up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH);
+}
+
+/* =====================================================================
+ * helper stuff
+ */
+
+/*
+ * shm_clockstats - dump and reset counters
+ */
+static void
+gpsd_clockstats(
+ int unit,
+ peerT * const peer
+ )
+{
+ clockprocT * const pp = peer->procptr;
+ gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr;
+
+ char logbuf[128];
+ unsigned int llen;
+
+ /* if snprintf() returns a negative values on errors (some older
+ * ones do) make sure we are NUL terminated. Using an unsigned
+ * result does the trick.
+ */
+ llen = snprintf(logbuf, sizeof(logbuf),
+ "good=%-3u badtime=%-3u baddate=%-3u badreply=%-3u recv=%-3u",
+ up->tc_good, up->tc_btime, up->tc_bdate,
+ up->tc_breply, up->tc_recv);
+ logbuf[min(llen, sizeof(logbuf)-1)] = '\0';
+ record_clock_stats(&peer->srcadr, logbuf);
+}
+
+/* -------------------------------------------------------------------
+ * Convert a GPSD timestam (ISO8601 Format) to an l_fp
+ */
+static BOOL
+convert_ascii_time(
+ l_fp * fp ,
+ const char * gps_time)
+{
+ char *ep;
+ struct tm gd;
+ struct timespec ts;
+ long dw;
+
+ /* Use 'strptime' to take the brunt of the work, then parse
+ * the fractional part manually, starting with a digit weight of
+ * 10^8 nanoseconds.
+ */
+ ts.tv_nsec = 0;
+ ep = strptime(gps_time, "%Y-%m-%dT%H:%M:%S", &gd);
+ if (*ep == '.') {
+ dw = 100000000;
+ while (isdigit(*++ep)) {
+ ts.tv_nsec += (*ep - '0') * dw;
+ dw /= 10;
+ }
+ }
+ if (ep[0] != 'Z' || ep[1] != '\0')
+ return FALSE;
+
+ /* now convert the whole thing into a 'l_fp' */
+ ts.tv_sec = (ntpcal_tm_to_rd(&gd) - DAY_NTP_STARTS) * SECSPERDAY
+ + ntpcal_tm_to_daysec(&gd);
+ *fp = tspec_intv_to_lfp(ts);
+
+ return TRUE;
+}
+
+/* -------------------------------------------------------------------
+ * Save the last timecode string, making sure it's properly truncated
+ * if necessary and NUL terminated in any case.
+ */
+static void
+save_ltc(
+ clockprocT * const pp,
+ const char * const tc)
+{
+ size_t len;
+
+ len = (tc) ? strlen(tc) : 0;
+ if (len >= sizeof(pp->a_lastcode))
+ len = sizeof(pp->a_lastcode) - 1;
+ pp->lencode = (u_short)len;
+ memcpy(pp->a_lastcode, tc, len);
+ pp->a_lastcode[len] = '\0';
+}
+
+/*
+ * -------------------------------------------------------------------
+ * asprintf replacement... it's not available everywhere...
+ */
+static int
+myasprintf(
+ char ** spp,
+ char const * fmt,
+ ... )
+{
+ size_t alen, plen;
+
+ alen = 32;
+ *spp = NULL;
+ do {
+ va_list va;
+
+ alen += alen;
+ free(*spp);
+ *spp = (char*)malloc(alen);
+ if (NULL == *spp)
+ return -1;
+
+ va_start(va, fmt);
+ plen = (size_t)vsnprintf(*spp, alen, fmt, va);
+ va_end(va);
+ } while (plen >= alen);
+
+ return (int)plen;
+}
+
+#else
+NONEMPTY_TRANSLATION_UNIT
+#endif /* REFCLOCK && CLOCK_GPSDJSON */
diff --git a/ntpd/refclock_gpsvme.c b/ntpd/refclock_gpsvme.c
new file mode 100644
index 0000000..66ccc9a
--- /dev/null
+++ b/ntpd/refclock_gpsvme.c
@@ -0,0 +1,253 @@
+/* refclock_psc.c: clock driver for Brandywine PCI-SyncClock32/HP-UX 11.X */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#if defined(REFCLOCK) && defined(CLOCK_GPSVME)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#ifdef __hpux
+#include <sys/rtprio.h> /* may already be included above */
+#include <sys/lock.h> /* NEEDED for PROCLOCK */
+#endif /* __hpux */
+
+#ifdef __linux__
+#include <sys/ioctl.h> /* for _IOR, ioctl */
+#endif /* __linux__ */
+
+enum { /* constants */
+ BUFSIZE = 32,
+ PSC_SYNC_OK = 0x40, /* Sync status bit */
+ DP_LEAPSEC_DAY10DAY1 = 0x82, /* DP RAM address */
+ DP_LEAPSEC_DAY1000DAY100 = 0x83,
+ DELAY = 1,
+ NUNIT = 2 /* max UNITS */
+};
+
+/* clock card registers */
+struct psc_regs {
+ uint32_t low_time; /* card base + 0x00 */
+ uint32_t high_time; /* card base + 0x04 */
+ uint32_t ext_low_time; /* card base + 0x08 */
+ uint32_t ext_high_time; /* card base + 0x0C */
+ uint8_t device_status; /* card base + 0x10 */
+ uint8_t device_control; /* card base + 0x11 */
+ uint8_t reserved0; /* card base + 0x12 */
+ uint8_t ext_100ns; /* card base + 0x13 */
+ uint8_t match_usec; /* card base + 0x14 */
+ uint8_t match_msec; /* card base + 0x15 */
+ uint8_t reserved1; /* card base + 0x16 */
+ uint8_t reserved2; /* card base + 0x17 */
+ uint8_t reserved3; /* card base + 0x18 */
+ uint8_t reserved4; /* card base + 0x19 */
+ uint8_t dp_ram_addr; /* card base + 0x1A */
+ uint8_t reserved5; /* card base + 0x1B */
+ uint8_t reserved6; /* card base + 0x1C */
+ uint8_t reserved7; /* card base + 0x1D */
+ uint8_t dp_ram_data; /* card base + 0x1E */
+ uint8_t reserved8; /* card base + 0x1F */
+} *volatile regp[NUNIT];
+
+#define PSC_REGS _IOR('K', 0, long) /* ioctl argument */
+
+/* Macros to swap byte order and convert BCD to binary */
+#define SWAP(val) ( ((val) >> 24) | (((val) & 0x00ff0000) >> 8) | \
+(((val) & 0x0000ff00) << 8) | (((val) & 0x000000ff) << 24) )
+#define BCD2INT2(val) ( ((val) >> 4 & 0x0f)*10 + ((val) & 0x0f) )
+#define BCD2INT3(val) ( ((val) >> 8 & 0x0f)*100 + ((val) >> 4 & 0x0f)*10 + \
+((val) & 0x0f) )
+
+/* PSC interface definitions */
+#define PRECISION (-20) /* precision assumed (1 us) */
+#define REFID "USNO" /* reference ID */
+#define DESCRIPTION "Brandywine PCI-SyncClock32"
+#define DEVICE "/dev/refclock%1d" /* device file */
+
+/* clock unit control structure */
+struct psc_unit {
+ short unit; /* NTP refclock unit number */
+ short last_hour; /* last hour (monitor leap sec) */
+ int msg_flag[2]; /* count error messages */
+};
+int fd[NUNIT]; /* file descriptor */
+
+/* Local function prototypes */
+static int psc_start(int, struct peer *);
+static void psc_shutdown(int, struct peer *);
+static void psc_poll(int, struct peer *);
+static void check_leap_sec(struct refclockproc *, int);
+
+/* Transfer vector */
+struct refclock refclock_gpsvme = {
+ psc_start, psc_shutdown, psc_poll, noentry, noentry, noentry, NOFLAGS
+};
+
+/* psc_start: open device and initialize data for processing */
+static int
+psc_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ char buf[BUFSIZE];
+ struct refclockproc *pp;
+ struct psc_unit *up = emalloc(sizeof *up);
+
+ if (unit < 0 || unit > 1) { /* support units 0 and 1 */
+ msyslog(LOG_ERR, "psc_start: bad unit: %d", unit);
+ return 0;
+ }
+
+ memset(up, '\0', sizeof *up);
+
+ snprintf(buf, sizeof(buf), DEVICE, unit); /* dev file name */
+ fd[unit] = open(buf, O_RDONLY); /* open device file */
+ if (fd[unit] < 0) {
+ msyslog(LOG_ERR, "psc_start: unit: %d, open failed. %m", unit);
+ return 0;
+ }
+
+ /* get the address of the mapped regs */
+ if (ioctl(fd[unit], PSC_REGS, &regp[unit]) < 0) {
+ msyslog(LOG_ERR, "psc_start: unit: %d, ioctl failed. %m", unit);
+ return 0;
+ }
+
+ /* initialize peer variables */
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = -1;
+ pp->unitptr = up;
+ get_systime(&pp->lastrec);
+ memcpy(&pp->refid, REFID, 4);
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ up->unit = unit;
+#ifdef __hpux
+ rtprio(0,120); /* set real time priority */
+ plock(PROCLOCK); /* lock process in memory */
+#endif /* __hpux */
+ return 1;
+}
+
+/* psc_shutdown: shut down the clock */
+static void
+psc_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ if (NULL != peer->procptr->unitptr)
+ free(peer->procptr->unitptr);
+ if (fd[unit] > 0)
+ close(fd[unit]);
+}
+
+/* psc_poll: read, decode, and record device time */
+static void
+psc_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp = peer->procptr;
+ struct psc_unit *up;
+ unsigned tlo, thi;
+ unsigned char status;
+
+ up = (struct psc_unit *) pp->unitptr;
+ tlo = regp[unit]->low_time; /* latch and read first 4 bytes */
+ thi = regp[unit]->high_time; /* read 4 higher order bytes */
+ status = regp[unit]->device_status; /* read device status byte */
+
+ if (!(status & PSC_SYNC_OK)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ if (!up->msg_flag[unit]) { /* write once to system log */
+ msyslog(LOG_WARNING,
+ "SYNCHRONIZATION LOST on unit %1d, status %02x\n",
+ unit, status);
+ up->msg_flag[unit] = 1;
+ }
+ return;
+ }
+
+ get_systime(&pp->lastrec);
+ pp->polls++;
+
+ tlo = SWAP(tlo); /* little to big endian swap on */
+ thi = SWAP(thi); /* copy of data */
+ /* convert the BCD time to broken down time used by refclockproc */
+ pp->day = BCD2INT3((thi & 0x0FFF0000) >> 16);
+ pp->hour = BCD2INT2((thi & 0x0000FF00) >> 8);
+ pp->minute = BCD2INT2(thi & 0x000000FF);
+ pp->second = BCD2INT2(tlo >> 24);
+ /* ntp_process() in ntp_refclock.c appears to use usec as fraction of
+ second in microseconds if usec is nonzero. */
+ pp->nsec = 1000000*BCD2INT3((tlo & 0x00FFF000) >> 12) +
+ BCD2INT3(tlo & 0x00000FFF);
+
+ snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
+ "%3.3d %2.2d:%2.2d:%2.2d.%09ld %02x %08x %08x", pp->day,
+ pp->hour, pp->minute, pp->second, pp->nsec, status, thi,
+ tlo);
+ pp->lencode = strlen(pp->a_lastcode);
+
+ /* compute the timecode timestamp */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ /* simulate the NTP receive and packet procedures */
+ refclock_receive(peer);
+ /* write clock statistics to file */
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+ /* With the first timecode beginning the day, check for a GPS
+ leap second notification. */
+ if (pp->hour < up->last_hour) {
+ check_leap_sec(pp, unit);
+ up->msg_flag[0] = up->msg_flag[1] = 0; /* reset flags */
+ }
+ up->last_hour = pp->hour;
+}
+
+/* check_leap_sec: read the Dual Port RAM leap second day registers. The
+ onboard GPS receiver should write the hundreds digit of day of year in
+ DP_LeapSec_Day1000Day100 and the tens and ones digits in
+ DP_LeapSec_Day10Day1. If these values are nonzero and today, we have
+ a leap second pending, so we set the pp->leap flag to LEAP_ADDSECOND.
+ If the BCD data are zero or a date other than today, set pp->leap to
+ LEAP_NOWARNING. */
+static void
+check_leap_sec(struct refclockproc *pp, int unit)
+{
+ unsigned char dhi, dlo;
+ int leap_day;
+
+ regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY10DAY1;
+ usleep(DELAY);
+ dlo = regp[unit]->dp_ram_data;
+ regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY1000DAY100;
+ usleep(DELAY);
+ dhi = regp[unit]->dp_ram_data;
+ leap_day = BCD2INT2(dlo) + 100*(dhi & 0x0F);
+
+ pp->leap = LEAP_NOWARNING; /* default */
+ if (leap_day && leap_day == pp->day) {
+ pp->leap = LEAP_ADDSECOND; /* leap second today */
+ msyslog(LOG_ERR, "LEAP_ADDSECOND flag set, day %d (%x %x).",
+ leap_day, dhi, dlo);
+ }
+}
+
+#else
+int refclock_gpsvme_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_heath.c b/ntpd/refclock_heath.c
new file mode 100644
index 0000000..435d8f6
--- /dev/null
+++ b/ntpd/refclock_heath.c
@@ -0,0 +1,450 @@
+/*
+ * refclock_heath - clock driver for Heath GC-1000
+ * (but no longer the GC-1001 Model II, which apparently never worked)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_HEATH)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* not HAVE_SYS_IOCTL_H */
+
+/*
+ * This driver supports the Heath GC-1000 Most Accurate Clock, with
+ * RS232C Output Accessory. This is a WWV/WWVH receiver somewhat less
+ * robust than other supported receivers. Its claimed accuracy is 100 ms
+ * when actually synchronized to the broadcast signal, but this doesn't
+ * happen even most of the time, due to propagation conditions, ambient
+ * noise sources, etc. When not synchronized, the accuracy is at the
+ * whim of the internal clock oscillator, which can wander into the
+ * sunset without warning. Since the indicated precision is 100 ms,
+ * expect a host synchronized only to this thing to wander to and fro,
+ * occasionally being rudely stepped when the offset exceeds the default
+ * clock_max of 128 ms.
+ *
+ * There were two GC-1000 versions supported by this driver. The original
+ * GC-1000 with RS-232 output first appeared in 1983, but dissapeared
+ * from the market a few years later. The GC-1001 II with RS-232 output
+ * first appeared circa 1990, but apparently is no longer manufactured.
+ * The two models differ considerably, both in interface and commands.
+ * The GC-1000 has a pseudo-bipolar timecode output triggered by a RTS
+ * transition. The timecode includes both the day of year and time of
+ * day. The GC-1001 II has a true bipolar output and a complement of
+ * single character commands. The timecode includes only the time of
+ * day.
+ *
+ * The GC-1001 II was apparently never tested and, based on a Coverity
+ * scan, apparently never worked [Bug 689]. Related code has been disabled.
+ *
+ * GC-1000
+ *
+ * The internal DIPswitches should be set to operate in MANUAL mode. The
+ * external DIPswitches should be set to GMT and 24-hour format.
+ *
+ * In MANUAL mode the clock responds to a rising edge of the request to
+ * send (RTS) modem control line by sending the timecode. Therefore, it
+ * is necessary that the operating system implement the TIOCMBIC and
+ * TIOCMBIS ioctl system calls and TIOCM_RTS control bit. Present
+ * restrictions require the use of a POSIX-compatible programming
+ * interface, although other interfaces may work as well.
+ *
+ * A simple hardware modification to the clock can be made which
+ * prevents the clock hearing the request to send (RTS) if the HI SPEC
+ * lamp is out. Route the HISPEC signal to the tone decoder board pin
+ * 19, from the display, pin 19. Isolate pin 19 of the decoder board
+ * first, but maintain connection with pin 10. Also isolate pin 38 of
+ * the CPU on the tone board, and use half an added 7400 to gate the
+ * original signal to pin 38 with that from pin 19.
+ *
+ * The clock message consists of 23 ASCII printing characters in the
+ * following format:
+ *
+ * hh:mm:ss.f AM dd/mm/yr<cr>
+ *
+ * hh:mm:ss.f = hours, minutes, seconds
+ * f = deciseconds ('?' when out of spec)
+ * AM/PM/bb = blank in 24-hour mode
+ * dd/mm/yr = day, month, year
+ *
+ * The alarm condition is indicated by '?', rather than a digit, at f.
+ * Note that 0?:??:??.? is displayed before synchronization is first
+ * established and hh:mm:ss.? once synchronization is established and
+ * then lost again for about a day.
+ *
+ * GC-1001 II
+ *
+ * Commands consist of a single letter and are case sensitive. When
+ * enterred in lower case, a description of the action performed is
+ * displayed. When enterred in upper case the action is performed.
+ * Following is a summary of descriptions as displayed by the clock:
+ *
+ * The clock responds with a command The 'A' command returns an ASCII
+ * local time string: HH:MM:SS.T xx<CR>, where
+ *
+ * HH = hours
+ * MM = minutes
+ * SS = seconds
+ * T = tenths-of-seconds
+ * xx = 'AM', 'PM', or ' '
+ * <CR> = carriage return
+ *
+ * The 'D' command returns 24 pairs of bytes containing the variable
+ * divisor value at the end of each of the previous 24 hours. This
+ * allows the timebase trimming process to be observed. UTC hour 00 is
+ * always returned first. The first byte of each pair is the high byte
+ * of (variable divisor * 16); the second byte is the low byte of
+ * (variable divisor * 16). For example, the byte pair 3C 10 would be
+ * returned for a divisor of 03C1 hex (961 decimal).
+ *
+ * The 'I' command returns: | TH | TL | ER | DH | DL | U1 | I1 | I2 | ,
+ * where
+ *
+ * TH = minutes since timebase last trimmed (high byte)
+ * TL = minutes since timebase last trimmed (low byte)
+ * ER = last accumulated error in 1.25 ms increments
+ * DH = high byte of (current variable divisor * 16)
+ * DL = low byte of (current variable divisor * 16)
+ * U1 = UT1 offset (/.1 s): | + | 4 | 2 | 1 | 0 | 0 | 0 | 0 |
+ * I1 = information byte 1: | W | C | D | I | U | T | Z | 1 | ,
+ * where
+ *
+ * W = set by WWV(H)
+ * C = CAPTURE LED on
+ * D = TRIM DN LED on
+ * I = HI SPEC LED on
+ * U = TRIM UP LED on
+ * T = DST switch on
+ * Z = UTC switch on
+ * 1 = UT1 switch on
+ *
+ * I2 = information byte 2: | 8 | 8 | 4 | 2 | 1 | D | d | S | ,
+ * where
+ *
+ * 8, 8, 4, 2, 1 = TIME ZONE switch settings
+ * D = DST bit (#55) in last-received frame
+ * d = DST bit (#2) in last-received frame
+ * S = clock is in simulation mode
+ *
+ * The 'P' command returns 24 bytes containing the number of frames
+ * received without error during UTC hours 00 through 23, providing an
+ * indication of hourly propagation. These bytes are updated each hour
+ * to reflect the previous 24 hour period. UTC hour 00 is always
+ * returned first.
+ *
+ * The 'T' command returns the UTC time: | HH | MM | SS | T0 | , where
+ * HH = tens-of-hours and hours (packed BCD)
+ * MM = tens-of-minutes and minutes (packed BCD)
+ * SS = tens-of-seconds and seconds (packed BCD)
+ * T = tenths-of-seconds (BCD)
+ *
+ * Fudge Factors
+ *
+ * A fudge time1 value of .04 s appears to center the clock offset
+ * residuals. The fudge time2 parameter is the local time offset east of
+ * Greenwich, which depends on DST. Sorry about that, but the clock
+ * gives no hint on what the DIPswitches say.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/heath%d" /* device name and unit */
+#define PRECISION (-4) /* precision assumed (about 100 ms) */
+#define REFID "WWV\0" /* reference ID */
+#define DESCRIPTION "Heath GC-1000 Most Accurate Clock" /* WRU */
+
+#define LENHEATH1 23 /* min timecode length */
+#if 0 /* BUG 689 */
+#define LENHEATH2 13 /* min timecode length */
+#endif
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Baud rate table. The GC-1000 supports 1200, 2400 and 4800; the
+ * GC-1001 II supports only 9600.
+ */
+static int speed[] = {B1200, B2400, B4800, B9600};
+
+/*
+ * Function prototypes
+ */
+static int heath_start (int, struct peer *);
+static void heath_shutdown (int, struct peer *);
+static void heath_receive (struct recvbuf *);
+static void heath_poll (int, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_heath = {
+ heath_start, /* start up driver */
+ heath_shutdown, /* shut down driver */
+ heath_poll, /* transmit poll message */
+ noentry, /* not used (old heath_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old heath_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * heath_start - open the devices and initialize data for processing
+ */
+static int
+heath_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+ fd = refclock_open(device, speed[peer->ttl & 0x3],
+ LDISC_REMOTE);
+ if (fd <= 0)
+ return (0);
+ pp = peer->procptr;
+ pp->io.clock_recv = heath_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy(&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * heath_shutdown - shut down the clock
+ */
+static void
+heath_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+}
+
+
+/*
+ * heath_receive - receive data from the serial interface
+ */
+static void
+heath_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ int month, day;
+ int i;
+ char dsec, a[5];
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
+ &trtmp);
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ switch (pp->lencode) {
+
+ /*
+ * GC-1000 timecode format: "hh:mm:ss.f AM mm/dd/yy"
+ * GC-1001 II timecode format: "hh:mm:ss.f "
+ */
+ case LENHEATH1:
+ if (sscanf(pp->a_lastcode,
+ "%2d:%2d:%2d.%c%5c%2d/%2d/%2d", &pp->hour,
+ &pp->minute, &pp->second, &dsec, a, &month, &day,
+ &pp->year) != 8) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ break;
+
+#if 0 /* BUG 689 */
+ /*
+ * GC-1001 II timecode format: "hh:mm:ss.f "
+ */
+ case LENHEATH2:
+ if (sscanf(pp->a_lastcode, "%2d:%2d:%2d.%c", &pp->hour,
+ &pp->minute, &pp->second, &dsec) != 4) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ } else {
+ struct tm *tm_time_p;
+ time_t now;
+
+ time(&now); /* we should grab 'now' earlier */
+ tm_time_p = gmtime(&now);
+ /*
+ * There is a window of time around midnight
+ * where this will Do The Wrong Thing.
+ */
+ if (tm_time_p) {
+ month = tm_time_p->tm_mon + 1;
+ day = tm_time_p->tm_mday;
+ } else {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ }
+ break;
+#endif
+
+ default:
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * We determine the day of the year from the DIPswitches. This
+ * should be fixed, since somebody might forget to set them.
+ * Someday this hazard will be fixed by a fiendish scheme that
+ * looks at the timecode and year the radio shows, then computes
+ * the residue of the seconds mod the seconds in a leap cycle.
+ * If in the third year of that cycle and the third and later
+ * months of that year, add one to the day. Then, correct the
+ * timecode accordingly. Icky pooh. This bit of nonsense could
+ * be avoided if the engineers had been required to write a
+ * device driver before finalizing the timecode format.
+ */
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+
+ /*
+ * Determine synchronization and last update
+ */
+ if (!isdigit((int)dsec))
+ pp->leap = LEAP_NOTINSYNC;
+ else {
+ pp->nsec = (dsec - '0') * 100000000;
+ pp->leap = LEAP_NOWARNING;
+ }
+ if (!refclock_process(pp))
+ refclock_report(peer, CEVNT_BADTIME);
+}
+
+
+/*
+ * heath_poll - called by the transmit procedure
+ */
+static void
+heath_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ int bits = TIOCM_RTS;
+
+ /*
+ * At each poll we check for timeout and toggle the RTS modem
+ * control line, then take a timestamp. Presumably, this is the
+ * event the radio captures to generate the timecode.
+ * Apparently, the radio takes about a second to make up its
+ * mind to send a timecode, so the receive timestamp is
+ * worthless.
+ */
+ pp = peer->procptr;
+
+ /*
+ * We toggle the RTS modem control lead (GC-1000) and sent a T
+ * (GC-1001 II) to kick a timecode loose from the radio. This
+ * code works only for POSIX and SYSV interfaces. With bsd you
+ * are on your own. We take a timestamp between the up and down
+ * edges to lengthen the pulse, which should be about 50 usec on
+ * a Sun IPC. With hotshot CPUs, the pulse might get too short.
+ * Later.
+ *
+ * Bug 689: Even though we no longer support the GC-1001 II,
+ * I'm leaving the 'T' write in for timing purposes.
+ */
+ if (ioctl(pp->io.fd, TIOCMBIC, (char *)&bits) < 0)
+ refclock_report(peer, CEVNT_FAULT);
+ get_systime(&pp->lastrec);
+ if (write(pp->io.fd, "T", 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+ ioctl(pp->io.fd, TIOCMBIS, (char *)&bits);
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("heath: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+ pp->polls++;
+}
+
+#else
+int refclock_heath_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_hopfpci.c b/ntpd/refclock_hopfpci.c
new file mode 100644
index 0000000..95bcab9
--- /dev/null
+++ b/ntpd/refclock_hopfpci.c
@@ -0,0 +1,258 @@
+/*
+ * refclock_hopfpci.c
+ *
+ * - clock driver for hopf 6039 PCI board (GPS or DCF77)
+ * Bernd Altmeier altmeier@atlsoft.de
+ *
+ * latest source and further information can be found at:
+ * http://www.ATLSoft.de/ntp
+ *
+ * In order to run this driver you have to install and test
+ * the PCI-board driver for your system first.
+ *
+ * On Linux/UNIX
+ *
+ * The driver attempts to open the device /dev/hopf6039 .
+ * The device entry will be made by the installation process of
+ * the kernel module for the PCI-bus board. The driver sources
+ * belongs to the delivery equipment of the PCI-board.
+ *
+ * On Windows NT/2000
+ *
+ * The driver attempts to open the device by calling the function
+ * "OpenHopfDevice()". This function will be installed by the
+ * Device Driver for the PCI-bus board. The driver belongs to the
+ * delivery equipment of the PCI-board.
+ *
+ *
+ * Start 21.03.2000 Revision: 01.20
+ * changes 22.12.2000 Revision: 01.40 flag1 = 1 sync even if Quarz
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_HOPF_PCI)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#undef fileno
+#include <ctype.h>
+#undef fileno
+
+#ifndef SYS_WINNT
+# include <sys/ipc.h>
+# include <sys/ioctl.h>
+# include <assert.h>
+# include <unistd.h>
+# include <stdio.h>
+# include "hopf6039.h"
+#else
+# include "hopf_PCI_io.h"
+#endif
+
+/*
+ * hopfpci interface definitions
+ */
+#define PRECISION (-10) /* precision assumed (1 ms) */
+#define REFID "hopf" /* reference ID */
+#define DESCRIPTION "hopf Elektronik PCI radio board"
+
+#define NSAMPLES 3 /* stages of median filter */
+#ifndef SYS_WINNT
+# define DEVICE "/dev/hopf6039" /* device name inode*/
+#else
+# define DEVICE "hopf6039" /* device name WinNT */
+#endif
+
+#define LEWAPWAR 0x20 /* leap second warning bit */
+
+#define HOPF_OPMODE 0xC0 /* operation mode mask */
+#define HOPF_INVALID 0x00 /* no time code available */
+#define HOPF_INTERNAL 0x40 /* internal clock */
+#define HOPF_RADIO 0x80 /* radio clock */
+#define HOPF_RADIOHP 0xC0 /* high precision radio clock */
+
+
+/*
+ * hopfclock unit control structure.
+ */
+struct hopfclock_unit {
+ short unit; /* NTP refclock unit number */
+ char leap_status; /* leap second flag */
+};
+int fd; /* file descr. */
+
+/*
+ * Function prototypes
+ */
+static int hopfpci_start (int, struct peer *);
+static void hopfpci_shutdown (int, struct peer *);
+static void hopfpci_poll (int unit, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_hopfpci = {
+ hopfpci_start, /* start up driver */
+ hopfpci_shutdown, /* shut down driver */
+ hopfpci_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * hopfpci_start - attach to hopf PCI board 6039
+ */
+static int
+hopfpci_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct hopfclock_unit *up;
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+
+#ifndef SYS_WINNT
+
+ fd = open(DEVICE,O_RDWR); /* try to open hopf clock device */
+
+#else
+ if (!OpenHopfDevice()) {
+ msyslog(LOG_ERR, "Start: %s unit: %d failed!", DEVICE, unit);
+ free(up);
+ return (0);
+ }
+#endif
+
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = INVALID_SOCKET;
+ pp->unitptr = up;
+
+ get_systime(&pp->lastrec);
+
+ /*
+ * Initialize miscellaneous peer variables
+ */
+ memcpy((char *)&pp->refid, REFID, 4);
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ up->leap_status = 0;
+ up->unit = (short) unit;
+ return (1);
+}
+
+
+/*
+ * hopfpci_shutdown - shut down the clock
+ */
+static void
+hopfpci_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+
+#ifndef SYS_WINNT
+ close(fd);
+#else
+ CloseHopfDevice();
+#endif
+ if (NULL != peer->procptr->unitptr)
+ free(peer->procptr->unitptr);
+}
+
+
+/*
+ * hopfpci_poll - called by the transmit procedure
+ */
+static void
+hopfpci_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ HOPFTIME m_time;
+
+ pp = peer->procptr;
+
+#ifndef SYS_WINNT
+ if (ioctl(fd, HOPF_CLOCK_GET_UTC, &m_time) < 0)
+ msyslog(LOG_ERR, "HOPF_P(%d): HOPF_CLOCK_GET_UTC: %m",
+ unit);
+#else
+ GetHopfSystemTime(&m_time);
+#endif
+ pp->polls++;
+
+ pp->day = ymd2yd(m_time.wYear,m_time.wMonth,m_time.wDay);
+ pp->hour = m_time.wHour;
+ pp->minute = m_time.wMinute;
+ pp->second = m_time.wSecond;
+ pp->nsec = m_time.wMilliseconds * 1000000;
+ if (m_time.wStatus & LEWAPWAR)
+ pp->leap = LEAP_ADDSECOND;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
+ "ST: %02X T: %02d:%02d:%02d.%03ld D: %02d.%02d.%04d",
+ m_time.wStatus, pp->hour, pp->minute, pp->second,
+ pp->nsec / 1000000, m_time.wDay, m_time.wMonth,
+ m_time.wYear);
+ pp->lencode = (u_short)strlen(pp->a_lastcode);
+
+ get_systime(&pp->lastrec);
+
+ /*
+ * If clock has no valid status then report error and exit
+ */
+ if ((m_time.wStatus & HOPF_OPMODE) == HOPF_INVALID) { /* time ok? */
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+
+ /*
+ * Test if time is running on internal quarz
+ * if CLK_FLAG1 is set, sychronize even if no radio operation
+ */
+
+ if ((m_time.wStatus & HOPF_OPMODE) == HOPF_INTERNAL){
+ if ((pp->sloppyclockflag & CLK_FLAG1) == 0) {
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+ }
+
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ return;
+}
+
+#else
+int refclock_hopfpci_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_hopfser.c b/ntpd/refclock_hopfser.c
new file mode 100644
index 0000000..dae8b37
--- /dev/null
+++ b/ntpd/refclock_hopfser.c
@@ -0,0 +1,372 @@
+/*
+ *
+ * refclock_hopfser.c
+ * - clock driver for hopf serial boards (GPS or DCF77)
+ *
+ * Date: 30.03.2000 Revision: 01.10
+ *
+ * latest source and further information can be found at:
+ * http://www.ATLSoft.de/ntp
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined(REFCLOCK) && (defined(CLOCK_HOPF_SERIAL))
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#if defined HAVE_SYS_MODEM_H
+# include <sys/modem.h>
+# ifndef __QNXNTO__
+# define TIOCMSET MCSETA
+# define TIOCMGET MCGETA
+# define TIOCM_RTS MRTS
+# endif
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# ifdef TERMIOS_NEEDS__SVID3
+# define _SVID3
+# endif
+# include <termios.h>
+# ifdef TERMIOS_NEEDS__SVID3
+# undef _SVID3
+# endif
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+#ifdef SYS_WINNT
+extern int async_write(int, const void *, unsigned int);
+#undef write
+#define write(fd, data, octets) async_write(fd, data, octets)
+#endif
+
+/*
+ * clock definitions
+ */
+#define DESCRIPTION "hopf Elektronik serial clock" /* Long name */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "hopf\0" /* reference ID */
+/*
+ * I/O definitions
+ */
+#define DEVICE "/dev/hopfclock%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+
+
+#define STX 0x02
+#define ETX 0x03
+#define CR 0x0c
+#define LF 0x0a
+
+/* parse states */
+#define REC_QUEUE_EMPTY 0
+#define REC_QUEUE_FULL 1
+
+#define HOPF_OPMODE 0x0C /* operation mode mask */
+#define HOPF_INVALID 0x00 /* no time code available */
+#define HOPF_INTERNAL 0x04 /* internal clock */
+#define HOPF_RADIO 0x08 /* radio clock */
+#define HOPF_RADIOHP 0x0C /* high precision radio clock */
+
+/*
+ * hopfclock unit control structure.
+ */
+struct hopfclock_unit {
+ l_fp laststamp; /* last receive timestamp */
+ short unit; /* NTP refclock unit number */
+ u_long polled; /* flag to detect noreplies */
+ char leap_status; /* leap second flag */
+ int rpt_next;
+};
+
+/*
+ * Function prototypes
+ */
+
+static int hopfserial_start (int, struct peer *);
+static void hopfserial_shutdown (int, struct peer *);
+static void hopfserial_receive (struct recvbuf *);
+static void hopfserial_poll (int, struct peer *);
+/* static void hopfserial_io (struct recvbuf *); */
+/*
+ * Transfer vector
+ */
+struct refclock refclock_hopfser = {
+ hopfserial_start, /* start up driver */
+ hopfserial_shutdown, /* shut down driver */
+ hopfserial_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * hopfserial_start - open the devices and initialize data for processing
+ */
+static int
+hopfserial_start (
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hopfclock_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char gpsdev[20];
+
+ snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit);
+
+ /* LDISC_STD, LDISC_RAW
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ fd = refclock_open(gpsdev, SPEED232, LDISC_CLK);
+ if (fd <= 0) {
+#ifdef DEBUG
+ printf("hopfSerialClock(%d) start: open %s failed\n", unit, gpsdev);
+#endif
+ return 0;
+ }
+
+ msyslog(LOG_NOTICE, "hopfSerialClock(%d) fd: %d dev: %s", unit, fd,
+ gpsdev);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->unitptr = up;
+ pp->io.clock_recv = hopfserial_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+#ifdef DEBUG
+ printf("hopfSerialClock(%d) io_addclock\n", unit);
+#endif
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ pp->unitptr = NULL;
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ pp->clockdesc = DESCRIPTION;
+ peer->precision = PRECISION;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+ up->leap_status = 0;
+ up->unit = (short) unit;
+
+ return (1);
+}
+
+
+/*
+ * hopfserial_shutdown - shut down the clock
+ */
+static void
+hopfserial_shutdown (
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hopfclock_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ if (NULL != up)
+ free(up);
+}
+
+
+
+/*
+ * hopfserial_receive - receive data from the serial interface
+ */
+
+static void
+hopfserial_receive (
+ struct recvbuf *rbufp
+ )
+{
+ struct hopfclock_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ int synch; /* synchhronization indicator */
+ int DoW; /* Day of Week */
+
+ int day, month; /* ddd conversion */
+ int converted;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ if (up->rpt_next == 0 )
+ return;
+
+ up->rpt_next = 0; /* wait until next poll interval occur */
+
+ pp->lencode = (u_short)refclock_gtlin(rbufp, pp->a_lastcode,
+ sizeof(pp->a_lastcode),
+ &pp->lastrec);
+ if (pp->lencode == 0)
+ return;
+
+ converted = sscanf(pp->a_lastcode,
+#if 1
+ "%1x%1x%2d%2d%2d%2d%2d%2d", /* ...cr,lf */
+#else
+ "%*c%1x%1x%2d%2d%2d%2d%2d%2d", /* stx...cr,lf,etx */
+#endif
+ &synch,
+ &DoW,
+ &pp->hour,
+ &pp->minute,
+ &pp->second,
+ &day,
+ &month,
+ &pp->year);
+
+
+ /*
+ Validate received values at least enough to prevent internal
+ array-bounds problems, etc.
+ */
+ if ((8 != converted) || (pp->hour < 0) || (pp->hour > 23) ||
+ (pp->minute < 0) || (pp->minute > 59) || (pp->second < 0) ||
+ (pp->second > 60) /*Allow for leap seconds.*/ ||
+ (day < 1) || (day > 31) ||
+ (month < 1) || (month > 12) ||
+ (pp->year < 0) || (pp->year > 99)) {
+ /* Data out of range. */
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ /*
+ some preparations
+ */
+ pp->day = ymd2yd(pp->year,month,day);
+ pp->leap=0;
+
+ /* Year-2000 check! */
+ /* wrap 2-digit date into 4-digit */
+
+ if(pp->year < YEAR_PIVOT) { pp->year += 100; } /* < 98 */
+ pp->year += 1900;
+
+ /* preparation for timecode ntpq rl command ! */
+
+#if 0
+ snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
+ "STATUS: %1X%1X, DATE: %02d.%02d.%04d TIME: %02d:%02d:%02d",
+ synch,
+ DoW,
+ day,
+ month,
+ pp->year,
+ pp->hour,
+ pp->minute,
+ pp->second);
+
+ pp->lencode = strlen(pp->a_lastcode);
+ if ((synch && 0xc) == 0 ){ /* time ok? */
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+#endif
+ /*
+ * If clock has no valid status then report error and exit
+ */
+ if ((synch & HOPF_OPMODE) == HOPF_INVALID ){ /* time ok? */
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+
+ /*
+ * Test if time is running on internal quarz
+ * if CLK_FLAG1 is set, sychronize even if no radio operation
+ */
+
+ if ((synch & HOPF_OPMODE) == HOPF_INTERNAL){
+ if ((pp->sloppyclockflag & CLK_FLAG1) == 0) {
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+ }
+
+
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+#if 0
+ msyslog(LOG_ERR, " D:%x D:%d D:%d",synch,pp->minute,pp->second);
+#endif
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+ return;
+}
+
+
+/*
+ * hopfserial_poll - called by the transmit procedure
+ *
+ */
+static void
+hopfserial_poll (
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hopfclock_unit *up;
+ struct refclockproc *pp;
+ pp = peer->procptr;
+
+ up = pp->unitptr;
+
+ pp->polls++;
+ up->rpt_next = 1;
+
+#if 0
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#endif
+
+ return;
+}
+
+#else
+int refclock_hopfser_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_hpgps.c b/ntpd/refclock_hpgps.c
new file mode 100644
index 0000000..1d26d25
--- /dev/null
+++ b/ntpd/refclock_hpgps.c
@@ -0,0 +1,623 @@
+/*
+ * refclock_hpgps - clock driver for HP 58503A GPS receiver
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_HPGPS)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/* Version 0.1 April 1, 1995
+ * 0.2 April 25, 1995
+ * tolerant of missing timecode response prompt and sends
+ * clear status if prompt indicates error;
+ * can use either local time or UTC from receiver;
+ * can get receiver status screen via flag4
+ *
+ * WARNING!: This driver is UNDER CONSTRUCTION
+ * Everything in here should be treated with suspicion.
+ * If it looks wrong, it probably is.
+ *
+ * Comments and/or questions to: Dave Vitanye
+ * Hewlett Packard Company
+ * dave@scd.hp.com
+ * (408) 553-2856
+ *
+ * Thanks to the author of the PST driver, which was the starting point for
+ * this one.
+ *
+ * This driver supports the HP 58503A Time and Frequency Reference Receiver.
+ * This receiver uses HP SmartClock (TM) to implement an Enhanced GPS receiver.
+ * The receiver accuracy when locked to GPS in normal operation is better
+ * than 1 usec. The accuracy when operating in holdover is typically better
+ * than 10 usec. per day.
+ *
+ * The same driver also handles the HP Z3801A which is available surplus
+ * from the cell phone industry. It's popular with hams.
+ * It needs a different line setup: 19200 baud, 7 data bits, odd parity
+ * That is selected by adding "mode 1" to the server line in ntp.conf
+ * HP Z3801A code from Jeff Mock added by Hal Murray, Sep 2005
+ *
+ *
+ * The receiver should be operated with factory default settings.
+ * Initial driver operation: expects the receiver to be already locked
+ * to GPS, configured and able to output timecode format 2 messages.
+ *
+ * The driver uses the poll sequence :PTIME:TCODE? to get a response from
+ * the receiver. The receiver responds with a timecode string of ASCII
+ * printing characters, followed by a <cr><lf>, followed by a prompt string
+ * issued by the receiver, in the following format:
+ * T#yyyymmddhhmmssMFLRVcc<cr><lf>scpi >
+ *
+ * The driver processes the response at the <cr> and <lf>, so what the
+ * driver sees is the prompt from the previous poll, followed by this
+ * timecode. The prompt from the current poll is (usually) left unread until
+ * the next poll. So (except on the very first poll) the driver sees this:
+ *
+ * scpi > T#yyyymmddhhmmssMFLRVcc<cr><lf>
+ *
+ * The T is the on-time character, at 980 msec. before the next 1PPS edge.
+ * The # is the timecode format type. We look for format 2.
+ * Without any of the CLK or PPS stuff, then, the receiver buffer timestamp
+ * at the <cr> is 24 characters later, which is about 25 msec. at 9600 bps,
+ * so the first approximation for fudge time1 is nominally -0.955 seconds.
+ * This number probably needs adjusting for each machine / OS type, so far:
+ * -0.955000 on an HP 9000 Model 712/80 HP-UX 9.05
+ * -0.953175 on an HP 9000 Model 370 HP-UX 9.10
+ *
+ * This receiver also provides a 1PPS signal, but I haven't figured out
+ * how to deal with any of the CLK or PPS stuff yet. Stay tuned.
+ *
+ */
+
+/*
+ * Fudge Factors
+ *
+ * Fudge time1 is used to accomodate the timecode serial interface adjustment.
+ * Fudge flag4 can be set to request a receiver status screen summary, which
+ * is recorded in the clockstats file.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/hpgps%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define SPEED232Z B19200 /* uart speed (19200 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "GPS\0" /* reference ID */
+#define DESCRIPTION "HP 58503A GPS Time and Frequency Reference Receiver"
+
+#define SMAX 23*80+1 /* for :SYSTEM:PRINT? status screen response */
+
+#define MTZONE 2 /* number of fields in timezone reply */
+#define MTCODET2 12 /* number of fields in timecode format T2 */
+#define NTCODET2 21 /* number of chars to checksum in format T2 */
+
+/*
+ * Tables to compute the day of year from yyyymmdd timecode.
+ * Viva la leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Unit control structure
+ */
+struct hpgpsunit {
+ int pollcnt; /* poll message counter */
+ int tzhour; /* timezone offset, hours */
+ int tzminute; /* timezone offset, minutes */
+ int linecnt; /* set for expected multiple line responses */
+ char *lastptr; /* pointer to receiver response data */
+ char statscrn[SMAX]; /* receiver status screen buffer */
+};
+
+/*
+ * Function prototypes
+ */
+static int hpgps_start (int, struct peer *);
+static void hpgps_shutdown (int, struct peer *);
+static void hpgps_receive (struct recvbuf *);
+static void hpgps_poll (int, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_hpgps = {
+ hpgps_start, /* start up driver */
+ hpgps_shutdown, /* shut down driver */
+ hpgps_poll, /* transmit poll message */
+ noentry, /* not used (old hpgps_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old hpgps_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * hpgps_start - open the devices and initialize data for processing
+ */
+static int
+hpgps_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hpgpsunit *up;
+ struct refclockproc *pp;
+ int fd;
+ int ldisc;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ * Default is HP 58503A, mode arg selects HP Z3801A
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+ ldisc = LDISC_CLK;
+ /* mode parameter to server config line shares ttl slot */
+ if (1 == peer->ttl)
+ ldisc |= LDISC_7O1;
+ fd = refclock_open(device, SPEED232Z, ldisc);
+ if (fd <= 0)
+ return (0);
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->io.clock_recv = hpgps_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ return (0);
+ }
+ pp->unitptr = up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->tzhour = 0;
+ up->tzminute = 0;
+
+ *up->statscrn = '\0';
+ up->lastptr = up->statscrn;
+ up->pollcnt = 2;
+
+ /*
+ * Get the identifier string, which is logged but otherwise ignored,
+ * and get the local timezone information
+ */
+ up->linecnt = 1;
+ if (write(pp->io.fd, "*IDN?\r:PTIME:TZONE?\r", 20) != 20)
+ refclock_report(peer, CEVNT_FAULT);
+
+ return (1);
+}
+
+
+/*
+ * hpgps_shutdown - shut down the clock
+ */
+static void
+hpgps_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hpgpsunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ if (NULL != up)
+ free(up);
+}
+
+
+/*
+ * hpgps_receive - receive data from the serial interface
+ */
+static void
+hpgps_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct hpgpsunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ char tcodechar1; /* identifies timecode format */
+ char tcodechar2; /* identifies timecode format */
+ char timequal; /* time figure of merit: 0-9 */
+ char freqqual; /* frequency figure of merit: 0-3 */
+ char leapchar; /* leapsecond: + or 0 or - */
+ char servchar; /* request for service: 0 = no, 1 = yes */
+ char syncchar; /* time info is invalid: 0 = no, 1 = yes */
+ short expectedsm; /* expected timecode byte checksum */
+ short tcodechksm; /* computed timecode byte checksum */
+ int i,m,n;
+ int month, day, lastday;
+ char *tcp; /* timecode pointer (skips over the prompt) */
+ char prompt[BMAX]; /* prompt in response from receiver */
+
+ /*
+ * Initialize pointers and read the receiver response
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+ *pp->a_lastcode = '\0';
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: lencode: %d timecode:%s\n",
+ pp->lencode, pp->a_lastcode);
+#endif
+
+ /*
+ * If there's no characters in the reply, we can quit now
+ */
+ if (pp->lencode == 0)
+ return;
+
+ /*
+ * If linecnt is greater than zero, we are getting information only,
+ * such as the receiver identification string or the receiver status
+ * screen, so put the receiver response at the end of the status
+ * screen buffer. When we have the last line, write the buffer to
+ * the clockstats file and return without further processing.
+ *
+ * If linecnt is zero, we are expecting either the timezone
+ * or a timecode. At this point, also write the response
+ * to the clockstats file, and go on to process the prompt (if any),
+ * timezone, or timecode and timestamp.
+ */
+
+
+ if (up->linecnt-- > 0) {
+ if ((int)(pp->lencode + 2) <= (SMAX - (up->lastptr - up->statscrn))) {
+ *up->lastptr++ = '\n';
+ memcpy(up->lastptr, pp->a_lastcode, pp->lencode);
+ up->lastptr += pp->lencode;
+ }
+ if (up->linecnt == 0)
+ record_clock_stats(&peer->srcadr, up->statscrn);
+
+ return;
+ }
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ pp->lastrec = trtmp;
+
+ up->lastptr = up->statscrn;
+ *up->lastptr = '\0';
+ up->pollcnt = 2;
+
+ /*
+ * We get down to business: get a prompt if one is there, issue
+ * a clear status command if it contains an error indication.
+ * Next, check for either the timezone reply or the timecode reply
+ * and decode it. If we don't recognize the reply, or don't get the
+ * proper number of decoded fields, or get an out of range timezone,
+ * or if the timecode checksum is bad, then we declare bad format
+ * and exit.
+ *
+ * Timezone format (including nominal prompt):
+ * scpi > -H,-M<cr><lf>
+ *
+ * Timecode format (including nominal prompt):
+ * scpi > T2yyyymmddhhmmssMFLRVcc<cr><lf>
+ *
+ */
+
+ strlcpy(prompt, pp->a_lastcode, sizeof(prompt));
+ tcp = strrchr(pp->a_lastcode,'>');
+ if (tcp == NULL)
+ tcp = pp->a_lastcode;
+ else
+ tcp++;
+ prompt[tcp - pp->a_lastcode] = '\0';
+ while ((*tcp == ' ') || (*tcp == '\t')) tcp++;
+
+ /*
+ * deal with an error indication in the prompt here
+ */
+ if (strrchr(prompt,'E') > strrchr(prompt,'s')){
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: error indicated in prompt: %s\n", prompt);
+#endif
+ if (write(pp->io.fd, "*CLS\r\r", 6) != 6)
+ refclock_report(peer, CEVNT_FAULT);
+ }
+
+ /*
+ * make sure we got a timezone or timecode format and
+ * then process accordingly
+ */
+ m = sscanf(tcp,"%c%c", &tcodechar1, &tcodechar2);
+
+ if (m != 2){
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: no format indicator\n");
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ switch (tcodechar1) {
+
+ case '+':
+ case '-':
+ m = sscanf(tcp,"%d,%d", &up->tzhour, &up->tzminute);
+ if (m != MTZONE) {
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: only %d fields recognized in timezone\n", m);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ if ((up->tzhour < -12) || (up->tzhour > 13) ||
+ (up->tzminute < -59) || (up->tzminute > 59)){
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: timezone %d, %d out of range\n",
+ up->tzhour, up->tzminute);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ return;
+
+ case 'T':
+ break;
+
+ default:
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: unrecognized reply format %c%c\n",
+ tcodechar1, tcodechar2);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ } /* end of tcodechar1 switch */
+
+
+ switch (tcodechar2) {
+
+ case '2':
+ m = sscanf(tcp,"%*c%*c%4d%2d%2d%2d%2d%2d%c%c%c%c%c%2hx",
+ &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second,
+ &timequal, &freqqual, &leapchar, &servchar, &syncchar,
+ &expectedsm);
+ n = NTCODET2;
+
+ if (m != MTCODET2){
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: only %d fields recognized in timecode\n", m);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ break;
+
+ default:
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: unrecognized timecode format %c%c\n",
+ tcodechar1, tcodechar2);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ } /* end of tcodechar2 format switch */
+
+ /*
+ * Compute and verify the checksum.
+ * Characters are summed starting at tcodechar1, ending at just
+ * before the expected checksum. Bail out if incorrect.
+ */
+ tcodechksm = 0;
+ while (n-- > 0) tcodechksm += *tcp++;
+ tcodechksm &= 0x00ff;
+
+ if (tcodechksm != expectedsm) {
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: checksum %2hX doesn't match %2hX expected\n",
+ tcodechksm, expectedsm);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Compute the day of year from the yyyymmdd format.
+ */
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ if ( ! isleap_4(pp->year) ) { /* Y2KFixes */
+ /* not a leap year */
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++) day += day1tab[i];
+ lastday = 365;
+ } else {
+ /* a leap year */
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++) day += day2tab[i];
+ lastday = 366;
+ }
+
+ /*
+ * Deal with the timezone offset here. The receiver timecode is in
+ * local time = UTC + :PTIME:TZONE, so SUBTRACT the timezone values.
+ * For example, Pacific Standard Time is -8 hours , 0 minutes.
+ * Deal with the underflows and overflows.
+ */
+ pp->minute -= up->tzminute;
+ pp->hour -= up->tzhour;
+
+ if (pp->minute < 0) {
+ pp->minute += 60;
+ pp->hour--;
+ }
+ if (pp->minute > 59) {
+ pp->minute -= 60;
+ pp->hour++;
+ }
+ if (pp->hour < 0) {
+ pp->hour += 24;
+ day--;
+ if (day < 1) {
+ pp->year--;
+ if ( isleap_4(pp->year) ) /* Y2KFixes */
+ day = 366;
+ else
+ day = 365;
+ }
+ }
+
+ if (pp->hour > 23) {
+ pp->hour -= 24;
+ day++;
+ if (day > lastday) {
+ pp->year++;
+ day = 1;
+ }
+ }
+
+ pp->day = day;
+
+ /*
+ * Decode the MFLRV indicators.
+ * NEED TO FIGURE OUT how to deal with the request for service,
+ * time quality, and frequency quality indicators some day.
+ */
+ if (syncchar != '0') {
+ pp->leap = LEAP_NOTINSYNC;
+ }
+ else {
+ pp->leap = LEAP_NOWARNING;
+ switch (leapchar) {
+
+ case '0':
+ break;
+
+ /* See http://bugs.ntp.org/1090
+ * Ignore leap announcements unless June or December.
+ * Better would be to use :GPSTime? to find the month,
+ * but that seems too likely to introduce other bugs.
+ */
+ case '+':
+ if ((month==6) || (month==12))
+ pp->leap = LEAP_ADDSECOND;
+ break;
+
+ case '-':
+ if ((month==6) || (month==12))
+ pp->leap = LEAP_DELSECOND;
+ break;
+
+ default:
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: unrecognized leap indicator: %c\n",
+ leapchar);
+#endif
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ } /* end of leapchar switch */
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec 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 (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+ /*
+ * If CLK_FLAG4 is set, ask for the status screen response.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG4){
+ up->linecnt = 22;
+ if (write(pp->io.fd, ":SYSTEM:PRINT?\r", 15) != 15)
+ refclock_report(peer, CEVNT_FAULT);
+ }
+}
+
+
+/*
+ * hpgps_poll - called by the transmit procedure
+ */
+static void
+hpgps_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hpgpsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Time to poll the clock. The HP 58503A responds to a
+ * ":PTIME:TCODE?" by returning a timecode in the format specified
+ * above. If nothing is heard from the clock for two polls,
+ * declare a timeout and keep going.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ if (write(pp->io.fd, ":PTIME:TCODE?\r", 14) != 14) {
+ refclock_report(peer, CEVNT_FAULT);
+ }
+ else
+ pp->polls++;
+}
+
+#else
+int refclock_hpgps_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_irig.c b/ntpd/refclock_irig.c
new file mode 100644
index 0000000..46c01fb
--- /dev/null
+++ b/ntpd/refclock_irig.c
@@ -0,0 +1,1045 @@
+/*
+ * refclock_irig - audio IRIG-B/E demodulator/decoder
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_IRIG)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#include "audio.h"
+
+/*
+ * Audio IRIG-B/E demodulator/decoder
+ *
+ * This driver synchronizes the computer time using data encoded in
+ * IRIG-B/E signals commonly produced by GPS receivers and other timing
+ * devices. The IRIG signal is an amplitude-modulated carrier with
+ * pulse-width modulated data bits. For IRIG-B, the carrier frequency is
+ * 1000 Hz and bit rate 100 b/s; for IRIG-E, the carrier frequenchy is
+ * 100 Hz and bit rate 10 b/s. The driver automatically recognizes which
+ & format is in use.
+ *
+ * The driver requires an audio codec or sound card with sampling rate 8
+ * kHz and mu-law companding. This is the same standard as used by the
+ * telephone industry and is supported by most hardware and operating
+ * systems, including Solaris, SunOS, FreeBSD, NetBSD and Linux. In this
+ * implementation, only one audio driver and codec can be supported on a
+ * single machine.
+ *
+ * The program processes 8000-Hz mu-law companded samples using separate
+ * signal filters for IRIG-B and IRIG-E, a comb filter, envelope
+ * detector and automatic threshold corrector. Cycle crossings relative
+ * to the corrected slice level determine the width of each pulse and
+ * its value - zero, one or position identifier.
+ *
+ * The data encode 20 BCD digits which determine the second, minute,
+ * hour and day of the year and sometimes the year and synchronization
+ * condition. The comb filter exponentially averages the corresponding
+ * samples of successive baud intervals in order to reliably identify
+ * the reference carrier cycle. A type-II phase-lock loop (PLL) performs
+ * additional integration and interpolation to accurately determine the
+ * zero crossing of that cycle, which determines the reference
+ * timestamp. A pulse-width discriminator demodulates the data pulses,
+ * which are then encoded as the BCD digits of the timecode.
+ *
+ * The timecode and reference timestamp are updated once each second
+ * with IRIG-B (ten seconds with IRIG-E) and local clock offset samples
+ * saved for later processing. At poll intervals of 64 s, the saved
+ * samples are processed by a trimmed-mean filter and used to update the
+ * system clock.
+ *
+ * An automatic gain control feature provides protection against
+ * overdriven or underdriven input signal amplitudes. It is designed to
+ * maintain adequate demodulator signal amplitude while avoiding
+ * occasional noise spikes. In order to assure reliable capture, the
+ * decompanded input signal amplitude must be greater than 100 units and
+ * the codec sample frequency error less than 250 PPM (.025 percent).
+ *
+ * Monitor Data
+ *
+ * The timecode format used for debugging and data recording includes
+ * data helpful in diagnosing problems with the IRIG signal and codec
+ * connections. The driver produces one line for each timecode in the
+ * following format:
+ *
+ * 00 00 98 23 19:26:52 2782 143 0.694 10 0.3 66.5 3094572411.00027
+ *
+ * If clockstats is enabled, the most recent line is written to the
+ * clockstats file every 64 s. If verbose recording is enabled (fudge
+ * flag 4) each line is written as generated.
+ *
+ * The first field containes the error flags in hex, where the hex bits
+ * are interpreted as below. This is followed by the year of century,
+ * day of year and time of day. Note that the time of day is for the
+ * previous minute, not the current time. The status indicator and year
+ * are not produced by some IRIG devices and appear as zeros. Following
+ * these fields are the carrier amplitude (0-3000), codec gain (0-255),
+ * modulation index (0-1), time constant (4-10), carrier phase error
+ * +-.5) and carrier frequency error (PPM). The last field is the on-
+ * time timestamp in NTP format.
+ *
+ * The error flags are defined as follows in hex:
+ *
+ * x01 Low signal. The carrier amplitude is less than 100 units. This
+ * is usually the result of no signal or wrong input port.
+ * x02 Frequency error. The codec frequency error is greater than 250
+ * PPM. This may be due to wrong signal format or (rarely)
+ * defective codec.
+ * x04 Modulation error. The IRIG modulation index is less than 0.5.
+ * This is usually the result of an overdriven codec, wrong signal
+ * format or wrong input port.
+ * x08 Frame synch error. The decoder frame does not match the IRIG
+ * frame. This is usually the result of an overdriven codec, wrong
+ * signal format or noisy IRIG signal. It may also be the result of
+ * an IRIG signature check which indicates a failure of the IRIG
+ * signal synchronization source.
+ * x10 Data bit error. The data bit length is out of tolerance. This is
+ * usually the result of an overdriven codec, wrong signal format
+ * or noisy IRIG signal.
+ * x20 Seconds numbering discrepancy. The decoder second does not match
+ * the IRIG second. This is usually the result of an overdriven
+ * codec, wrong signal format or noisy IRIG signal.
+ * x40 Codec error (overrun). The machine is not fast enough to keep up
+ * with the codec.
+ * x80 Device status error (Spectracom).
+ *
+ *
+ * Once upon a time, an UltrSPARC 30 and Solaris 2.7 kept the clock
+ * within a few tens of microseconds relative to the IRIG-B signal.
+ * Accuracy with IRIG-E was about ten times worse. Unfortunately, Sun
+ * broke the 2.7 audio driver in 2.8, which has a 10-ms sawtooth
+ * modulation.
+ *
+ * Unlike other drivers, which can have multiple instantiations, this
+ * one supports only one. It does not seem likely that more than one
+ * audio codec would be useful in a single machine. More than one would
+ * probably chew up too much CPU time anyway.
+ *
+ * Fudge factors
+ *
+ * Fudge flag4 causes the dubugging output described above to be
+ * recorded in the clockstats file. Fudge flag2 selects the audio input
+ * port, where 0 is the mike port (default) and 1 is the line-in port.
+ * It does not seem useful to select the compact disc player port. Fudge
+ * flag3 enables audio monitoring of the input signal. For this purpose,
+ * the monitor gain is set t a default value. Fudgetime2 is used as a
+ * frequency vernier for broken codec sample frequency.
+ *
+ * Alarm codes
+ *
+ * CEVNT_BADTIME invalid date or time
+ * CEVNT_TIMEOUT no IRIG data since last poll
+ */
+/*
+ * Interface definitions
+ */
+#define DEVICE_AUDIO "/dev/audio" /* audio device name */
+#define PRECISION (-17) /* precision assumed (about 10 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "Generic IRIG Audio Driver" /* WRU */
+#define AUDIO_BUFSIZ 320 /* audio buffer size (40 ms) */
+#define SECOND 8000 /* nominal sample rate (Hz) */
+#define BAUD 80 /* samples per baud interval */
+#define OFFSET 128 /* companded sample offset */
+#define SIZE 256 /* decompanding table size */
+#define CYCLE 8 /* samples per bit */
+#define SUBFLD 10 /* bits per frame */
+#define FIELD 100 /* bits per second */
+#define MINTC 2 /* min PLL time constant */
+#define MAXTC 10 /* max PLL time constant max */
+#define MAXAMP 3000. /* maximum signal amplitude */
+#define MINAMP 2000. /* minimum signal amplitude */
+#define DRPOUT 100. /* dropout signal amplitude */
+#define MODMIN 0.5 /* minimum modulation index */
+#define MAXFREQ (250e-6 * SECOND) /* freq tolerance (.025%) */
+
+/*
+ * The on-time synchronization point is the positive-going zero crossing
+ * of the first cycle of the second. The IIR baseband filter phase delay
+ * is 1.03 ms for IRIG-B and 3.47 ms for IRIG-E. The fudge value 2.68 ms
+ * due to the codec and other causes was determined by calibrating to a
+ * PPS signal from a GPS receiver.
+ *
+ * The results with a 2.4-GHz P4 running FreeBSD 6.1 are generally
+ * within .02 ms short-term with .02 ms jitter. The processor load due
+ * to the driver is 0.51 percent.
+ */
+#define IRIG_B ((1.03 + 2.68) / 1000) /* IRIG-B system delay (s) */
+#define IRIG_E ((3.47 + 2.68) / 1000) /* IRIG-E system delay (s) */
+
+/*
+ * Data bit definitions
+ */
+#define BIT0 0 /* zero */
+#define BIT1 1 /* one */
+#define BITP 2 /* position identifier */
+
+/*
+ * Error flags
+ */
+#define IRIG_ERR_AMP 0x01 /* low carrier amplitude */
+#define IRIG_ERR_FREQ 0x02 /* frequency tolerance exceeded */
+#define IRIG_ERR_MOD 0x04 /* low modulation index */
+#define IRIG_ERR_SYNCH 0x08 /* frame synch error */
+#define IRIG_ERR_DECODE 0x10 /* frame decoding error */
+#define IRIG_ERR_CHECK 0x20 /* second numbering discrepancy */
+#define IRIG_ERR_ERROR 0x40 /* codec error (overrun) */
+#define IRIG_ERR_SIGERR 0x80 /* IRIG status error (Spectracom) */
+
+static char hexchar[] = "0123456789abcdef";
+
+/*
+ * IRIG unit control structure
+ */
+struct irigunit {
+ u_char timecode[2 * SUBFLD + 1]; /* timecode string */
+ l_fp timestamp; /* audio sample timestamp */
+ l_fp tick; /* audio sample increment */
+ l_fp refstamp; /* reference timestamp */
+ l_fp chrstamp; /* baud timestamp */
+ l_fp prvstamp; /* previous baud timestamp */
+ double integ[BAUD]; /* baud integrator */
+ double phase, freq; /* logical clock phase and frequency */
+ double zxing; /* phase detector integrator */
+ double yxing; /* cycle phase */
+ double exing; /* envelope phase */
+ double modndx; /* modulation index */
+ double irig_b; /* IRIG-B signal amplitude */
+ double irig_e; /* IRIG-E signal amplitude */
+ int errflg; /* error flags */
+ /*
+ * Audio codec variables
+ */
+ double comp[SIZE]; /* decompanding table */
+ double signal; /* peak signal for AGC */
+ int port; /* codec port */
+ int gain; /* codec gain */
+ int mongain; /* codec monitor gain */
+ int seccnt; /* second interval counter */
+
+ /*
+ * RF variables
+ */
+ double bpf[9]; /* IRIG-B filter shift register */
+ double lpf[5]; /* IRIG-E filter shift register */
+ double envmin, envmax; /* envelope min and max */
+ double slice; /* envelope slice level */
+ double intmin, intmax; /* integrated envelope min and max */
+ double maxsignal; /* integrated peak amplitude */
+ double noise; /* integrated noise amplitude */
+ double lastenv[CYCLE]; /* last cycle amplitudes */
+ double lastint[CYCLE]; /* last integrated cycle amplitudes */
+ double lastsig; /* last carrier sample */
+ double fdelay; /* filter delay */
+ int decim; /* sample decimation factor */
+ int envphase; /* envelope phase */
+ int envptr; /* envelope phase pointer */
+ int envsw; /* envelope state */
+ int envxing; /* envelope slice crossing */
+ int tc; /* time constant */
+ int tcount; /* time constant counter */
+ int badcnt; /* decimation interval counter */
+
+ /*
+ * Decoder variables
+ */
+ int pulse; /* cycle counter */
+ int cycles; /* carrier cycles */
+ int dcycles; /* data cycles */
+ int lastbit; /* last code element */
+ int second; /* previous second */
+ int bitcnt; /* bit count in frame */
+ int frmcnt; /* bit count in second */
+ int xptr; /* timecode pointer */
+ int bits; /* demodulated bits */
+};
+
+/*
+ * Function prototypes
+ */
+static int irig_start (int, struct peer *);
+static void irig_shutdown (int, struct peer *);
+static void irig_receive (struct recvbuf *);
+static void irig_poll (int, struct peer *);
+
+/*
+ * More function prototypes
+ */
+static void irig_base (struct peer *, double);
+static void irig_rf (struct peer *, double);
+static void irig_baud (struct peer *, int);
+static void irig_decode (struct peer *, int);
+static void irig_gain (struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_irig = {
+ irig_start, /* start up driver */
+ irig_shutdown, /* shut down driver */
+ irig_poll, /* transmit poll message */
+ noentry, /* not used (old irig_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old irig_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * irig_start - open the devices and initialize data for processing
+ */
+static int
+irig_start(
+ int unit, /* instance number (used for PCM) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ /*
+ * Local variables
+ */
+ int fd; /* file descriptor */
+ int i; /* index */
+ double step; /* codec adjustment */
+
+ /*
+ * Open audio device
+ */
+ fd = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit);
+ if (fd < 0)
+ return (0);
+#ifdef DEBUG
+ if (debug)
+ audio_show();
+#endif
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->io.clock_recv = irig_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ return (0);
+ }
+ pp->unitptr = up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->tc = MINTC;
+ up->decim = 1;
+ up->gain = 127;
+
+ /*
+ * The companded samples are encoded sign-magnitude. The table
+ * contains all the 256 values in the interest of speed.
+ */
+ up->comp[0] = up->comp[OFFSET] = 0.;
+ up->comp[1] = 1; up->comp[OFFSET + 1] = -1.;
+ up->comp[2] = 3; up->comp[OFFSET + 2] = -3.;
+ step = 2.;
+ for (i = 3; i < OFFSET; i++) {
+ up->comp[i] = up->comp[i - 1] + step;
+ up->comp[OFFSET + i] = -up->comp[i];
+ if (i % 16 == 0)
+ step *= 2.;
+ }
+ DTOLFP(1. / SECOND, &up->tick);
+ return (1);
+}
+
+
+/*
+ * irig_shutdown - shut down the clock
+ */
+static void
+irig_shutdown(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ if (NULL != up)
+ free(up);
+}
+
+
+/*
+ * irig_receive - receive data from the audio device
+ *
+ * This routine reads input samples and adjusts the logical clock to
+ * track the irig clock by dropping or duplicating codec samples.
+ */
+static void
+irig_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ /*
+ * Local variables
+ */
+ double sample; /* codec sample */
+ u_char *dpt; /* buffer pointer */
+ int bufcnt; /* buffer counter */
+ l_fp ltemp; /* l_fp temp */
+
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Main loop - read until there ain't no more. Note codec
+ * samples are bit-inverted.
+ */
+ DTOLFP((double)rbufp->recv_length / SECOND, &ltemp);
+ L_SUB(&rbufp->recv_time, &ltemp);
+ up->timestamp = rbufp->recv_time;
+ dpt = rbufp->recv_buffer;
+ for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) {
+ sample = up->comp[~*dpt++ & 0xff];
+
+ /*
+ * Variable frequency oscillator. The codec oscillator
+ * runs at the nominal rate of 8000 samples per second,
+ * or 125 us per sample. A frequency change of one unit
+ * results in either duplicating or deleting one sample
+ * per second, which results in a frequency change of
+ * 125 PPM.
+ */
+ up->phase += (up->freq + clock_codec) / SECOND;
+ up->phase += pp->fudgetime2 / 1e6;
+ if (up->phase >= .5) {
+ up->phase -= 1.;
+ } else if (up->phase < -.5) {
+ up->phase += 1.;
+ irig_rf(peer, sample);
+ irig_rf(peer, sample);
+ } else {
+ irig_rf(peer, sample);
+ }
+ L_ADD(&up->timestamp, &up->tick);
+ sample = fabs(sample);
+ if (sample > up->signal)
+ up->signal = sample;
+ up->signal += (sample - up->signal) /
+ 1000;
+
+ /*
+ * Once each second, determine the IRIG format and gain.
+ */
+ up->seccnt = (up->seccnt + 1) % SECOND;
+ if (up->seccnt == 0) {
+ if (up->irig_b > up->irig_e) {
+ up->decim = 1;
+ up->fdelay = IRIG_B;
+ } else {
+ up->decim = 10;
+ up->fdelay = IRIG_E;
+ }
+ up->irig_b = up->irig_e = 0;
+ irig_gain(peer);
+
+ }
+ }
+
+ /*
+ * Set the input port and monitor gain for the next buffer.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ up->port = 2;
+ else
+ up->port = 1;
+ if (pp->sloppyclockflag & CLK_FLAG3)
+ up->mongain = MONGAIN;
+ else
+ up->mongain = 0;
+}
+
+
+/*
+ * irig_rf - RF processing
+ *
+ * This routine filters the RF signal using a bandass filter for IRIG-B
+ * and a lowpass filter for IRIG-E. In case of IRIG-E, the samples are
+ * decimated by a factor of ten. Note that the codec filters function as
+ * roofing filters to attenuate both the high and low ends of the
+ * passband. IIR filter coefficients were determined using Matlab Signal
+ * Processing Toolkit.
+ */
+static void
+irig_rf(
+ struct peer *peer, /* peer structure pointer */
+ double sample /* current signal sample */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ /*
+ * Local variables
+ */
+ double irig_b, irig_e; /* irig filter outputs */
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * IRIG-B filter. Matlab 4th-order IIR elliptic, 800-1200 Hz
+ * bandpass, 0.3 dB passband ripple, -50 dB stopband ripple,
+ * phase delay 1.03 ms.
+ */
+ irig_b = (up->bpf[8] = up->bpf[7]) * 6.505491e-001;
+ irig_b += (up->bpf[7] = up->bpf[6]) * -3.875180e+000;
+ irig_b += (up->bpf[6] = up->bpf[5]) * 1.151180e+001;
+ irig_b += (up->bpf[5] = up->bpf[4]) * -2.141264e+001;
+ irig_b += (up->bpf[4] = up->bpf[3]) * 2.712837e+001;
+ irig_b += (up->bpf[3] = up->bpf[2]) * -2.384486e+001;
+ irig_b += (up->bpf[2] = up->bpf[1]) * 1.427663e+001;
+ irig_b += (up->bpf[1] = up->bpf[0]) * -5.352734e+000;
+ up->bpf[0] = sample - irig_b;
+ irig_b = up->bpf[0] * 4.952157e-003
+ + up->bpf[1] * -2.055878e-002
+ + up->bpf[2] * 4.401413e-002
+ + up->bpf[3] * -6.558851e-002
+ + up->bpf[4] * 7.462108e-002
+ + up->bpf[5] * -6.558851e-002
+ + up->bpf[6] * 4.401413e-002
+ + up->bpf[7] * -2.055878e-002
+ + up->bpf[8] * 4.952157e-003;
+ up->irig_b += irig_b * irig_b;
+
+ /*
+ * IRIG-E filter. Matlab 4th-order IIR elliptic, 130-Hz lowpass,
+ * 0.3 dB passband ripple, -50 dB stopband ripple, phase delay
+ * 3.47 ms.
+ */
+ irig_e = (up->lpf[4] = up->lpf[3]) * 8.694604e-001;
+ irig_e += (up->lpf[3] = up->lpf[2]) * -3.589893e+000;
+ irig_e += (up->lpf[2] = up->lpf[1]) * 5.570154e+000;
+ irig_e += (up->lpf[1] = up->lpf[0]) * -3.849667e+000;
+ up->lpf[0] = sample - irig_e;
+ irig_e = up->lpf[0] * 3.215696e-003
+ + up->lpf[1] * -1.174951e-002
+ + up->lpf[2] * 1.712074e-002
+ + up->lpf[3] * -1.174951e-002
+ + up->lpf[4] * 3.215696e-003;
+ up->irig_e += irig_e * irig_e;
+
+ /*
+ * Decimate by a factor of either 1 (IRIG-B) or 10 (IRIG-E).
+ */
+ up->badcnt = (up->badcnt + 1) % up->decim;
+ if (up->badcnt == 0) {
+ if (up->decim == 1)
+ irig_base(peer, irig_b);
+ else
+ irig_base(peer, irig_e);
+ }
+}
+
+/*
+ * irig_base - baseband processing
+ *
+ * This routine processes the baseband signal and demodulates the AM
+ * carrier using a synchronous detector. It then synchronizes to the
+ * data frame at the baud rate and decodes the width-modulated data
+ * pulses.
+ */
+static void
+irig_base(
+ struct peer *peer, /* peer structure pointer */
+ double sample /* current signal sample */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ /*
+ * Local variables
+ */
+ double lope; /* integrator output */
+ double env; /* envelope detector output */
+ double dtemp;
+ int carphase; /* carrier phase */
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Synchronous baud integrator. Corresponding samples of current
+ * and past baud intervals are integrated to refine the envelope
+ * amplitude and phase estimate. We keep one cycle (1 ms) of the
+ * raw data and one baud (10 ms) of the integrated data.
+ */
+ up->envphase = (up->envphase + 1) % BAUD;
+ up->integ[up->envphase] += (sample - up->integ[up->envphase]) /
+ (5 * up->tc);
+ lope = up->integ[up->envphase];
+ carphase = up->envphase % CYCLE;
+ up->lastenv[carphase] = sample;
+ up->lastint[carphase] = lope;
+
+ /*
+ * Phase detector. Find the negative-going zero crossing
+ * relative to sample 4 in the 8-sample sycle. A phase change of
+ * 360 degrees produces an output change of one unit.
+ */
+ if (up->lastsig > 0 && lope <= 0)
+ up->zxing += (double)(carphase - 4) / CYCLE;
+ up->lastsig = lope;
+
+ /*
+ * End of the baud. Update signal/noise estimates and PLL
+ * phase, frequency and time constant.
+ */
+ if (up->envphase == 0) {
+ up->maxsignal = up->intmax; up->noise = up->intmin;
+ up->intmin = 1e6; up->intmax = -1e6;
+ if (up->maxsignal < DRPOUT)
+ up->errflg |= IRIG_ERR_AMP;
+ if (up->maxsignal > 0)
+ up->modndx = (up->maxsignal - up->noise) /
+ up->maxsignal;
+ else
+ up->modndx = 0;
+ if (up->modndx < MODMIN)
+ up->errflg |= IRIG_ERR_MOD;
+ if (up->errflg & (IRIG_ERR_AMP | IRIG_ERR_FREQ |
+ IRIG_ERR_MOD | IRIG_ERR_SYNCH)) {
+ up->tc = MINTC;
+ up->tcount = 0;
+ }
+
+ /*
+ * Update PLL phase and frequency. The PLL time constant
+ * is set initially to stabilize the frequency within a
+ * minute or two, then increases to the maximum. The
+ * frequency is clamped so that the PLL capture range
+ * cannot be exceeded.
+ */
+ dtemp = up->zxing * up->decim / BAUD;
+ up->yxing = dtemp;
+ up->zxing = 0.;
+ up->phase += dtemp / up->tc;
+ up->freq += dtemp / (4. * up->tc * up->tc);
+ if (up->freq > MAXFREQ) {
+ up->freq = MAXFREQ;
+ up->errflg |= IRIG_ERR_FREQ;
+ } else if (up->freq < -MAXFREQ) {
+ up->freq = -MAXFREQ;
+ up->errflg |= IRIG_ERR_FREQ;
+ }
+ }
+
+ /*
+ * Synchronous demodulator. There are eight samples in the cycle
+ * and ten cycles in the baud. Since the PLL has aligned the
+ * negative-going zero crossing at sample 4, the maximum
+ * amplitude is at sample 2 and minimum at sample 6. The
+ * beginning of the data pulse is determined from the integrated
+ * samples, while the end of the pulse is determined from the
+ * raw samples. The raw data bits are demodulated relative to
+ * the slice level and left-shifted in the decoding register.
+ */
+ if (carphase != 7)
+ return;
+
+ lope = (up->lastint[2] - up->lastint[6]) / 2.;
+ if (lope > up->intmax)
+ up->intmax = lope;
+ if (lope < up->intmin)
+ up->intmin = lope;
+
+ /*
+ * Pulse code demodulator and reference timestamp. The decoder
+ * looks for a sequence of ten bits; the first two bits must be
+ * one, the last two bits must be zero. Frame synch is asserted
+ * when three correct frames have been found.
+ */
+ up->pulse = (up->pulse + 1) % 10;
+ up->cycles <<= 1;
+ if (lope >= (up->maxsignal + up->noise) / 2.)
+ up->cycles |= 1;
+ if ((up->cycles & 0x303c0f03) == 0x300c0300) {
+ if (up->pulse != 0)
+ up->errflg |= IRIG_ERR_SYNCH;
+ up->pulse = 0;
+ }
+
+ /*
+ * Assemble the baud and max/min to get the slice level for the
+ * next baud. The slice level is based on the maximum over the
+ * first two bits and the minimum over the last two bits, with
+ * the slice level halfway between the maximum and minimum.
+ */
+ env = (up->lastenv[2] - up->lastenv[6]) / 2.;
+ up->dcycles <<= 1;
+ if (env >= up->slice)
+ up->dcycles |= 1;
+ switch(up->pulse) {
+
+ case 0:
+ irig_baud(peer, up->dcycles);
+ if (env < up->envmin)
+ up->envmin = env;
+ up->slice = (up->envmax + up->envmin) / 2;
+ up->envmin = 1e6; up->envmax = -1e6;
+ break;
+
+ case 1:
+ up->envmax = env;
+ break;
+
+ case 2:
+ if (env > up->envmax)
+ up->envmax = env;
+ break;
+
+ case 9:
+ up->envmin = env;
+ break;
+ }
+}
+
+/*
+ * irig_baud - update the PLL and decode the pulse-width signal
+ */
+static void
+irig_baud(
+ struct peer *peer, /* peer structure pointer */
+ int bits /* decoded bits */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+ double dtemp;
+ l_fp ltemp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * The PLL time constant starts out small, in order to
+ * sustain a frequency tolerance of 250 PPM. It
+ * gradually increases as the loop settles down. Note
+ * that small wiggles are not believed, unless they
+ * persist for lots of samples.
+ */
+ up->exing = -up->yxing;
+ if (fabs(up->envxing - up->envphase) <= 1) {
+ up->tcount++;
+ if (up->tcount > 20 * up->tc) {
+ up->tc++;
+ if (up->tc > MAXTC)
+ up->tc = MAXTC;
+ up->tcount = 0;
+ up->envxing = up->envphase;
+ } else {
+ up->exing -= up->envxing - up->envphase;
+ }
+ } else {
+ up->tcount = 0;
+ up->envxing = up->envphase;
+ }
+
+ /*
+ * Strike the baud timestamp as the positive zero crossing of
+ * the first bit, accounting for the codec delay and filter
+ * delay.
+ */
+ up->prvstamp = up->chrstamp;
+ dtemp = up->decim * (up->exing / SECOND) + up->fdelay;
+ DTOLFP(dtemp, &ltemp);
+ up->chrstamp = up->timestamp;
+ L_SUB(&up->chrstamp, &ltemp);
+
+ /*
+ * The data bits are collected in ten-bit bauds. The first two
+ * bits are not used. The resulting patterns represent runs of
+ * 0-1 bits (0), 2-4 bits (1) and 5-7 bits (PI). The remaining
+ * 8-bit run represents a soft error and is treated as 0.
+ */
+ switch (up->dcycles & 0xff) {
+
+ case 0x00: /* 0-1 bits (0) */
+ case 0x80:
+ irig_decode(peer, BIT0);
+ break;
+
+ case 0xc0: /* 2-4 bits (1) */
+ case 0xe0:
+ case 0xf0:
+ irig_decode(peer, BIT1);
+ break;
+
+ case 0xf8: /* (5-7 bits (PI) */
+ case 0xfc:
+ case 0xfe:
+ irig_decode(peer, BITP);
+ break;
+
+ default: /* 8 bits (error) */
+ irig_decode(peer, BIT0);
+ up->errflg |= IRIG_ERR_DECODE;
+ }
+}
+
+
+/*
+ * irig_decode - decode the data
+ *
+ * This routine assembles bauds into digits, digits into frames and
+ * frames into the timecode fields. Bits can have values of zero, one
+ * or position identifier. There are four bits per digit, ten digits per
+ * frame and ten frames per second.
+ */
+static void
+irig_decode(
+ struct peer *peer, /* peer structure pointer */
+ int bit /* data bit (0, 1 or 2) */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ /*
+ * Local variables
+ */
+ int syncdig; /* sync digit (Spectracom) */
+ char sbs[6 + 1]; /* binary seconds since 0h */
+ char spare[2 + 1]; /* mulligan digits */
+ int temp;
+
+ syncdig = 0;
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Assemble frame bits.
+ */
+ up->bits >>= 1;
+ if (bit == BIT1) {
+ up->bits |= 0x200;
+ } else if (bit == BITP && up->lastbit == BITP) {
+
+ /*
+ * Frame sync - two adjacent position identifiers, which
+ * mark the beginning of the second. The reference time
+ * is the beginning of the second position identifier,
+ * so copy the character timestamp to the reference
+ * timestamp.
+ */
+ if (up->frmcnt != 1)
+ up->errflg |= IRIG_ERR_SYNCH;
+ up->frmcnt = 1;
+ up->refstamp = up->prvstamp;
+ }
+ up->lastbit = bit;
+ if (up->frmcnt % SUBFLD == 0) {
+
+ /*
+ * End of frame. Encode two hexadecimal digits in
+ * little-endian timecode field. Note frame 1 is shifted
+ * right one bit to account for the marker PI.
+ */
+ temp = up->bits;
+ if (up->frmcnt == 10)
+ temp >>= 1;
+ if (up->xptr >= 2) {
+ up->timecode[--up->xptr] = hexchar[temp & 0xf];
+ up->timecode[--up->xptr] = hexchar[(temp >> 5) &
+ 0xf];
+ }
+ if (up->frmcnt == 0) {
+
+ /*
+ * End of second. Decode the timecode and wind
+ * the clock. Not all IRIG generators have the
+ * year; if so, it is nonzero after year 2000.
+ * Not all have the hardware status bit; if so,
+ * it is lit when the source is okay and dim
+ * when bad. We watch this only if the year is
+ * nonzero. Not all are configured for signature
+ * control. If so, all BCD digits are set to
+ * zero if the source is bad. In this case the
+ * refclock_process() will reject the timecode
+ * as invalid.
+ */
+ up->xptr = 2 * SUBFLD;
+ if (sscanf((char *)up->timecode,
+ "%6s%2d%1d%2s%3d%2d%2d%2d", sbs, &pp->year,
+ &syncdig, spare, &pp->day, &pp->hour,
+ &pp->minute, &pp->second) != 8)
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+ up->second = (up->second + up->decim) % 60;
+
+ /*
+ * Raise an alarm if the day field is zero,
+ * which happens when signature control is
+ * enabled and the device has lost
+ * synchronization. Raise an alarm if the year
+ * field is nonzero and the sync indicator is
+ * zero, which happens when a Spectracom radio
+ * has lost synchronization. Raise an alarm if
+ * the expected second does not agree with the
+ * decoded second, which happens with a garbled
+ * IRIG signal. We are very particular.
+ */
+ if (pp->day == 0 || (pp->year != 0 && syncdig ==
+ 0))
+ up->errflg |= IRIG_ERR_SIGERR;
+ if (pp->second != up->second)
+ up->errflg |= IRIG_ERR_CHECK;
+ up->second = pp->second;
+
+ /*
+ * Wind the clock only if there are no errors
+ * and the time constant has reached the
+ * maximum.
+ */
+ if (up->errflg == 0 && up->tc == MAXTC) {
+ pp->lastref = pp->lastrec;
+ pp->lastrec = up->refstamp;
+ if (!refclock_process(pp))
+ refclock_report(peer,
+ CEVNT_BADTIME);
+ }
+ snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
+ "%02x %02d %03d %02d:%02d:%02d %4.0f %3d %6.3f %2d %6.2f %6.1f %s",
+ up->errflg, pp->year, pp->day,
+ pp->hour, pp->minute, pp->second,
+ up->maxsignal, up->gain, up->modndx,
+ up->tc, up->exing * 1e6 / SECOND, up->freq *
+ 1e6 / SECOND, ulfptoa(&pp->lastrec, 6));
+ pp->lencode = strlen(pp->a_lastcode);
+ up->errflg = 0;
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ record_clock_stats(&peer->srcadr,
+ pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("irig %s\n",
+ pp->a_lastcode);
+#endif /* DEBUG */
+ }
+ }
+ }
+ up->frmcnt = (up->frmcnt + 1) % FIELD;
+}
+
+
+/*
+ * irig_poll - called by the transmit procedure
+ *
+ * This routine sweeps up the timecode updates since the last poll. For
+ * IRIG-B there should be at least 60 updates; for IRIG-E there should
+ * be at least 6. If nothing is heard, a timeout event is declared.
+ */
+static void
+irig_poll(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+
+ }
+ refclock_receive(peer);
+ if (!(pp->sloppyclockflag & CLK_FLAG4)) {
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("irig %s\n", pp->a_lastcode);
+#endif /* DEBUG */
+ }
+ pp->polls++;
+
+}
+
+
+/*
+ * irig_gain - adjust codec gain
+ *
+ * This routine is called at the end of each second. It uses the AGC to
+ * bradket the maximum signal level between MINAMP and MAXAMP to avoid
+ * hunting. The routine also jiggles the input port and selectively
+ * mutes the monitor.
+ */
+static void
+irig_gain(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Apparently, the codec uses only the high order bits of the
+ * gain control field. Thus, it may take awhile for changes to
+ * wiggle the hardware bits.
+ */
+ if (up->maxsignal < MINAMP) {
+ up->gain += 4;
+ if (up->gain > MAXGAIN)
+ up->gain = MAXGAIN;
+ } else if (up->maxsignal > MAXAMP) {
+ up->gain -= 4;
+ if (up->gain < 0)
+ up->gain = 0;
+ }
+ audio_gain(up->gain, up->mongain, up->port);
+}
+
+
+#else
+int refclock_irig_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_jjy.c b/ntpd/refclock_jjy.c
new file mode 100644
index 0000000..d8ec6b7
--- /dev/null
+++ b/ntpd/refclock_jjy.c
@@ -0,0 +1,1743 @@
+/*
+ * refclock_jjy - clock driver for JJY receivers
+ */
+
+/**********************************************************************/
+/* */
+/* Copyright (C) 2001-2011, Takao Abe. All rights reserved. */
+/* */
+/* Permission to use, copy, modify, and distribute this software */
+/* and its documentation for any purpose is hereby granted */
+/* without fee, provided that the following conditions are met: */
+/* */
+/* One retains the entire copyright notice properly, and both the */
+/* copyright notice and this license. in the documentation and/or */
+/* other materials provided with the distribution. */
+/* */
+/* This software and the name of the author must not be used to */
+/* endorse or promote products derived from this software without */
+/* prior written permission. */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESSED OR IMPLIED */
+/* WARRANTIES OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, THE */
+/* IMPLIED WARRANTIES OF MERCHANTABLILITY AND FITNESS FOR A */
+/* PARTICULAR PURPOSE. */
+/* IN NO EVENT SHALL THE AUTHOR TAKAO ABE BE LIABLE FOR ANY DIRECT, */
+/* INDIRECT, GENERAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
+/* ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE */
+/* GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS */
+/* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, */
+/* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ( INCLUDING */
+/* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF */
+/* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+/* */
+/* This driver is developed in my private time, and is opened as */
+/* voluntary contributions for the NTP. */
+/* The manufacturer of the JJY receiver has not participated in */
+/* a development of this driver. */
+/* The manufacturer does not warrant anything about this driver, */
+/* and is not liable for anything about this driver. */
+/* */
+/**********************************************************************/
+/* */
+/* Author Takao Abe */
+/* Email takao_abe@xurb.jp */
+/* Homepage http://www.bea.hi-ho.ne.jp/abetakao/ */
+/* */
+/* The email address abetakao@bea.hi-ho.ne.jp is never read */
+/* from 2010, because a few filtering rule are provided by the */
+/* "hi-ho.ne.jp", and lots of spam mail are reached. */
+/* New email address for supporting the refclock_jjy is */
+/* takao_abe@xurb.jp */
+/* */
+/**********************************************************************/
+/* */
+/* History */
+/* */
+/* 2001/07/15 */
+/* [New] Support the Tristate Ltd. JJY receiver */
+/* */
+/* 2001/08/04 */
+/* [Change] Log to clockstats even if bad reply */
+/* [Fix] PRECISION = (-3) (about 100 ms) */
+/* [Add] Support the C-DEX Co.Ltd. JJY receiver */
+/* */
+/* 2001/12/04 */
+/* [Fix] C-DEX JST2000 ( fukusima@goto.info.waseda.ac.jp ) */
+/* */
+/* 2002/07/12 */
+/* [Fix] Portability for FreeBSD ( patched by the user ) */
+/* */
+/* 2004/10/31 */
+/* [Change] Command send timing for the Tristate Ltd. JJY receiver */
+/* JJY-01 ( Firmware version 2.01 ) */
+/* Thanks to Andy Taki for testing under FreeBSD */
+/* */
+/* 2004/11/28 */
+/* [Add] Support the Echo Keisokuki LT-2000 receiver */
+/* */
+/* 2006/11/04 */
+/* [Fix] C-DEX JST2000 */
+/* Thanks to Hideo Kuramatsu for the patch */
+/* */
+/* 2009/04/05 */
+/* [Add] Support the CITIZEN T.I.C JJY-200 receiver */
+/* */
+/* 2010/11/20 */
+/* [Change] Bug 1618 ( Harmless ) */
+/* Code clean up ( Remove unreachable codes ) in */
+/* jjy_start() */
+/* [Change] Change clockstats format of the Tristate JJY01/02 */
+/* Issues more command to get the status of the receiver */
+/* when "fudge 127.127.40.X flag1 1" is specified */
+/* ( DATE,STIM -> DCST,STUS,DATE,STIM ) */
+/* */
+/* 2011/04/30 */
+/* [Add] Support the Tristate Ltd. TS-GPSclock-01 */
+/* */
+/**********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_JJY)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_tty.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/**********************************************************************/
+/* */
+/* The Tristate Ltd. JJY receiver JJY01 */
+/* */
+/* Command Response Remarks */
+/* ------------ ---------------------- --------------------- */
+/* dcst<CR><LF> VALID|INVALID<CR><LF> */
+/* stus<CR><LF> ADJUSTED|UNADJUSTED<CR><LF> */
+/* date<CR><LF> YYYY/MM/DD XXX<CR><LF> */
+/* time<CR><LF> HH:MM:SS<CR><LF> Not used by this driver */
+/* stim<CR><LF> HH:MM:SS<CR><LF> Reply at just second */
+/* */
+/* During synchronization after a receiver is turned on, */
+/* It replies the past time from 2000/01/01 00:00:00. */
+/* The function "refclock_process" checks the time and tells */
+/* as an insanity time. */
+/* */
+/**********************************************************************/
+/* */
+/* The C-DEX Co. Ltd. JJY receiver JST2000 */
+/* */
+/* Command Response Remarks */
+/* ------------ ---------------------- --------------------- */
+/* <ENQ>1J<ETX> <STX>JYYMMDD HHMMSSS<ETX> */
+/* */
+/**********************************************************************/
+/* */
+/* The Echo Keisokuki Co. Ltd. JJY receiver LT2000 */
+/* */
+/* Command Response Remarks */
+/* ------------ ---------------------- --------------------- */
+/* # Mode 1 (Request&Send) */
+/* T YYMMDDWHHMMSS<BCC1><BCC2><CR> */
+/* C Mode 2 (Continuous) */
+/* YYMMDDWHHMMSS<ST1><ST2><ST3><ST4><CR> */
+/* <SUB> Second signal */
+/* */
+/**********************************************************************/
+/* */
+/* The CITIZEN T.I.C CO., LTD. JJY receiver JJY200 */
+/* */
+/* Command Response Remarks */
+/* ------------ ---------------------- --------------------- */
+/* 'XX YY/MM/DD W HH:MM:SS<CR> */
+/* XX: OK|NG|ER */
+/* W: 0(Monday)-6(Sunday) */
+/* */
+/**********************************************************************/
+/* */
+/* The Tristate Ltd. GPS clock TS-GPSCLOCK-01 */
+/* */
+/* This clock has NMEA mode and command/respose mode. */
+/* When this jjy driver are used, set to command/respose mode */
+/* of this clock by the onboard switch SW4, and make sure the */
+/* LED-Y is tured on. */
+/* Other than this JJY driver, the refclock driver type 20, */
+/* generic NMEA driver, works with the NMEA mode of this clock. */
+/* */
+/* Command Response Remarks */
+/* ------------ ---------------------- --------------------- */
+/* stus<CR><LF> *R|*G|*U|+U<CR><LF> */
+/* date<CR><LF> YY/MM/DD<CR><LF> */
+/* time<CR><LF> HH:MM:SS<CR><LF> */
+/* */
+/**********************************************************************/
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/jjy%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define SPEED232_TRISTATE_JJY01 B9600 /* UART speed (9600 baud) */
+#define SPEED232_CDEX_JST2000 B9600 /* UART speed (9600 baud) */
+#define SPEED232_ECHOKEISOKUKI_LT2000 B9600 /* UART speed (9600 baud) */
+#define SPEED232_CITIZENTIC_JJY200 B4800 /* UART speed (4800 baud) */
+#define SPEED232_TRISTATE_GPSCLOCK01 B38400 /* USB speed (38400 baud) */
+#define REFID "JJY" /* reference ID */
+#define DESCRIPTION "JJY Receiver"
+#define PRECISION (-3) /* precision assumed (about 100 ms) */
+
+/*
+ * JJY unit control structure
+ */
+struct jjyunit {
+ char unittype ; /* UNITTYPE_XXXXXXXXXX */
+ short operationmode ; /* Echo Keisokuki LT-2000 : 1 or 2 */
+ short version ;
+ short linediscipline ; /* LDISC_CLK or LDISC_RAW */
+ char bPollFlag ; /* Set by jjy_pool and Reset by jjy_receive */
+ int linecount ;
+ int lineerror ;
+ int year, month, day, hour, minute, second, msecond ;
+/* LDISC_RAW only */
+#define MAX_LINECOUNT 8
+#define MAX_RAWBUF 64
+ int lineexpect ;
+ int charexpect [ MAX_LINECOUNT ] ;
+ int charcount ;
+ char rawbuf [ MAX_RAWBUF ] ;
+};
+
+#define UNITTYPE_TRISTATE_JJY01 1
+#define UNITTYPE_CDEX_JST2000 2
+#define UNITTYPE_ECHOKEISOKUKI_LT2000 3
+#define UNITTYPE_CITIZENTIC_JJY200 4
+#define UNITTYPE_TRISTATE_GPSCLOCK01 5
+
+/*
+ * Function prototypes
+ */
+
+static int jjy_start (int, struct peer *);
+static void jjy_shutdown (int, struct peer *);
+
+static void jjy_poll (int, struct peer *);
+static void jjy_poll_tristate_jjy01 (int, struct peer *);
+static void jjy_poll_cdex_jst2000 (int, struct peer *);
+static void jjy_poll_echokeisokuki_lt2000 (int, struct peer *);
+static void jjy_poll_citizentic_jjy200 (int, struct peer *);
+static void jjy_poll_tristate_gpsclock01 (int, struct peer *);
+
+static void jjy_receive (struct recvbuf *);
+static int jjy_receive_tristate_jjy01 (struct recvbuf *);
+static int jjy_receive_cdex_jst2000 (struct recvbuf *);
+static int jjy_receive_echokeisokuki_lt2000 (struct recvbuf *);
+static int jjy_receive_citizentic_jjy200 (struct recvbuf *);
+static int jjy_receive_tristate_gpsclock01 (struct recvbuf *);
+
+static void printableString ( char*, int, char*, int ) ;
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_jjy = {
+ jjy_start, /* start up driver */
+ jjy_shutdown, /* shutdown driver */
+ jjy_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * Start up driver return code
+ */
+#define RC_START_SUCCESS 1
+#define RC_START_ERROR 0
+
+/*
+ * Local constants definition
+ */
+
+#define MAX_LOGTEXT 64
+
+/*
+ * Tristate JJY01/JJY02 constants definition
+ */
+
+#define TS_JJY01_COMMAND_NUMBER_DATE 1
+#define TS_JJY01_COMMAND_NUMBER_TIME 2
+#define TS_JJY01_COMMAND_NUMBER_STIM 3
+#define TS_JJY01_COMMAND_NUMBER_STUS 4
+#define TS_JJY01_COMMAND_NUMBER_DCST 5
+
+#define TS_JJY01_REPLY_DATE "yyyy/mm/dd www\r\n"
+#define TS_JJY01_REPLY_STIM "hh:mm:ss\r\n"
+#define TS_JJY01_REPLY_STUS_YES "adjusted\r\n"
+#define TS_JJY01_REPLY_STUS_NO "unadjusted\r\n"
+#define TS_JJY01_REPLY_DCST_VALID "valid\r\n"
+#define TS_JJY01_REPLY_DCST_INVALID "invalid\r\n"
+
+#define TS_JJY01_REPLY_LENGTH_DATE 14 /* Length without <CR><LF> */
+#define TS_JJY01_REPLY_LENGTH_STIM 8 /* Length without <CR><LF> */
+#define TS_JJY01_REPLY_LENGTH_STUS_YES 8 /* Length without <CR><LF> */
+#define TS_JJY01_REPLY_LENGTH_STUS_NO 10 /* Length without <CR><LF> */
+#define TS_JJY01_REPLY_LENGTH_DCST_VALID 5 /* Length without <CR><LF> */
+#define TS_JJY01_REPLY_LENGTH_DCST_INVALID 7 /* Length without <CR><LF> */
+
+static struct
+{
+ char commandNumber ;
+ char *commandLog ;
+ char *command ;
+ int commandLength ;
+} tristate_jjy01_command_sequence[] =
+{
+ /* dcst<CR><LF> -> VALID<CR><LF> or INVALID<CR><LF> */
+ { TS_JJY01_COMMAND_NUMBER_DCST, "dcst", "dcst\r\n", 6 },
+ /* stus<CR><LF> -> ADJUSTED<CR><LF> or UNADJUSTED<CR><LF> */
+ { TS_JJY01_COMMAND_NUMBER_STUS, "stus", "stus\r\n", 6 },
+ /* date<CR><LF> -> YYYY/MM/DD WWW<CR><LF> */
+ { TS_JJY01_COMMAND_NUMBER_DATE, "date", "date\r\n", 6 },
+ /* stim<CR><LF> -> HH:MM:SS<CR><LF> */
+ { TS_JJY01_COMMAND_NUMBER_STIM, "stim", "stim\r\n", 6 },
+ /* End of command */
+ { 0, NULL, NULL, 0 }
+} ;
+
+/*
+ * Tristate TS-GPSCLOCK01 constants definition
+ */
+
+#define TS_GPSCLOCK01_COMMAND_NUMBER_DATE 1
+#define TS_GPSCLOCK01_COMMAND_NUMBER_TIME 2
+#define TS_GPSCLOCK01_COMMAND_NUMBER_STUS 4
+
+#define TS_GPSCLOCK01_REPLY_DATE "yyyy/mm/dd\r\n"
+#define TS_GPSCLOCK01_REPLY_TIME "hh:mm:ss\r\n"
+#define TS_GPSCLOCK01_REPLY_STUS_RTC "*R\r\n"
+#define TS_GPSCLOCK01_REPLY_STUS_GPS "*G\r\n"
+#define TS_GPSCLOCK01_REPLY_STUS_UTC "*U\r\n"
+#define TS_GPSCLOCK01_REPLY_STUS_PPS "+U\r\n"
+
+#define TS_GPSCLOCK01_REPLY_LENGTH_DATE 10 /* Length without <CR><LF> */
+#define TS_GPSCLOCK01_REPLY_LENGTH_TIME 8 /* Length without <CR><LF> */
+#define TS_GPSCLOCK01_REPLY_LENGTH_STUS 2 /* Length without <CR><LF> */
+
+static struct
+{
+ char commandNumber ;
+ char *commandLog ;
+ char *command ;
+ int commandLength ;
+} tristate_gpsclock01_command_sequence[] =
+{
+ /* stus<CR><LF> -> *R<CR><LF> or *G<CR><LF> or *U<CR><LF> or +U<CR><LF> */
+ { TS_GPSCLOCK01_COMMAND_NUMBER_STUS, "stus", "stus\r\n", 6 },
+ /* date<CR><LF> -> YYYY/MM/DD WWW<CR><LF> */
+ { TS_GPSCLOCK01_COMMAND_NUMBER_DATE, "date", "date\r\n", 6 },
+ /* time<CR><LF> -> HH:MM:SS<CR><LF> */
+ { TS_GPSCLOCK01_COMMAND_NUMBER_TIME, "time", "time\r\n", 6 },
+ /* End of command */
+ { 0, NULL, NULL, 0 }
+} ;
+
+/**************************************************************************************************/
+/* jjy_start - open the devices and initialize data for processing */
+/**************************************************************************************************/
+static int
+jjy_start ( int unit, struct peer *peer )
+{
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ int fd ;
+ char *pDeviceName ;
+ short iDiscipline ;
+ int iSpeed232 ;
+
+ char sLogText [ MAX_LOGTEXT ] , sDevText [ MAX_LOGTEXT ] ;
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "jjy_start (refclock_jjy.c) : %s mode=%d ", ntoa(&peer->srcadr), peer->ttl ) ;
+ printf ( DEVICE, unit ) ;
+ printf ( "\n" ) ;
+ }
+#endif
+ snprintf ( sDevText, sizeof(sDevText), DEVICE, unit ) ;
+ snprintf ( sLogText, sizeof(sLogText), "*Initialze* %s mode=%d", sDevText, peer->ttl ) ;
+ record_clock_stats ( &peer->srcadr, sLogText ) ;
+
+ /*
+ * Open serial port
+ */
+ pDeviceName = emalloc ( strlen(DEVICE) + 10 );
+ snprintf ( pDeviceName, strlen(DEVICE) + 10, DEVICE, unit ) ;
+
+ /*
+ * peer->ttl is a mode number specified by "127.127.40.X mode N" in the ntp.conf
+ */
+ switch ( peer->ttl ) {
+ case 0 :
+ case 1 :
+ iDiscipline = LDISC_CLK ;
+ iSpeed232 = SPEED232_TRISTATE_JJY01 ;
+ break ;
+ case 2 :
+ iDiscipline = LDISC_RAW ;
+ iSpeed232 = SPEED232_CDEX_JST2000 ;
+ break ;
+ case 3 :
+ iDiscipline = LDISC_CLK ;
+ iSpeed232 = SPEED232_ECHOKEISOKUKI_LT2000 ;
+ break ;
+ case 4 :
+ iDiscipline = LDISC_CLK ;
+ iSpeed232 = SPEED232_CITIZENTIC_JJY200 ;
+ break ;
+ case 5 :
+ iDiscipline = LDISC_CLK ;
+ iSpeed232 = SPEED232_TRISTATE_GPSCLOCK01 ;
+ break ;
+ default :
+ msyslog ( LOG_ERR, "JJY receiver [ %s mode %d ] : Unsupported mode",
+ ntoa(&peer->srcadr), peer->ttl ) ;
+ free ( (void*) pDeviceName ) ;
+ return RC_START_ERROR ;
+ }
+
+ fd = refclock_open ( pDeviceName, iSpeed232, iDiscipline ) ;
+ if ( fd <= 0 ) {
+ free ( (void*) pDeviceName ) ;
+ return RC_START_ERROR ;
+ }
+ free ( (void*) pDeviceName ) ;
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc (sizeof(*up));
+ memset ( up, 0, sizeof(*up) ) ;
+ up->linediscipline = iDiscipline ;
+
+ /*
+ * peer->ttl is a mode number specified by "127.127.40.X mode N" in the ntp.conf
+ */
+ switch ( peer->ttl ) {
+ case 0 :
+ /*
+ * The mode 0 is a default clock type at this time.
+ * But this will be change to auto-detect mode in the future.
+ */
+ case 1 :
+ up->unittype = UNITTYPE_TRISTATE_JJY01 ;
+ up->version = 100 ;
+ /* 2010/11/20 */
+ /* Command sequence is defined by the struct tristate_jjy01_command_sequence, */
+ /* and the following 3 lines are not used in the mode LDISC_CLK. */
+ /* up->lineexpect = 2 ; */
+ /* up->charexpect[0] = 14 ; */ /* YYYY/MM/DD WWW<CR><LF> */
+ /* up->charexpect[1] = 8 ; */ /* HH:MM:SS<CR><LF> */
+ break ;
+ case 2 :
+ up->unittype = UNITTYPE_CDEX_JST2000 ;
+ up->lineexpect = 1 ;
+ up->charexpect[0] = 15 ; /* <STX>JYYMMDD HHMMSSS<ETX> */
+ break ;
+ case 3 :
+ up->unittype = UNITTYPE_ECHOKEISOKUKI_LT2000 ;
+ up->operationmode = 2 ; /* Mode 2 : Continuous mode */
+ up->lineexpect = 1 ;
+ switch ( up->operationmode ) {
+ case 1 :
+ up->charexpect[0] = 15 ; /* YYMMDDWHHMMSS<BCC1><BCC2><CR> */
+ break ;
+ case 2 :
+ up->charexpect[0] = 17 ; /* YYMMDDWHHMMSS<ST1><ST2><ST3><ST4><CR> */
+ break ;
+ }
+ break ;
+ case 4 :
+ up->unittype = UNITTYPE_CITIZENTIC_JJY200 ;
+ up->lineexpect = 1 ;
+ up->charexpect[0] = 23 ; /* 'XX YY/MM/DD W HH:MM:SS<CR> */
+ break ;
+ case 5 :
+ up->unittype = UNITTYPE_TRISTATE_GPSCLOCK01 ;
+ break ;
+
+ /* 2010/11/20 */
+ /* The "default:" section of this switch block is never executed, */
+ /* because the former switch block traps the same "default:" case. */
+ /* This "default:" section codes are removed to avoid spending time */
+ /* in the future looking, though the codes are functionally harmless. */
+
+ }
+
+ pp = peer->procptr ;
+ pp->unitptr = up ;
+ pp->io.clock_recv = jjy_receive ;
+ pp->io.srcclock = peer ;
+ pp->io.datalen = 0 ;
+ pp->io.fd = fd ;
+ if ( ! io_addclock(&pp->io) ) {
+ close ( fd ) ;
+ pp->io.fd = -1 ;
+ free ( up ) ;
+ pp->unitptr = NULL ;
+ return RC_START_ERROR ;
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION ;
+ pp->clockdesc = DESCRIPTION ;
+ memcpy ( (char*)&pp->refid, REFID, strlen(REFID) ) ;
+
+ return RC_START_SUCCESS ;
+
+}
+
+
+/**************************************************************************************************/
+/* jjy_shutdown - shutdown the clock */
+/**************************************************************************************************/
+static void
+jjy_shutdown ( int unit, struct peer *peer )
+{
+
+ struct jjyunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr ;
+ up = pp->unitptr ;
+ if ( -1 != pp->io.fd )
+ io_closeclock ( &pp->io ) ;
+ if ( NULL != up )
+ free ( up ) ;
+
+}
+
+
+/**************************************************************************************************/
+/* jjy_receive - receive data from the serial interface */
+/**************************************************************************************************/
+static void
+jjy_receive ( struct recvbuf *rbufp )
+{
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ struct peer *peer;
+
+ l_fp tRecvTimestamp; /* arrival timestamp */
+ int rc ;
+ char sLogText [ MAX_LOGTEXT ] ;
+ int i, bCntrlChar ;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer ;
+ pp = peer->procptr ;
+ up = pp->unitptr ;
+
+ /*
+ * Get next input line
+ */
+ pp->lencode = refclock_gtlin ( rbufp, pp->a_lastcode, BMAX, &tRecvTimestamp ) ;
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ /*
+ * The reply with <STX> and <ETX> may give a blank line
+ */
+ if ( pp->lencode == 0 && up->charcount == 0 ) return ;
+ /*
+ * Copy received charaters to temporary buffer
+ */
+ for ( i = 0 ;
+ i < pp->lencode && up->charcount < MAX_RAWBUF - 2 ;
+ i ++ , up->charcount ++ ) {
+ up->rawbuf[up->charcount] = pp->a_lastcode[i] ;
+ }
+ while ( up->charcount > 0 && up->rawbuf[0] < ' ' ) {
+ for ( i = 0 ; i < up->charcount - 1 ; i ++ )
+ up->rawbuf[i] = up->rawbuf[i+1] ;
+ up->charcount -- ;
+ }
+ bCntrlChar = 0 ;
+ for ( i = 0 ; i < up->charcount ; i ++ ) {
+ if ( up->rawbuf[i] < ' ' ) {
+ bCntrlChar = 1 ;
+ break ;
+ }
+ }
+ if ( pp->lencode > 0 && up->linecount < up->lineexpect ) {
+ if ( bCntrlChar == 0 &&
+ up->charcount < up->charexpect[up->linecount] )
+ return ;
+ }
+ up->rawbuf[up->charcount] = 0 ;
+ } else {
+ /*
+ * The reply with <CR><LF> gives a blank line
+ */
+ if ( pp->lencode == 0 ) return ;
+ }
+ /*
+ * We get down to business
+ */
+
+#ifdef DEBUG
+ if ( debug ) {
+ if ( up->linediscipline == LDISC_RAW ) {
+ printableString( sLogText, MAX_LOGTEXT, up->rawbuf, up->charcount ) ;
+ } else {
+ printableString( sLogText, MAX_LOGTEXT, pp->a_lastcode, pp->lencode ) ;
+ }
+ printf ( "jjy_receive (refclock_jjy.c) : [%s]\n", sLogText ) ;
+ }
+#endif
+
+ pp->lastrec = tRecvTimestamp ;
+
+ up->linecount ++ ;
+
+ if ( up->lineerror != 0 ) return ;
+
+ switch ( up->unittype ) {
+
+ case UNITTYPE_TRISTATE_JJY01 :
+ rc = jjy_receive_tristate_jjy01 ( rbufp ) ;
+ break ;
+
+ case UNITTYPE_CDEX_JST2000 :
+ rc = jjy_receive_cdex_jst2000 ( rbufp ) ;
+ break ;
+
+ case UNITTYPE_ECHOKEISOKUKI_LT2000 :
+ rc = jjy_receive_echokeisokuki_lt2000 ( rbufp ) ;
+ break ;
+
+ case UNITTYPE_CITIZENTIC_JJY200 :
+ rc = jjy_receive_citizentic_jjy200 ( rbufp ) ;
+ break ;
+
+ case UNITTYPE_TRISTATE_GPSCLOCK01 :
+ rc = jjy_receive_tristate_gpsclock01 ( rbufp ) ;
+ break ;
+
+ default :
+ rc = 0 ;
+ break ;
+
+ }
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ if ( up->linecount <= up->lineexpect &&
+ up->charcount > up->charexpect[up->linecount-1] ) {
+ for ( i = 0 ;
+ i < up->charcount - up->charexpect[up->linecount-1] ;
+ i ++ ) {
+ up->rawbuf[i] = up->rawbuf[i+up->charexpect[up->linecount-1]] ;
+ }
+ up->charcount -= up->charexpect[up->linecount-1] ;
+ } else {
+ up->charcount = 0 ;
+ }
+ }
+
+ if ( rc == 0 ) {
+ return ;
+ }
+
+ up->bPollFlag = 0 ;
+
+ if ( up->lineerror != 0 ) {
+ refclock_report ( peer, CEVNT_BADREPLY ) ;
+ strlcpy ( sLogText, "BAD REPLY [",
+ sizeof( sLogText ) ) ;
+ if ( up->linediscipline == LDISC_RAW ) {
+ strlcat ( sLogText, up->rawbuf,
+ sizeof( sLogText ) ) ;
+ } else {
+ strlcat ( sLogText, pp->a_lastcode,
+ sizeof( sLogText ) ) ;
+ }
+ sLogText[MAX_LOGTEXT-1] = 0 ;
+ if ( strlen ( sLogText ) < MAX_LOGTEXT - 2 )
+ strlcat ( sLogText, "]",
+ sizeof( sLogText ) ) ;
+ record_clock_stats ( &peer->srcadr, sLogText ) ;
+ return ;
+ }
+
+ pp->year = up->year ;
+ pp->day = ymd2yd ( up->year, up->month, up->day ) ;
+ pp->hour = up->hour ;
+ pp->minute = up->minute ;
+ pp->second = up->second ;
+ pp->nsec = up->msecond * 1000000;
+
+ /*
+ * JST to UTC
+ */
+ pp->hour -= 9 ;
+ if ( pp->hour < 0 ) {
+ pp->hour += 24 ;
+ pp->day -- ;
+ if ( pp->day < 1 ) {
+ pp->year -- ;
+ pp->day = ymd2yd ( pp->year, 12, 31 ) ;
+ }
+ }
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "jjy_receive (refclock_jjy.c) : %04d/%02d/%02d %02d:%02d:%02d.%1d JST ",
+ up->year, up->month, up->day, up->hour,
+ up->minute, up->second, up->msecond/100 ) ;
+ printf ( "( %04d/%03d %02d:%02d:%02d.%1d UTC )\n",
+ pp->year, pp->day, pp->hour, pp->minute,
+ pp->second, (int)(pp->nsec/100000000) ) ;
+ }
+#endif
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+
+ snprintf ( sLogText, sizeof(sLogText),
+ "%04d/%02d/%02d %02d:%02d:%02d.%1d JST",
+ up->year, up->month, up->day,
+ up->hour, up->minute, up->second, up->msecond/100 ) ;
+ record_clock_stats ( &peer->srcadr, sLogText ) ;
+
+ if ( ! refclock_process ( pp ) ) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return ;
+ }
+
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+}
+
+/**************************************************************************************************/
+
+static int
+jjy_receive_tristate_jjy01 ( struct recvbuf *rbufp )
+{
+#ifdef DEBUG
+ static char *sFunctionName = "jjy_receive_tristate_jjy01" ;
+#endif
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ struct peer *peer;
+
+ char *pBuf ;
+ int iLen ;
+ int rc ;
+
+ int bOverMidnight = 0 ;
+
+ char sLogText [ MAX_LOGTEXT ], sReplyText [ MAX_LOGTEXT ] ;
+
+ char *pCmd ;
+ int iCmdLen ;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer ;
+ pp = peer->procptr ;
+ up = pp->unitptr ;
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ pBuf = up->rawbuf ;
+ iLen = up->charcount ;
+ } else {
+ pBuf = pp->a_lastcode ;
+ iLen = pp->lencode ;
+ }
+
+ switch ( tristate_jjy01_command_sequence[up->linecount-1].commandNumber ) {
+
+ case TS_JJY01_COMMAND_NUMBER_DATE : /* YYYY/MM/DD WWW */
+
+ if ( iLen != TS_JJY01_REPLY_LENGTH_DATE ) {
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ rc = sscanf ( pBuf, "%4d/%2d/%2d", &up->year,
+ &up->month, &up->day ) ;
+ if ( rc != 3 || up->year < 2000 || up->month < 1 ||
+ up->month > 12 || up->day < 1 || up->day > 31 ) {
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ /*** Start of modification on 2004/10/31 ***/
+ /*
+ * Following codes are moved from the function jjy_poll_tristate_jjy01 in this source.
+ * The Tristate JJY-01 ( Firmware version 1.01 ) accepts "time" and "stim" commands without any delay.
+ * But the JJY-01 ( Firmware version 2.01 ) does not accept these commands continuously,
+ * so this driver issues the second command "stim" after the reply of the first command "date".
+ */
+
+ /*** 2010/11/20 ***/
+ /*
+ * Codes of a next command issue are moved to the end of this function.
+ */
+
+ /*** End of modification ***/
+
+ break ;
+
+ case TS_JJY01_COMMAND_NUMBER_TIME : /* HH:MM:SS */
+ case TS_JJY01_COMMAND_NUMBER_STIM : /* HH:MM:SS */
+
+ if ( iLen != TS_JJY01_REPLY_LENGTH_STIM ) {
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ rc = sscanf ( pBuf, "%2d:%2d:%2d", &up->hour,
+ &up->minute, &up->second ) ;
+ if ( rc != 3 || up->hour > 23 || up->minute > 59 ||
+ up->second > 60 ) {
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ up->msecond = 0 ;
+ if ( up->hour == 0 && up->minute == 0 && up->second <= 2 ) {
+ /*
+ * The command "date" and "time" ( or "stim" ) were sent to the JJY receiver separately,
+ * and the JJY receiver replies a date and time separately.
+ * Just after midnight transitions, we ignore this time.
+ */
+ bOverMidnight = 1 ;
+ }
+ break ;
+
+ case TS_JJY01_COMMAND_NUMBER_STUS :
+
+ if ( ( iLen == TS_JJY01_REPLY_LENGTH_STUS_YES
+ && strncmp( pBuf, TS_JJY01_REPLY_STUS_YES,
+ TS_JJY01_REPLY_LENGTH_STUS_YES ) == 0 )
+ || ( iLen == TS_JJY01_REPLY_LENGTH_STUS_NO
+ && strncmp( pBuf, TS_JJY01_REPLY_STUS_NO,
+ TS_JJY01_REPLY_LENGTH_STUS_NO ) == 0 ) ) {
+ /* Good */
+ } else {
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ break ;
+
+ case TS_JJY01_COMMAND_NUMBER_DCST :
+
+ if ( ( iLen == TS_JJY01_REPLY_LENGTH_DCST_VALID
+ && strncmp( pBuf, TS_JJY01_REPLY_DCST_VALID,
+ TS_JJY01_REPLY_LENGTH_DCST_VALID ) == 0 )
+ || ( iLen == TS_JJY01_REPLY_LENGTH_DCST_INVALID
+ && strncmp( pBuf, TS_JJY01_REPLY_DCST_INVALID,
+ TS_JJY01_REPLY_LENGTH_DCST_INVALID ) == 0 ) ) {
+ /* Good */
+ } else {
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ break ;
+
+ default : /* Unexpected reply */
+
+ up->lineerror = 1 ;
+ break ;
+
+ }
+
+ /* Clockstats Log */
+
+ printableString( sReplyText, sizeof(sReplyText), pBuf, iLen ) ;
+ snprintf ( sLogText, sizeof(sLogText), "%d: %s -> %c: %s",
+ up->linecount,
+ tristate_jjy01_command_sequence[up->linecount-1].commandLog,
+ ( up->lineerror == 0 )
+ ? ( ( bOverMidnight == 0 )
+ ? 'O'
+ : 'S' )
+ : 'X',
+ sReplyText ) ;
+ record_clock_stats ( &peer->srcadr, sLogText ) ;
+
+ /* Check before issue next command */
+
+ if ( up->lineerror != 0 ) {
+ /* Do not issue next command */
+ return 0 ;
+ }
+
+ if ( bOverMidnight != 0 ) {
+ /* Do not issue next command */
+ return 0 ;
+ }
+
+ if ( tristate_jjy01_command_sequence[up->linecount].command == NULL ) {
+ /* Command sequence completed */
+ return 1 ;
+ }
+
+ /* Issue next command */
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : send '%s'\n",
+ sFunctionName, tristate_jjy01_command_sequence[up->linecount].commandLog ) ;
+ }
+#endif
+
+ pCmd = tristate_jjy01_command_sequence[up->linecount].command ;
+ iCmdLen = tristate_jjy01_command_sequence[up->linecount].commandLength ;
+ if ( write ( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+ return 0 ;
+
+}
+
+/**************************************************************************************************/
+
+static int
+jjy_receive_cdex_jst2000 ( struct recvbuf *rbufp )
+{
+#ifdef DEBUG
+ static char *sFunctionName = "jjy_receive_cdex_jst2000" ;
+#endif
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ struct peer *peer;
+
+ char *pBuf ;
+ int iLen ;
+ int rc ;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer ;
+ pp = peer->procptr ;
+ up = pp->unitptr ;
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ pBuf = up->rawbuf ;
+ iLen = up->charcount ;
+ } else {
+ pBuf = pp->a_lastcode ;
+ iLen = pp->lencode ;
+ }
+
+ switch ( up->linecount ) {
+
+ case 1 : /* JYYMMDD HHMMSSS */
+
+ if ( iLen != 15 ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : Reply length error ( iLen=%d )\n",
+ sFunctionName, iLen ) ;
+ }
+#endif
+ up->lineerror = 1 ;
+ break ;
+ }
+ rc = sscanf ( pBuf, "J%2d%2d%2d%*1d%2d%2d%2d%1d",
+ &up->year, &up->month, &up->day,
+ &up->hour, &up->minute, &up->second,
+ &up->msecond ) ;
+ if ( rc != 7 || up->month < 1 || up->month > 12 ||
+ up->day < 1 || up->day > 31 || up->hour > 23 ||
+ up->minute > 59 || up->second > 60 ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : Time error (rc=%d) [ %02d %02d %02d * %02d %02d %02d.%1d ]\n",
+ sFunctionName, rc, up->year,
+ up->month, up->day, up->hour,
+ up->minute, up->second,
+ up->msecond ) ;
+ }
+#endif
+ up->lineerror = 1 ;
+ break ;
+ }
+ up->year += 2000 ;
+ up->msecond *= 100 ;
+ break ;
+
+ default : /* Unexpected reply */
+
+ up->lineerror = 1 ;
+ break ;
+
+ }
+
+ return 1 ;
+
+}
+
+/**************************************************************************************************/
+
+static int
+jjy_receive_echokeisokuki_lt2000 ( struct recvbuf *rbufp )
+{
+#ifdef DEBUG
+ static char *sFunctionName = "jjy_receive_echokeisokuki_lt2000" ;
+#endif
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ struct peer *peer;
+
+ char *pBuf ;
+ int iLen ;
+ int rc ;
+ int i, ibcc, ibcc1, ibcc2 ;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer ;
+ pp = peer->procptr ;
+ up = pp->unitptr ;
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ pBuf = up->rawbuf ;
+ iLen = up->charcount ;
+ } else {
+ pBuf = pp->a_lastcode ;
+ iLen = pp->lencode ;
+ }
+
+ switch ( up->linecount ) {
+
+ case 1 : /* YYMMDDWHHMMSS<BCC1><BCC2> or YYMMDDWHHMMSS<ST1><ST2><ST3><ST4> */
+
+ if ( ( up->operationmode == 1 && iLen != 15 ) ||
+ ( up->operationmode == 2 && iLen != 17 ) ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : Reply length error ( iLen=%d )\n",
+ sFunctionName, iLen ) ;
+ }
+#endif
+ if ( up->operationmode == 1 ) {
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : send '#'\n", sFunctionName ) ;
+ }
+#endif
+ if ( write ( pp->io.fd, "#",1 ) != 1 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+ }
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ if ( up->operationmode == 1 ) {
+
+ for ( i = ibcc = 0 ; i < 13 ; i ++ )
+ ibcc ^= pBuf[i] ;
+ ibcc1 = 0x30 | ( ( ibcc >> 4 ) & 0xF ) ;
+ ibcc2 = 0x30 | ( ( ibcc ) & 0xF ) ;
+ if ( pBuf[13] != ibcc1 || pBuf[14] != ibcc2 ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : BCC error ( Recv=%02X,%02X / Calc=%02X,%02X)\n",
+ sFunctionName,
+ pBuf[13] & 0xFF,
+ pBuf[14] & 0xFF,
+ ibcc1, ibcc2 ) ;
+ }
+#endif
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ }
+
+ rc = sscanf ( pBuf, "%2d%2d%2d%*1d%2d%2d%2d",
+ &up->year, &up->month, &up->day,
+ &up->hour, &up->minute, &up->second ) ;
+ if ( rc != 6 || up->month < 1 || up->month > 12 ||
+ up->day < 1 || up->day > 31 || up->hour > 23 ||
+ up->minute > 59 || up->second > 60 ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : Time error (rc=%d) [ %02d %02d %02d * %02d %02d %02d ]\n",
+ sFunctionName, rc, up->year,
+ up->month, up->day, up->hour,
+ up->minute, up->second ) ;
+ }
+#endif
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ up->year += 2000 ;
+
+ if ( up->operationmode == 2 ) {
+
+ /* A time stamp comes on every 0.5 seccond in the mode 2 of the LT-2000. */
+ up->msecond = 500 ;
+ pp->second -- ;
+ if ( pp->second < 0 ) {
+ pp->second = 59 ;
+ pp->minute -- ;
+ if ( pp->minute < 0 ) {
+ pp->minute = 59 ;
+ pp->hour -- ;
+ if ( pp->hour < 0 ) {
+ pp->hour = 23 ;
+ pp->day -- ;
+ if ( pp->day < 1 ) {
+ pp->year -- ;
+ pp->day = ymd2yd ( pp->year, 12, 31 ) ;
+ }
+ }
+ }
+ }
+
+ /* Switch from mode 2 to mode 1 in order to restraint of useless time stamp. */
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : send '#'\n",
+ sFunctionName ) ;
+ }
+#endif
+ if ( write ( pp->io.fd, "#",1 ) != 1 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+ }
+
+ break ;
+
+ default : /* Unexpected reply */
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : send '#'\n",
+ sFunctionName ) ;
+ }
+#endif
+ if ( write ( pp->io.fd, "#",1 ) != 1 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+ up->lineerror = 1 ;
+ break ;
+
+ }
+
+ return 1 ;
+
+}
+
+/**************************************************************************************************/
+
+static int
+jjy_receive_citizentic_jjy200 ( struct recvbuf *rbufp )
+{
+#ifdef DEBUG
+ static char *sFunctionName = "jjy_receive_citizentic_jjy200" ;
+#endif
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ struct peer *peer;
+
+ char *pBuf ;
+ int iLen ;
+ int rc ;
+ char cApostrophe, sStatus[3] ;
+ int iWeekday ;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer ;
+ pp = peer->procptr ;
+ up = pp->unitptr ;
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ pBuf = up->rawbuf ;
+ iLen = up->charcount ;
+ } else {
+ pBuf = pp->a_lastcode ;
+ iLen = pp->lencode ;
+ }
+
+ /*
+ * JJY-200 sends a timestamp every second.
+ * So, a timestamp is ignored unless it is right after polled.
+ */
+ if ( ! up->bPollFlag )
+ return 0 ;
+
+ switch ( up->linecount ) {
+
+ case 1 : /* 'XX YY/MM/DD W HH:MM:SS<CR> */
+
+ if ( iLen != 23 ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : Reply length error ( iLen=%d )\n",
+ sFunctionName, iLen ) ;
+ }
+#endif
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ rc = sscanf ( pBuf, "%c%2s %2d/%2d/%2d %1d %2d:%2d:%2d",
+ &cApostrophe, sStatus, &up->year,
+ &up->month, &up->day, &iWeekday,
+ &up->hour, &up->minute, &up->second ) ;
+ sStatus[2] = 0 ;
+ if ( rc != 9 || cApostrophe != '\'' ||
+ strcmp( sStatus, "OK" ) != 0 || up->month < 1 ||
+ up->month > 12 || up->day < 1 || up->day > 31 ||
+ iWeekday > 6 || up->hour > 23 || up->minute > 59 ||
+ up->second > 60 ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : Time error (rc=%d) [ %c %2s %02d %02d %02d %d %02d %02d %02d ]\n",
+ sFunctionName, rc, cApostrophe,
+ sStatus, up->year, up->month,
+ up->day, iWeekday, up->hour,
+ up->minute, up->second ) ;
+ }
+#endif
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ up->year += 2000 ;
+ up->msecond = 0 ;
+
+ break ;
+
+ default : /* Unexpected reply */
+
+ up->lineerror = 1 ;
+ break ;
+
+ }
+
+ return 1 ;
+
+}
+
+/**************************************************************************************************/
+
+static int
+jjy_receive_tristate_gpsclock01 ( struct recvbuf *rbufp )
+{
+#ifdef DEBUG
+ static char *sFunctionName = "jjy_receive_tristate_gpsclock01" ;
+#endif
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ struct peer *peer;
+
+ char *pBuf ;
+ int iLen ;
+ int rc ;
+
+ int bOverMidnight = 0 ;
+
+ char sLogText [ MAX_LOGTEXT ], sReplyText [ MAX_LOGTEXT ] ;
+
+ char *pCmd ;
+ int iCmdLen ;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer ;
+ pp = peer->procptr ;
+ up = pp->unitptr ;
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ pBuf = up->rawbuf ;
+ iLen = up->charcount ;
+ } else {
+ pBuf = pp->a_lastcode ;
+ iLen = pp->lencode ;
+ }
+
+ /*
+ * Ignore NMEA data stream
+ */
+ if ( iLen > 5
+ && ( strncmp( pBuf, "$GP", 3 ) == 0 || strncmp( pBuf, "$PFEC", 5 ) == 0 ) ) {
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : Skip NMEA stream [%s]\n",
+ sFunctionName, pBuf ) ;
+ }
+#endif
+ return 0 ;
+ }
+
+ /*
+ * Skip command prompt '$Cmd>' from the TS-GPSclock-01
+ */
+ if ( iLen == 5 && strncmp( pBuf, "$Cmd>", 5 ) == 0 ) {
+ return 0 ;
+ } else if ( iLen > 5 && strncmp( pBuf, "$Cmd>", 5 ) == 0 ) {
+ pBuf += 5 ;
+ iLen -= 5 ;
+ }
+
+ /*
+ * Ignore NMEA data stream after command prompt
+ */
+ if ( iLen > 5
+ && ( strncmp( pBuf, "$GP", 3 ) == 0 || strncmp( pBuf, "$PFEC", 5 ) == 0 ) ) {
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : Skip NMEA stream [%s]\n",
+ sFunctionName, pBuf ) ;
+ }
+#endif
+ return 0 ;
+ }
+
+ switch ( tristate_gpsclock01_command_sequence[up->linecount-1].commandNumber ) {
+
+ case TS_GPSCLOCK01_COMMAND_NUMBER_DATE : /* YYYY/MM/DD */
+
+ if ( iLen != TS_GPSCLOCK01_REPLY_LENGTH_DATE ) {
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ rc = sscanf ( pBuf, "%4d/%2d/%2d", &up->year, &up->month, &up->day ) ;
+ if ( rc != 3 || up->year < 2000 || up->month < 1 || up->month > 12 ||
+ up->day < 1 || up->day > 31 ) {
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ break ;
+
+ case TS_GPSCLOCK01_COMMAND_NUMBER_TIME : /* HH:MM:SS */
+
+ if ( iLen != TS_GPSCLOCK01_REPLY_LENGTH_TIME ) {
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ rc = sscanf ( pBuf, "%2d:%2d:%2d", &up->hour, &up->minute, &up->second ) ;
+ if ( rc != 3 || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ up->msecond = 0 ;
+
+ if ( up->hour == 0 && up->minute == 0 && up->second <= 2 ) {
+ /*
+ * The command "date" and "time" were sent to the JJY receiver separately,
+ * and the JJY receiver replies a date and time separately.
+ * Just after midnight transitions, we ignore this time.
+ */
+ bOverMidnight = 1 ;
+ }
+
+ break ;
+
+ case TS_GPSCLOCK01_COMMAND_NUMBER_STUS :
+
+ if ( iLen == TS_GPSCLOCK01_REPLY_LENGTH_STUS
+ && ( strncmp( pBuf, TS_GPSCLOCK01_REPLY_STUS_RTC, TS_GPSCLOCK01_REPLY_LENGTH_STUS ) == 0
+ || strncmp( pBuf, TS_GPSCLOCK01_REPLY_STUS_GPS, TS_GPSCLOCK01_REPLY_LENGTH_STUS ) == 0
+ || strncmp( pBuf, TS_GPSCLOCK01_REPLY_STUS_UTC, TS_GPSCLOCK01_REPLY_LENGTH_STUS ) == 0
+ || strncmp( pBuf, TS_GPSCLOCK01_REPLY_STUS_PPS, TS_GPSCLOCK01_REPLY_LENGTH_STUS ) == 0 ) ) {
+ /* Good */
+ } else {
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ break ;
+
+ default : /* Unexpected reply */
+
+ up->lineerror = 1 ;
+ break ;
+
+ }
+
+ /* Clockstats Log */
+
+ printableString( sReplyText, sizeof(sReplyText), pBuf, iLen ) ;
+ snprintf ( sLogText, sizeof(sLogText), "%d: %s -> %c: %s",
+ up->linecount,
+ tristate_gpsclock01_command_sequence[up->linecount-1].commandLog,
+ ( up->lineerror == 0 )
+ ? ( ( bOverMidnight == 0 )
+ ? 'O'
+ : 'S' )
+ : 'X',
+ sReplyText ) ;
+ record_clock_stats ( &peer->srcadr, sLogText ) ;
+
+ /* Check before issue next command */
+
+ if ( up->lineerror != 0 ) {
+ /* Do not issue next command */
+ return 0 ;
+ }
+
+ if ( bOverMidnight != 0 ) {
+ /* Do not issue next command */
+ return 0 ;
+ }
+
+ if ( tristate_gpsclock01_command_sequence[up->linecount].command == NULL ) {
+ /* Command sequence completed */
+ return 1 ;
+ }
+
+ /* Issue next command */
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : send '%s'\n",
+ sFunctionName, tristate_gpsclock01_command_sequence[up->linecount].commandLog ) ;
+ }
+#endif
+
+ pCmd = tristate_gpsclock01_command_sequence[up->linecount].command ;
+ iCmdLen = tristate_gpsclock01_command_sequence[up->linecount].commandLength ;
+ if ( write ( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+ return 0 ;
+
+}
+
+/**************************************************************************************************/
+/* jjy_poll - called by the transmit procedure */
+/**************************************************************************************************/
+static void
+jjy_poll ( int unit, struct peer *peer )
+{
+
+ struct jjyunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr ;
+
+ if ( pp->polls > 0 && up->linecount == 0 ) {
+ /*
+ * No reply for last command
+ */
+ refclock_report ( peer, CEVNT_TIMEOUT ) ;
+ }
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "jjy_poll (refclock_jjy.c) : %ld\n", pp->polls ) ;
+ }
+#endif
+
+ pp->polls ++ ;
+
+ up->bPollFlag = 1 ;
+ up->linecount = 0 ;
+ up->lineerror = 0 ;
+ up->charcount = 0 ;
+
+ switch ( up->unittype ) {
+
+ case UNITTYPE_TRISTATE_JJY01 :
+ jjy_poll_tristate_jjy01 ( unit, peer ) ;
+ break ;
+
+ case UNITTYPE_CDEX_JST2000 :
+ jjy_poll_cdex_jst2000 ( unit, peer ) ;
+ break ;
+
+ case UNITTYPE_ECHOKEISOKUKI_LT2000 :
+ jjy_poll_echokeisokuki_lt2000 ( unit, peer ) ;
+ break ;
+
+ case UNITTYPE_CITIZENTIC_JJY200 :
+ jjy_poll_citizentic_jjy200 ( unit, peer ) ;
+ break ;
+
+ case UNITTYPE_TRISTATE_GPSCLOCK01 :
+ jjy_poll_tristate_gpsclock01 ( unit, peer ) ;
+ break ;
+
+ default :
+ break ;
+
+ }
+
+}
+
+/**************************************************************************************************/
+
+static void
+jjy_poll_tristate_jjy01 ( int unit, struct peer *peer )
+{
+#ifdef DEBUG
+ static char *sFunctionName = "jjy_poll_tristate_jjy01" ;
+#endif
+
+ struct jjyunit *up;
+ struct refclockproc *pp;
+
+ char *pCmd ;
+ int iCmdLen ;
+
+ pp = peer->procptr;
+ up = pp->unitptr ;
+
+ if ( ( pp->sloppyclockflag & CLK_FLAG1 ) == 0 ) {
+ up->linecount = 2 ;
+ }
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : flag1=%X CLK_FLAG1=%X up->linecount=%d\n",
+ sFunctionName, pp->sloppyclockflag, CLK_FLAG1,
+ up->linecount ) ;
+ }
+#endif
+
+ /*
+ * Send a first command
+ */
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : send '%s'\n",
+ sFunctionName,
+ tristate_jjy01_command_sequence[up->linecount].commandLog ) ;
+ }
+#endif
+
+ pCmd = tristate_jjy01_command_sequence[up->linecount].command ;
+ iCmdLen = tristate_jjy01_command_sequence[up->linecount].commandLength ;
+ if ( write ( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+}
+
+/**************************************************************************************************/
+
+static void
+jjy_poll_cdex_jst2000 ( int unit, struct peer *peer )
+{
+
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ /*
+ * Send "<ENQ>1J<ETX>" command
+ */
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "jjy_poll_cdex_jst2000 (refclock_jjy.c) : send '<ENQ>1J<ETX>'\n" ) ;
+ }
+#endif
+
+ if ( write ( pp->io.fd, "\0051J\003", 4 ) != 4 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+}
+
+/**************************************************************************************************/
+
+static void
+jjy_poll_echokeisokuki_lt2000 ( int unit, struct peer *peer )
+{
+
+ struct jjyunit *up;
+ struct refclockproc *pp;
+
+ char sCmd[2] ;
+
+ pp = peer->procptr;
+ up = pp->unitptr ;
+
+ /*
+ * Send "T" or "C" command
+ */
+
+ switch ( up->operationmode ) {
+ case 1 : sCmd[0] = 'T' ; break ;
+ case 2 : sCmd[0] = 'C' ; break ;
+ }
+ sCmd[1] = 0 ;
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "jjy_poll_echokeisokuki_lt2000 (refclock_jjy.c) : send '%s'\n", sCmd ) ;
+ }
+#endif
+
+ if ( write ( pp->io.fd, sCmd, 1 ) != 1 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+}
+
+/**************************************************************************************************/
+
+static void
+jjy_poll_citizentic_jjy200 ( int unit, struct peer *peer )
+{
+
+ /* Do nothing ( up->bPollFlag is set by the jjy_poll ) */
+
+}
+
+/**************************************************************************************************/
+
+static void
+jjy_poll_tristate_gpsclock01 ( int unit, struct peer *peer )
+{
+#ifdef DEBUG
+ static char *sFunctionName = "jjy_poll_tristate_gpsclock01" ;
+#endif
+
+ struct jjyunit *up;
+ struct refclockproc *pp;
+
+ char *pCmd ;
+ int iCmdLen ;
+
+ pp = peer->procptr;
+ up = pp->unitptr ;
+
+ if ( ( pp->sloppyclockflag & CLK_FLAG1 ) == 0 ) {
+ up->linecount = 1 ;
+ }
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : flag1=%X CLK_FLAG1=%X up->linecount=%d\n",
+ sFunctionName, pp->sloppyclockflag, CLK_FLAG1,
+ up->linecount ) ;
+ }
+#endif
+
+ /*
+ * Send a first command
+ */
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : send '%s'\n",
+ sFunctionName,
+ tristate_gpsclock01_command_sequence[up->linecount].commandLog ) ;
+ }
+#endif
+
+ pCmd = tristate_gpsclock01_command_sequence[up->linecount].command ;
+ iCmdLen = tristate_gpsclock01_command_sequence[up->linecount].commandLength ;
+ if ( write ( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+}
+
+/**************************************************************************************************/
+
+static void
+printableString ( char *sOutput, int iOutputLen, char *sInput, int iInputLen )
+{
+ char *printableControlChar[] = {
+ "<NUL>", "<SOH>", "<STX>", "<ETX>",
+ "<EOT>", "<ENQ>", "<ACK>", "<BEL>",
+ "<BS>" , "<HT>" , "<LF>" , "<VT>" ,
+ "<FF>" , "<CR>" , "<SO>" , "<SI>" ,
+ "<DLE>", "<DC1>", "<DC2>", "<DC3>",
+ "<DC4>", "<NAK>", "<SYN>", "<ETB>",
+ "<CAN>", "<EM>" , "<SUB>", "<ESC>",
+ "<FS>" , "<GS>" , "<RS>" , "<US>" ,
+ " " } ;
+ size_t InputLen;
+ size_t OutputLen;
+ size_t i;
+ size_t j;
+ size_t n;
+
+ InputLen = (size_t)iInputLen;
+ OutputLen = (size_t)iOutputLen;
+ for ( i = j = 0 ; i < InputLen && j < OutputLen ; i ++ ) {
+ if ( isprint( sInput[i] ) ) {
+ n = 1 ;
+ if ( j + 1 >= OutputLen )
+ break ;
+ sOutput[j] = sInput[i] ;
+ } else if ( ( sInput[i] & 0xFF ) <
+ COUNTOF(printableControlChar) ) {
+ n = strlen( printableControlChar[sInput[i] & 0xFF] ) ;
+ if ( j + n + 1 >= OutputLen )
+ break ;
+ strlcpy( sOutput + j,
+ printableControlChar[sInput[i] & 0xFF],
+ OutputLen - j ) ;
+ } else {
+ n = 5 ;
+ if ( j + n + 1 >= OutputLen )
+ break ;
+ snprintf( sOutput + j, OutputLen - j, "<x%X>",
+ sInput[i] & 0xFF ) ;
+ }
+ j += n ;
+ }
+
+ sOutput[min(j, iOutputLen - 1)] = '\0' ;
+
+}
+
+/**************************************************************************************************/
+
+#else
+int refclock_jjy_bs ;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_jupiter.c b/ntpd/refclock_jupiter.c
new file mode 100644
index 0000000..377fc56
--- /dev/null
+++ b/ntpd/refclock_jupiter.c
@@ -0,0 +1,1121 @@
+/*
+ * Copyright (c) 1997, 1998, 2003
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory.
+ * 4. The name of the University may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_JUPITER) && defined(HAVE_PPSAPI)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "jupiter.h"
+
+#ifdef HAVE_PPSAPI
+# include "ppsapi_timepps.h"
+#endif
+
+#ifdef WORDS_BIGENDIAN
+#define getshort(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))
+#define putshort(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))
+#else
+#define getshort(s) ((u_short)(s))
+#define putshort(s) ((u_short)(s))
+#endif
+
+/*
+ * This driver supports the Rockwell Jupiter GPS Receiver board
+ * adapted to precision timing applications. It requires the
+ * ppsclock line discipline or streams module described in the
+ * Line Disciplines and Streams Drivers page. It also requires a
+ * gadget box and 1-PPS level converter, such as described in the
+ * Pulse-per-second (PPS) Signal Interfacing page.
+ *
+ * It may work (with minor modifications) with other Rockwell GPS
+ * receivers such as the CityTracker.
+ */
+
+/*
+ * GPS Definitions
+ */
+#define DEVICE "/dev/gps%d" /* device name and unit */
+#define SPEED232 B9600 /* baud */
+
+/*
+ * Radio interface parameters
+ */
+#define PRECISION (-18) /* precision assumed (about 4 us) */
+#define REFID "GPS\0" /* reference id */
+#define DESCRIPTION "Rockwell Jupiter GPS Receiver" /* who we are */
+#define DEFFUDGETIME 0 /* default fudge time (ms) */
+
+/* Unix timestamp for the GPS epoch: January 6, 1980 */
+#define GPS_EPOCH 315964800
+
+/* Double short to unsigned int */
+#define DS2UI(p) ((getshort((p)[1]) << 16) | getshort((p)[0]))
+
+/* Double short to signed int */
+#define DS2I(p) ((getshort((p)[1]) << 16) | getshort((p)[0]))
+
+/* One week's worth of seconds */
+#define WEEKSECS (7 * 24 * 60 * 60)
+
+/*
+ * Jupiter unit control structure.
+ */
+struct instance {
+ struct peer *peer; /* peer */
+ u_int pollcnt; /* poll message counter */
+ u_int polled; /* Hand in a time sample? */
+#ifdef HAVE_PPSAPI
+ pps_params_t pps_params; /* pps parameters */
+ pps_info_t pps_info; /* last pps data */
+ pps_handle_t pps_handle; /* pps handle */
+ u_int assert; /* pps edge to use */
+ u_int hardpps; /* enable kernel mode */
+ struct timespec ts; /* last timestamp */
+#endif
+ l_fp limit;
+ u_int gpos_gweek; /* Current GPOS GPS week number */
+ u_int gpos_sweek; /* Current GPOS GPS seconds into week */
+ u_int gweek; /* current GPS week number */
+ u_int32 lastsweek; /* last seconds into GPS week */
+ time_t timecode; /* current ntp timecode */
+ u_int32 stime; /* used to detect firmware bug */
+ int wantid; /* don't reconfig on channel id msg */
+ u_int moving; /* mobile platform? */
+ u_char sloppyclockflag; /* fudge flags */
+ u_short sbuf[512]; /* local input buffer */
+ int ssize; /* space used in sbuf */
+};
+
+/*
+ * Function prototypes
+ */
+static void jupiter_canmsg (struct instance *, u_int);
+static u_short jupiter_cksum (u_short *, u_int);
+static int jupiter_config (struct instance *);
+static void jupiter_debug (struct peer *, const char *,
+ const char *, ...)
+ __attribute__ ((format (printf, 3, 4)));
+static char * jupiter_parse_t (struct instance *, u_short *);
+static char * jupiter_parse_gpos (struct instance *, u_short *);
+static void jupiter_platform (struct instance *, u_int);
+static void jupiter_poll (int, struct peer *);
+static void jupiter_control (int, const struct refclockstat *,
+ struct refclockstat *, struct peer *);
+#ifdef HAVE_PPSAPI
+static int jupiter_ppsapi (struct instance *);
+static int jupiter_pps (struct instance *);
+#endif /* HAVE_PPSAPI */
+static int jupiter_recv (struct instance *);
+static void jupiter_receive (struct recvbuf *rbufp);
+static void jupiter_reqmsg (struct instance *, u_int, u_int);
+static void jupiter_reqonemsg(struct instance *, u_int);
+static char * jupiter_send (struct instance *, struct jheader *);
+static void jupiter_shutdown(int, struct peer *);
+static int jupiter_start (int, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_jupiter = {
+ jupiter_start, /* start up driver */
+ jupiter_shutdown, /* shut down driver */
+ jupiter_poll, /* transmit poll message */
+ jupiter_control, /* (clock control) */
+ noentry, /* (clock init) */
+ noentry, /* (clock buginfo) */
+ NOFLAGS /* not used */
+};
+
+/*
+ * jupiter_start - open the devices and initialize data for processing
+ */
+static int
+jupiter_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct instance *instance;
+ int fd;
+ char gpsdev[20];
+
+ /*
+ * Open serial port
+ */
+ snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit);
+ fd = refclock_open(gpsdev, SPEED232, LDISC_RAW);
+ if (fd <= 0) {
+ jupiter_debug(peer, "jupiter_start", "open %s: %m",
+ gpsdev);
+ return (0);
+ }
+
+ /* Allocate unit structure */
+ instance = emalloc_zero(sizeof(*instance));
+ instance->peer = peer;
+ pp = peer->procptr;
+ pp->io.clock_recv = jupiter_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(instance);
+ return (0);
+ }
+ pp->unitptr = instance;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+#ifdef HAVE_PPSAPI
+ instance->assert = 1;
+ instance->hardpps = 0;
+ /*
+ * Start the PPSAPI interface if it is there. Default to use
+ * the assert edge and do not enable the kernel hardpps.
+ */
+ if (time_pps_create(fd, &instance->pps_handle) < 0) {
+ instance->pps_handle = 0;
+ msyslog(LOG_ERR,
+ "refclock_jupiter: time_pps_create failed: %m");
+ }
+ else if (!jupiter_ppsapi(instance))
+ goto clean_up;
+#endif /* HAVE_PPSAPI */
+
+ /* Ensure the receiver is properly configured */
+ if (!jupiter_config(instance))
+ goto clean_up;
+
+ return (1);
+
+clean_up:
+ jupiter_shutdown(unit, peer);
+ pp->unitptr = 0;
+ return (0);
+}
+
+/*
+ * jupiter_shutdown - shut down the clock
+ */
+static void
+jupiter_shutdown(int unit, struct peer *peer)
+{
+ struct instance *instance;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ instance = pp->unitptr;
+ if (!instance)
+ return;
+
+#ifdef HAVE_PPSAPI
+ if (instance->pps_handle) {
+ time_pps_destroy(instance->pps_handle);
+ instance->pps_handle = 0;
+ }
+#endif /* HAVE_PPSAPI */
+
+ if (pp->io.fd != -1)
+ io_closeclock(&pp->io);
+ free(instance);
+}
+
+/*
+ * jupiter_config - Configure the receiver
+ */
+static int
+jupiter_config(struct instance *instance)
+{
+ jupiter_debug(instance->peer, "jupiter_config", "init receiver");
+
+ /*
+ * Initialize the unit variables
+ */
+ instance->sloppyclockflag = instance->peer->procptr->sloppyclockflag;
+ instance->moving = !!(instance->sloppyclockflag & CLK_FLAG2);
+ if (instance->moving)
+ jupiter_debug(instance->peer, "jupiter_config",
+ "mobile platform");
+
+ instance->pollcnt = 2;
+ instance->polled = 0;
+ instance->gpos_gweek = 0;
+ instance->gpos_sweek = 0;
+ instance->gweek = 0;
+ instance->lastsweek = 2 * WEEKSECS;
+ instance->timecode = 0;
+ instance->stime = 0;
+ instance->ssize = 0;
+
+ /* Stop outputting all messages */
+ jupiter_canmsg(instance, JUPITER_ALL);
+
+ /* Request the receiver id so we can syslog the firmware version */
+ jupiter_reqonemsg(instance, JUPITER_O_ID);
+
+ /* Flag that this the id was requested (so we don't get called again) */
+ instance->wantid = 1;
+
+ /* Request perodic time mark pulse messages */
+ jupiter_reqmsg(instance, JUPITER_O_PULSE, 1);
+
+ /* Request perodic geodetic position status */
+ jupiter_reqmsg(instance, JUPITER_O_GPOS, 1);
+
+ /* Set application platform type */
+ if (instance->moving)
+ jupiter_platform(instance, JUPITER_I_PLAT_MED);
+ else
+ jupiter_platform(instance, JUPITER_I_PLAT_LOW);
+
+ return (1);
+}
+
+#ifdef HAVE_PPSAPI
+/*
+ * Initialize PPSAPI
+ */
+int
+jupiter_ppsapi(
+ struct instance *instance /* unit structure pointer */
+ )
+{
+ int capability;
+
+ if (time_pps_getcap(instance->pps_handle, &capability) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_jupiter: time_pps_getcap failed: %m");
+ return (0);
+ }
+ memset(&instance->pps_params, 0, sizeof(pps_params_t));
+ if (!instance->assert)
+ instance->pps_params.mode = capability & PPS_CAPTURECLEAR;
+ else
+ instance->pps_params.mode = capability & PPS_CAPTUREASSERT;
+ if (!(instance->pps_params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) {
+ msyslog(LOG_ERR,
+ "refclock_jupiter: invalid capture edge %d",
+ instance->assert);
+ return (0);
+ }
+ instance->pps_params.mode |= PPS_TSFMT_TSPEC;
+ if (time_pps_setparams(instance->pps_handle, &instance->pps_params) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_jupiter: time_pps_setparams failed: %m");
+ return (0);
+ }
+ if (instance->hardpps) {
+ if (time_pps_kcbind(instance->pps_handle, PPS_KC_HARDPPS,
+ instance->pps_params.mode & ~PPS_TSFMT_TSPEC,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_jupiter: time_pps_kcbind failed: %m");
+ return (0);
+ }
+ hardpps_enable = 1;
+ }
+/* instance->peer->precision = PPS_PRECISION; */
+
+#if DEBUG
+ if (debug) {
+ time_pps_getparams(instance->pps_handle, &instance->pps_params);
+ jupiter_debug(instance->peer, "refclock_jupiter",
+ "pps capability 0x%x version %d mode 0x%x kern %d",
+ capability, instance->pps_params.api_version,
+ instance->pps_params.mode, instance->hardpps);
+ }
+#endif
+
+ return (1);
+}
+
+/*
+ * Get PPSAPI timestamps.
+ *
+ * Return 0 on failure and 1 on success.
+ */
+static int
+jupiter_pps(struct instance *instance)
+{
+ pps_info_t pps_info;
+ struct timespec timeout, ts;
+ double dtemp;
+ l_fp tstmp;
+
+ /*
+ * Convert the timespec nanoseconds field to ntp l_fp units.
+ */
+ if (instance->pps_handle == 0)
+ return 1;
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ memcpy(&pps_info, &instance->pps_info, sizeof(pps_info_t));
+ if (time_pps_fetch(instance->pps_handle, PPS_TSFMT_TSPEC, &instance->pps_info,
+ &timeout) < 0)
+ return 1;
+ if (instance->pps_params.mode & PPS_CAPTUREASSERT) {
+ if (pps_info.assert_sequence ==
+ instance->pps_info.assert_sequence)
+ return 1;
+ ts = instance->pps_info.assert_timestamp;
+ } else if (instance->pps_params.mode & PPS_CAPTURECLEAR) {
+ if (pps_info.clear_sequence ==
+ instance->pps_info.clear_sequence)
+ return 1;
+ ts = instance->pps_info.clear_timestamp;
+ } else {
+ return 1;
+ }
+ if ((instance->ts.tv_sec == ts.tv_sec) && (instance->ts.tv_nsec == ts.tv_nsec))
+ return 1;
+ instance->ts = ts;
+
+ tstmp.l_ui = (u_int32)ts.tv_sec + JAN_1970;
+ dtemp = ts.tv_nsec * FRAC / 1e9;
+ tstmp.l_uf = (u_int32)dtemp;
+ instance->peer->procptr->lastrec = tstmp;
+ return 0;
+}
+#endif /* HAVE_PPSAPI */
+
+/*
+ * jupiter_poll - jupiter watchdog routine
+ */
+static void
+jupiter_poll(int unit, struct peer *peer)
+{
+ struct instance *instance;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ instance = pp->unitptr;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+
+ /*
+ * If we haven't had a response in a while, reset the receiver.
+ */
+ if (instance->pollcnt > 0) {
+ instance->pollcnt--;
+ } else {
+ refclock_report(peer, CEVNT_TIMEOUT);
+
+ /* Request the receiver id to trigger a reconfig */
+ jupiter_reqonemsg(instance, JUPITER_O_ID);
+ instance->wantid = 0;
+ }
+
+ /*
+ * polled every 64 seconds. Ask jupiter_receive to hand in
+ * a timestamp.
+ */
+ instance->polled = 1;
+ pp->polls++;
+}
+
+/*
+ * jupiter_control - fudge control
+ */
+static void
+jupiter_control(
+ int unit, /* unit (not used) */
+ const struct refclockstat *in, /* input parameters (not used) */
+ struct refclockstat *out, /* output parameters (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct instance *instance;
+ u_char sloppyclockflag;
+
+ pp = peer->procptr;
+ instance = pp->unitptr;
+
+ DTOLFP(pp->fudgetime2, &instance->limit);
+ /* Force positive value. */
+ if (L_ISNEG(&instance->limit))
+ L_NEG(&instance->limit);
+
+#ifdef HAVE_PPSAPI
+ instance->assert = !(pp->sloppyclockflag & CLK_FLAG3);
+ jupiter_ppsapi(instance);
+#endif /* HAVE_PPSAPI */
+
+ sloppyclockflag = instance->sloppyclockflag;
+ instance->sloppyclockflag = pp->sloppyclockflag;
+ if ((instance->sloppyclockflag & CLK_FLAG2) !=
+ (sloppyclockflag & CLK_FLAG2)) {
+ jupiter_debug(peer,
+ "jupiter_control",
+ "mode switch: reset receiver");
+ jupiter_config(instance);
+ return;
+ }
+}
+
+/*
+ * jupiter_receive - receive gps data
+ * Gag me!
+ */
+static void
+jupiter_receive(struct recvbuf *rbufp)
+{
+ int bpcnt, cc, size, ppsret;
+ time_t last_timecode;
+ u_int32 laststime;
+ char *cp;
+ u_char *bp;
+ u_short *sp;
+ struct jid *ip;
+ struct jheader *hp;
+ struct peer *peer;
+ struct refclockproc *pp;
+ struct instance *instance;
+ l_fp tstamp;
+
+ /* Initialize pointers and read the timecode and timestamp */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ instance = pp->unitptr;
+
+ bp = (u_char *)rbufp->recv_buffer;
+ bpcnt = rbufp->recv_length;
+
+ /* This shouldn't happen */
+ if (bpcnt > (int)sizeof(instance->sbuf) - instance->ssize)
+ bpcnt = sizeof(instance->sbuf) - instance->ssize;
+
+ /* Append to input buffer */
+ memcpy((u_char *)instance->sbuf + instance->ssize, bp, bpcnt);
+ instance->ssize += bpcnt;
+
+ /* While there's at least a header and we parse an intact message */
+ while (instance->ssize > sizeof(*hp) && (cc = jupiter_recv(instance)) > 0) {
+ instance->pollcnt = 2;
+
+ tstamp = rbufp->recv_time;
+ hp = (struct jheader *)instance->sbuf;
+ sp = (u_short *)(hp + 1);
+ size = cc - sizeof(*hp);
+ switch (getshort(hp->id)) {
+
+ case JUPITER_O_PULSE:
+ if (size != sizeof(struct jpulse)) {
+ jupiter_debug(peer,
+ "jupiter_receive", "pulse: len %d != %u",
+ size, (int)sizeof(struct jpulse));
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+
+ /*
+ * There appears to be a firmware bug related
+ * to the pulse message; in addition to the one
+ * per second messages, we get an extra pulse
+ * message once an hour (on the anniversary of
+ * the cold start). It seems to come 200 ms
+ * after the one requested. So if we've seen a
+ * pulse message in the last 210 ms, we skip
+ * this one.
+ */
+ laststime = instance->stime;
+ instance->stime = DS2UI(((struct jpulse *)sp)->stime);
+ if (laststime != 0 && instance->stime - laststime <= 21) {
+ jupiter_debug(peer, "jupiter_receive",
+ "avoided firmware bug (stime %.2f, laststime %.2f)",
+ (double)instance->stime * 0.01, (double)laststime * 0.01);
+ break;
+ }
+
+ /* Retrieve pps timestamp */
+ ppsret = jupiter_pps(instance);
+
+ /*
+ * Add one second if msg received early
+ * (i.e. before limit, a.k.a. fudgetime2) in
+ * the second.
+ */
+ L_SUB(&tstamp, &pp->lastrec);
+ if (!L_ISGEQ(&tstamp, &instance->limit))
+ ++pp->lastrec.l_ui;
+
+ /* Parse timecode (even when there's no pps) */
+ last_timecode = instance->timecode;
+ if ((cp = jupiter_parse_t(instance, sp)) != NULL) {
+ jupiter_debug(peer,
+ "jupiter_receive", "pulse: %s", cp);
+ break;
+ }
+
+ /* Bail if we didn't get a pps timestamp */
+ if (ppsret)
+ break;
+
+ /* Bail if we don't have the last timecode yet */
+ if (last_timecode == 0)
+ break;
+
+ /* Add the new sample to a median filter */
+ tstamp.l_ui = JAN_1970 + (u_int32)last_timecode;
+ tstamp.l_uf = 0;
+
+ refclock_process_offset(pp, tstamp, pp->lastrec, pp->fudgetime1);
+
+ /*
+ * The clock will blurt a timecode every second
+ * but we only want one when polled. If we
+ * havn't been polled, bail out.
+ */
+ if (!instance->polled)
+ break;
+ instance->polled = 0;
+
+ /*
+ * It's a live one! Remember this time.
+ */
+
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+ /*
+ * If we get here - what we got from the clock is
+ * OK, so say so
+ */
+ refclock_report(peer, CEVNT_NOMINAL);
+
+ /*
+ * We have succeeded in answering the poll.
+ * Turn off the flag and return
+ */
+ instance->polled = 0;
+ break;
+
+ case JUPITER_O_GPOS:
+ if (size != sizeof(struct jgpos)) {
+ jupiter_debug(peer,
+ "jupiter_receive", "gpos: len %d != %u",
+ size, (int)sizeof(struct jgpos));
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+
+ if ((cp = jupiter_parse_gpos(instance, sp)) != NULL) {
+ jupiter_debug(peer,
+ "jupiter_receive", "gpos: %s", cp);
+ break;
+ }
+ break;
+
+ case JUPITER_O_ID:
+ if (size != sizeof(struct jid)) {
+ jupiter_debug(peer,
+ "jupiter_receive", "id: len %d != %u",
+ size, (int)sizeof(struct jid));
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+ /*
+ * If we got this message because the Jupiter
+ * just powered instance, it needs to be reconfigured.
+ */
+ ip = (struct jid *)sp;
+ jupiter_debug(peer,
+ "jupiter_receive", "%s chan ver %s, %s (%s)",
+ ip->chans, ip->vers, ip->date, ip->opts);
+ msyslog(LOG_DEBUG,
+ "jupiter_receive: %s chan ver %s, %s (%s)",
+ ip->chans, ip->vers, ip->date, ip->opts);
+ if (instance->wantid)
+ instance->wantid = 0;
+ else {
+ jupiter_debug(peer,
+ "jupiter_receive", "reset receiver");
+ jupiter_config(instance);
+ /*
+ * Restore since jupiter_config() just
+ * zeroed it
+ */
+ instance->ssize = cc;
+ }
+ break;
+
+ default:
+ jupiter_debug(peer,
+ "jupiter_receive", "unknown message id %d",
+ getshort(hp->id));
+ break;
+ }
+ instance->ssize -= cc;
+ if (instance->ssize < 0) {
+ fprintf(stderr, "jupiter_recv: negative ssize!\n");
+ abort();
+ } else if (instance->ssize > 0)
+ memcpy(instance->sbuf, (u_char *)instance->sbuf + cc, instance->ssize);
+ }
+}
+
+static char *
+jupiter_parse_t(struct instance *instance, u_short *sp)
+{
+ struct tm *tm;
+ char *cp;
+ struct jpulse *jp;
+ u_int32 sweek;
+ time_t last_timecode;
+ u_short flags;
+
+ jp = (struct jpulse *)sp;
+
+ /* The timecode is presented as seconds into the current GPS week */
+ sweek = DS2UI(jp->sweek) % WEEKSECS;
+
+ /*
+ * If we don't know the current GPS week, calculate it from the
+ * current time. (It's too bad they didn't include this
+ * important value in the pulse message). We'd like to pick it
+ * up from one of the other messages like gpos or chan but they
+ * don't appear to be synchronous with time keeping and changes
+ * too soon (something like 10 seconds before the new GPS
+ * week).
+ *
+ * If we already know the current GPS week, increment it when
+ * we wrap into a new week.
+ */
+ if (instance->gweek == 0) {
+ if (!instance->gpos_gweek) {
+ return ("jupiter_parse_t: Unknown gweek");
+ }
+
+ instance->gweek = instance->gpos_gweek;
+
+ /*
+ * Fix warps. GPOS has GPS time and PULSE has UTC.
+ * Plus, GPOS need not be completely in synch with
+ * the PPS signal.
+ */
+ if (instance->gpos_sweek >= sweek) {
+ if ((instance->gpos_sweek - sweek) > WEEKSECS / 2)
+ ++instance->gweek;
+ }
+ else {
+ if ((sweek - instance->gpos_sweek) > WEEKSECS / 2)
+ --instance->gweek;
+ }
+ }
+ else if (sweek == 0 && instance->lastsweek == WEEKSECS - 1) {
+ ++instance->gweek;
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "NEW gps week %u", instance->gweek);
+ }
+
+ /*
+ * See if the sweek stayed the same (this happens when there is
+ * no pps pulse).
+ *
+ * Otherwise, look for time warps:
+ *
+ * - we have stored at least one lastsweek and
+ * - the sweek didn't increase by one and
+ * - we didn't wrap to a new GPS week
+ *
+ * Then we warped.
+ */
+ if (instance->lastsweek == sweek)
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "gps sweek not incrementing (%d)",
+ sweek);
+ else if (instance->lastsweek != 2 * WEEKSECS &&
+ instance->lastsweek + 1 != sweek &&
+ !(sweek == 0 && instance->lastsweek == WEEKSECS - 1))
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "gps sweek jumped (was %d, now %d)",
+ instance->lastsweek, sweek);
+ instance->lastsweek = sweek;
+
+ /* This timecode describes next pulse */
+ last_timecode = instance->timecode;
+ instance->timecode =
+ GPS_EPOCH + (instance->gweek * WEEKSECS) + sweek;
+
+ if (last_timecode == 0)
+ /* XXX debugging */
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "UTC <none> (gweek/sweek %u/%u)",
+ instance->gweek, sweek);
+ else {
+ /* XXX debugging */
+ tm = gmtime(&last_timecode);
+ cp = asctime(tm);
+
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "UTC %.24s (gweek/sweek %u/%u)",
+ cp, instance->gweek, sweek);
+
+ /* Billboard last_timecode (which is now the current time) */
+ instance->peer->procptr->year = tm->tm_year + 1900;
+ instance->peer->procptr->day = tm->tm_yday + 1;
+ instance->peer->procptr->hour = tm->tm_hour;
+ instance->peer->procptr->minute = tm->tm_min;
+ instance->peer->procptr->second = tm->tm_sec;
+ }
+
+ flags = getshort(jp->flags);
+
+ /* Toss if not designated "valid" by the gps */
+ if ((flags & JUPITER_O_PULSE_VALID) == 0) {
+ refclock_report(instance->peer, CEVNT_BADTIME);
+ return ("time mark not valid");
+ }
+
+ /* We better be sync'ed to UTC... */
+ if ((flags & JUPITER_O_PULSE_UTC) == 0) {
+ refclock_report(instance->peer, CEVNT_BADTIME);
+ return ("time mark not sync'ed to UTC");
+ }
+
+ return (NULL);
+}
+
+static char *
+jupiter_parse_gpos(struct instance *instance, u_short *sp)
+{
+ struct jgpos *jg;
+ time_t t;
+ struct tm *tm;
+ char *cp;
+
+ jg = (struct jgpos *)sp;
+
+ if (jg->navval != 0) {
+ /*
+ * Solution not valid. Use caution and refuse
+ * to determine GPS week from this message.
+ */
+ instance->gpos_gweek = 0;
+ instance->gpos_sweek = 0;
+ return ("Navigation solution not valid");
+ }
+
+ instance->gpos_gweek = jg->gweek;
+ instance->gpos_sweek = DS2UI(jg->sweek);
+ while(instance->gpos_sweek >= WEEKSECS) {
+ instance->gpos_sweek -= WEEKSECS;
+ ++instance->gpos_gweek;
+ }
+ instance->gweek = 0;
+
+ t = GPS_EPOCH + (instance->gpos_gweek * WEEKSECS) + instance->gpos_sweek;
+ tm = gmtime(&t);
+ cp = asctime(tm);
+
+ jupiter_debug(instance->peer,
+ "jupiter_parse_g", "GPS %.24s (gweek/sweek %u/%u)",
+ cp, instance->gpos_gweek, instance->gpos_sweek);
+ return (NULL);
+}
+
+/*
+ * jupiter_debug - print debug messages
+ */
+static void
+jupiter_debug(
+ struct peer * peer,
+ const char * function,
+ const char * fmt,
+ ...
+ )
+{
+ char buffer[200];
+ va_list ap;
+
+ va_start(ap, fmt);
+ /*
+ * Print debug message to stdout
+ * In the future, we may want to get get more creative...
+ */
+ mvsnprintf(buffer, sizeof(buffer), fmt, ap);
+ record_clock_stats(&peer->srcadr, buffer);
+#ifdef DEBUG
+ if (debug) {
+ printf("%s: %s\n", function, buffer);
+ fflush(stdout);
+ }
+#endif
+
+ va_end(ap);
+}
+
+/* Checksum and transmit a message to the Jupiter */
+static char *
+jupiter_send(struct instance *instance, struct jheader *hp)
+{
+ u_int len, size;
+ int cc;
+ u_short *sp;
+ static char errstr[132];
+
+ size = sizeof(*hp);
+ hp->hsum = putshort(jupiter_cksum((u_short *)hp,
+ (size / sizeof(u_short)) - 1));
+ len = getshort(hp->len);
+ if (len > 0) {
+ sp = (u_short *)(hp + 1);
+ sp[len] = putshort(jupiter_cksum(sp, len));
+ size += (len + 1) * sizeof(u_short);
+ }
+
+ if ((cc = write(instance->peer->procptr->io.fd, (char *)hp, size)) < 0) {
+ msnprintf(errstr, sizeof(errstr), "write: %m");
+ return (errstr);
+ } else if (cc != (int)size) {
+ snprintf(errstr, sizeof(errstr), "short write (%d != %u)", cc, size);
+ return (errstr);
+ }
+ return (NULL);
+}
+
+/* Request periodic message output */
+static struct {
+ struct jheader jheader;
+ struct jrequest jrequest;
+} reqmsg = {
+ { putshort(JUPITER_SYNC), 0,
+ putshort((sizeof(struct jrequest) / sizeof(u_short)) - 1),
+ 0, JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK |
+ JUPITER_FLAG_CONN | JUPITER_FLAG_LOG, 0 },
+ { 0, 0, 0, 0 }
+};
+
+/* An interval of zero means to output on trigger */
+static void
+jupiter_reqmsg(struct instance *instance, u_int id,
+ u_int interval)
+{
+ struct jheader *hp;
+ struct jrequest *rp;
+ char *cp;
+
+ hp = &reqmsg.jheader;
+ hp->id = putshort(id);
+ rp = &reqmsg.jrequest;
+ rp->trigger = putshort(interval == 0);
+ rp->interval = putshort(interval);
+ if ((cp = jupiter_send(instance, hp)) != NULL)
+ jupiter_debug(instance->peer, "jupiter_reqmsg", "%u: %s", id, cp);
+}
+
+/* Cancel periodic message output */
+static struct jheader canmsg = {
+ putshort(JUPITER_SYNC), 0, 0, 0,
+ JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_DISC,
+ 0
+};
+
+static void
+jupiter_canmsg(struct instance *instance, u_int id)
+{
+ struct jheader *hp;
+ char *cp;
+
+ hp = &canmsg;
+ hp->id = putshort(id);
+ if ((cp = jupiter_send(instance, hp)) != NULL)
+ jupiter_debug(instance->peer, "jupiter_canmsg", "%u: %s", id, cp);
+}
+
+/* Request a single message output */
+static struct jheader reqonemsg = {
+ putshort(JUPITER_SYNC), 0, 0, 0,
+ JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_QUERY,
+ 0
+};
+
+static void
+jupiter_reqonemsg(struct instance *instance, u_int id)
+{
+ struct jheader *hp;
+ char *cp;
+
+ hp = &reqonemsg;
+ hp->id = putshort(id);
+ if ((cp = jupiter_send(instance, hp)) != NULL)
+ jupiter_debug(instance->peer, "jupiter_reqonemsg", "%u: %s", id, cp);
+}
+
+/* Set the platform dynamics */
+static struct {
+ struct jheader jheader;
+ struct jplat jplat;
+} platmsg = {
+ { putshort(JUPITER_SYNC), putshort(JUPITER_I_PLAT),
+ putshort((sizeof(struct jplat) / sizeof(u_short)) - 1), 0,
+ JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK, 0 },
+ { 0, 0, 0 }
+};
+
+static void
+jupiter_platform(struct instance *instance, u_int platform)
+{
+ struct jheader *hp;
+ struct jplat *pp;
+ char *cp;
+
+ hp = &platmsg.jheader;
+ pp = &platmsg.jplat;
+ pp->platform = putshort(platform);
+ if ((cp = jupiter_send(instance, hp)) != NULL)
+ jupiter_debug(instance->peer, "jupiter_platform", "%u: %s", platform, cp);
+}
+
+/* Checksum "len" shorts */
+static u_short
+jupiter_cksum(u_short *sp, u_int len)
+{
+ u_short sum, x;
+
+ sum = 0;
+ while (len-- > 0) {
+ x = *sp++;
+ sum += getshort(x);
+ }
+ return (~sum + 1);
+}
+
+/* Return the size of the next message (or zero if we don't have it all yet) */
+static int
+jupiter_recv(struct instance *instance)
+{
+ int n, len, size, cc;
+ struct jheader *hp;
+ u_char *bp;
+ u_short *sp;
+
+ /* Must have at least a header's worth */
+ cc = sizeof(*hp);
+ size = instance->ssize;
+ if (size < cc)
+ return (0);
+
+ /* Search for the sync short if missing */
+ sp = instance->sbuf;
+ hp = (struct jheader *)sp;
+ if (getshort(hp->sync) != JUPITER_SYNC) {
+ /* Wasn't at the front, sync up */
+ jupiter_debug(instance->peer, "jupiter_recv", "syncing");
+ bp = (u_char *)sp;
+ n = size;
+ while (n >= 2) {
+ if (bp[0] != (JUPITER_SYNC & 0xff)) {
+ /*
+ jupiter_debug(instance->peer, "{0x%x}", bp[0]);
+ */
+ ++bp;
+ --n;
+ continue;
+ }
+ if (bp[1] == ((JUPITER_SYNC >> 8) & 0xff))
+ break;
+ /*
+ jupiter_debug(instance->peer, "{0x%x 0x%x}", bp[0], bp[1]);
+ */
+ bp += 2;
+ n -= 2;
+ }
+ /*
+ jupiter_debug(instance->peer, "\n");
+ */
+ /* Shuffle data to front of input buffer */
+ if (n > 0)
+ memcpy(sp, bp, n);
+ size = n;
+ instance->ssize = size;
+ if (size < cc || hp->sync != JUPITER_SYNC)
+ return (0);
+ }
+
+ if (jupiter_cksum(sp, (cc / sizeof(u_short) - 1)) !=
+ getshort(hp->hsum)) {
+ jupiter_debug(instance->peer, "jupiter_recv", "bad header checksum!");
+ /* This is drastic but checksum errors should be rare */
+ instance->ssize = 0;
+ return (0);
+ }
+
+ /* Check for a payload */
+ len = getshort(hp->len);
+ if (len > 0) {
+ n = (len + 1) * sizeof(u_short);
+ /* Not enough data yet */
+ if (size < cc + n)
+ return (0);
+
+ /* Check payload checksum */
+ sp = (u_short *)(hp + 1);
+ if (jupiter_cksum(sp, len) != getshort(sp[len])) {
+ jupiter_debug(instance->peer,
+ "jupiter_recv", "bad payload checksum!");
+ /* This is drastic but checksum errors should be rare */
+ instance->ssize = 0;
+ return (0);
+ }
+ cc += n;
+ }
+ return (cc);
+}
+
+#else /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */
+int refclock_jupiter_bs;
+#endif /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */
diff --git a/ntpd/refclock_leitch.c b/ntpd/refclock_leitch.c
new file mode 100644
index 0000000..69ffdc5
--- /dev/null
+++ b/ntpd/refclock_leitch.c
@@ -0,0 +1,600 @@
+/*
+ * refclock_leitch - clock driver for the Leitch CSD-5300 Master Clock
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntp_types.h"
+
+#if defined(REFCLOCK) && defined(CLOCK_LEITCH)
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "timevalops.h"
+#include "ntp_stdlib.h"
+
+
+/*
+ * Driver for Leitch CSD-5300 Master Clock System
+ *
+ * COMMANDS:
+ * DATE: D <CR>
+ * TIME: T <CR>
+ * STATUS: S <CR>
+ * LOOP: L <CR>
+ *
+ * FORMAT:
+ * DATE: YYMMDD<CR>
+ * TIME: <CR>/HHMMSS <CR>/HHMMSS <CR>/HHMMSS <CR>/
+ * second bondaried on the stop bit of the <CR>
+ * second boundaries at '/' above.
+ * STATUS: G (good), D (diag fail), T (time not provided) or
+ * P (last phone update failed)
+ */
+#define PRECISION (-20) /* 1x10-8 */
+#define MAXUNITS 1 /* max number of LEITCH units */
+#define LEITCHREFID "ATOM" /* reference id */
+#define LEITCH_DESCRIPTION "Leitch: CSD 5300 Master Clock System Driver"
+#define LEITCH232 "/dev/leitch%d" /* name of radio device */
+#define SPEED232 B300 /* uart speed (300 baud) */
+#ifdef DEBUG
+#define leitch_send(A,M) \
+if (debug) fprintf(stderr,"write leitch %s\n",M); \
+if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
+ if (debug) \
+ fprintf(stderr, "leitch_send: unit %d send failed\n", A->unit); \
+ else \
+ msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
+#else
+#define leitch_send(A,M) \
+if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
+ msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
+#endif
+
+#define STATE_IDLE 0
+#define STATE_DATE 1
+#define STATE_TIME1 2
+#define STATE_TIME2 3
+#define STATE_TIME3 4
+
+/*
+ * LEITCH unit control structure
+ */
+struct leitchunit {
+ struct peer *peer;
+ struct refclockio leitchio;
+ u_char unit;
+ short year;
+ short yearday;
+ short month;
+ short day;
+ short hour;
+ short second;
+ short minute;
+ short state;
+ u_short fudge1;
+ l_fp reftime1;
+ l_fp reftime2;
+ l_fp reftime3;
+ l_fp codetime1;
+ l_fp codetime2;
+ l_fp codetime3;
+ u_long yearstart;
+};
+
+/*
+ * Function prototypes
+ */
+static void leitch_init (void);
+static int leitch_start (int, struct peer *);
+static void leitch_shutdown (int, struct peer *);
+static void leitch_poll (int, struct peer *);
+static void leitch_control (int, const struct refclockstat *, struct refclockstat *, struct peer *);
+#define leitch_buginfo noentry
+static void leitch_receive (struct recvbuf *);
+static void leitch_process (struct leitchunit *);
+#if 0
+static void leitch_timeout (struct peer *);
+#endif
+static int leitch_get_date (struct recvbuf *, struct leitchunit *);
+static int leitch_get_time (struct recvbuf *, struct leitchunit *, int);
+static int days_per_year (int);
+
+static struct leitchunit leitchunits[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+static u_char stratumtouse[MAXUNITS];
+static u_int32 refid[MAXUNITS];
+
+static char days_in_month [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_leitch = {
+ leitch_start, leitch_shutdown, leitch_poll,
+ leitch_control, leitch_init, leitch_buginfo, NOFLAGS
+};
+
+/*
+ * leitch_init - initialize internal leitch driver data
+ */
+static void
+leitch_init(void)
+{
+ int i;
+
+ memset((char*)leitchunits, 0, sizeof(leitchunits));
+ memset((char*)unitinuse, 0, sizeof(unitinuse));
+ for (i = 0; i < MAXUNITS; i++)
+ memcpy((char *)&refid[i], LEITCHREFID, 4);
+}
+
+/*
+ * leitch_shutdown - shut down a LEITCH clock
+ */
+static void
+leitch_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct leitchunit *leitch;
+
+ if (unit >= MAXUNITS) {
+ return;
+ }
+ leitch = &leitchunits[unit];
+ if (-1 != leitch->leitchio.fd)
+ io_closeclock(&leitch->leitchio);
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_shutdown()\n");
+#endif
+}
+
+/*
+ * leitch_poll - called by the transmit procedure
+ */
+static void
+leitch_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct leitchunit *leitch;
+
+ /* start the state machine rolling */
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_poll()\n");
+#endif
+ if (unit >= MAXUNITS) {
+ /* XXXX syslog it */
+ return;
+ }
+
+ leitch = &leitchunits[unit];
+
+ if (leitch->state != STATE_IDLE) {
+ /* reset and wait for next poll */
+ /* XXXX syslog it */
+ leitch->state = STATE_IDLE;
+ } else {
+ leitch_send(leitch,"D\r");
+ leitch->state = STATE_DATE;
+ }
+}
+
+static void
+leitch_control(
+ int unit,
+ const struct refclockstat *in,
+ struct refclockstat *out,
+ struct peer *passed_peer
+ )
+{
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR,
+ "leitch_control: unit %d invalid", unit);
+ return;
+ }
+
+ if (in) {
+ if (in->haveflags & CLK_HAVEVAL1)
+ stratumtouse[unit] = (u_char)(in->fudgeval1);
+ if (in->haveflags & CLK_HAVEVAL2)
+ refid[unit] = in->fudgeval2;
+ if (unitinuse[unit]) {
+ struct peer *peer;
+
+ peer = (&leitchunits[unit])->peer;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ }
+ }
+
+ if (out) {
+ memset((char *)out, 0, sizeof (struct refclockstat));
+ out->type = REFCLK_ATOM_LEITCH;
+ out->haveflags = CLK_HAVEVAL1 | CLK_HAVEVAL2;
+ out->fudgeval1 = (int32)stratumtouse[unit];
+ out->fudgeval2 = refid[unit];
+ out->p_lastcode = "";
+ out->clockdesc = LEITCH_DESCRIPTION;
+ }
+}
+
+/*
+ * leitch_start - open the LEITCH devices and initialize data for processing
+ */
+static int
+leitch_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct leitchunit *leitch;
+ int fd232;
+ char leitchdev[20];
+
+ /*
+ * Check configuration info.
+ */
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "leitch_start: unit %d invalid", unit);
+ return (0);
+ }
+
+ if (unitinuse[unit]) {
+ msyslog(LOG_ERR, "leitch_start: unit %d in use", unit);
+ return (0);
+ }
+
+ /*
+ * Open serial port.
+ */
+ snprintf(leitchdev, sizeof(leitchdev), LEITCH232, unit);
+ fd232 = open(leitchdev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ msyslog(LOG_ERR,
+ "leitch_start: open of %s: %m", leitchdev);
+ return (0);
+ }
+
+ leitch = &leitchunits[unit];
+ memset(leitch, 0, sizeof(*leitch));
+
+#if defined(HAVE_SYSV_TTYS)
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ { struct termio ttyb;
+ if (ioctl(fd232, TCGETA, &ttyb) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TCGETA): %m", leitchdev);
+ 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) {
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TCSETA): %m", leitchdev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_SYSV_TTYS */
+#if defined(HAVE_TERMIOS)
+ /*
+ * POSIX serial line parameters (termios interface)
+ */
+ { struct termios ttyb, *ttyp;
+
+ ttyp = &ttyb;
+ if (tcgetattr(fd232, ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: tcgetattr(%s): %m", leitchdev);
+ goto screwed;
+ }
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ 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) {
+ msyslog(LOG_ERR,
+ "leitch_start: tcsetattr(%s): %m", leitchdev);
+ goto screwed;
+ }
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: tcflush(%s): %m", leitchdev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_TERMIOS */
+#if defined(HAVE_BSD_TTYS)
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ */
+ {
+ struct sgttyb ttyb;
+
+ if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TIOCGETP): %m", leitchdev);
+ goto screwed;
+ }
+ ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
+ ttyb.sg_erase = ttyb.sg_kill = '\0';
+ ttyb.sg_flags = EVENP|ODDP|CRMOD;
+ if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TIOCSETP): %m", leitchdev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_BSD_TTYS */
+
+ /*
+ * Set up the structures
+ */
+ leitch->peer = peer;
+ leitch->unit = unit;
+ leitch->state = STATE_IDLE;
+ leitch->fudge1 = 15; /* 15ms */
+
+ leitch->leitchio.clock_recv = leitch_receive;
+ leitch->leitchio.srcclock = peer;
+ leitch->leitchio.datalen = 0;
+ leitch->leitchio.fd = fd232;
+ if (!io_addclock(&leitch->leitchio)) {
+ leitch->leitchio.fd = -1;
+ goto screwed;
+ }
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success.
+ */
+ peer->precision = PRECISION;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ unitinuse[unit] = 1;
+ return(1);
+
+ /*
+ * Something broke; abandon ship.
+ */
+ screwed:
+ close(fd232);
+ return(0);
+}
+
+/*
+ * leitch_receive - receive data from the serial interface on a leitch
+ * clock
+ */
+static void
+leitch_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct leitchunit *leitch = rbufp->recv_peer->procptr->unitptr;
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_recieve(%*.*s)\n",
+ rbufp->recv_length, rbufp->recv_length,
+ rbufp->recv_buffer);
+#endif
+ if (rbufp->recv_length != 7)
+ return; /* The date is return with a trailing newline,
+ discard it. */
+
+ switch (leitch->state) {
+ case STATE_IDLE: /* unexpected, discard and resync */
+ return;
+ case STATE_DATE:
+ if (!leitch_get_date(rbufp,leitch)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+ leitch_send(leitch,"T\r");
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%u\n",leitch->yearday);
+#endif
+ leitch->state = STATE_TIME1;
+ break;
+ case STATE_TIME1:
+ if (!leitch_get_time(rbufp,leitch,1)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, 1, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime1.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+ leitch->reftime1.l_uf = 0;
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%lu\n", (u_long)leitch->reftime1.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime1.l_uf);
+ leitch->codetime1 = rbufp->recv_time;
+ leitch->state = STATE_TIME2;
+ break;
+ case STATE_TIME2:
+ if (!leitch_get_time(rbufp,leitch,2)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, 1, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime2.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%lu\n", (u_long)leitch->reftime2.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime2.l_uf);
+ leitch->codetime2 = rbufp->recv_time;
+ leitch->state = STATE_TIME3;
+ break;
+ case STATE_TIME3:
+ if (!leitch_get_time(rbufp,leitch,3)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, GMT, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime3.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%lu\n", (u_long)leitch->reftime3.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime3.l_uf);
+ leitch->codetime3 = rbufp->recv_time;
+ leitch_process(leitch);
+ leitch->state = STATE_IDLE;
+ break;
+ default:
+ msyslog(LOG_ERR,
+ "leitech_receive: invalid state %d unit %d",
+ leitch->state, leitch->unit);
+ }
+}
+
+/*
+ * leitch_process - process a pile of samples from the clock
+ *
+ * This routine uses a three-stage median filter to calculate offset and
+ * dispersion. reduce jitter. The dispersion is calculated as the span
+ * of the filter (max - min), unless the quality character (format 2) is
+ * non-blank, in which case the dispersion is calculated on the basis of
+ * the inherent tolerance of the internal radio oscillator, which is
+ * +-2e-5 according to the radio specifications.
+ */
+static void
+leitch_process(
+ struct leitchunit *leitch
+ )
+{
+ l_fp off;
+ l_fp tmp_fp;
+ /*double doffset;*/
+
+ off = leitch->reftime1;
+ L_SUB(&off,&leitch->codetime1);
+ tmp_fp = leitch->reftime2;
+ L_SUB(&tmp_fp,&leitch->codetime2);
+ if (L_ISGEQ(&off,&tmp_fp))
+ off = tmp_fp;
+ tmp_fp = leitch->reftime3;
+ L_SUB(&tmp_fp,&leitch->codetime3);
+
+ if (L_ISGEQ(&off,&tmp_fp))
+ off = tmp_fp;
+ /*LFPTOD(&off, doffset);*/
+ refclock_receive(leitch->peer);
+}
+
+/*
+ * days_per_year
+ */
+static int
+days_per_year(
+ int year
+ )
+{
+ if (year%4) { /* not a potential leap year */
+ return (365);
+ } else {
+ if (year % 100) { /* is a leap year */
+ return (366);
+ } else {
+ if (year % 400) {
+ return (365);
+ } else {
+ return (366);
+ }
+ }
+ }
+}
+
+static int
+leitch_get_date(
+ struct recvbuf *rbufp,
+ struct leitchunit *leitch
+ )
+{
+ int i;
+
+ if (rbufp->recv_length < 6)
+ return(0);
+#undef BAD /* confict: defined as (-1) in AIX sys/param.h */
+#define BAD(A) (rbufp->recv_buffer[A] < '0') || (rbufp->recv_buffer[A] > '9')
+ if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
+ return(0);
+#define ATOB(A) ((rbufp->recv_buffer[A])-'0')
+ leitch->year = ATOB(0)*10 + ATOB(1);
+ leitch->month = ATOB(2)*10 + ATOB(3);
+ leitch->day = ATOB(4)*10 + ATOB(5);
+
+ /* sanity checks */
+ if (leitch->month > 12)
+ return(0);
+ if (leitch->day > days_in_month[leitch->month-1])
+ return(0);
+
+ /* calculate yearday */
+ i = 0;
+ leitch->yearday = leitch->day;
+
+ while ( i < (leitch->month-1) )
+ leitch->yearday += days_in_month[i++];
+
+ if ((days_per_year((leitch->year>90?1900:2000)+leitch->year)==365) &&
+ leitch->month > 2)
+ leitch->yearday--;
+
+ return(1);
+}
+
+/*
+ * leitch_get_time
+ */
+static int
+leitch_get_time(
+ struct recvbuf *rbufp,
+ struct leitchunit *leitch,
+ int which
+ )
+{
+ if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
+ return(0);
+ leitch->hour = ATOB(0)*10 +ATOB(1);
+ leitch->minute = ATOB(2)*10 +ATOB(3);
+ leitch->second = ATOB(4)*10 +ATOB(5);
+
+ if ((leitch->hour > 23) || (leitch->minute > 60) ||
+ (leitch->second > 60))
+ return(0);
+ return(1);
+}
+
+#else
+NONEMPTY_TRANSLATION_UNIT
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_local.c b/ntpd/refclock_local.c
new file mode 100644
index 0000000..d1b2871
--- /dev/null
+++ b/ntpd/refclock_local.c
@@ -0,0 +1,230 @@
+
+/*
+ * refclock_local - local pseudo-clock driver
+ *
+ * wjm 17-aug-1995: add a hook for special treatment of VMS_LOCALUNIT
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef REFCLOCK
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif
+
+/*
+ * This is a hack to allow a machine to use its own system clock as a
+ * reference clock, i.e., to free-run using no outside clock discipline
+ * source. Note that the clock selection algorithm will not select this
+ * driver unless all other sources of synchronization have been lost.
+ * This is useful if you want to use NTP in an isolated environment
+ * with no radio clock or NIST modem available. Pick a machine that you
+ * figure has a good clock oscillator and configure it with this
+ * driver. Set the clock using the best means available, like
+ * eyeball-and-wristwatch. Then, point all the other machines at this
+ * one or use broadcast (not multicast) mode to distribute time.
+ *
+ * Another application for this driver is if you want to use a
+ * particular server's clock as the clock of last resort when all other
+ * normal synchronization sources have gone away. This is especially
+ * useful if that server has an ovenized oscillator. However, the
+ * preferred was to do this is using orphan mode. See the documentation.
+ *
+ * A third application for this driver is when an external discipline
+ * source is available, such as the NIST "lockclock" program, which
+ * synchronizes the local clock via a telephone modem and the NIST
+ * Automated Computer Time Service (ACTS), or the Digital Time
+ * Synchronization Service (DTSS), which runs on DCE machines. In this
+ * case the stratum should be set at zero, indicating a bona fide
+ * stratum-1 source. Exercise some caution with this, since there is no
+ * easy way to telegraph via NTP that something might be wrong in the
+ * discipline source itself. In the case of DTSS, the local clock can
+ * have a rather large jitter, depending on the interval between
+ * corrections and the intrinsic frequency error of the clock
+ * oscillator. In extreme cases, this can cause clients to exceed the
+ * 128-ms slew window and drop off the NTP subnet.
+ *
+ * Fudge Factors
+ *
+ * If fudge flag1 is lit, the leap second bit is set in the peer
+ * status word. It should be set early in the day of a leap second
+ * event and set dark on the day after the event.
+ *
+ * Note the fudge time1 and time2 have been deprecated. The fudge time1
+ * was intended to apply a bias offset. This can be done using the Unix
+ * date command. The fudge time2 was intended to apply a bias frequency.
+ * This can be done using the frequency file and/or the freq
+ * configuration command.
+ */
+/*
+ * Local interface definitions
+ */
+#define PRECISION (-7) /* about 10 ms precision */
+#define DESCRIPTION "Undisciplined local clock" /* WRU */
+#define STRATUM 5 /* default stratum */
+#define DISPERSION .01 /* default dispersion (10 ms) */
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+/*
+ * Imported from ntp_proto
+ */
+extern s_char sys_precision;
+
+/*
+ * Function prototypes
+ */
+static int local_start (int, struct peer *);
+static void local_poll (int, struct peer *);
+
+/*
+ * Local variables
+ */
+static u_long poll_time; /* last time polled */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_local = {
+ local_start, /* start up driver */
+ noentry, /* shut down driver (not used) */
+ local_poll, /* transmit poll message */
+ noentry, /* not used (old lcl_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old lcl_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * local_start - start up the clock
+ */
+static int
+local_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = sys_precision;
+ pp->leap = LEAP_NOTINSYNC;
+ peer->stratum = STRATUM;
+ pp->stratum = STRATUM;
+ pp->clockdesc = DESCRIPTION;
+ memcpy(&pp->refid, "LOCL", 4);
+ poll_time = current_time;
+ return (1);
+}
+
+
+/*
+ * local_poll - called by the transmit procedure
+ *
+ * LOCKCLOCK: If the kernel supports the nanokernel or microkernel
+ * system calls, the leap bits are extracted from the kernel. If there
+ * is a kernel error or the kernel leap bits are set to 11, the NTP leap
+ * bits are set to 11 and the stratum is set to infinity. Otherwise, the
+ * NTP leap bits are set to the kernel leap bits and the stratum is set
+ * as fudged. This behavior does not faithfully follow the
+ * specification, but is probably more appropriate in a multiple-server
+ * national laboratory network.
+ */
+static void
+local_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+#if defined(KERNEL_PLL) && defined(LOCKCLOCK)
+ struct timex ntv;
+#endif /* KERNEL_PLL LOCKCLOCK */
+ struct refclockproc *pp;
+
+ /*
+ * Do no evil unless the house is dark or lit with our own lamp.
+ */
+ if (!(sys_peer == NULL || sys_peer == peer))
+ return;
+
+#if defined(VMS) && defined(VMS_LOCALUNIT)
+ if (unit == VMS_LOCALUNIT) {
+ extern void vms_local_poll(struct peer *);
+
+ vms_local_poll(peer);
+ return;
+ }
+#endif /* VMS && VMS_LOCALUNIT */
+
+ pp = peer->procptr;
+ pp->polls++;
+
+ /*
+ * Ramble through the usual filtering and grooming code, which
+ * is essentially a no-op and included mostly for pretty
+ * billboards. We allow a one-time time adjustment using fudge
+ * time1 (s) and a continuous frequency adjustment using fudge
+ * time 2 (ppm).
+ */
+ poll_time = current_time;
+ refclock_process_offset(pp, pp->lastrec, pp->lastrec, 0);
+
+ /*
+ * If another process is disciplining the system clock, we set
+ * the leap bits and quality indicators from the kernel.
+ */
+#if defined(KERNEL_PLL) && defined(LOCKCLOCK)
+ memset(&ntv, 0, sizeof ntv);
+ switch (ntp_adjtime(&ntv)) {
+ case TIME_OK:
+ pp->leap = LEAP_NOWARNING;
+ peer->stratum = pp->stratum;
+ break;
+
+ case TIME_INS:
+ pp->leap = LEAP_ADDSECOND;
+ peer->stratum = pp->stratum;
+ break;
+
+ case TIME_DEL:
+ pp->leap = LEAP_DELSECOND;
+ peer->stratum = pp->stratum;
+ break;
+
+ default:
+ pp->leap = LEAP_NOTINSYNC;
+ peer->stratum = STRATUM_UNSPEC;
+ }
+ pp->disp = 0;
+ pp->jitter = 0;
+#else /* KERNEL_PLL LOCKCLOCK */
+ if (pp->sloppyclockflag & CLK_FLAG1)
+ pp->leap = LEAP_ADDSECOND;
+ else
+ pp->leap = LEAP_NOWARNING;
+ pp->disp = DISPERSION;
+ pp->jitter = 0;
+#endif /* KERNEL_PLL LOCKCLOCK */
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+}
+#else
+int refclock_local_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_msfees.c b/ntpd/refclock_msfees.c
new file mode 100644
index 0000000..8399c96
--- /dev/null
+++ b/ntpd/refclock_msfees.c
@@ -0,0 +1,1450 @@
+/* refclock_ees - clock driver for the EES M201 receiver */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ntp_types.h"
+
+#if defined(REFCLOCK) && defined(CLOCK_MSFEES) && defined(PPS)
+
+/* Currently REQUIRES STREAM and PPSCD. CLK and CBREAK modes
+ * were removed as the code was overly hairy, they weren't in use
+ * (hence probably didn't work). Still in RCS file at cl.cam.ac.uk
+ */
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "timevalops.h"
+
+#include <ctype.h>
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+#if defined(STREAM)
+#include <stropts.h>
+#endif
+
+#ifdef HAVE_SYS_TERMIOS_H
+# include <sys/termios.h>
+#endif
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+
+#include "ntp_stdlib.h"
+
+int dbg = 0;
+/*
+ fudgefactor = fudgetime1;
+ os_delay = fudgetime2;
+ offset_fudge = os_delay + fudgefactor + inherent_delay;
+ stratumtouse = fudgeval1 & 0xf
+ dbg = fudgeval2;
+ sloppyclockflag = flags & CLK_FLAG1;
+ 1 log smoothing summary when processing sample
+ 4 dump the buffer from the clock
+ 8 EIOGETKD the last n uS time stamps
+ if (flags & CLK_FLAG2 && unitinuse) ees->leaphold = 0;
+ ees->dump_vals = flags & CLK_FLAG3;
+ ees->usealldata = flags & CLK_FLAG4;
+
+
+ bug->values[0] = (ees->lasttime) ? current_time - ees->lasttime : 0;
+ bug->values[1] = (ees->clocklastgood)?current_time-ees->clocklastgood:0;
+ bug->values[2] = (u_long)ees->status;
+ bug->values[3] = (u_long)ees->lastevent;
+ bug->values[4] = (u_long)ees->reason;
+ bug->values[5] = (u_long)ees->nsamples;
+ bug->values[6] = (u_long)ees->codestate;
+ bug->values[7] = (u_long)ees->day;
+ bug->values[8] = (u_long)ees->hour;
+ bug->values[9] = (u_long)ees->minute;
+ bug->values[10] = (u_long)ees->second;
+ bug->values[11] = (u_long)ees->tz;
+ bug->values[12] = ees->yearstart;
+ bug->values[13] = (ees->leaphold > current_time) ?
+ ees->leaphold - current_time : 0;
+ bug->values[14] = inherent_delay[unit].l_uf;
+ bug->values[15] = offset_fudge[unit].l_uf;
+
+ bug->times[0] = ees->reftime;
+ bug->times[1] = ees->arrvtime;
+ bug->times[2] = ees->lastsampletime;
+ bug->times[3] = ees->offset;
+ bug->times[4] = ees->lowoffset;
+ bug->times[5] = ees->highoffset;
+ bug->times[6] = inherent_delay[unit];
+ bug->times[8] = os_delay[unit];
+ bug->times[7] = fudgefactor[unit];
+ bug->times[9] = offset_fudge[unit];
+ bug->times[10]= ees->yearstart, 0;
+ */
+
+/* This should support the use of an EES M201 receiver with RS232
+ * output (modified to transmit time once per second).
+ *
+ * For the format of the message sent by the clock, see the EESM_
+ * definitions below.
+ *
+ * It appears to run free for an integral number of minutes, until the error
+ * reaches 4mS, at which point it steps at second = 01.
+ * It appears that sometimes it steps 4mS (say at 7 min interval),
+ * then the next minute it decides that it was an error, so steps back.
+ * On the next minute it steps forward again :-(
+ * This is typically 16.5uS/S then 3975uS at the 4min re-sync,
+ * or 9.5uS/S then 3990.5uS at a 7min re-sync,
+ * at which point it may lose the "00" second time stamp.
+ * I assume that the most accurate time is just AFTER the re-sync.
+ * Hence remember the last cycle interval,
+ *
+ * Can run in any one of:
+ *
+ * PPSCD PPS signal sets CD which interupts, and grabs the current TOD
+ * (sun) *in the interupt code*, so as to avoid problems with
+ * the STREAMS scheduling.
+ *
+ * It appears that it goes 16.5 uS slow each second, then every 4 mins it
+ * generates no "00" second tick, and gains 3975 uS. Ho Hum ! (93/2/7)
+ */
+
+/* Definitions */
+#ifndef MAXUNITS
+#define MAXUNITS 4 /* maximum number of EES units permitted */
+#endif
+
+#ifndef EES232
+#define EES232 "/dev/ees%d" /* Device to open to read the data */
+#endif
+
+/* Other constant stuff */
+#ifndef EESPRECISION
+#define EESPRECISION (-10) /* what the heck - 2**-10 = 1ms */
+#endif
+#ifndef EESREFID
+#define EESREFID "MSF\0" /* String to identify the clock */
+#endif
+#ifndef EESHSREFID
+#define EESHSREFID (0x7f7f0000 | ((REFCLK_MSF_EES) << 8)) /* Numeric refid */
+#endif
+
+/* Description of clock */
+#define EESDESCRIPTION "EES M201 MSF Receiver"
+
+/* Speed we run the clock port at. If this is changed the UARTDELAY
+ * value should be recomputed to suit.
+ */
+#ifndef SPEED232
+#define SPEED232 B9600 /* 9600 baud */
+#endif
+
+/* What is the inherent delay for this mode of working, i.e. when is the
+ * data time stamped.
+ */
+#define SAFETY_SHIFT 10 /* Split the shift to avoid overflow */
+#define BITS_TO_L_FP(bits, baud) \
+(((((bits)*2 +1) << (FRACTION_PREC-SAFETY_SHIFT)) / (2*baud)) << SAFETY_SHIFT)
+#define INH_DELAY_CBREAK BITS_TO_L_FP(119, 9600)
+#define INH_DELAY_PPS BITS_TO_L_FP( 0, 9600)
+
+#ifndef STREAM_PP1
+#define STREAM_PP1 "ppsclocd\0<-- patch space for module name1 -->"
+#endif
+#ifndef STREAM_PP2
+#define STREAM_PP2 "ppsclock\0<-- patch space for module name2 -->"
+#endif
+
+ /* Offsets of the bytes of the serial line code. The clock gives
+ * local time with a GMT/BST indication. The EESM_ definitions
+ * give offsets into ees->lastcode.
+ */
+#define EESM_CSEC 0 /* centiseconds - always zero in our clock */
+#define EESM_SEC 1 /* seconds in BCD */
+#define EESM_MIN 2 /* minutes in BCD */
+#define EESM_HOUR 3 /* hours in BCD */
+#define EESM_DAYWK 4 /* day of week (Sun = 0 etc) */
+#define EESM_DAY 5 /* day of month in BCD */
+#define EESM_MON 6 /* month in BCD */
+#define EESM_YEAR 7 /* year MOD 100 in BCD */
+#define EESM_LEAP 8 /* 0x0f if leap year, otherwise zero */
+#define EESM_BST 9 /* 0x03 if BST, 0x00 if GMT */
+#define EESM_MSFOK 10 /* 0x3f if radio good, otherwise zero */
+ /* followed by a frame alignment byte (0xff) /
+ / which is not put into the lastcode buffer*/
+
+/* Length of the serial time code, in characters. The first length
+ * is less the frame alignment byte.
+ */
+#define LENEESPRT (EESM_MSFOK+1)
+#define LENEESCODE (LENEESPRT+1)
+
+ /* Code state. */
+#define EESCS_WAIT 0 /* waiting for start of timecode */
+#define EESCS_GOTSOME 1 /* have an incomplete time code buffered */
+
+ /* Default fudge factor and character to receive */
+#define DEFFUDGETIME 0 /* Default user supplied fudge factor */
+#ifndef DEFOSTIME
+#define DEFOSTIME 0 /* Default OS delay -- passed by Make ? */
+#endif
+#define DEFINHTIME INH_DELAY_PPS /* inherent delay due to sample point*/
+
+ /* Limits on things. Reduce the number of samples to SAMPLEREDUCE by median
+ * elimination. If we're running with an accurate clock, chose the BESTSAMPLE
+ * as the estimated offset, otherwise average the remainder.
+ */
+#define FULLSHIFT 6 /* NCODES root 2 */
+#define NCODES (1<< FULLSHIFT) /* 64 */
+#define REDUCESHIFT (FULLSHIFT -1) /* SAMPLEREDUCE root 2 */
+
+ /* Towards the high ( Why ?) end of half */
+#define BESTSAMPLE ((samplereduce * 3) /4) /* 24 */
+
+ /* Leap hold time. After a leap second the clock will no longer be
+ * reliable until it resynchronizes. Hope 40 minutes is enough. */
+#define EESLEAPHOLD (40 * 60)
+
+#define EES_STEP_F (1 << 24) /* the receiver steps in units of about 4ms */
+#define EES_STEP_F_GRACE (EES_STEP_F/8) /*Allow for slop of 1/8 which is .5ms*/
+#define EES_STEP_NOTE (1 << 21)/* Log any unexpected jumps, say .5 ms .... */
+#define EES_STEP_NOTES 50 /* Only do a limited number */
+#define MAX_STEP 16 /* Max number of steps to remember */
+
+ /* debug is a bit mask of debugging that is wanted */
+#define DB_SYSLOG_SMPLI 0x0001
+#define DB_SYSLOG_SMPLE 0x0002
+#define DB_SYSLOG_SMTHI 0x0004
+#define DB_SYSLOG_NSMTHE 0x0008
+#define DB_SYSLOG_NSMTHI 0x0010
+#define DB_SYSLOG_SMTHE 0x0020
+#define DB_PRINT_EV 0x0040
+#define DB_PRINT_CDT 0x0080
+#define DB_PRINT_CDTC 0x0100
+#define DB_SYSLOG_KEEPD 0x0800
+#define DB_SYSLOG_KEEPE 0x1000
+#define DB_LOG_DELTAS 0x2000
+#define DB_PRINT_DELTAS 0x4000
+#define DB_LOG_AWAITMORE 0x8000
+#define DB_LOG_SAMPLES 0x10000
+#define DB_NO_PPS 0x20000
+#define DB_INC_PPS 0x40000
+#define DB_DUMP_DELTAS 0x80000
+
+ struct eesunit { /* EES unit control structure. */
+ struct peer *peer; /* associated peer structure */
+ struct refclockio io; /* given to the I/O handler */
+ l_fp reftime; /* reference time */
+ l_fp lastsampletime; /* time as in txt from last EES msg */
+ l_fp arrvtime; /* Time at which pkt arrived */
+ l_fp codeoffsets[NCODES]; /* the time of arrival of 232 codes */
+ l_fp offset; /* chosen offset (for clkbug) */
+ l_fp lowoffset; /* lowest sample offset (for clkbug) */
+ l_fp highoffset; /* highest " " (for clkbug) */
+ char lastcode[LENEESCODE+6]; /* last time code we received */
+ u_long lasttime; /* last time clock heard from */
+ u_long clocklastgood; /* last time good radio seen */
+ u_char lencode; /* length of code in buffer */
+ u_char nsamples; /* number of samples we've collected */
+ u_char codestate; /* state of 232 code reception */
+ 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 hour; /* hour of day */
+ u_char minute; /* minute of hour */
+ u_char second; /* seconds of minute */
+ char tz; /* timezone from clock */
+ u_char ttytype; /* method used */
+ u_char dump_vals; /* Should clock values be dumped */
+ u_char usealldata; /* Use ALL samples */
+ u_short day; /* day of year from last code */
+ u_long yearstart; /* start of current year */
+ u_long leaphold; /* time of leap hold expiry */
+ u_long badformat; /* number of bad format codes */
+ u_long baddata; /* number of invalid time codes */
+ u_long timestarted; /* time we started this */
+ long last_pps_no; /* The serial # of the last PPS */
+ char fix_pending; /* Is a "sync to time" pending ? */
+ /* Fine tuning - compensate for 4 mS ramping .... */
+ l_fp last_l; /* last time stamp */
+ u_char last_steps[MAX_STEP]; /* Most recent n steps */
+ int best_av_step; /* Best guess at average step */
+ char best_av_step_count; /* # of steps over used above */
+ char this_step; /* Current pos in buffer */
+ int last_step_late; /* How late the last step was (0-59) */
+ long jump_fsecs; /* # of fractions of a sec last jump */
+ u_long last_step; /* time of last step */
+ int last_step_secs; /* Number of seconds in last step */
+ int using_ramp; /* 1 -> noemal, -1 -> over stepped */
+ };
+#define last_sec last_l.l_ui
+#define last_sfsec last_l.l_f
+#define this_uisec ((ees->arrvtime).l_ui)
+#define this_sfsec ((ees->arrvtime).l_f)
+#define msec(x) ((x) / (1<<22))
+#define LAST_STEPS (sizeof ees->last_steps / sizeof ees->last_steps[0])
+#define subms(x) ((((((x < 0) ? (-(x)) : (x)) % (1<<22))/2) * 625) / (1<<(22 -5)))
+
+/* Bitmask for what methods to try to use -- currently only PPS enabled */
+#define T_CBREAK 1
+#define T_PPS 8
+/* macros to test above */
+#define is_cbreak(x) ((x)->ttytype & T_CBREAK)
+#define is_pps(x) ((x)->ttytype & T_PPS)
+#define is_any(x) ((x)->ttytype)
+
+#define CODEREASON 20 /* reason codes */
+
+/* Data space for the unit structures. Note that we allocate these on
+ * the fly, but never give them back. */
+static struct eesunit *eesunits[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 inherent_delay[MAXUNITS]; /* when time stamp is taken */
+static l_fp fudgefactor[MAXUNITS]; /* fudgetime1 */
+static l_fp os_delay[MAXUNITS]; /* fudgetime2 */
+static l_fp offset_fudge[MAXUNITS]; /* Sum of above */
+static u_char stratumtouse[MAXUNITS];
+static u_char sloppyclockflag[MAXUNITS];
+
+static int deltas[60];
+
+static l_fp acceptable_slop; /* = { 0, 1 << (FRACTION_PREC -2) }; */
+static l_fp onesec; /* = { 1, 0 }; */
+
+#ifndef DUMP_BUF_SIZE /* Size of buffer to be used by dump_buf */
+#define DUMP_BUF_SIZE 10112
+#endif
+
+/* ees_reset - reset the count back to zero */
+#define ees_reset(ees) (ees)->nsamples = 0; \
+(ees)->codestate = EESCS_WAIT
+
+/* ees_event - record and report an event */
+#define ees_event(ees, evcode) if ((ees)->status != (u_char)(evcode)) \
+ees_report_event((ees), (evcode))
+
+ /* Find the precision of the system clock by reading it */
+#define USECS 1000000
+#define MINSTEP 5 /* some systems increment uS on each call */
+#define MAXLOOPS (USECS/9)
+
+/*
+ * Function prototypes
+ */
+
+static int msfees_start P((int unit, struct peer *peer));
+static void msfees_shutdown P((int unit, struct peer *peer));
+static void msfees_poll P((int unit, struct peer *peer));
+static void msfees_init P((void));
+static void dump_buf P((l_fp *coffs, int from, int to, char *text));
+static void ees_report_event P((struct eesunit *ees, int code));
+static void ees_receive P((struct recvbuf *rbufp));
+static void ees_process P((struct eesunit *ees));
+static int offcompare P((const void *va, const void *vb));
+
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_msfees = {
+ msfees_start, /* start up driver */
+ msfees_shutdown, /* shut down driver */
+ msfees_poll, /* transmit poll message */
+ noentry, /* not used */
+ msfees_init, /* initialize driver */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+
+static void
+dump_buf(
+ l_fp *coffs,
+ int from,
+ int to,
+ char *text
+ )
+{
+ char buff[DUMP_BUF_SIZE + 80];
+ int i;
+ register char *ptr = buff;
+
+ snprintf(buff, sizeof(buff), text);
+ for (i = from; i < to; i++) {
+ ptr += strlen(ptr);
+ if ((ptr - buff) > DUMP_BUF_SIZE) {
+ msyslog(LOG_DEBUG, "D: %s", buff);
+ ptr = buff;
+ }
+ snprintf(ptr, sizeof(buff) - (ptr - buff),
+ " %06d", ((int)coffs[i].l_f) / 4295);
+ }
+ msyslog(LOG_DEBUG, "D: %s", buff);
+}
+
+/* msfees_init - initialize internal ees driver data */
+static void
+msfees_init(void)
+{
+ register int i;
+ /* Just zero the data arrays */
+ memset((char *)eesunits, 0, sizeof eesunits);
+ memset((char *)unitinuse, 0, sizeof unitinuse);
+
+ acceptable_slop.l_ui = 0;
+ acceptable_slop.l_uf = 1 << (FRACTION_PREC -2);
+
+ onesec.l_ui = 1;
+ onesec.l_uf = 0;
+
+ /* Initialize fudge factors to default. */
+ for (i = 0; i < MAXUNITS; i++) {
+ fudgefactor[i].l_ui = 0;
+ fudgefactor[i].l_uf = DEFFUDGETIME;
+ os_delay[i].l_ui = 0;
+ os_delay[i].l_uf = DEFOSTIME;
+ inherent_delay[i].l_ui = 0;
+ inherent_delay[i].l_uf = DEFINHTIME;
+ offset_fudge[i] = os_delay[i];
+ L_ADD(&offset_fudge[i], &fudgefactor[i]);
+ L_ADD(&offset_fudge[i], &inherent_delay[i]);
+ stratumtouse[i] = 0;
+ sloppyclockflag[i] = 0;
+ }
+}
+
+
+/* msfees_start - open the EES devices and initialize data for processing */
+static int
+msfees_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct eesunit *ees;
+ register int i;
+ int fd232 = -1;
+ char eesdev[20];
+ struct termios ttyb, *ttyp;
+ struct refclockproc *pp;
+ pp = peer->procptr;
+
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)",
+ unit, MAXUNITS-1);
+ return 0;
+ }
+ if (unitinuse[unit]) {
+ msyslog(LOG_ERR, "ees clock: unit number %d in use", unit);
+ return 0;
+ }
+
+ /* Unit okay, attempt to open the devices. We do them both at
+ * once to make sure we can */
+ snprintf(eesdev, sizeof(eesdev), EES232, unit);
+
+ fd232 = open(eesdev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ msyslog(LOG_ERR, "ees clock: open of %s failed: %m", eesdev);
+ return 0;
+ }
+
+#ifdef TIOCEXCL
+ /* Set for exclusive use */
+ if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) {
+ msyslog(LOG_ERR, "ees clock: ioctl(%s, TIOCEXCL): %m", eesdev);
+ goto screwed;
+ }
+#endif
+
+ /* STRIPPED DOWN VERSION: Only PPS CD is supported at the moment */
+
+ /* Set port characteristics. If we don't have a STREAMS module or
+ * a clock line discipline, cooked mode is just usable, even though it
+ * strips the top bit. The only EES byte which uses the top
+ * bit is the year, and we don't use that anyway. If we do
+ * have the line discipline, we choose raw mode, and the
+ * line discipline code will block up the messages.
+ */
+
+ /* STIPPED DOWN VERSION: Only PPS CD is supported at the moment */
+
+ ttyp = &ttyb;
+ if (tcgetattr(fd232, ttyp) < 0) {
+ msyslog(LOG_ERR, "msfees_start: tcgetattr(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_oflag = 0;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ msyslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ msyslog(LOG_ERR, "msfees_start: tcflush(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ inherent_delay[unit].l_uf = INH_DELAY_PPS;
+
+ /* offset fudge (how *late* the timestamp is) = fudge + os delays */
+ offset_fudge[unit] = os_delay[unit];
+ L_ADD(&offset_fudge[unit], &fudgefactor[unit]);
+ L_ADD(&offset_fudge[unit], &inherent_delay[unit]);
+
+ /* Looks like this might succeed. Find memory for the structure.
+ * Look to see if there are any unused ones, if not we malloc() one.
+ */
+ if (eesunits[unit] != 0) /* The one we want is okay */
+ ees = eesunits[unit];
+ else {
+ /* Look for an unused, but allocated struct */
+ for (i = 0; i < MAXUNITS; i++) {
+ if (!unitinuse[i] && eesunits[i] != 0)
+ break;
+ }
+
+ if (i < MAXUNITS) { /* Reclaim this one */
+ ees = eesunits[i];
+ eesunits[i] = 0;
+ } /* no spare -- make a new one */
+ else ees = (struct eesunit *) emalloc(sizeof(struct eesunit));
+ }
+ memset((char *)ees, 0, sizeof(struct eesunit));
+ eesunits[unit] = ees;
+
+ /* Set up the structures */
+ ees->peer = peer;
+ ees->unit = (u_char)unit;
+ ees->timestarted= current_time;
+ ees->ttytype = 0;
+ ees->io.clock_recv= ees_receive;
+ ees->io.srcclock= peer;
+ ees->io.datalen = 0;
+ ees->io.fd = fd232;
+
+ /* Okay. Push one of the two (linked into the kernel, or dynamically
+ * loaded) STREAMS module, and give it to the I/O code to start
+ * receiving stuff.
+ */
+
+#ifdef STREAM
+ {
+ int rc1;
+ /* Pop any existing onews first ... */
+ while (ioctl(fd232, I_POP, 0 ) >= 0) ;
+
+ /* Now try pushing either of the possible modules */
+ if ((rc1=ioctl(fd232, I_PUSH, STREAM_PP1)) < 0 &&
+ ioctl(fd232, I_PUSH, STREAM_PP2) < 0) {
+ msyslog(LOG_ERR,
+ "ees clock: Push of `%s' and `%s' to %s failed %m",
+ STREAM_PP1, STREAM_PP2, eesdev);
+ goto screwed;
+ }
+ else {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "I: ees clock: PUSHed %s on %s",
+ (rc1 >= 0) ? STREAM_PP1 : STREAM_PP2, eesdev);
+ ees->ttytype |= T_PPS;
+ }
+ }
+#endif /* STREAM */
+
+ /* Add the clock */
+ if (!io_addclock(&ees->io)) {
+ /* Oh shit. Just close and return. */
+ msyslog(LOG_ERR, "ees clock: io_addclock(%s): %m", eesdev);
+ goto screwed;
+ }
+
+
+ /* All done. Initialize a few random peer variables, then
+ * return success. */
+ peer->precision = sys_precision;
+ peer->stratum = stratumtouse[unit];
+ if (stratumtouse[unit] <= 1) {
+ memcpy((char *)&pp->refid, EESREFID, 4);
+ if (unit > 0 && unit < 10)
+ ((char *)&pp->refid)[3] = '0' + unit;
+ } else {
+ peer->refid = htonl(EESHSREFID);
+ }
+ unitinuse[unit] = 1;
+ pp->unitptr = &eesunits[unit];
+ pp->clockdesc = EESDESCRIPTION;
+ msyslog(LOG_ERR, "ees clock: %s OK on %d", eesdev, unit);
+ return (1);
+
+ screwed:
+ if (fd232 != -1)
+ (void) close(fd232);
+ return (0);
+}
+
+
+/* msfees_shutdown - shut down a EES clock */
+static void
+msfees_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct eesunit *ees;
+
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR,
+ "ees clock: INTERNAL ERROR, unit number %d invalid (max %d)",
+ unit, MAXUNITS);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ msyslog(LOG_ERR,
+ "ees clock: INTERNAL ERROR, unit number %d not in use", unit);
+ return;
+ }
+
+ /* Tell the I/O module to turn us off. We're history. */
+ ees = eesunits[unit];
+ io_closeclock(&ees->io);
+ unitinuse[unit] = 0;
+}
+
+
+/* ees_report_event - note the occurance of an event */
+static void
+ees_report_event(
+ struct eesunit *ees,
+ int code
+ )
+{
+ if (ees->status != (u_char)code) {
+ ees->status = (u_char)code;
+ if (code != CEVNT_NOMINAL)
+ ees->lastevent = (u_char)code;
+ /* Should report event to trap handler in here.
+ * Soon...
+ */
+ }
+}
+
+
+/* ees_receive - receive data from the serial interface on an EES clock */
+static void
+ees_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register int n_sample;
+ register int day;
+ register struct eesunit *ees;
+ register u_char *dpt; /* Data PoinTeR: move along ... */
+ register u_char *dpend; /* Points just *after* last data char */
+ register char *cp;
+ l_fp tmp;
+ int call_pps_sample = 0;
+ l_fp pps_arrvstamp;
+ int sincelast;
+ int pps_step = 0;
+ int suspect_4ms_step = 0;
+ struct ppsclockev ppsclockev;
+ long *ptr = (long *) &ppsclockev;
+ int rc;
+ int request;
+#ifdef HAVE_CIOGETEV
+ request = CIOGETEV;
+#endif
+#ifdef HAVE_TIOCGPPSEV
+ request = TIOCGPPSEV;
+#endif
+
+ /* Get the clock this applies to and a pointer to the data */
+ ees = (struct eesunit *)rbufp->recv_peer->procptr->unitptr;
+ dpt = (u_char *)&rbufp->recv_space;
+ dpend = dpt + rbufp->recv_length;
+ if ((dbg & DB_LOG_AWAITMORE) && (rbufp->recv_length != LENEESCODE))
+ printf("[%d] ", rbufp->recv_length);
+
+ /* Check out our state and process appropriately */
+ switch (ees->codestate) {
+ case EESCS_WAIT:
+ /* Set an initial guess at the timestamp as the recv time.
+ * If just running in CBREAK mode, we can't improve this.
+ * If we have the CLOCK Line Discipline, PPSCD, or sime such,
+ * then we will do better later ....
+ */
+ ees->arrvtime = rbufp->recv_time;
+ ees->codestate = EESCS_GOTSOME;
+ ees->lencode = 0;
+ /*FALLSTHROUGH*/
+
+ case EESCS_GOTSOME:
+ cp = &(ees->lastcode[ees->lencode]);
+
+ /* Gobble the bytes until the final (possibly stripped) 0xff */
+ while (dpt < dpend && (*dpt & 0x7f) != 0x7f) {
+ *cp++ = (char)*dpt++;
+ ees->lencode++;
+ /* Oh dear -- too many bytes .. */
+ if (ees->lencode > LENEESPRT) {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO,
+ "I: ees clock: %d + %d > %d [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]",
+ ees->lencode, dpend - dpt, LENEESPRT,
+#define D(x) (ees->lastcode[x])
+ D(0), D(1), D(2), D(3), D(4), D(5), D(6),
+ D(7), D(8), D(9), D(10), D(11), D(12));
+#undef D
+ ees->badformat++;
+ ees->reason = CODEREASON + 1;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+ }
+ /* Gave up because it was end of the buffer, rather than ff */
+ if (dpt == dpend) {
+ /* Incomplete. Wait for more. */
+ if (dbg & DB_LOG_AWAITMORE)
+ msyslog(LOG_INFO,
+ "I: ees clock %d: %p == %p: await more",
+ ees->unit, dpt, dpend);
+ return;
+ }
+
+ /* This shouldn't happen ... ! */
+ if ((*dpt & 0x7f) != 0x7f) {
+ msyslog(LOG_INFO, "I: ees clock: %0x & 0x7f != 0x7f", *dpt);
+ ees->badformat++;
+ ees->reason = CODEREASON + 2;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Skip the 0xff */
+ dpt++;
+
+ /* Finally, got a complete buffer. Mainline code will
+ * continue on. */
+ cp = ees->lastcode;
+ break;
+
+ default:
+ msyslog(LOG_ERR, "ees clock: INTERNAL ERROR: %d state %d",
+ ees->unit, ees->codestate);
+ ees->reason = CODEREASON + 5;
+ ees_event(ees, CEVNT_FAULT);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Boy! After all that crap, the lastcode buffer now contains
+ * something we hope will be a valid time code. Do length
+ * checks and sanity checks on constant data.
+ */
+ ees->codestate = EESCS_WAIT;
+ ees->lasttime = current_time;
+ if (ees->lencode != LENEESPRT) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 6;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ cp = ees->lastcode;
+
+ /* Check that centisecond is zero */
+ if (cp[EESM_CSEC] != 0) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 7;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Check flag formats */
+ if (cp[EESM_LEAP] != 0 && cp[EESM_LEAP] != 0x0f) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 8;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ if (cp[EESM_BST] != 0 && cp[EESM_BST] != 0x03) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 9;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ if (cp[EESM_MSFOK] != 0 && cp[EESM_MSFOK] != 0x3f) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 10;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* So far, so good. Compute day, hours, minutes, seconds,
+ * time zone. Do range checks on these.
+ */
+
+#define bcdunpack(val) ( (((val)>>4) & 0x0f) * 10 + ((val) & 0x0f) )
+#define istrue(x) ((x)?1:0)
+
+ ees->second = bcdunpack(cp[EESM_SEC]); /* second */
+ ees->minute = bcdunpack(cp[EESM_MIN]); /* minute */
+ ees->hour = bcdunpack(cp[EESM_HOUR]); /* hour */
+
+ day = bcdunpack(cp[EESM_DAY]); /* day of month */
+
+ switch (bcdunpack(cp[EESM_MON])) { /* month */
+
+ /* Add in lengths of all previous months. Add one more
+ if it is a leap year and after February.
+ */
+ case 12: day += NOV; /*FALLSTHROUGH*/
+ case 11: day += OCT; /*FALLSTHROUGH*/
+ case 10: day += SEP; /*FALLSTHROUGH*/
+ case 9: day += AUG; /*FALLSTHROUGH*/
+ case 8: day += JUL; /*FALLSTHROUGH*/
+ case 7: day += JUN; /*FALLSTHROUGH*/
+ case 6: day += MAY; /*FALLSTHROUGH*/
+ case 5: day += APR; /*FALLSTHROUGH*/
+ case 4: day += MAR; /*FALLSTHROUGH*/
+ case 3: day += FEB;
+ if (istrue(cp[EESM_LEAP])) day++; /*FALLSTHROUGH*/
+ case 2: day += JAN; /*FALLSTHROUGH*/
+ case 1: break;
+ default: ees->baddata++;
+ ees->reason = CODEREASON + 11;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+
+ ees->day = day;
+
+ /* Get timezone. The clocktime routine wants the number
+ * of hours to add to the delivered time to get UT.
+ * Currently -1 if BST flag set, 0 otherwise. This
+ * is the place to tweak things if double summer time
+ * ever happens.
+ */
+ ees->tz = istrue(cp[EESM_BST]) ? -1 : 0;
+
+ if (ees->day > 366 || ees->day < 1 ||
+ ees->hour > 23 || ees->minute > 59 || ees->second > 59) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 12;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+
+ n_sample = ees->nsamples;
+
+ /* Now, compute the reference time value: text -> tmp.l_ui */
+ if (!clocktime(ees->day, ees->hour, ees->minute, ees->second,
+ ees->tz, rbufp->recv_time.l_ui, &ees->yearstart,
+ &tmp.l_ui)) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 13;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+ tmp.l_uf = 0;
+
+ /* DON'T use ees->arrvtime -- it may be < reftime */
+ ees->lastsampletime = tmp;
+
+ /* If we are synchronised to the radio, update the reference time.
+ * Also keep a note of when clock was last good.
+ */
+ if (istrue(cp[EESM_MSFOK])) {
+ ees->reftime = tmp;
+ ees->clocklastgood = current_time;
+ }
+
+
+ /* Compute the offset. For the fractional part of the
+ * offset we use the expected delay for the message.
+ */
+ ees->codeoffsets[n_sample].l_ui = tmp.l_ui;
+ ees->codeoffsets[n_sample].l_uf = 0;
+
+ /* Number of seconds since the last step */
+ sincelast = this_uisec - ees->last_step;
+
+ memset((char *) &ppsclockev, 0, sizeof ppsclockev);
+
+ rc = ioctl(ees->io.fd, request, (char *) &ppsclockev);
+ if (dbg & DB_PRINT_EV) fprintf(stderr,
+ "[%x] CIOGETEV u%d %d (%x %d) gave %d (%d): %08lx %08lx %ld\n",
+ DB_PRINT_EV, ees->unit, ees->io.fd, request, is_pps(ees),
+ rc, errno, ptr[0], ptr[1], ptr[2]);
+
+ /* If we managed to get the time of arrival, process the info */
+ if (rc >= 0) {
+ int conv = -1;
+ pps_step = ppsclockev.serial - ees->last_pps_no;
+
+ /* Possible that PPS triggered, but text message didn't */
+ if (pps_step == 2) msyslog(LOG_ERR, "pps step = 2 @ %02d", ees->second);
+ if (pps_step == 2 && ees->second == 1) suspect_4ms_step |= 1;
+ if (pps_step == 2 && ees->second == 2) suspect_4ms_step |= 4;
+
+ /* allow for single loss of PPS only */
+ if (pps_step != 1 && pps_step != 2)
+ fprintf(stderr, "PPS step: %d too far off %ld (%d)\n",
+ ppsclockev.serial, ees->last_pps_no, pps_step);
+ else {
+ pps_arrvstamp = tval_stamp_to_lfp(ppsclockev.tv);
+ /* if ((ABS(time difference) - 0.25) < 0)
+ * then believe it ...
+ */
+ l_fp diff;
+ diff = pps_arrvstamp;
+ conv = 0;
+ L_SUB(&diff, &ees->arrvtime);
+ if (dbg & DB_PRINT_CDT)
+ printf("[%x] Have %lx.%08lx and %lx.%08lx -> %lx.%08lx @ %s",
+ DB_PRINT_CDT, (long)ees->arrvtime.l_ui, (long)ees->arrvtime.l_uf,
+ (long)pps_arrvstamp.l_ui, (long)pps_arrvstamp.l_uf,
+ (long)diff.l_ui, (long)diff.l_uf,
+ ctime(&(ppsclockev.tv.tv_sec)));
+ if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
+ L_SUB(&diff, &acceptable_slop);
+ if (L_ISNEG(&diff)) { /* AOK -- pps_sample */
+ ees->arrvtime = pps_arrvstamp;
+ conv++;
+ call_pps_sample++;
+ }
+ /* Some loss of some signals around sec = 1 */
+ else if (ees->second == 1) {
+ diff = pps_arrvstamp;
+ L_ADD(&diff, &onesec);
+ L_SUB(&diff, &ees->arrvtime);
+ if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
+ L_SUB(&diff, &acceptable_slop);
+ msyslog(LOG_ERR, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s",
+ pps_arrvstamp.l_ui - ees->arrvtime.l_ui,
+ pps_arrvstamp.l_uf,
+ ees->arrvtime.l_uf,
+ diff.l_ui, diff.l_uf,
+ (int)ppsclockev.tv.tv_usec,
+ ctime(&(ppsclockev.tv.tv_sec)));
+ if (L_ISNEG(&diff)) { /* AOK -- pps_sample */
+ suspect_4ms_step |= 2;
+ ees->arrvtime = pps_arrvstamp;
+ L_ADD(&ees->arrvtime, &onesec);
+ conv++;
+ call_pps_sample++;
+ }
+ }
+ }
+ ees->last_pps_no = ppsclockev.serial;
+ if (dbg & DB_PRINT_CDTC)
+ printf(
+ "[%x] %08lx %08lx %d u%d (%d %d)\n",
+ DB_PRINT_CDTC, (long)pps_arrvstamp.l_ui,
+ (long)pps_arrvstamp.l_uf, conv, ees->unit,
+ call_pps_sample, pps_step);
+ }
+
+ /* See if there has been a 4ms jump at a minute boundry */
+ { l_fp delta;
+#define delta_isec delta.l_ui
+#define delta_ssec delta.l_i
+#define delta_sfsec delta.l_f
+ long delta_f_abs;
+
+ delta.l_i = ees->arrvtime.l_i;
+ delta.l_f = ees->arrvtime.l_f;
+
+ L_SUB(&delta, &ees->last_l);
+ delta_f_abs = delta_sfsec;
+ if (delta_f_abs < 0) delta_f_abs = -delta_f_abs;
+
+ /* Dump the deltas each minute */
+ if (dbg & DB_DUMP_DELTAS)
+ {
+ if (/*0 <= ees->second && */
+ ees->second < COUNTOF(deltas))
+ deltas[ees->second] = delta_sfsec;
+ /* Dump on second 1, as second 0 sometimes missed */
+ if (ees->second == 1) {
+ char text[16 * COUNTOF(deltas)];
+ char *cptr=text;
+ int i;
+ for (i = 0; i < COUNTOF(deltas); i++) {
+ snprintf(cptr, sizeof(text) / COUNTOF(deltas),
+ " %d.%04d", msec(deltas[i]),
+ subms(deltas[i]));
+ cptr += strlen(cptr);
+ }
+ msyslog(LOG_ERR, "Deltas: %d.%04d<->%d.%04d: %s",
+ msec(EES_STEP_F - EES_STEP_F_GRACE), subms(EES_STEP_F - EES_STEP_F_GRACE),
+ msec(EES_STEP_F + EES_STEP_F_GRACE), subms(EES_STEP_F + EES_STEP_F_GRACE),
+ text+1);
+ for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) deltas[i] = 0;
+ }
+ }
+
+ /* Lets see if we have a 4 mS step at a minute boundaary */
+ if ( ((EES_STEP_F - EES_STEP_F_GRACE) < delta_f_abs) &&
+ (delta_f_abs < (EES_STEP_F + EES_STEP_F_GRACE)) &&
+ (ees->second == 0 || ees->second == 1 || ees->second == 2) &&
+ (sincelast < 0 || sincelast > 122)
+ ) { /* 4ms jump at min boundry */
+ int old_sincelast;
+ int count=0;
+ int sum = 0;
+ /* Yes -- so compute the ramp time */
+ if (ees->last_step == 0) sincelast = 0;
+ old_sincelast = sincelast;
+
+ /* First time in, just set "ees->last_step" */
+ if(ees->last_step) {
+ int other_step = 0;
+ int third_step = 0;
+ int this_step = (sincelast + (60 /2)) / 60;
+ int p_step = ees->this_step;
+ int p;
+ ees->last_steps[p_step] = this_step;
+ p= p_step;
+ p_step++;
+ if (p_step >= LAST_STEPS) p_step = 0;
+ ees->this_step = p_step;
+ /* Find the "average" interval */
+ while (p != p_step) {
+ int this = ees->last_steps[p];
+ if (this == 0) break;
+ if (this != this_step) {
+ if (other_step == 0 && (
+ this== (this_step +2) ||
+ this== (this_step -2) ||
+ this== (this_step +1) ||
+ this== (this_step -1)))
+ other_step = this;
+ if (other_step != this) {
+ int idelta = (this_step - other_step);
+ if (idelta < 0) idelta = - idelta;
+ if (third_step == 0 && (
+ (idelta == 1) ? (
+ this == (other_step +1) ||
+ this == (other_step -1) ||
+ this == (this_step +1) ||
+ this == (this_step -1))
+ :
+ (
+ this == (this_step + other_step)/2
+ )
+ )) third_step = this;
+ if (third_step != this) break;
+ }
+ }
+ sum += this;
+ p--;
+ if (p < 0) p += LAST_STEPS;
+ count++;
+ }
+ msyslog(LOG_ERR, "MSF%d: %d: This=%d (%d), other=%d/%d, sum=%d, count=%d, pps_step=%d, suspect=%x", ees->unit, p, ees->last_steps[p], this_step, other_step, third_step, sum, count, pps_step, suspect_4ms_step);
+ if (count != 0) sum = ((sum * 60) + (count /2)) / count;
+#define SV(x) (ees->last_steps[(x + p_step) % LAST_STEPS])
+ msyslog(LOG_ERR, "MSF%d: %x steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
+ ees->unit, suspect_4ms_step, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
+ SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
+ printf("MSF%d: steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ ees->unit, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
+ SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
+#undef SV
+ ees->jump_fsecs = delta_sfsec;
+ ees->using_ramp = 1;
+ if (sincelast > 170)
+ ees->last_step_late += sincelast - ((sum) ? sum : ees->last_step_secs);
+ else ees->last_step_late = 30;
+ if (ees->last_step_late < -60 || ees->last_step_late > 120) ees->last_step_late = 30;
+ if (ees->last_step_late < 0) ees->last_step_late = 0;
+ if (ees->last_step_late >= 60) ees->last_step_late = 59;
+ sincelast = 0;
+ }
+ else { /* First time in -- just save info */
+ ees->last_step_late = 30;
+ ees->jump_fsecs = delta_sfsec;
+ ees->using_ramp = 1;
+ sum = 4 * 60;
+ }
+ ees->last_step = this_uisec;
+ printf("MSF%d: d=%3ld.%04ld@%d :%d:%d:$%d:%d:%d\n",
+ ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec),
+ ees->second, old_sincelast, ees->last_step_late, count, sum,
+ ees->last_step_secs);
+ msyslog(LOG_ERR, "MSF%d: d=%3d.%04d@%d :%d:%d:%d:%d:%d",
+ ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second,
+ old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs);
+ if (sum) ees->last_step_secs = sum;
+ }
+ /* OK, so not a 4ms step at a minute boundry */
+ else {
+ if (suspect_4ms_step) msyslog(LOG_ERR,
+ "MSF%d: suspect = %x, but delta of %d.%04d [%d.%04d<%d.%04d<%d.%04d: %d %d]",
+ ees->unit, suspect_4ms_step, msec(delta_sfsec), subms(delta_sfsec),
+ msec(EES_STEP_F - EES_STEP_F_GRACE),
+ subms(EES_STEP_F - EES_STEP_F_GRACE),
+ (int)msec(delta_f_abs),
+ (int)subms(delta_f_abs),
+ msec(EES_STEP_F + EES_STEP_F_GRACE),
+ subms(EES_STEP_F + EES_STEP_F_GRACE),
+ ees->second,
+ sincelast);
+ if ((delta_f_abs > EES_STEP_NOTE) && ees->last_l.l_i) {
+ static int ees_step_notes = EES_STEP_NOTES;
+ if (ees_step_notes > 0) {
+ ees_step_notes--;
+ printf("MSF%d: D=%3ld.%04ld@%02d :%d%s\n",
+ ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec),
+ ees->second, sincelast, ees_step_notes ? "" : " -- NO MORE !");
+ msyslog(LOG_ERR, "MSF%d: D=%3d.%04d@%02d :%d%s",
+ ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, (ees->last_step) ? sincelast : -1, ees_step_notes ? "" : " -- NO MORE !");
+ }
+ }
+ }
+ }
+ ees->last_l = ees->arrvtime;
+
+ /* IF we have found that it's ramping
+ * && it's within twice the expected ramp period
+ * && there is a non zero step size (avoid /0 !)
+ * THEN we twiddle things
+ */
+ if (ees->using_ramp &&
+ sincelast < (ees->last_step_secs)*2 &&
+ ees->last_step_secs)
+ { long sec_of_ramp = sincelast + ees->last_step_late;
+ long fsecs;
+ l_fp inc;
+
+ /* Ramp time may vary, so may ramp for longer than last time */
+ if (sec_of_ramp > (ees->last_step_secs + 120))
+ sec_of_ramp = ees->last_step_secs;
+
+ /* sec_of_ramp * ees->jump_fsecs may overflow 2**32 */
+ fsecs = sec_of_ramp * (ees->jump_fsecs / ees->last_step_secs);
+
+ if (dbg & DB_LOG_DELTAS) msyslog(LOG_ERR,
+ "[%x] MSF%d: %3ld/%03d -> d=%11ld (%d|%ld)",
+ DB_LOG_DELTAS,
+ ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
+ pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
+ if (dbg & DB_PRINT_DELTAS) printf(
+ "MSF%d: %3ld/%03d -> d=%11ld (%ld|%ld)\n",
+ ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
+ (long)pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
+
+ /* Must sign extend the result */
+ inc.l_i = (fsecs < 0) ? -1 : 0;
+ inc.l_f = fsecs;
+ if (dbg & DB_INC_PPS)
+ { L_SUB(&pps_arrvstamp, &inc);
+ L_SUB(&ees->arrvtime, &inc);
+ }
+ else
+ { L_ADD(&pps_arrvstamp, &inc);
+ L_ADD(&ees->arrvtime, &inc);
+ }
+ }
+ else {
+ if (dbg & DB_LOG_DELTAS) msyslog(LOG_ERR,
+ "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x",
+ DB_LOG_DELTAS,
+ ees->unit, ees->using_ramp,
+ sincelast,
+ (ees->last_step_secs)*2,
+ ees->last_step_secs);
+ if (dbg & DB_PRINT_DELTAS) printf(
+ "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x\n",
+ DB_LOG_DELTAS,
+ ees->unit, ees->using_ramp,
+ sincelast,
+ (ees->last_step_secs)*2,
+ ees->last_step_secs);
+ }
+
+ L_SUB(&ees->arrvtime, &offset_fudge[ees->unit]);
+ L_SUB(&pps_arrvstamp, &offset_fudge[ees->unit]);
+
+ if (call_pps_sample && !(dbg & DB_NO_PPS)) {
+ /* Sigh -- it expects its args negated */
+ L_NEG(&pps_arrvstamp);
+ /*
+ * I had to disable this here, since it appears there is no pointer to the
+ * peer structure.
+ *
+ (void) pps_sample(peer, &pps_arrvstamp);
+ */
+ }
+
+ /* Subtract off the local clock time stamp */
+ L_SUB(&ees->codeoffsets[n_sample], &ees->arrvtime);
+ if (dbg & DB_LOG_SAMPLES) msyslog(LOG_ERR,
+ "MSF%d: [%x] %d (ees: %d %d) (pps: %d %d)%s",
+ ees->unit, DB_LOG_DELTAS, n_sample,
+ ees->codeoffsets[n_sample].l_f,
+ ees->codeoffsets[n_sample].l_f / 4295,
+ pps_arrvstamp.l_f,
+ pps_arrvstamp.l_f /4295,
+ (dbg & DB_NO_PPS) ? " [no PPS]" : "");
+
+ if (ees->nsamples++ == NCODES-1) ees_process(ees);
+
+ /* Done! */
+}
+
+
+/* offcompare - auxiliary comparison routine for offset sort */
+
+static int
+offcompare(
+ const void *va,
+ const void *vb
+ )
+{
+ const l_fp *a = (const l_fp *)va;
+ const l_fp *b = (const l_fp *)vb;
+ return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
+}
+
+
+/* ees_process - process a pile of samples from the clock */
+static void
+ees_process(
+ struct eesunit *ees
+ )
+{
+ static int last_samples = -1;
+ register int i, j;
+ register int noff;
+ register l_fp *coffs = ees->codeoffsets;
+ l_fp offset, tmp;
+ double dispersion; /* ++++ */
+ int lostsync, isinsync;
+ int samples = ees->nsamples;
+ int samplelog = 0; /* keep "gcc -Wall" happy ! */
+ int samplereduce = (samples + 1) / 2;
+ double doffset;
+
+ /* Reset things to zero so we don't have to worry later */
+ ees_reset(ees);
+
+ if (sloppyclockflag[ees->unit]) {
+ samplelog = (samples < 2) ? 0 :
+ (samples < 5) ? 1 :
+ (samples < 9) ? 2 :
+ (samples < 17) ? 3 :
+ (samples < 33) ? 4 : 5;
+ samplereduce = (1 << samplelog);
+ }
+
+ if (samples != last_samples &&
+ ((samples != (last_samples-1)) || samples < 3)) {
+ msyslog(LOG_ERR, "Samples=%d (%d), samplereduce=%d ....",
+ samples, last_samples, samplereduce);
+ last_samples = samples;
+ }
+ if (samples < 1) return;
+
+ /* If requested, dump the raw data we have in the buffer */
+ if (ees->dump_vals)
+ dump_buf(coffs, 0, samples, "Raw data is:");
+
+ /* Sort the offsets, trim off the extremes, then choose one. */
+ qsort(coffs, (size_t)samples, sizeof(coffs[0]), offcompare);
+
+ noff = samples;
+ i = 0;
+ while ((noff - i) > samplereduce) {
+ /* Trim off the sample which is further away
+ * from the median. We work this out by doubling
+ * the median, subtracting off the end samples, and
+ * looking at the sign of the answer, using the
+ * identity (c-b)-(b-a) == 2*b-a-c
+ */
+ tmp = coffs[(noff + i)/2];
+ L_ADD(&tmp, &tmp);
+ L_SUB(&tmp, &coffs[i]);
+ L_SUB(&tmp, &coffs[noff-1]);
+ if (L_ISNEG(&tmp)) noff--; else i++;
+ }
+
+ /* If requested, dump the reduce data we have in the buffer */
+ if (ees->dump_vals) dump_buf(coffs, i, noff, "Reduced to:");
+
+ /* What we do next depends on the setting of the sloppy clock flag.
+ * If it is on, average the remainder to derive our estimate.
+ * Otherwise, just pick a representative value from the remaining stuff
+ */
+ if (sloppyclockflag[ees->unit]) {
+ offset.l_ui = offset.l_uf = 0;
+ for (j = i; j < noff; j++)
+ L_ADD(&offset, &coffs[j]);
+ for (j = samplelog; j > 0; j--)
+ L_RSHIFTU(&offset);
+ }
+ else offset = coffs[i+BESTSAMPLE];
+
+ /* Compute the dispersion as the difference between the
+ * lowest and highest offsets that remain in the
+ * consideration list.
+ *
+ * It looks like MOST clocks have MOD (max error), so halve it !
+ */
+ tmp = coffs[noff-1];
+ L_SUB(&tmp, &coffs[i]);
+#define FRACT_SEC(n) ((1 << 30) / (n/2))
+ dispersion = LFPTOFP(&tmp) / 2; /* ++++ */
+ if (dbg & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE)) msyslog(
+ (dbg & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] Offset=%06d (%d), disp=%f%s [%d], %d %d=%d %d:%d %d=%d %d",
+ dbg & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE),
+ offset.l_f / 4295, offset.l_f,
+ (dispersion * 1526) / 100,
+ (sloppyclockflag[ees->unit]) ? " by averaging" : "",
+ FRACT_SEC(10) / 4295,
+ (coffs[0].l_f) / 4295,
+ i,
+ (coffs[i].l_f) / 4295,
+ (coffs[samples/2].l_f) / 4295,
+ (coffs[i+BESTSAMPLE].l_f) / 4295,
+ noff-1,
+ (coffs[noff-1].l_f) / 4295,
+ (coffs[samples-1].l_f) / 4295);
+
+ /* Are we playing silly wotsits ?
+ * If we are using all data, see if there is a "small" delta,
+ * and if so, blurr this with 3/4 of the delta from the last value
+ */
+ if (ees->usealldata && ees->offset.l_uf) {
+ long diff = (long) (ees->offset.l_uf - offset.l_uf);
+
+ /* is the delta small enough ? */
+ if ((- FRACT_SEC(100)) < diff && diff < FRACT_SEC(100)) {
+ int samd = (64 * 4) / samples;
+ long new;
+ if (samd < 2) samd = 2;
+ new = offset.l_uf + ((diff * (samd -1)) / samd);
+
+ /* Sign change -> need to fix up int part */
+ if ((new & 0x80000000) !=
+ (((long) offset.l_uf) & 0x80000000))
+ { NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "I: %lx != %lx (%lx %lx), so add %d",
+ new & 0x80000000,
+ ((long) offset.l_uf) & 0x80000000,
+ new, (long) offset.l_uf,
+ (new < 0) ? -1 : 1);
+ offset.l_ui += (new < 0) ? -1 : 1;
+ }
+ dispersion /= 4;
+ if (dbg & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE)) msyslog(
+ (dbg & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] Smooth data: %ld -> %ld, dispersion now %f",
+ dbg & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE),
+ ((long) offset.l_uf) / 4295, new / 4295,
+ (dispersion * 1526) / 100);
+ offset.l_uf = (unsigned long) new;
+ }
+ else if (dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog(
+ (dbg & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
+ "[%x] No smooth as delta not %d < %ld < %d",
+ dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
+ - FRACT_SEC(100), diff, FRACT_SEC(100));
+ }
+ else if (dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog(
+ (dbg & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] No smooth as flag=%x and old=%x=%d (%d:%d)",
+ dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
+ ees->usealldata, ees->offset.l_f, ees->offset.l_uf,
+ offset.l_f, ees->offset.l_f - offset.l_f);
+
+ /* Collect offset info for debugging info */
+ ees->offset = offset;
+ ees->lowoffset = coffs[i];
+ ees->highoffset = coffs[noff-1];
+
+ /* Determine synchronization status. Can be unsync'd either
+ * by a report from the clock or by a leap hold.
+ *
+ * Loss of the radio signal for a short time does not cause
+ * us to go unsynchronised, since the receiver keeps quite
+ * good time on its own. The spec says 20ms in 4 hours; the
+ * observed drift in our clock (Cambridge) is about a second
+ * a day, but even that keeps us within the inherent tolerance
+ * of the clock for about 15 minutes. Observation shows that
+ * the typical "short" outage is 3 minutes, so to allow us
+ * to ride out those, we will give it 5 minutes.
+ */
+ lostsync = current_time - ees->clocklastgood > 300 ? 1 : 0;
+ isinsync = (lostsync || ees->leaphold > current_time) ? 0 : 1;
+
+ /* Done. Use time of last good, synchronised code as the
+ * reference time, and lastsampletime as the receive time.
+ */
+ if (ees->fix_pending) {
+ msyslog(LOG_ERR, "MSF%d: fix_pending=%d -> jump %x.%08x",
+ ees->fix_pending, ees->unit, offset.l_i, offset.l_f);
+ ees->fix_pending = 0;
+ }
+ LFPTOD(&offset, doffset);
+ refclock_receive(ees->peer);
+ ees_event(ees, lostsync ? CEVNT_PROP : CEVNT_NOMINAL);
+}
+
+/* msfees_poll - called by the transmit procedure */
+static void
+msfees_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d invalid",
+ unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d unused",
+ unit);
+ return;
+ }
+
+ ees_process(eesunits[unit]);
+
+ if ((current_time - eesunits[unit]->lasttime) > 150)
+ ees_event(eesunits[unit], CEVNT_FAULT);
+}
+
+
+#else
+NONEMPTY_TRANSLATION_UNIT
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_mx4200.c b/ntpd/refclock_mx4200.c
new file mode 100644
index 0000000..c942229
--- /dev/null
+++ b/ntpd/refclock_mx4200.c
@@ -0,0 +1,1631 @@
+/*
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66.
+ *
+ * Copyright (c) 1992 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory.
+ * 4. The name of the University may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Modified: Marc Brett <marc.brett@westgeo.com> Sept, 1999.
+ *
+ * 1. Added support for alternate PPS schemes, with code mostly
+ * copied from the Oncore driver (Thanks, Poul-Henning Kamp).
+ * This code runs on SunOS 4.1.3 with ppsclock-1.6a1 and Solaris 7.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_MX4200) && defined(HAVE_PPSAPI)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "mx4200.h"
+
+#ifdef HAVE_SYS_TERMIOS_H
+# include <sys/termios.h>
+#endif
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+
+#ifndef HAVE_STRUCT_PPSCLOCKEV
+struct ppsclockev {
+# ifdef HAVE_STRUCT_TIMESPEC
+ struct timespec tv;
+# else
+ struct timeval tv;
+# endif
+ u_int serial;
+};
+#endif /* ! HAVE_STRUCT_PPSCLOCKEV */
+
+#ifdef HAVE_PPSAPI
+# include "ppsapi_timepps.h"
+#endif /* HAVE_PPSAPI */
+
+/*
+ * This driver supports the Magnavox Model MX 4200 GPS Receiver
+ * adapted to precision timing applications. It requires the
+ * ppsclock line discipline or streams module described in the
+ * Line Disciplines and Streams Drivers page. It also requires a
+ * gadget box and 1-PPS level converter, such as described in the
+ * Pulse-per-second (PPS) Signal Interfacing page.
+ *
+ * It's likely that other compatible Magnavox receivers such as the
+ * MX 4200D, MX 9212, MX 9012R, MX 9112 will be supported by this code.
+ */
+
+/*
+ * Check this every time you edit the code!
+ */
+#define YEAR_LAST_MODIFIED 2000
+
+/*
+ * GPS Definitions
+ */
+#define DEVICE "/dev/gps%d" /* device name and unit */
+#define SPEED232 B4800 /* baud */
+
+/*
+ * Radio interface parameters
+ */
+#define PRECISION (-18) /* precision assumed (about 4 us) */
+#define REFID "GPS\0" /* reference id */
+#define DESCRIPTION "Magnavox MX4200 GPS Receiver" /* who we are */
+#define DEFFUDGETIME 0 /* default fudge time (ms) */
+
+#define SLEEPTIME 32 /* seconds to wait for reconfig to complete */
+
+/*
+ * Position Averaging.
+ */
+#define INTERVAL 1 /* Interval between position measurements (s) */
+#define AVGING_TIME 24 /* Number of hours to average */
+#define NOT_INITIALIZED -9999. /* initial pivot longitude */
+
+/*
+ * MX4200 unit control structure.
+ */
+struct mx4200unit {
+ u_int pollcnt; /* poll message counter */
+ u_int polled; /* Hand in a time sample? */
+ u_int lastserial; /* last pps serial number */
+ struct ppsclockev ppsev; /* PPS control structure */
+ double avg_lat; /* average latitude */
+ double avg_lon; /* average longitude */
+ double avg_alt; /* average height */
+ double central_meridian; /* central meridian */
+ double N_fixes; /* Number of position measurements */
+ int last_leap; /* leap second warning */
+ u_int moving; /* mobile platform? */
+ u_long sloppyclockflag; /* fudge flags */
+ u_int known; /* position known yet? */
+ u_long clamp_time; /* when to stop postion averaging */
+ u_long log_time; /* when to print receiver status */
+ pps_handle_t pps_h;
+ pps_params_t pps_p;
+ pps_info_t pps_i;
+};
+
+static char pmvxg[] = "PMVXG";
+
+/* XXX should be somewhere else */
+#ifdef __GNUC__
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
+#ifndef __attribute__
+#define __attribute__(args)
+#endif /* __attribute__ */
+#endif /* __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) */
+#else
+#ifndef __attribute__
+#define __attribute__(args)
+#endif /* __attribute__ */
+#endif /* __GNUC__ */
+/* XXX end */
+
+/*
+ * Function prototypes
+ */
+static int mx4200_start (int, struct peer *);
+static void mx4200_shutdown (int, struct peer *);
+static void mx4200_receive (struct recvbuf *);
+static void mx4200_poll (int, struct peer *);
+
+static char * mx4200_parse_t (struct peer *);
+static char * mx4200_parse_p (struct peer *);
+static char * mx4200_parse_s (struct peer *);
+int mx4200_cmpl_fp (const void *, const void *);
+static int mx4200_config (struct peer *);
+static void mx4200_ref (struct peer *);
+static void mx4200_send (struct peer *, char *, ...)
+ __attribute__ ((format (printf, 2, 3)));
+static u_char mx4200_cksum (char *, int);
+static int mx4200_jday (int, int, int);
+static void mx4200_debug (struct peer *, char *, ...)
+ __attribute__ ((format (printf, 2, 3)));
+static int mx4200_pps (struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_mx4200 = {
+ mx4200_start, /* start up driver */
+ mx4200_shutdown, /* shut down driver */
+ mx4200_poll, /* transmit poll message */
+ noentry, /* not used (old mx4200_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old mx4200_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+
+/*
+ * mx4200_start - open the devices and initialize data for processing
+ */
+static int
+mx4200_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char gpsdev[20];
+
+ /*
+ * Open serial port
+ */
+ snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit);
+ fd = refclock_open(gpsdev, SPEED232, LDISC_PPS);
+ if (fd <= 0)
+ return 0;
+
+ /*
+ * Allocate unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->io.clock_recv = mx4200_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ return (0);
+ }
+ pp->unitptr = up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+ /* Ensure the receiver is properly configured */
+ return mx4200_config(peer);
+}
+
+
+/*
+ * mx4200_shutdown - shut down the clock
+ */
+static void
+mx4200_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ if (NULL != up)
+ free(up);
+}
+
+
+/*
+ * mx4200_config - Configure the receiver
+ */
+static int
+mx4200_config(
+ struct peer *peer
+ )
+{
+ char tr_mode;
+ int add_mode;
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+ int mode;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Initialize the unit variables
+ *
+ * STRANGE BEHAVIOUR WARNING: The fudge flags are not available
+ * at the time mx4200_start is called. These are set later,
+ * and so the code must be prepared to handle changing flags.
+ */
+ up->sloppyclockflag = pp->sloppyclockflag;
+ if (pp->sloppyclockflag & CLK_FLAG2) {
+ up->moving = 1; /* Receiver on mobile platform */
+ msyslog(LOG_DEBUG, "mx4200_config: mobile platform");
+ } else {
+ up->moving = 0; /* Static Installation */
+ }
+ up->pollcnt = 2;
+ up->polled = 0;
+ up->known = 0;
+ up->avg_lat = 0.0;
+ up->avg_lon = 0.0;
+ up->avg_alt = 0.0;
+ up->central_meridian = NOT_INITIALIZED;
+ up->N_fixes = 0.0;
+ up->last_leap = 0; /* LEAP_NOWARNING */
+ up->clamp_time = current_time + (AVGING_TIME * 60 * 60);
+ up->log_time = current_time + SLEEPTIME;
+
+ if (time_pps_create(pp->io.fd, &up->pps_h) < 0) {
+ perror("time_pps_create");
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_create failed: %m");
+ return (0);
+ }
+ if (time_pps_getcap(up->pps_h, &mode) < 0) {
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_getcap failed: %m");
+ return (0);
+ }
+
+ if (time_pps_getparams(up->pps_h, &up->pps_p) < 0) {
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_getparams failed: %m");
+ return (0);
+ }
+
+ /* nb. only turn things on, if someone else has turned something
+ * on before we get here, leave it alone!
+ */
+
+ up->pps_p.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
+ up->pps_p.mode &= mode; /* only set what is legal */
+
+ if (time_pps_setparams(up->pps_h, &up->pps_p) < 0) {
+ perror("time_pps_setparams");
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_setparams failed: %m");
+ exit(1);
+ }
+
+ if (time_pps_kcbind(up->pps_h, PPS_KC_HARDPPS, PPS_CAPTUREASSERT,
+ PPS_TSFMT_TSPEC) < 0) {
+ perror("time_pps_kcbind");
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_kcbind failed: %m");
+ exit(1);
+ }
+
+
+ /*
+ * "007" Control Port Configuration
+ * Zero the output list (do it twice to flush possible junk)
+ */
+ mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg,
+ PMVXG_S_PORTCONF,
+ /* control port output block Label */
+ 1); /* clear current output control list (1=yes) */
+ /* add/delete sentences from list */
+ /* must be null */
+ /* sentence output rate (sec) */
+ /* precision for position output */
+ /* nmea version for cga & gll output */
+ /* pass-through control */
+ mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg,
+ PMVXG_S_PORTCONF, 1);
+
+ /*
+ * Request software configuration so we can syslog the firmware version
+ */
+ mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_SOFTCONF);
+
+ /*
+ * "001" Initialization/Mode Control, Part A
+ * Where ARE we?
+ */
+ mx4200_send(peer, "%s,%03d,,,,,,,,,,", pmvxg,
+ PMVXG_S_INITMODEA);
+ /* day of month */
+ /* month of year */
+ /* year */
+ /* gmt */
+ /* latitude DDMM.MMMM */
+ /* north/south */
+ /* longitude DDDMM.MMMM */
+ /* east/west */
+ /* height */
+ /* Altitude Reference 1=MSL */
+
+ /*
+ * "001" Initialization/Mode Control, Part B
+ * Start off in 2d/3d coast mode, holding altitude to last known
+ * value if only 3 satellites available.
+ */
+ mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d",
+ pmvxg, PMVXG_S_INITMODEB,
+ 3, /* 2d/3d coast */
+ /* reserved */
+ 0.1, /* hor accel fact as per Steve (m/s**2) */
+ 0.1, /* ver accel fact as per Steve (m/s**2) */
+ 10, /* vdop */
+ 10, /* hdop limit as per Steve */
+ 5, /* elevation limit as per Steve (deg) */
+ 'U', /* time output mode (UTC) */
+ 0); /* local time offset from gmt (HHHMM) */
+
+ /*
+ * "023" Time Recovery Configuration
+ * Get UTC time from a stationary receiver.
+ * (Set field 1 'D' == dynamic if we are on a moving platform).
+ * (Set field 1 'S' == static if we are not moving).
+ * (Set field 1 'K' == known position if we can initialize lat/lon/alt).
+ */
+
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ up->moving = 1; /* Receiver on mobile platform */
+ else
+ up->moving = 0; /* Static Installation */
+
+ up->pollcnt = 2;
+ if (up->moving) {
+ /* dynamic: solve for pos, alt, time, while moving */
+ tr_mode = 'D';
+ } else {
+ /* static: solve for pos, alt, time, while stationary */
+ tr_mode = 'S';
+ }
+ mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg,
+ PMVXG_S_TRECOVCONF,
+ tr_mode, /* time recovery mode (see above ) */
+ 'U', /* synchronize to UTC */
+ 'A', /* always output a time pulse */
+ 500, /* max time error in ns */
+ 0, /* user bias in ns */
+ 1); /* output "830" sentences to control port */
+ /* Multi-satellite mode */
+
+ /*
+ * Output position information (to calculate fixed installation
+ * location) only if we are not moving
+ */
+ if (up->moving) {
+ add_mode = 2; /* delete from list */
+ } else {
+ add_mode = 1; /* add to list */
+ }
+
+
+ /*
+ * "007" Control Port Configuration
+ * Output "021" position, height, velocity reports
+ */
+ mx4200_send(peer, "%s,%03d,%03d,%d,%d,,%d,,,", pmvxg,
+ PMVXG_S_PORTCONF,
+ PMVXG_D_PHV, /* control port output block Label */
+ 0, /* clear current output control list (0=no) */
+ add_mode, /* add/delete sentences from list (1=add, 2=del) */
+ /* must be null */
+ INTERVAL); /* sentence output rate (sec) */
+ /* precision for position output */
+ /* nmea version for cga & gll output */
+ /* pass-through control */
+
+ return (1);
+}
+
+/*
+ * mx4200_ref - Reconfigure unit as a reference station at a known position.
+ */
+static void
+mx4200_ref(
+ struct peer *peer
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+ double minute, lat, lon, alt;
+ char lats[16], lons[16];
+ char nsc, ewc;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /* Should never happen! */
+ if (up->moving) return;
+
+ /*
+ * Set up to output status information in the near future
+ */
+ up->log_time = current_time + SLEEPTIME;
+
+ /*
+ * "007" Control Port Configuration
+ * Stop outputting "021" position, height, velocity reports
+ */
+ mx4200_send(peer, "%s,%03d,%03d,%d,%d,,,,,", pmvxg,
+ PMVXG_S_PORTCONF,
+ PMVXG_D_PHV, /* control port output block Label */
+ 0, /* clear current output control list (0=no) */
+ 2); /* add/delete sentences from list (2=delete) */
+ /* must be null */
+ /* sentence output rate (sec) */
+ /* precision for position output */
+ /* nmea version for cga & gll output */
+ /* pass-through control */
+
+ /*
+ * "001" Initialization/Mode Control, Part B
+ * Put receiver in fully-constrained 2d nav mode
+ */
+ mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d",
+ pmvxg, PMVXG_S_INITMODEB,
+ 2, /* 2d nav */
+ /* reserved */
+ 0.1, /* hor accel fact as per Steve (m/s**2) */
+ 0.1, /* ver accel fact as per Steve (m/s**2) */
+ 10, /* vdop */
+ 10, /* hdop limit as per Steve */
+ 5, /* elevation limit as per Steve (deg) */
+ 'U', /* time output mode (UTC) */
+ 0); /* local time offset from gmt (HHHMM) */
+
+ /*
+ * "023" Time Recovery Configuration
+ * Get UTC time from a stationary receiver. Solve for time only.
+ * This should improve the time resolution dramatically.
+ */
+ mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg,
+ PMVXG_S_TRECOVCONF,
+ 'K', /* known position: solve for time only */
+ 'U', /* synchronize to UTC */
+ 'A', /* always output a time pulse */
+ 500, /* max time error in ns */
+ 0, /* user bias in ns */
+ 1); /* output "830" sentences to control port */
+ /* Multi-satellite mode */
+
+ /*
+ * "000" Initialization/Mode Control - Part A
+ * Fix to our averaged position.
+ */
+ if (up->central_meridian != NOT_INITIALIZED) {
+ up->avg_lon += up->central_meridian;
+ if (up->avg_lon < -180.0) up->avg_lon += 360.0;
+ if (up->avg_lon > 180.0) up->avg_lon -= 360.0;
+ }
+
+ if (up->avg_lat >= 0.0) {
+ lat = up->avg_lat;
+ nsc = 'N';
+ } else {
+ lat = up->avg_lat * (-1.0);
+ nsc = 'S';
+ }
+ if (up->avg_lon >= 0.0) {
+ lon = up->avg_lon;
+ ewc = 'E';
+ } else {
+ lon = up->avg_lon * (-1.0);
+ ewc = 'W';
+ }
+ alt = up->avg_alt;
+ minute = (lat - (double)(int)lat) * 60.0;
+ snprintf(lats, sizeof(lats), "%02d%02.4f", (int)lat, minute);
+ minute = (lon - (double)(int)lon) * 60.0;
+ snprintf(lons, sizeof(lons), "%03d%02.4f", (int)lon, minute);
+
+ mx4200_send(peer, "%s,%03d,,,,,%s,%c,%s,%c,%.2f,%d", pmvxg,
+ PMVXG_S_INITMODEA,
+ /* day of month */
+ /* month of year */
+ /* year */
+ /* gmt */
+ lats, /* latitude DDMM.MMMM */
+ nsc, /* north/south */
+ lons, /* longitude DDDMM.MMMM */
+ ewc, /* east/west */
+ alt, /* Altitude */
+ 1); /* Altitude Reference (0=WGS84 ellipsoid, 1=MSL geoid)*/
+
+ msyslog(LOG_DEBUG,
+ "mx4200: reconfig to fixed location: %s %c, %s %c, %.2f m",
+ lats, nsc, lons, ewc, alt );
+
+}
+
+/*
+ * mx4200_poll - mx4200 watchdog routine
+ */
+static void
+mx4200_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+
+ /*
+ * If we haven't had a response in a while, reset the receiver.
+ */
+ if (up->pollcnt > 0) {
+ up->pollcnt--;
+ } else {
+ refclock_report(peer, CEVNT_TIMEOUT);
+
+ /*
+ * Request a "000" status message which should trigger a
+ * reconfig
+ */
+ mx4200_send(peer, "%s,%03d",
+ "CDGPQ", /* query from CDU to GPS */
+ PMVXG_D_STATUS); /* label of desired sentence */
+ }
+
+ /*
+ * polled every 64 seconds. Ask mx4200_receive to hand in
+ * a timestamp.
+ */
+ up->polled = 1;
+ pp->polls++;
+
+ /*
+ * Output receiver status information.
+ */
+ if ((up->log_time > 0) && (current_time > up->log_time)) {
+ up->log_time = 0;
+ /*
+ * Output the following messages once, for debugging.
+ * "004" Mode Data
+ * "523" Time Recovery Parameters
+ */
+ mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_MODEDATA);
+ mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_TRECOVUSEAGE);
+ }
+}
+
+static char char2hex[] = "0123456789ABCDEF";
+
+/*
+ * mx4200_receive - receive gps data
+ */
+static void
+mx4200_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char *cp;
+ int sentence_type;
+ u_char ck;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * If operating mode has been changed, then reinitialize the receiver
+ * before doing anything else.
+ */
+ if ((pp->sloppyclockflag & CLK_FLAG2) !=
+ (up->sloppyclockflag & CLK_FLAG2)) {
+ up->sloppyclockflag = pp->sloppyclockflag;
+ mx4200_debug(peer,
+ "mx4200_receive: mode switch: reset receiver\n");
+ mx4200_config(peer);
+ return;
+ }
+ up->sloppyclockflag = pp->sloppyclockflag;
+
+ /*
+ * Read clock output. Automatically handles STREAMS, CLKLDISC.
+ */
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
+
+ /*
+ * There is a case where <cr><lf> generates 2 timestamps.
+ */
+ if (pp->lencode == 0)
+ return;
+
+ up->pollcnt = 2;
+ pp->a_lastcode[pp->lencode] = '\0';
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ mx4200_debug(peer, "mx4200_receive: %d %s\n",
+ pp->lencode, pp->a_lastcode);
+
+ /*
+ * The structure of the control port sentences is based on the
+ * NMEA-0183 Standard for interfacing Marine Electronics
+ * Navigation Devices (Version 1.5)
+ *
+ * $PMVXG,XXX, ....................*CK<cr><lf>
+ *
+ * $ Sentence Start Identifier (reserved char)
+ * (Start-of-Sentence Identifier)
+ * P Special ID (Proprietary)
+ * MVX Originator ID (Magnavox)
+ * G Interface ID (GPS)
+ * , Field Delimiters (reserved char)
+ * XXX Sentence Type
+ * ...... Data
+ * * Checksum Field Delimiter (reserved char)
+ * CK Checksum
+ * <cr><lf> Carriage-Return/Line Feed (reserved chars)
+ * (End-of-Sentence Identifier)
+ *
+ * Reject if any important landmarks are missing.
+ */
+ cp = pp->a_lastcode + pp->lencode - 3;
+ if (cp < pp->a_lastcode || *pp->a_lastcode != '$' || cp[0] != '*' ) {
+ mx4200_debug(peer, "mx4200_receive: bad format\n");
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Check and discard the checksum
+ */
+ ck = mx4200_cksum(&pp->a_lastcode[1], pp->lencode - 4);
+ if (char2hex[ck >> 4] != cp[1] || char2hex[ck & 0xf] != cp[2]) {
+ mx4200_debug(peer, "mx4200_receive: bad checksum\n");
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ *cp = '\0';
+
+ /*
+ * Get the sentence type.
+ */
+ sentence_type = 0;
+ if ((cp = strchr(pp->a_lastcode, ',')) == NULL) {
+ mx4200_debug(peer, "mx4200_receive: no sentence\n");
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ cp++;
+ sentence_type = strtol(cp, &cp, 10);
+
+ /*
+ * Process the sentence according to its type.
+ */
+ switch (sentence_type) {
+
+ /*
+ * "000" Status message
+ */
+ case PMVXG_D_STATUS:
+ /*
+ * XXX
+ * Since we configure the receiver to not give us status
+ * messages and since the receiver outputs status messages by
+ * default after being reset to factory defaults when sent the
+ * "$PMVXG,018,C\r\n" message, any status message we get
+ * indicates the reciever needs to be initialized; thus, it is
+ * not necessary to decode the status message.
+ */
+ if ((cp = mx4200_parse_s(peer)) != NULL) {
+ mx4200_debug(peer,
+ "mx4200_receive: status: %s\n", cp);
+ }
+ mx4200_debug(peer, "mx4200_receive: reset receiver\n");
+ mx4200_config(peer);
+ break;
+
+ /*
+ * "021" Position, Height, Velocity message,
+ * if we are still averaging our position
+ */
+ case PMVXG_D_PHV:
+ if (!up->known) {
+ /*
+ * Parse the message, calculating our averaged position.
+ */
+ if ((cp = mx4200_parse_p(peer)) != NULL) {
+ mx4200_debug(peer, "mx4200_receive: pos: %s\n", cp);
+ return;
+ }
+ mx4200_debug(peer,
+ "mx4200_receive: position avg %f %.9f %.9f %.4f\n",
+ up->N_fixes, up->avg_lat, up->avg_lon, up->avg_alt);
+ /*
+ * Reinitialize as a reference station
+ * if position is well known.
+ */
+ if (current_time > up->clamp_time) {
+ up->known++;
+ mx4200_debug(peer, "mx4200_receive: reconfiguring!\n");
+ mx4200_ref(peer);
+ }
+ }
+ break;
+
+ /*
+ * Print to the syslog:
+ * "004" Mode Data
+ * "030" Software Configuration
+ * "523" Time Recovery Parameters Currently in Use
+ */
+ case PMVXG_D_MODEDATA:
+ case PMVXG_D_SOFTCONF:
+ case PMVXG_D_TRECOVUSEAGE:
+
+ if ((cp = mx4200_parse_s(peer)) != NULL) {
+ mx4200_debug(peer,
+ "mx4200_receive: multi-record: %s\n", cp);
+ }
+ break;
+
+ /*
+ * "830" Time Recovery Results message
+ */
+ case PMVXG_D_TRECOVOUT:
+
+ /*
+ * Capture the last PPS signal.
+ * Precision timestamp is returned in pp->lastrec
+ */
+ if (0 != mx4200_pps(peer)) {
+ mx4200_debug(peer, "mx4200_receive: pps failure\n");
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+
+
+ /*
+ * Parse the time recovery message, and keep the info
+ * to print the pretty billboards.
+ */
+ if ((cp = mx4200_parse_t(peer)) != NULL) {
+ mx4200_debug(peer, "mx4200_receive: time: %s\n", cp);
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Add the new sample to a median filter.
+ */
+ if (!refclock_process(pp)) {
+ mx4200_debug(peer,"mx4200_receive: offset: %.6f\n",
+ pp->offset);
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * The clock will blurt a timecode every second but we only
+ * want one when polled. If we havn't been polled, bail out.
+ */
+ if (!up->polled)
+ return;
+
+ /*
+ * Return offset and dispersion to control module. We use
+ * lastrec 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.
+ */
+ mx4200_debug(peer, "mx4200_receive: process time: ");
+ mx4200_debug(peer, "%4d-%03d %02d:%02d:%02d at %s, %.6f\n",
+ pp->year, pp->day, pp->hour, pp->minute, pp->second,
+ prettydate(&pp->lastrec), pp->offset);
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+ /*
+ * We have succeeded in answering the poll.
+ * Turn off the flag and return
+ */
+ up->polled = 0;
+ break;
+
+ /*
+ * Ignore all other sentence types
+ */
+ default:
+ break;
+
+ } /* switch (sentence_type) */
+
+ return;
+}
+
+
+/*
+ * Parse a mx4200 time recovery message. Returns a string if error.
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,830,T,YYYY,MM,DD,HH:MM:SS,U,S,FFFFFF,PPPPP,BBBBBB,LL
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 830=Time Recovery Results
+ * This sentence is output approximately 1 second
+ * preceding the 1PPS output. It indicates the
+ * exact time of the next pulse, whether or not the
+ * time mark will be valid (based on operator-specified
+ * error tolerance), the time to which the pulse is
+ * synchronized, the receiver operating mode,
+ * and the time error of the *last* 1PPS output.
+ * 1 char Time Mark Valid: T=Valid, F=Not Valid
+ * 2 int Year: 1993-
+ * 3 int Month of Year: 1-12
+ * 4 int Day of Month: 1-31
+ * 5 int Time of Day: HH:MM:SS
+ * 6 char Time Synchronization: U=UTC, G=GPS
+ * 7 char Time Recovery Mode: D=Dynamic, S=Static,
+ * K=Known Position, N=No Time Recovery
+ * 8 int Oscillator Offset: The filter's estimate of the oscillator
+ * frequency error, in parts per billion (ppb).
+ * 9 int Time Mark Error: The computed error of the *last* pulse
+ * output, in nanoseconds.
+ * 10 int User Time Bias: Operator specified bias, in nanoseconds
+ * 11 int Leap Second Flag: Indicates that a leap second will
+ * occur. This value is usually zero, except during
+ * the week prior to the leap second occurrence, when
+ * this value will be set to +1 or -1. A value of
+ * +1 indicates that GPS time will be 1 second
+ * further ahead of UTC time.
+ *
+ */
+static char *
+mx4200_parse_t(
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+ char time_mark_valid, time_sync, op_mode;
+ int sentence_type, valid;
+ int year, day_of_year, month, day_of_month;
+ int hour, minute, second, leapsec_warn;
+ int oscillator_offset, time_mark_error, time_bias;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ leapsec_warn = 0; /* Not all receivers output leap second warnings (!) */
+ sscanf(pp->a_lastcode,
+ "$PMVXG,%d,%c,%d,%d,%d,%d:%d:%d,%c,%c,%d,%d,%d,%d",
+ &sentence_type, &time_mark_valid, &year, &month, &day_of_month,
+ &hour, &minute, &second, &time_sync, &op_mode,
+ &oscillator_offset, &time_mark_error, &time_bias, &leapsec_warn);
+
+ if (sentence_type != PMVXG_D_TRECOVOUT)
+ return ("wrong rec-type");
+
+ switch (time_mark_valid) {
+ case 'T':
+ valid = 1;
+ break;
+ case 'F':
+ valid = 0;
+ break;
+ default:
+ return ("bad pulse-valid");
+ }
+
+ switch (time_sync) {
+ case 'G':
+ return ("synchronized to GPS; should be UTC");
+ case 'U':
+ break; /* UTC -> ok */
+ default:
+ return ("not synchronized to UTC");
+ }
+
+ /*
+ * Check for insane time (allow for possible leap seconds)
+ */
+ if (second > 60 || minute > 59 || hour > 23 ||
+ second < 0 || minute < 0 || hour < 0) {
+ mx4200_debug(peer,
+ "mx4200_parse_t: bad time %02d:%02d:%02d",
+ hour, minute, second);
+ if (leapsec_warn != 0)
+ mx4200_debug(peer, " (leap %+d\n)", leapsec_warn);
+ mx4200_debug(peer, "\n");
+ refclock_report(peer, CEVNT_BADTIME);
+ return ("bad time");
+ }
+ if ( second == 60 ) {
+ msyslog(LOG_DEBUG,
+ "mx4200: leap second! %02d:%02d:%02d",
+ hour, minute, second);
+ }
+
+ /*
+ * Check for insane date
+ * (Certainly can't be any year before this code was last altered!)
+ */
+ if (day_of_month > 31 || month > 12 ||
+ day_of_month < 1 || month < 1 || year < YEAR_LAST_MODIFIED) {
+ mx4200_debug(peer,
+ "mx4200_parse_t: bad date (%4d-%02d-%02d)\n",
+ year, month, day_of_month);
+ refclock_report(peer, CEVNT_BADDATE);
+ return ("bad date");
+ }
+
+ /*
+ * Silly Hack for MX4200:
+ * ASCII message is for *next* 1PPS signal, but we have the
+ * timestamp for the *last* 1PPS signal. So we have to subtract
+ * a second. Discard if we are on a month boundary to avoid
+ * possible leap seconds and leap days.
+ */
+ second--;
+ if (second < 0) {
+ second = 59;
+ minute--;
+ if (minute < 0) {
+ minute = 59;
+ hour--;
+ if (hour < 0) {
+ hour = 23;
+ day_of_month--;
+ if (day_of_month < 1) {
+ return ("sorry, month boundary");
+ }
+ }
+ }
+ }
+
+ /*
+ * Calculate Julian date
+ */
+ if (!(day_of_year = mx4200_jday(year, month, day_of_month))) {
+ mx4200_debug(peer,
+ "mx4200_parse_t: bad julian date %d (%4d-%02d-%02d)\n",
+ day_of_year, year, month, day_of_month);
+ refclock_report(peer, CEVNT_BADDATE);
+ return("invalid julian date");
+ }
+
+ /*
+ * Setup leap second indicator
+ */
+ switch (leapsec_warn) {
+ case 0:
+ pp->leap = LEAP_NOWARNING;
+ break;
+ case 1:
+ pp->leap = LEAP_ADDSECOND;
+ break;
+ case -1:
+ pp->leap = LEAP_DELSECOND;
+ break;
+ default:
+ pp->leap = LEAP_NOTINSYNC;
+ }
+
+ /*
+ * Any change to the leap second warning status?
+ */
+ if (leapsec_warn != up->last_leap ) {
+ msyslog(LOG_DEBUG,
+ "mx4200: leap second warning: %d to %d (%d)",
+ up->last_leap, leapsec_warn, pp->leap);
+ }
+ up->last_leap = leapsec_warn;
+
+ /*
+ * Copy time data for billboard monitoring.
+ */
+
+ pp->year = year;
+ pp->day = day_of_year;
+ pp->hour = hour;
+ pp->minute = minute;
+ pp->second = second;
+
+ /*
+ * Toss if sentence is marked invalid
+ */
+ if (!valid || pp->leap == LEAP_NOTINSYNC) {
+ mx4200_debug(peer, "mx4200_parse_t: time mark not valid\n");
+ refclock_report(peer, CEVNT_BADTIME);
+ return ("pulse invalid");
+ }
+
+ return (NULL);
+}
+
+/*
+ * Calculate the checksum
+ */
+static u_char
+mx4200_cksum(
+ register char *cp,
+ register int n
+ )
+{
+ register u_char ck;
+
+ for (ck = 0; n-- > 0; cp++)
+ ck ^= *cp;
+ return (ck);
+}
+
+/*
+ * Tables to compute the day of year. Viva la leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Calculate the the Julian Day
+ */
+static int
+mx4200_jday(
+ int year,
+ int month,
+ int day_of_month
+ )
+{
+ register int day, i;
+ int leap_year;
+
+ /*
+ * Is this a leap year ?
+ */
+ if (year % 4) {
+ leap_year = 0; /* FALSE */
+ } else {
+ if (year % 100) {
+ leap_year = 1; /* TRUE */
+ } else {
+ if (year % 400) {
+ leap_year = 0; /* FALSE */
+ } else {
+ leap_year = 1; /* TRUE */
+ }
+ }
+ }
+
+ /*
+ * Calculate the Julian Date
+ */
+ day = day_of_month;
+
+ if (leap_year) {
+ /* a leap year */
+ if (day > day2tab[month - 1]) {
+ return (0);
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ } else {
+ /* not a leap year */
+ if (day > day1tab[month - 1]) {
+ return (0);
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ }
+ return (day);
+}
+
+/*
+ * Parse a mx4200 position/height/velocity sentence.
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,021,SSSSSS.SS,DDMM.MMMM,N,DDDMM.MMMM,E,HHHHH.H,GGGG.G,EEEE.E,WWWW.W,MM
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 021=Position, Height Velocity Data
+ * This sentence gives the receiver position, height,
+ * navigation mode, and velocity north/east.
+ * *This sentence is intended for post-analysis
+ * applications.*
+ * 1 float UTC measurement time (seconds into week)
+ * 2 float WGS-84 Lattitude (degrees, minutes)
+ * 3 char N=North, S=South
+ * 4 float WGS-84 Longitude (degrees, minutes)
+ * 5 char E=East, W=West
+ * 6 float Altitude (meters above mean sea level)
+ * 7 float Geoidal height (meters)
+ * 8 float East velocity (m/sec)
+ * 9 float West Velocity (m/sec)
+ * 10 int Navigation Mode
+ * Mode if navigating:
+ * 1 = Position from remote device
+ * 2 = 2-D position
+ * 3 = 3-D position
+ * 4 = 2-D differential position
+ * 5 = 3-D differential position
+ * 6 = Static
+ * 8 = Position known -- reference station
+ * 9 = Position known -- Navigator
+ * Mode if not navigating:
+ * 51 = Too few satellites
+ * 52 = DOPs too large
+ * 53 = Position STD too large
+ * 54 = Velocity STD too large
+ * 55 = Too many iterations for velocity
+ * 56 = Too many iterations for position
+ * 57 = 3 sat startup failed
+ * 58 = Command abort
+ */
+static char *
+mx4200_parse_p(
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+ int sentence_type, mode;
+ double mtime, lat, lon, alt, geoid, vele, veln;
+ char north_south, east_west;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /* Should never happen! */
+ if (up->moving) return ("mobile platform - no pos!");
+
+ sscanf ( pp->a_lastcode,
+ "$PMVXG,%d,%lf,%lf,%c,%lf,%c,%lf,%lf,%lf,%lf,%d",
+ &sentence_type, &mtime, &lat, &north_south, &lon, &east_west,
+ &alt, &geoid, &vele, &veln, &mode);
+
+ /* Sentence type */
+ if (sentence_type != PMVXG_D_PHV)
+ return ("wrong rec-type");
+
+ /*
+ * return if not navigating
+ */
+ if (mode > 10)
+ return ("not navigating");
+ if (mode != 3 && mode != 5)
+ return ("not navigating in 3D");
+
+ /* Latitude (always +ve) and convert DDMM.MMMM to decimal */
+ if (lat < 0.0) return ("negative latitude");
+ if (lat > 9000.0) lat = 9000.0;
+ lat *= 0.01;
+ lat = ((int)lat) + (((lat - (int)lat)) * 1.6666666666666666);
+
+ /* North/South */
+ switch (north_south) {
+ case 'N':
+ break;
+ case 'S':
+ lat *= -1.0;
+ break;
+ default:
+ return ("invalid north/south indicator");
+ }
+
+ /* Longitude (always +ve) and convert DDDMM.MMMM to decimal */
+ if (lon < 0.0) return ("negative longitude");
+ if (lon > 180.0) lon = 180.0;
+ lon *= 0.01;
+ lon = ((int)lon) + (((lon - (int)lon)) * 1.6666666666666666);
+
+ /* East/West */
+ switch (east_west) {
+ case 'E':
+ break;
+ case 'W':
+ lon *= -1.0;
+ break;
+ default:
+ return ("invalid east/west indicator");
+ }
+
+ /*
+ * Normalize longitude to near 0 degrees.
+ * Assume all data are clustered around first reading.
+ */
+ if (up->central_meridian == NOT_INITIALIZED) {
+ up->central_meridian = lon;
+ mx4200_debug(peer,
+ "mx4200_receive: central meridian = %.9f \n",
+ up->central_meridian);
+ }
+ lon -= up->central_meridian;
+ if (lon < -180.0) lon += 360.0;
+ if (lon > 180.0) lon -= 360.0;
+
+ /*
+ * Calculate running averages
+ */
+
+ up->avg_lon = (up->N_fixes * up->avg_lon) + lon;
+ up->avg_lat = (up->N_fixes * up->avg_lat) + lat;
+ up->avg_alt = (up->N_fixes * up->avg_alt) + alt;
+
+ up->N_fixes += 1.0;
+
+ up->avg_lon /= up->N_fixes;
+ up->avg_lat /= up->N_fixes;
+ up->avg_alt /= up->N_fixes;
+
+ mx4200_debug(peer,
+ "mx4200_receive: position rdg %.0f: %.9f %.9f %.4f (CM=%.9f)\n",
+ up->N_fixes, lat, lon, alt, up->central_meridian);
+
+ return (NULL);
+}
+
+/*
+ * Parse a mx4200 Status sentence
+ * Parse a mx4200 Mode Data sentence
+ * Parse a mx4200 Software Configuration sentence
+ * Parse a mx4200 Time Recovery Parameters Currently in Use sentence
+ * (used only for logging raw strings)
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,000,XXX,XX,X,HHMM,X
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 000=Status.
+ * Returns status of the receiver to the controller.
+ * 1 Current Receiver Status:
+ * ACQ = Satellite re-acquisition
+ * ALT = Constellation selection
+ * COR = Providing corrections (for reference stations only)
+ * IAC = Initial acquisition
+ * IDL = Idle, no satellites
+ * NAV = Navigation
+ * STS = Search the Sky (no almanac available)
+ * TRK = Tracking
+ * 2 Number of satellites that should be visible
+ * 3 Number of satellites being tracked
+ * 4 Time since last navigation status if not currently navigating
+ * (hours, minutes)
+ * 5 Initialization status:
+ * 0 = Waiting for initialization parameters
+ * 1 = Initialization completed
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,004,C,R,D,H.HH,V.VV,TT,HHHH,VVVV,T
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 004=Software Configuration.
+ * Defines the navigation mode and criteria for
+ * acceptable navigation for the receiver.
+ * 1 Constrain Altitude Mode:
+ * 0 = Auto. Constrain altitude (2-D solution) and use
+ * manual altitude input when 3 sats avalable. Do
+ * not constrain altitude (3-D solution) when 4 sats
+ * available.
+ * 1 = Always constrain altitude (2-D solution).
+ * 2 = Never constrain altitude (3-D solution).
+ * 3 = Coast. Constrain altitude (2-D solution) and use
+ * last GPS altitude calculation when 3 sats avalable.
+ * Do not constrain altitude (3-D solution) when 4 sats
+ * available.
+ * 2 Altitude Reference: (always 0 for MX4200)
+ * 0 = Ellipsoid
+ * 1 = Geoid (MSL)
+ * 3 Differential Navigation Control:
+ * 0 = Disabled
+ * 1 = Enabled
+ * 4 Horizontal Acceleration Constant (m/sec**2)
+ * 5 Vertical Acceleration Constant (m/sec**2) (0 for MX4200)
+ * 6 Tracking Elevation Limit (degrees)
+ * 7 HDOP Limit
+ * 8 VDOP Limit
+ * 9 Time Output Mode:
+ * U = UTC
+ * L = Local time
+ * 10 Local Time Offset (minutes) (absent on MX4200)
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,030,NNNN,FFF
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 030=Software Configuration.
+ * This sentence contains the navigation processor
+ * and baseband firmware version numbers.
+ * 1 Nav Processor Version Number
+ * 2 Baseband Firmware Version Number
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,523,M,S,M,EEEE,BBBBBB,C,R
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 523=Time Recovery Parameters Currently in Use.
+ * This sentence contains the configuration of the
+ * time recovery feature of the receiver.
+ * 1 Time Recovery Mode:
+ * D = Dynamic; solve for position and time while moving
+ * S = Static; solve for position and time while stationary
+ * K = Known position input, solve for time only
+ * N = No time recovery
+ * 2 Time Synchronization:
+ * U = UTC time
+ * G = GPS time
+ * 3 Time Mark Mode:
+ * A = Always output a time pulse
+ * V = Only output time pulse if time is valid (as determined
+ * by Maximum Time Error)
+ * 4 Maximum Time Error - the maximum error (in nanoseconds) for
+ * which a time mark will be considered valid.
+ * 5 User Time Bias - external bias in nanoseconds
+ * 6 Time Message Control:
+ * 0 = Do not output the time recovery message
+ * 1 = Output the time recovery message (record 830) to
+ * Control port
+ * 2 = Output the time recovery message (record 830) to
+ * Equipment port
+ * 7 Reserved
+ * 8 Position Known PRN (absent on MX 4200)
+ *
+ */
+static char *
+mx4200_parse_s(
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+ int sentence_type;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ sscanf ( pp->a_lastcode, "$PMVXG,%d", &sentence_type);
+
+ /* Sentence type */
+ switch (sentence_type) {
+
+ case PMVXG_D_STATUS:
+ msyslog(LOG_DEBUG,
+ "mx4200: status: %s", pp->a_lastcode);
+ break;
+ case PMVXG_D_MODEDATA:
+ msyslog(LOG_DEBUG,
+ "mx4200: mode data: %s", pp->a_lastcode);
+ break;
+ case PMVXG_D_SOFTCONF:
+ msyslog(LOG_DEBUG,
+ "mx4200: firmware configuration: %s", pp->a_lastcode);
+ break;
+ case PMVXG_D_TRECOVUSEAGE:
+ msyslog(LOG_DEBUG,
+ "mx4200: time recovery parms: %s", pp->a_lastcode);
+ break;
+ default:
+ return ("wrong rec-type");
+ }
+
+ return (NULL);
+}
+
+/*
+ * Process a PPS signal, placing a timestamp in pp->lastrec.
+ */
+static int
+mx4200_pps(
+ struct peer *peer
+ )
+{
+ int temp_serial;
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+
+ struct timespec timeout;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Grab the timestamp of the PPS signal.
+ */
+ temp_serial = up->pps_i.assert_sequence;
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ if (time_pps_fetch(up->pps_h, PPS_TSFMT_TSPEC, &(up->pps_i),
+ &timeout) < 0) {
+ mx4200_debug(peer,
+ "mx4200_pps: time_pps_fetch: serial=%lu, %m\n",
+ (unsigned long)up->pps_i.assert_sequence);
+ refclock_report(peer, CEVNT_FAULT);
+ return(1);
+ }
+ if (temp_serial == up->pps_i.assert_sequence) {
+ mx4200_debug(peer,
+ "mx4200_pps: assert_sequence serial not incrementing: %lu\n",
+ (unsigned long)up->pps_i.assert_sequence);
+ refclock_report(peer, CEVNT_FAULT);
+ return(1);
+ }
+ /*
+ * Check pps serial number against last one
+ */
+ if (up->lastserial + 1 != up->pps_i.assert_sequence &&
+ up->lastserial != 0) {
+ if (up->pps_i.assert_sequence == up->lastserial) {
+ mx4200_debug(peer, "mx4200_pps: no new pps event\n");
+ } else {
+ mx4200_debug(peer, "mx4200_pps: missed %lu pps events\n",
+ up->pps_i.assert_sequence - up->lastserial - 1UL);
+ }
+ refclock_report(peer, CEVNT_FAULT);
+ }
+ up->lastserial = up->pps_i.assert_sequence;
+
+ /*
+ * Return the timestamp in pp->lastrec
+ */
+
+ pp->lastrec.l_ui = up->pps_i.assert_timestamp.tv_sec +
+ (u_int32) JAN_1970;
+ pp->lastrec.l_uf = ((double)(up->pps_i.assert_timestamp.tv_nsec) *
+ 4.2949672960) + 0.5;
+
+ return(0);
+}
+
+/*
+ * mx4200_debug - print debug messages
+ */
+static void
+mx4200_debug(struct peer *peer, char *fmt, ...)
+{
+#ifdef DEBUG
+ va_list ap;
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+
+ if (debug) {
+ va_start(ap, fmt);
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Print debug message to stdout
+ * In the future, we may want to get get more creative...
+ */
+ mvprintf(fmt, ap);
+
+ va_end(ap);
+ }
+#endif
+}
+
+/*
+ * Send a character string to the receiver. Checksum is appended here.
+ */
+#if defined(__STDC__)
+static void
+mx4200_send(struct peer *peer, char *fmt, ...)
+#else
+static void
+mx4200_send(peer, fmt, va_alist)
+ struct peer *peer;
+ char *fmt;
+ va_dcl
+#endif /* __STDC__ */
+{
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+
+ register char *cp;
+ register int n, m;
+ va_list ap;
+ char buf[1024];
+ u_char ck;
+
+#if defined(__STDC__)
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif /* __STDC__ */
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ cp = buf;
+ *cp++ = '$';
+ n = VSNPRINTF((cp, sizeof(buf) - 1, fmt, ap));
+ ck = mx4200_cksum(cp, n);
+ cp += n;
+ ++n;
+ n += SNPRINTF((cp, sizeof(buf) - n - 5, "*%02X\r\n", ck));
+
+ m = write(pp->io.fd, buf, (unsigned)n);
+ if (m < 0)
+ msyslog(LOG_ERR, "mx4200_send: write: %m (%s)", buf);
+ mx4200_debug(peer, "mx4200_send: %d %s\n", m, buf);
+ va_end(ap);
+}
+
+#else
+int refclock_mx4200_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_neoclock4x.c b/ntpd/refclock_neoclock4x.c
new file mode 100644
index 0000000..a0067e9
--- /dev/null
+++ b/ntpd/refclock_neoclock4x.c
@@ -0,0 +1,1124 @@
+/*
+ *
+ * Refclock_neoclock4x.c
+ * - NeoClock4X driver for DCF77 or FIA Timecode
+ *
+ * Date: 2009-12-04 v1.16
+ *
+ * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir
+ * for details about the NeoClock4X device
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X))
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#if defined HAVE_SYS_MODEM_H
+# include <sys/modem.h>
+# ifndef __QNXNTO__
+# define TIOCMSET MCSETA
+# define TIOCMGET MCGETA
+# define TIOCM_RTS MRTS
+# endif
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# ifdef TERMIOS_NEEDS__SVID3
+# define _SVID3
+# endif
+# include <termios.h>
+# ifdef TERMIOS_NEEDS__SVID3
+# undef _SVID3
+# endif
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+/*
+ * NTP version 4.20 change the pp->msec field to pp->nsec.
+ * To allow to support older ntp versions with this sourcefile
+ * you can define NTP_PRE_420 to allow this driver to compile
+ * with ntp version back to 4.1.2.
+ *
+ */
+#if 0
+#define NTP_PRE_420
+#endif
+
+/*
+ * If you want the driver for whatever reason to not use
+ * the TX line to send anything to your NeoClock4X
+ * device you must tell the NTP refclock driver which
+ * firmware you NeoClock4X device uses.
+ *
+ * If you want to enable this feature change the "#if 0"
+ * line to "#if 1" and make sure that the defined firmware
+ * matches the firmware off your NeoClock4X receiver!
+ *
+ */
+
+#if 0
+#define NEOCLOCK4X_FIRMWARE NEOCLOCK4X_FIRMWARE_VERSION_A
+#endif
+
+/* at this time only firmware version A is known */
+#define NEOCLOCK4X_FIRMWARE_VERSION_A 'A'
+
+#define NEOCLOCK4X_TIMECODELEN 37
+
+#define NEOCLOCK4X_OFFSET_SERIAL 3
+#define NEOCLOCK4X_OFFSET_RADIOSIGNAL 9
+#define NEOCLOCK4X_OFFSET_DAY 12
+#define NEOCLOCK4X_OFFSET_MONTH 14
+#define NEOCLOCK4X_OFFSET_YEAR 16
+#define NEOCLOCK4X_OFFSET_HOUR 18
+#define NEOCLOCK4X_OFFSET_MINUTE 20
+#define NEOCLOCK4X_OFFSET_SECOND 22
+#define NEOCLOCK4X_OFFSET_HSEC 24
+#define NEOCLOCK4X_OFFSET_DOW 26
+#define NEOCLOCK4X_OFFSET_TIMESOURCE 28
+#define NEOCLOCK4X_OFFSET_DSTSTATUS 29
+#define NEOCLOCK4X_OFFSET_QUARZSTATUS 30
+#define NEOCLOCK4X_OFFSET_ANTENNA1 31
+#define NEOCLOCK4X_OFFSET_ANTENNA2 33
+#define NEOCLOCK4X_OFFSET_CRC 35
+
+#define NEOCLOCK4X_DRIVER_VERSION "1.16 (2009-12-04)"
+
+#define NSEC_TO_MILLI 1000000
+
+struct neoclock4x_unit {
+ l_fp laststamp; /* last receive timestamp */
+ short unit; /* NTP refclock unit number */
+ u_long polled; /* flag to detect noreplies */
+ char leap_status; /* leap second flag */
+ int recvnow;
+
+ char firmware[80];
+ char firmwaretag;
+ char serial[7];
+ char radiosignal[4];
+ char timesource;
+ char dststatus;
+ char quarzstatus;
+ int antenna1;
+ int antenna2;
+ int utc_year;
+ int utc_month;
+ int utc_day;
+ int utc_hour;
+ int utc_minute;
+ int utc_second;
+ int utc_msec;
+};
+
+static int neoclock4x_start (int, struct peer *);
+static void neoclock4x_shutdown (int, struct peer *);
+static void neoclock4x_receive (struct recvbuf *);
+static void neoclock4x_poll (int, struct peer *);
+static void neoclock4x_control (int, const struct refclockstat *, struct refclockstat *, struct peer *);
+
+static int neol_atoi_len (const char str[], int *, int);
+static int neol_hexatoi_len (const char str[], int *, int);
+static void neol_jdn_to_ymd (unsigned long, int *, int *, int *);
+static void neol_localtime (unsigned long, int* , int*, int*, int*, int*, int*);
+static unsigned long neol_mktime (int, int, int, int, int, int);
+#if !defined(NEOCLOCK4X_FIRMWARE)
+static int neol_query_firmware (int, int, char *, int);
+static int neol_check_firmware (int, const char*, char *);
+#endif
+
+struct refclock refclock_neoclock4x = {
+ neoclock4x_start, /* start up driver */
+ neoclock4x_shutdown, /* shut down driver */
+ neoclock4x_poll, /* transmit poll message */
+ neoclock4x_control,
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+static int
+neoclock4x_start(int unit,
+ struct peer *peer)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char dev[20];
+ int sl232;
+#if defined(HAVE_TERMIOS)
+ struct termios termsettings;
+#endif
+#if !defined(NEOCLOCK4X_FIRMWARE)
+ int tries;
+#endif
+
+ (void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit);
+
+ /* LDISC_STD, LDISC_RAW
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ fd = refclock_open(dev, B2400, LDISC_STD);
+ if(fd <= 0)
+ {
+ return (0);
+ }
+
+#if defined(HAVE_TERMIOS)
+
+#if 1
+ if(tcgetattr(fd, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+
+ /* 2400 Baud 8N2 */
+ termsettings.c_iflag = IGNBRK | IGNPAR | ICRNL;
+ termsettings.c_oflag = 0;
+ termsettings.c_cflag = CS8 | CSTOPB | CLOCAL | CREAD;
+ (void)cfsetispeed(&termsettings, (u_int)B2400);
+ (void)cfsetospeed(&termsettings, (u_int)B2400);
+
+ if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+
+#else
+ if(tcgetattr(fd, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+
+ /* 2400 Baud 8N2 */
+ termsettings.c_cflag &= ~PARENB;
+ termsettings.c_cflag |= CSTOPB;
+ termsettings.c_cflag &= ~CSIZE;
+ termsettings.c_cflag |= CS8;
+
+ if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+#endif
+
+#elif defined(HAVE_SYSV_TTYS)
+ if(ioctl(fd, TCGETA, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+
+ /* 2400 Baud 8N2 */
+ termsettings.c_cflag &= ~PARENB;
+ termsettings.c_cflag |= CSTOPB;
+ termsettings.c_cflag &= ~CSIZE;
+ termsettings.c_cflag |= CS8;
+
+ if(ioctl(fd, TCSETA, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+#else
+ msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit);
+ (void) close(fd);
+ return (0);
+#endif
+
+#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
+ /* turn on RTS, and DTR for power supply */
+ /* NeoClock4x is powered from serial line */
+ if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+#ifdef TIOCM_RTS
+ sl232 = sl232 | TIOCM_DTR | TIOCM_RTS; /* turn on RTS, and DTR for power supply */
+#else
+ sl232 = sl232 | CIOCM_DTR | CIOCM_RTS; /* turn on RTS, and DTR for power supply */
+#endif
+ if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+#else
+ msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!",
+ unit);
+ (void) close(fd);
+ return (0);
+#endif
+
+ up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit));
+ if(!(up))
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit);
+ (void) close(fd);
+ return (0);
+ }
+
+ memset((char *)up, 0, sizeof(struct neoclock4x_unit));
+ pp = peer->procptr;
+ pp->clockdesc = "NeoClock4X";
+ pp->unitptr = up;
+ pp->io.clock_recv = neoclock4x_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ /*
+ * no fudge time is given by user!
+ * use 169.583333 ms to compensate the serial line delay
+ * formula is:
+ * 2400 Baud / 11 bit = 218.18 charaters per second
+ * (NeoClock4X timecode len)
+ */
+ pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = -10;
+ memcpy((char *)&pp->refid, "neol", 4);
+
+ up->leap_status = 0;
+ up->unit = unit;
+ strlcpy(up->firmware, "?", sizeof(up->firmware));
+ up->firmwaretag = '?';
+ strlcpy(up->serial, "?", sizeof(up->serial));
+ strlcpy(up->radiosignal, "?", sizeof(up->radiosignal));
+ up->timesource = '?';
+ up->dststatus = '?';
+ up->quarzstatus = '?';
+ up->antenna1 = -1;
+ up->antenna2 = -1;
+ up->utc_year = 0;
+ up->utc_month = 0;
+ up->utc_day = 0;
+ up->utc_hour = 0;
+ up->utc_minute = 0;
+ up->utc_second = 0;
+ up->utc_msec = 0;
+
+#if defined(NEOCLOCK4X_FIRMWARE)
+#if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A
+ strlcpy(up->firmware, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)",
+ sizeof(up->firmware));
+ up->firmwaretag = 'A';
+#else
+ msyslog(LOG_EMERG, "NeoClock4X(%d): unknown firmware defined at compile time for NeoClock4X",
+ unit);
+ (void) close(fd);
+ pp->io.fd = -1;
+ free(pp->unitptr);
+ pp->unitptr = NULL;
+ return (0);
+#endif
+#else
+ for(tries=0; tries < 5; tries++)
+ {
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries);
+ /* wait 3 seconds for receiver to power up */
+ sleep(3);
+ if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware)))
+ {
+ break;
+ }
+ }
+
+ /* can I handle this firmware version? */
+ if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag))
+ {
+ (void) close(fd);
+ pp->io.fd = -1;
+ free(pp->unitptr);
+ pp->unitptr = NULL;
+ return (0);
+ }
+#endif
+
+ if(!io_addclock(&pp->io))
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit);
+ (void) close(fd);
+ pp->io.fd = -1;
+ free(pp->unitptr);
+ pp->unitptr = NULL;
+ return (0);
+ }
+
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit);
+
+ return (1);
+}
+
+static void
+neoclock4x_shutdown(int unit,
+ struct peer *peer)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+ int sl232;
+
+ if(NULL != peer)
+ {
+ pp = peer->procptr;
+ if(pp != NULL)
+ {
+ up = pp->unitptr;
+ if(up != NULL)
+ {
+ if(-1 != pp->io.fd)
+ {
+#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
+ /* turn on RTS, and DTR for power supply */
+ /* NeoClock4x is powered from serial line */
+ if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m",
+ unit);
+ }
+#ifdef TIOCM_RTS
+ /* turn on RTS, and DTR for power supply */
+ sl232 &= ~(TIOCM_DTR | TIOCM_RTS);
+#else
+ /* turn on RTS, and DTR for power supply */
+ sl232 &= ~(CIOCM_DTR | CIOCM_RTS);
+#endif
+ if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m",
+ unit);
+ }
+#endif
+ io_closeclock(&pp->io);
+ }
+ free(up);
+ pp->unitptr = NULL;
+ }
+ }
+ }
+
+ msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit);
+
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit);
+}
+
+static void
+neoclock4x_receive(struct recvbuf *rbufp)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ unsigned long calc_utc;
+ int day;
+ int month; /* ddd conversion */
+ int c;
+ int dsec;
+ unsigned char calc_chksum;
+ int recv_chksum;
+
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /* wait till poll interval is reached */
+ if(0 == up->recvnow)
+ return;
+
+ /* reset poll interval flag */
+ up->recvnow = 0;
+
+ /* read last received timecode */
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
+ pp->leap = LEAP_NOWARNING;
+
+ if(NEOCLOCK4X_TIMECODELEN != pp->lencode)
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s",
+ up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode);
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2);
+
+ /* calculate checksum */
+ calc_chksum = 0;
+ for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++)
+ {
+ calc_chksum += pp->a_lastcode[c];
+ }
+ if(recv_chksum != calc_chksum)
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s",
+ up->unit, pp->a_lastcode);
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /* Allow synchronization even is quartz clock is
+ * never initialized.
+ * WARNING: This is dangerous!
+ */
+ up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS];
+ if(0==(pp->sloppyclockflag & CLK_FLAG2))
+ {
+ if('I' != up->quarzstatus)
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s",
+ up->unit, pp->a_lastcode);
+ pp->leap = LEAP_NOTINSYNC;
+ refclock_report(peer, CEVNT_BADDATE);
+ return;
+ }
+ }
+ if('I' != up->quarzstatus)
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s",
+ up->unit, pp->a_lastcode);
+ }
+
+ /*
+ * If NeoClock4X is not synchronized to a radio clock
+ * check if we're allowed to synchronize with the quartz
+ * clock.
+ */
+ up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE];
+ if(0==(pp->sloppyclockflag & CLK_FLAG2))
+ {
+ if('A' != up->timesource)
+ {
+ /* not allowed to sync with quartz clock */
+ if(0==(pp->sloppyclockflag & CLK_FLAG1))
+ {
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+ }
+ }
+
+ /* this should only used when first install is done */
+ if(pp->sloppyclockflag & CLK_FLAG4)
+ {
+ msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s",
+ up->unit, pp->a_lastcode);
+ }
+
+ /* 123456789012345678901234567890123456789012345 */
+ /* S/N123456DCF1004021010001202ASX1213CR\r\n */
+
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &dsec, 2);
+#if defined(NTP_PRE_420)
+ pp->msec = dsec * 10; /* convert 1/100s from neoclock to real miliseconds */
+#else
+ pp->nsec = dsec * 10 * NSEC_TO_MILLI; /* convert 1/100s from neoclock to nanoseconds */
+#endif
+
+ memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3);
+ up->radiosignal[3] = 0;
+ memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6);
+ up->serial[6] = 0;
+ up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS];
+ neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2);
+ neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2);
+
+ /*
+ Validate received values at least enough to prevent internal
+ array-bounds problems, etc.
+ */
+ if((pp->hour < 0) || (pp->hour > 23) ||
+ (pp->minute < 0) || (pp->minute > 59) ||
+ (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
+ (day < 1) || (day > 31) ||
+ (month < 1) || (month > 12) ||
+ (pp->year < 0) || (pp->year > 99)) {
+ /* Data out of range. */
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s",
+ up->unit, pp->a_lastcode);
+ refclock_report(peer, CEVNT_BADDATE);
+ return;
+ }
+
+ /* Year-2000 check not needed anymore. Same problem
+ * will arise at 2099 but what should we do...?
+ *
+ * wrap 2-digit date into 4-digit
+ *
+ * if(pp->year < YEAR_PIVOT)
+ * {
+ * pp->year += 100;
+ * }
+ */
+ pp->year += 2000;
+
+ /* adjust NeoClock4X local time to UTC */
+ calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second);
+ calc_utc -= 3600;
+ /* adjust NeoClock4X daylight saving time if needed */
+ if('S' == up->dststatus)
+ calc_utc -= 3600;
+ neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second);
+
+ /*
+ some preparations
+ */
+ pp->day = ymd2yd(pp->year, month, day);
+ pp->leap = 0;
+
+ if(pp->sloppyclockflag & CLK_FLAG4)
+ {
+ msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld",
+ up->unit,
+ pp->year, month, day,
+ pp->hour, pp->minute, pp->second,
+#if defined(NTP_PRE_420)
+ pp->msec
+#else
+ pp->nsec/NSEC_TO_MILLI
+#endif
+ );
+ }
+
+ up->utc_year = pp->year;
+ up->utc_month = month;
+ up->utc_day = day;
+ up->utc_hour = pp->hour;
+ up->utc_minute = pp->minute;
+ up->utc_second = pp->second;
+#if defined(NTP_PRE_420)
+ up->utc_msec = pp->msec;
+#else
+ up->utc_msec = pp->nsec/NSEC_TO_MILLI;
+#endif
+
+ if(!refclock_process(pp))
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit);
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ refclock_receive(peer);
+
+ /* report good status */
+ refclock_report(peer, CEVNT_NOMINAL);
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+}
+
+static void
+neoclock4x_poll(int unit,
+ struct peer *peer)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ pp->polls++;
+ up->recvnow = 1;
+}
+
+static void
+neoclock4x_control(int unit,
+ const struct refclockstat *in,
+ struct refclockstat *out,
+ struct peer *peer)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+
+ if(NULL == peer)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
+ return;
+ }
+
+ pp = peer->procptr;
+ if(NULL == pp)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
+ return;
+ }
+
+ up = pp->unitptr;
+ if(NULL == up)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
+ return;
+ }
+
+ if(NULL != in)
+ {
+ /* check to see if a user supplied time offset is given */
+ if(in->haveflags & CLK_HAVETIME1)
+ {
+ pp->fudgetime1 = in->fudgetime1;
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.",
+ unit, pp->fudgetime1);
+ }
+
+ /* notify */
+ if(pp->sloppyclockflag & CLK_FLAG1)
+ {
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit);
+ }
+ else
+ {
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit);
+ }
+ }
+
+ if(NULL != out)
+ {
+ char *tt;
+ char tmpbuf[80];
+
+ out->kv_list = (struct ctl_var *)0;
+ out->type = REFCLK_NEOCLOCK4X;
+
+ snprintf(tmpbuf, sizeof(tmpbuf)-1,
+ "%04d-%02d-%02d %02d:%02d:%02d.%03d",
+ up->utc_year, up->utc_month, up->utc_day,
+ up->utc_hour, up->utc_minute, up->utc_second,
+ up->utc_msec);
+ tt = add_var(&out->kv_list, sizeof(tmpbuf)-1, RO|DEF);
+ snprintf(tt, sizeof(tmpbuf)-1, "calc_utc=\"%s\"", tmpbuf);
+
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ snprintf(tt, 39, "radiosignal=\"%s\"", up->radiosignal);
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ snprintf(tt, 39, "antenna1=\"%d\"", up->antenna1);
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ snprintf(tt, 39, "antenna2=\"%d\"", up->antenna2);
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ if('A' == up->timesource)
+ snprintf(tt, 39, "timesource=\"radio\"");
+ else if('C' == up->timesource)
+ snprintf(tt, 39, "timesource=\"quartz\"");
+ else
+ snprintf(tt, 39, "timesource=\"unknown\"");
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ if('I' == up->quarzstatus)
+ snprintf(tt, 39, "quartzstatus=\"synchronized\"");
+ else if('X' == up->quarzstatus)
+ snprintf(tt, 39, "quartzstatus=\"not synchronized\"");
+ else
+ snprintf(tt, 39, "quartzstatus=\"unknown\"");
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ if('S' == up->dststatus)
+ snprintf(tt, 39, "dststatus=\"summer\"");
+ else if('W' == up->dststatus)
+ snprintf(tt, 39, "dststatus=\"winter\"");
+ else
+ snprintf(tt, 39, "dststatus=\"unknown\"");
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ snprintf(tt, 79, "firmware=\"%s\"", up->firmware);
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ snprintf(tt, 39, "firmwaretag=\"%c\"", up->firmwaretag);
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ snprintf(tt, 79, "driver version=\"%s\"", NEOCLOCK4X_DRIVER_VERSION);
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ snprintf(tt, 79, "serialnumber=\"%s\"", up->serial);
+ }
+}
+
+static int
+neol_hexatoi_len(const char str[],
+ int *result,
+ int maxlen)
+{
+ int hexdigit;
+ int i;
+ int n = 0;
+
+ for(i=0; isxdigit((int)str[i]) && i < maxlen; i++)
+ {
+ hexdigit = isdigit((int)str[i]) ? toupper(str[i]) - '0' : toupper(str[i]) - 'A' + 10;
+ n = 16 * n + hexdigit;
+ }
+ *result = n;
+ return (n);
+}
+
+static int
+neol_atoi_len(const char str[],
+ int *result,
+ int maxlen)
+{
+ int digit;
+ int i;
+ int n = 0;
+
+ for(i=0; isdigit((int)str[i]) && i < maxlen; i++)
+ {
+ digit = str[i] - '0';
+ n = 10 * n + digit;
+ }
+ *result = n;
+ return (n);
+}
+
+/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
+ * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
+ * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
+ *
+ * [For the Julian calendar (which was used in Russia before 1917,
+ * Britain & colonies before 1752, anywhere else before 1582,
+ * and is still in use by some communities) leave out the
+ * -year/100+year/400 terms, and add 10.]
+ *
+ * This algorithm was first published by Gauss (I think).
+ *
+ * WARNING: this function will overflow on 2106-02-07 06:28:16 on
+ * machines were long is 32-bit! (However, as time_t is signed, we
+ * will already get problems at other places on 2038-01-19 03:14:08)
+ */
+static unsigned long
+neol_mktime(int year,
+ int mon,
+ int day,
+ int hour,
+ int min,
+ int sec)
+{
+ if (0 >= (int) (mon -= 2)) { /* 1..12 . 11,12,1..10 */
+ mon += 12; /* Puts Feb last since it has leap day */
+ year -= 1;
+ }
+ return (((
+ (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
+ year*365 - 719499
+ )*24 + hour /* now have hours */
+ )*60 + min /* now have minutes */
+ )*60 + sec; /* finally seconds */
+}
+
+static void
+neol_localtime(unsigned long utc,
+ int* year,
+ int* month,
+ int* day,
+ int* hour,
+ int* min,
+ int* sec)
+{
+ *sec = utc % 60;
+ utc /= 60;
+ *min = utc % 60;
+ utc /= 60;
+ *hour = utc % 24;
+ utc /= 24;
+
+ /* JDN Date 1/1/1970 */
+ neol_jdn_to_ymd(utc + 2440588L, year, month, day);
+}
+
+static void
+neol_jdn_to_ymd(unsigned long jdn,
+ int *yy,
+ int *mm,
+ int *dd)
+{
+ unsigned long x, z, m, d, y;
+ unsigned long daysPer400Years = 146097UL;
+ unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL;
+
+ x = jdn + 68569UL;
+ z = 4UL * x / daysPer400Years;
+ x = x - (daysPer400Years * z + 3UL) / 4UL;
+ y = 4000UL * (x + 1) / fudgedDaysPer4000Years;
+ x = x - 1461UL * y / 4UL + 31UL;
+ m = 80UL * x / 2447UL;
+ d = x - 2447UL * m / 80UL;
+ x = m / 11UL;
+ m = m + 2UL - 12UL * x;
+ y = 100UL * (z - 49UL) + y + x;
+
+ *yy = (int)y;
+ *mm = (int)m;
+ *dd = (int)d;
+}
+
+#if !defined(NEOCLOCK4X_FIRMWARE)
+static int
+neol_query_firmware(int fd,
+ int unit,
+ char *firmware,
+ int maxlen)
+{
+ char tmpbuf[256];
+ int len;
+ int lastsearch;
+ unsigned char c;
+ int last_c_was_crlf;
+ int last_crlf_conv_len;
+ int init;
+ int read_errors;
+ int flag = 0;
+ int chars_read;
+
+ /* wait a little bit */
+ sleep(1);
+ if(-1 != write(fd, "V", 1))
+ {
+ /* wait a little bit */
+ sleep(1);
+ memset(tmpbuf, 0x00, sizeof(tmpbuf));
+
+ len = 0;
+ lastsearch = 0;
+ last_c_was_crlf = 0;
+ last_crlf_conv_len = 0;
+ init = 1;
+ read_errors = 0;
+ chars_read = 0;
+ for(;;)
+ {
+ if(read_errors > 5)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit);
+ strlcpy(tmpbuf, "unknown due to timeout", sizeof(tmpbuf));
+ break;
+ }
+ if(chars_read > 500)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit);
+ strlcpy(tmpbuf, "unknown due to garbage input", sizeof(tmpbuf));
+ break;
+ }
+ if(-1 == read(fd, &c, 1))
+ {
+ if(EAGAIN != errno)
+ {
+ msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %m", unit);
+ read_errors++;
+ }
+ else
+ {
+ sleep(1);
+ }
+ continue;
+ }
+ else
+ {
+ chars_read++;
+ }
+
+ if(init)
+ {
+ if(0xA9 != c) /* wait for (c) char in input stream */
+ continue;
+
+ strlcpy(tmpbuf, "(c)", sizeof(tmpbuf));
+ len = 3;
+ init = 0;
+ continue;
+ }
+
+#if 0
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c);
+#endif
+
+ if(0x0A == c || 0x0D == c)
+ {
+ if(last_c_was_crlf)
+ {
+ char *ptr;
+ ptr = strstr(&tmpbuf[lastsearch], "S/N");
+ if(NULL != ptr)
+ {
+ tmpbuf[last_crlf_conv_len] = 0;
+ flag = 1;
+ break;
+ }
+ /* convert \n to / */
+ last_crlf_conv_len = len;
+ tmpbuf[len++] = ' ';
+ tmpbuf[len++] = '/';
+ tmpbuf[len++] = ' ';
+ lastsearch = len;
+ }
+ last_c_was_crlf = 1;
+ }
+ else
+ {
+ last_c_was_crlf = 0;
+ if(0x00 != c)
+ tmpbuf[len++] = (char) c;
+ }
+ tmpbuf[len] = '\0';
+ if(len > sizeof(tmpbuf)-5)
+ break;
+ }
+ }
+ else
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit);
+ strlcpy(tmpbuf, "unknown error", sizeof(tmpbuf));
+ }
+ if (strlcpy(firmware, tmpbuf, maxlen) >= maxlen)
+ strlcpy(firmware, "buffer too small", maxlen);
+
+ if(flag)
+ {
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware);
+
+ if(strstr(firmware, "/R2"))
+ {
+ msyslog(LOG_INFO, "NeoClock4X(%d): Your NeoClock4X uses the new R2 firmware release. Please note the changed LED behaviour.", unit);
+ }
+
+ }
+
+ return (flag);
+}
+
+static int
+neol_check_firmware(int unit,
+ const char *firmware,
+ char *firmwaretag)
+{
+ char *ptr;
+
+ *firmwaretag = '?';
+ ptr = strstr(firmware, "NDF:");
+ if(NULL != ptr)
+ {
+ if((strlen(firmware) - strlen(ptr)) >= 7)
+ {
+ if(':' == *(ptr+5) && '*' == *(ptr+6))
+ *firmwaretag = *(ptr+4);
+ }
+ }
+
+ if('A' != *firmwaretag)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag);
+ return (0);
+ }
+
+ return (1);
+}
+#endif
+
+#else
+int refclock_neoclock4x_bs;
+#endif /* REFCLOCK */
+
+/*
+ * History:
+ * refclock_neoclock4x.c
+ *
+ * 2002/04/27 cjh
+ * Revision 1.0 first release
+ *
+ * 2002/07/15 cjh
+ * preparing for bitkeeper reposity
+ *
+ * 2002/09/09 cjh
+ * Revision 1.1
+ * - don't assume sprintf returns an int anymore
+ * - change the way the firmware version is read
+ * - some customers would like to put a device called
+ * data diode to the NeoClock4X device to disable
+ * the write line. We need to now the firmware
+ * version even in this case. We made a compile time
+ * definition in this case. The code was previously
+ * only available on request.
+ *
+ * 2003/01/08 cjh
+ * Revision 1.11
+ * - changing xprinf to xnprinf to avoid buffer overflows
+ * - change some logic
+ * - fixed memory leaks if drivers can't initialize
+ *
+ * 2003/01/10 cjh
+ * Revision 1.12
+ * - replaced ldiv
+ * - add code to support FreeBSD
+ *
+ * 2003/07/07 cjh
+ * Revision 1.13
+ * - fix reporting of clock status
+ * changes. previously a bad clock
+ * status was never reset.
+ *
+ * 2004/04/07 cjh
+ * Revision 1.14
+ * - open serial port in a way
+ * AIX and some other OS can
+ * handle much better
+ *
+ * 2006/01/11 cjh
+ * Revision 1.15
+ * - remove some unsued #ifdefs
+ * - fix nsec calculation, closes #499
+ *
+ * 2009/12/04 cjh
+ * Revision 1.16
+ * - change license to ntp COPYRIGHT notice. This should allow Debian
+ * to add this refclock driver in further releases.
+ * - detect R2 hardware
+ *
+ */
+
+
+
+
+
+
diff --git a/ntpd/refclock_nmea.c b/ntpd/refclock_nmea.c
new file mode 100644
index 0000000..58867f4
--- /dev/null
+++ b/ntpd/refclock_nmea.c
@@ -0,0 +1,1956 @@
+/*
+ * refclock_nmea.c - clock driver for an NMEA GPS CLOCK
+ * Michael Petry Jun 20, 1994
+ * based on refclock_heathn.c
+ *
+ * Updated to add support for Accord GPS Clock
+ * Venu Gopal Dec 05, 2007
+ * neo.venu@gmail.com, venugopal_d@pgad.gov.in
+ *
+ * Updated to process 'time1' fudge factor
+ * Venu Gopal May 05, 2008
+ *
+ * Converted to common PPSAPI code, separate PPS fudge time1
+ * from serial timecode fudge time2.
+ * Dave Hart July 1, 2009
+ * hart@ntp.org, davehart@davehart.com
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ntp_types.h"
+
+#if defined(REFCLOCK) && defined(CLOCK_NMEA)
+
+#define NMEA_WRITE_SUPPORT 0 /* no write support at the moment */
+
+#include <sys/stat.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_calendar.h"
+#include "timespecops.h"
+
+#ifdef HAVE_PPSAPI
+# include "ppsapi_timepps.h"
+# include "refclock_atom.h"
+#endif /* HAVE_PPSAPI */
+
+
+/*
+ * This driver supports NMEA-compatible GPS receivers
+ *
+ * Prototype was refclock_trak.c, Thanks a lot.
+ *
+ * The receiver used spits out the NMEA sentences for boat navigation.
+ * And you thought it was an information superhighway. Try a raging river
+ * filled with rapids and whirlpools that rip away your data and warp time.
+ *
+ * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in.
+ * On startup if initialization of the PPSAPI fails, it will fall back
+ * to the "normal" timestamps.
+ *
+ * The PPSAPI part of the driver understands fudge flag2 and flag3. If
+ * flag2 is set, it will use the clear edge of the pulse. If flag3 is
+ * set, kernel hardpps is enabled.
+ *
+ * GPS sentences other than RMC (the default) may be enabled by setting
+ * the relevent bits of 'mode' in the server configuration line
+ * server 127.127.20.x mode X
+ *
+ * bit 0 - enables RMC (1)
+ * bit 1 - enables GGA (2)
+ * bit 2 - enables GLL (4)
+ * bit 3 - enables ZDA (8) - Standard Time & Date
+ * bit 3 - enables ZDG (8) - Accord GPS Clock's custom sentence with GPS time
+ * very close to standard ZDA
+ *
+ * Multiple sentences may be selected except when ZDG/ZDA is selected.
+ *
+ * bit 4/5/6 - selects the baudrate for serial port :
+ * 0 for 4800 (default)
+ * 1 for 9600
+ * 2 for 19200
+ * 3 for 38400
+ * 4 for 57600
+ * 5 for 115200
+ */
+#define NMEA_MESSAGE_MASK 0x0000FF0FU
+#define NMEA_BAUDRATE_MASK 0x00000070U
+#define NMEA_BAUDRATE_SHIFT 4
+
+#define NMEA_DELAYMEAS_MASK 0x80
+#define NMEA_EXTLOG_MASK 0x00010000U
+#define NMEA_DATETRUST_MASK 0x02000000U
+
+#define NMEA_PROTO_IDLEN 5 /* tag name must be at least 5 chars */
+#define NMEA_PROTO_MINLEN 6 /* min chars in sentence, excluding CS */
+#define NMEA_PROTO_MAXLEN 80 /* max chars in sentence, excluding CS */
+#define NMEA_PROTO_FIELDS 32 /* not official; limit on fields per record */
+
+/*
+ * We check the timecode format and decode its contents. We only care
+ * about a few of them, the most important being the $GPRMC format:
+ *
+ * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC
+ *
+ * mode (0,1,2,3) selects sentence ANY/ALL, RMC, GGA, GLL, ZDA
+ * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21
+ * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F
+ * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77
+ *
+ * Defining GPZDA to support Standard Time & Date
+ * sentence. The sentence has the following format
+ *
+ * $--ZDA,HHMMSS.SS,DD,MM,YYYY,TH,TM,*CS<CR><LF>
+ *
+ * Apart from the familiar fields,
+ * 'TH' Time zone Hours
+ * 'TM' Time zone Minutes
+ *
+ * Defining GPZDG to support Accord GPS Clock's custom NMEA
+ * sentence. The sentence has the following format
+ *
+ * $GPZDG,HHMMSS.S,DD,MM,YYYY,AA.BB,V*CS<CR><LF>
+ *
+ * It contains the GPS timestamp valid for next PPS pulse.
+ * Apart from the familiar fields,
+ * 'AA.BB' denotes the signal strength( should be < 05.00 )
+ * 'V' denotes the GPS sync status :
+ * '0' indicates INVALID time,
+ * '1' indicates accuracy of +/-20 ms
+ * '2' indicates accuracy of +/-100 ns
+ *
+ * Defining PGRMF for Garmin GPS Fix Data
+ * $PGRMF,WN,WS,DATE,TIME,LS,LAT,LAT_DIR,LON,LON_DIR,MODE,FIX,SPD,DIR,PDOP,TDOP
+ * WN -- GPS week number (weeks since 1980-01-06, mod 1024)
+ * WS -- GPS seconds in week
+ * LS -- GPS leap seconds, accumulated ( UTC + LS == GPS )
+ * FIX -- Fix type: 0=nofix, 1=2D, 2=3D
+ * DATE/TIME are standard date/time strings in UTC time scale
+ *
+ * The GPS time can be used to get the full century for the truncated
+ * date spec.
+ */
+
+/*
+ * Definitions
+ */
+#define DEVICE "/dev/gps%d" /* GPS serial device */
+#define PPSDEV "/dev/gpspps%d" /* PPSAPI device override */
+#define SPEED232 B4800 /* uart speed (4800 bps) */
+#define PRECISION (-9) /* precision assumed (about 2 ms) */
+#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference id */
+#define DESCRIPTION "NMEA GPS Clock" /* who we are */
+#ifndef O_NOCTTY
+#define M_NOCTTY 0
+#else
+#define M_NOCTTY O_NOCTTY
+#endif
+#ifndef O_NONBLOCK
+#define M_NONBLOCK 0
+#else
+#define M_NONBLOCK O_NONBLOCK
+#endif
+#define PPSOPENMODE (O_RDWR | M_NOCTTY | M_NONBLOCK)
+
+/* NMEA sentence array indexes for those we use */
+#define NMEA_GPRMC 0 /* recommended min. nav. */
+#define NMEA_GPGGA 1 /* fix and quality */
+#define NMEA_GPGLL 2 /* geo. lat/long */
+#define NMEA_GPZDA 3 /* date/time */
+/*
+ * $GPZDG is a proprietary sentence that violates the spec, by not
+ * using $P and an assigned company identifier to prefix the sentence
+ * identifier. When used with this driver, the system needs to be
+ * isolated from other NTP networks, as it operates in GPS time, not
+ * UTC as is much more common. GPS time is >15 seconds different from
+ * UTC due to not respecting leap seconds since 1970 or so. Other
+ * than the different timebase, $GPZDG is similar to $GPZDA.
+ */
+#define NMEA_GPZDG 4
+#define NMEA_PGRMF 5
+#define NMEA_ARRAY_SIZE (NMEA_PGRMF + 1)
+
+/*
+ * Sentence selection mode bits
+ */
+#define USE_GPRMC 0x00000001u
+#define USE_GPGGA 0x00000002u
+#define USE_GPGLL 0x00000004u
+#define USE_GPZDA 0x00000008u
+#define USE_PGRMF 0x00000100u
+
+/* mapping from sentence index to controlling mode bit */
+static const u_int32 sentence_mode[NMEA_ARRAY_SIZE] =
+{
+ USE_GPRMC,
+ USE_GPGGA,
+ USE_GPGLL,
+ USE_GPZDA,
+ USE_GPZDA,
+ USE_PGRMF
+};
+
+/* date formats we support */
+enum date_fmt {
+ DATE_1_DDMMYY, /* use 1 field with 2-digit year */
+ DATE_3_DDMMYYYY /* use 3 fields with 4-digit year */
+};
+
+/* results for 'field_init()'
+ *
+ * Note: If a checksum is present, the checksum test must pass OK or the
+ * sentence is tagged invalid.
+ */
+#define CHECK_EMPTY -1 /* no data */
+#define CHECK_INVALID 0 /* not a valid NMEA sentence */
+#define CHECK_VALID 1 /* valid but without checksum */
+#define CHECK_CSVALID 2 /* valid with checksum OK */
+
+/*
+ * Unit control structure
+ */
+typedef struct {
+#ifdef HAVE_PPSAPI
+ struct refclock_atom atom; /* PPSAPI structure */
+ int ppsapi_fd; /* fd used with PPSAPI */
+ u_char ppsapi_tried; /* attempt PPSAPI once */
+ u_char ppsapi_lit; /* time_pps_create() worked */
+ u_char ppsapi_gate; /* system is on PPS */
+#endif /* HAVE_PPSAPI */
+ u_char gps_time; /* use GPS time, not UTC */
+ u_short century_cache; /* cached current century */
+ l_fp last_reftime; /* last processed reference stamp */
+ short epoch_warp; /* last epoch warp, for logging */
+ /* tally stats, reset each poll cycle */
+ struct
+ {
+ u_int total;
+ u_int accepted;
+ u_int rejected; /* GPS said not enough signal */
+ u_int malformed; /* Bad checksum, invalid date or time */
+ u_int filtered; /* mode bits, not GPZDG, same second */
+ u_int pps_used;
+ }
+ tally;
+ /* per sentence checksum seen flag */
+ u_char cksum_type[NMEA_ARRAY_SIZE];
+} nmea_unit;
+
+/*
+ * helper for faster field access
+ */
+typedef struct {
+ char *base; /* buffer base */
+ char *cptr; /* current field ptr */
+ int blen; /* buffer length */
+ int cidx; /* current field index */
+} nmea_data;
+
+/*
+ * NMEA gps week/time information
+ * This record contains the number of weeks since 1980-01-06 modulo
+ * 1024, the seconds elapsed since start of the week, and the number of
+ * leap seconds that are the difference between GPS and UTC time scale.
+ */
+typedef struct {
+ u_int32 wt_time; /* seconds since weekstart */
+ u_short wt_week; /* week number */
+ short wt_leap; /* leap seconds */
+} gps_weektm;
+
+/*
+ * The GPS week time scale starts on Sunday, 1980-01-06. We need the
+ * rata die number of this day.
+ */
+#ifndef DAY_GPS_STARTS
+#define DAY_GPS_STARTS 722820
+#endif
+
+/*
+ * Function prototypes
+ */
+static void nmea_init (void);
+static int nmea_start (int, struct peer *);
+static void nmea_shutdown (int, struct peer *);
+static void nmea_receive (struct recvbuf *);
+static void nmea_poll (int, struct peer *);
+#ifdef HAVE_PPSAPI
+static void nmea_control (int, const struct refclockstat *,
+ struct refclockstat *, struct peer *);
+#define NMEA_CONTROL nmea_control
+#else
+#define NMEA_CONTROL noentry
+#endif /* HAVE_PPSAPI */
+static void nmea_timer (int, struct peer *);
+
+/* parsing helpers */
+static int field_init (nmea_data * data, char * cp, int len);
+static char * field_parse (nmea_data * data, int fn);
+static void field_wipe (nmea_data * data, ...);
+static u_char parse_qual (nmea_data * data, int idx,
+ char tag, int inv);
+static int parse_time (struct calendar * jd, long * nsec,
+ nmea_data *, int idx);
+static int parse_date (struct calendar *jd, nmea_data*,
+ int idx, enum date_fmt fmt);
+static int parse_weekdata (gps_weektm *, nmea_data *,
+ int weekidx, int timeidx, int leapidx);
+/* calendar / date helpers */
+static int unfold_day (struct calendar * jd, u_int32 rec_ui);
+static int unfold_century (struct calendar * jd, u_int32 rec_ui);
+static int gpsfix_century (struct calendar * jd, const gps_weektm * wd,
+ u_short * ccentury);
+static l_fp eval_gps_time (struct peer * peer, const struct calendar * gpst,
+ const struct timespec * gpso, const l_fp * recv);
+
+static int nmead_open (const char * device);
+static void save_ltc (struct refclockproc * const, const char * const,
+ size_t);
+
+/*
+ * If we want the driver to ouput sentences, too: re-enable the send
+ * support functions by defining NMEA_WRITE_SUPPORT to non-zero...
+ */
+#if NMEA_WRITE_SUPPORT
+
+static void gps_send(int, const char *, struct peer *);
+# ifdef SYS_WINNT
+# undef write /* ports/winnt/include/config.h: #define write _write */
+extern int async_write(int, const void *, unsigned int);
+# define write(fd, data, octets) async_write(fd, data, octets)
+# endif /* SYS_WINNT */
+
+#endif /* NMEA_WRITE_SUPPORT */
+
+static int32_t g_gpsMinBase;
+static int32_t g_gpsMinYear;
+
+/*
+ * -------------------------------------------------------------------
+ * Transfer vector
+ * -------------------------------------------------------------------
+ */
+struct refclock refclock_nmea = {
+ nmea_start, /* start up driver */
+ nmea_shutdown, /* shut down driver */
+ nmea_poll, /* transmit poll message */
+ NMEA_CONTROL, /* fudge control */
+ nmea_init, /* initialize driver */
+ noentry, /* buginfo */
+ nmea_timer /* called once per second */
+};
+
+/*
+ * -------------------------------------------------------------------
+ * nmea_init - initialise data
+ *
+ * calculates a few runtime constants that cannot be made compile time
+ * constants.
+ * -------------------------------------------------------------------
+ */
+static void
+nmea_init(void)
+{
+ struct calendar date;
+
+ /* - calculate min. base value for GPS epoch & century unfolding
+ * This assumes that the build system was roughly in sync with
+ * the world, and that really synchronising to a time before the
+ * program was created would be unsafe or insane. If the build
+ * date cannot be stablished, at least use the start of GPS
+ * (1980-01-06) as minimum, because GPS can surely NOT
+ * synchronise beyond it's own big bang. We add a little safety
+ * margin for the fuzziness of the build date, which is in an
+ * undefined time zone. */
+ if (ntpcal_get_build_date(&date))
+ g_gpsMinBase = ntpcal_date_to_rd(&date) - 2;
+ else
+ g_gpsMinBase = 0;
+
+ if (g_gpsMinBase < DAY_GPS_STARTS)
+ g_gpsMinBase = DAY_GPS_STARTS;
+
+ ntpcal_rd_to_date(&date, g_gpsMinBase);
+ g_gpsMinYear = date.year;
+ g_gpsMinBase -= DAY_NTP_STARTS;
+}
+
+/*
+ * -------------------------------------------------------------------
+ * nmea_start - open the GPS devices and initialize data for processing
+ *
+ * return 0 on error, 1 on success. Even on error the peer structures
+ * must be in a state that permits 'nmea_shutdown()' to clean up all
+ * resources, because it will be called immediately to do so.
+ * -------------------------------------------------------------------
+ */
+static int
+nmea_start(
+ int unit,
+ struct peer * peer
+ )
+{
+ struct refclockproc * const pp = peer->procptr;
+ nmea_unit * const up = emalloc_zero(sizeof(*up));
+ char device[20];
+ size_t devlen;
+ u_int32 rate;
+ int baudrate;
+ char * baudtext;
+
+
+ /* Get baudrate choice from mode byte bits 4/5/6 */
+ rate = (peer->ttl & NMEA_BAUDRATE_MASK) >> NMEA_BAUDRATE_SHIFT;
+
+ switch (rate) {
+ case 0:
+ baudrate = SPEED232;
+ baudtext = "4800";
+ break;
+ case 1:
+ baudrate = B9600;
+ baudtext = "9600";
+ break;
+ case 2:
+ baudrate = B19200;
+ baudtext = "19200";
+ break;
+ case 3:
+ baudrate = B38400;
+ baudtext = "38400";
+ break;
+#ifdef B57600
+ case 4:
+ baudrate = B57600;
+ baudtext = "57600";
+ break;
+#endif
+#ifdef B115200
+ case 5:
+ baudrate = B115200;
+ baudtext = "115200";
+ break;
+#endif
+ default:
+ baudrate = SPEED232;
+ baudtext = "4800 (fallback)";
+ break;
+ }
+
+ /* Allocate and initialize unit structure */
+ pp->unitptr = (caddr_t)up;
+ pp->io.fd = -1;
+ pp->io.clock_recv = nmea_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ /* force change detection on first valid message */
+ memset(&up->last_reftime, 0xFF, sizeof(up->last_reftime));
+ /* force checksum on GPRMC, see below */
+ up->cksum_type[NMEA_GPRMC] = CHECK_CSVALID;
+#ifdef HAVE_PPSAPI
+ up->ppsapi_fd = -1;
+#endif
+ ZERO(up->tally);
+
+ /* Initialize miscellaneous variables */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy(&pp->refid, REFID, 4);
+
+ /* Open serial port. Use CLK line discipline, if available. */
+ devlen = snprintf(device, sizeof(device), DEVICE, unit);
+ if (devlen >= sizeof(device)) {
+ msyslog(LOG_ERR, "%s clock device name too long",
+ refnumtoa(&peer->srcadr));
+ return FALSE; /* buffer overflow */
+ }
+ pp->io.fd = refclock_open(device, baudrate, LDISC_CLK);
+ if (0 >= pp->io.fd) {
+ pp->io.fd = nmead_open(device);
+ if (-1 == pp->io.fd)
+ return FALSE;
+ }
+ LOGIF(CLOCKINFO, (LOG_NOTICE, "%s serial %s open at %s bps",
+ refnumtoa(&peer->srcadr), device, baudtext));
+
+ /* succeed if this clock can be added */
+ return io_addclock(&pp->io) != 0;
+}
+
+
+/*
+ * -------------------------------------------------------------------
+ * nmea_shutdown - shut down a GPS clock
+ *
+ * NOTE this routine is called after nmea_start() returns failure,
+ * as well as during a normal shutdown due to ntpq :config unpeer.
+ * -------------------------------------------------------------------
+ */
+static void
+nmea_shutdown(
+ int unit,
+ struct peer * peer
+ )
+{
+ struct refclockproc * const pp = peer->procptr;
+ nmea_unit * const up = (nmea_unit *)pp->unitptr;
+
+ UNUSED_ARG(unit);
+
+ if (up != NULL) {
+#ifdef HAVE_PPSAPI
+ if (up->ppsapi_lit)
+ time_pps_destroy(up->atom.handle);
+ if (up->ppsapi_tried && up->ppsapi_fd != pp->io.fd)
+ close(up->ppsapi_fd);
+#endif
+ free(up);
+ }
+ pp->unitptr = (caddr_t)NULL;
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ pp->io.fd = -1;
+}
+
+/*
+ * -------------------------------------------------------------------
+ * nmea_control - configure fudge params
+ * -------------------------------------------------------------------
+ */
+#ifdef HAVE_PPSAPI
+static void
+nmea_control(
+ int unit,
+ const struct refclockstat * in_st,
+ struct refclockstat * out_st,
+ struct peer * peer
+ )
+{
+ struct refclockproc * const pp = peer->procptr;
+ nmea_unit * const up = (nmea_unit *)pp->unitptr;
+
+ char device[32];
+ size_t devlen;
+
+ UNUSED_ARG(in_st);
+ UNUSED_ARG(out_st);
+
+ /*
+ * PPS control
+ *
+ * If /dev/gpspps$UNIT can be opened that will be used for
+ * PPSAPI. Otherwise, the GPS serial device /dev/gps$UNIT
+ * already opened is used for PPSAPI as well. (This might not
+ * work, in which case the PPS API remains unavailable...)
+ */
+
+ /* Light up the PPSAPI interface if not yet attempted. */
+ if ((CLK_FLAG1 & pp->sloppyclockflag) && !up->ppsapi_tried) {
+ up->ppsapi_tried = TRUE;
+ devlen = snprintf(device, sizeof(device), PPSDEV, unit);
+ if (devlen < sizeof(device)) {
+ up->ppsapi_fd = open(device, PPSOPENMODE,
+ S_IRUSR | S_IWUSR);
+ } else {
+ up->ppsapi_fd = -1;
+ msyslog(LOG_ERR, "%s PPS device name too long",
+ refnumtoa(&peer->srcadr));
+ }
+ if (-1 == up->ppsapi_fd)
+ up->ppsapi_fd = pp->io.fd;
+ if (refclock_ppsapi(up->ppsapi_fd, &up->atom)) {
+ /* use the PPS API for our own purposes now. */
+ up->ppsapi_lit = refclock_params(
+ pp->sloppyclockflag, &up->atom);
+ if (!up->ppsapi_lit) {
+ /* failed to configure, drop PPS unit */
+ time_pps_destroy(up->atom.handle);
+ msyslog(LOG_WARNING,
+ "%s set PPSAPI params fails",
+ refnumtoa(&peer->srcadr));
+ }
+ /* note: the PPS I/O handle remains valid until
+ * flag1 is cleared or the clock is shut down.
+ */
+ } else {
+ msyslog(LOG_WARNING,
+ "%s flag1 1 but PPSAPI fails",
+ refnumtoa(&peer->srcadr));
+ }
+ }
+
+ /* shut down PPS API if activated */
+ if (!(CLK_FLAG1 & pp->sloppyclockflag) && up->ppsapi_tried) {
+ /* shutdown PPS API */
+ if (up->ppsapi_lit)
+ time_pps_destroy(up->atom.handle);
+ up->atom.handle = 0;
+ /* close/drop PPS fd */
+ if (up->ppsapi_fd != pp->io.fd)
+ close(up->ppsapi_fd);
+ up->ppsapi_fd = -1;
+
+ /* clear markers and peer items */
+ up->ppsapi_gate = FALSE;
+ up->ppsapi_lit = FALSE;
+ up->ppsapi_tried = FALSE;
+
+ peer->flags &= ~FLAG_PPS;
+ peer->precision = PRECISION;
+ }
+}
+#endif /* HAVE_PPSAPI */
+
+/*
+ * -------------------------------------------------------------------
+ * nmea_timer - called once per second
+ * this only polls (older?) Oncore devices now
+ *
+ * Usually 'nmea_receive()' can get a timestamp every second, but at
+ * least one Motorola unit needs prompting each time. Doing so in
+ * 'nmea_poll()' gives only one sample per poll cycle, which actually
+ * defeats the purpose of the median filter. Polling once per second
+ * seems a much better idea.
+ * -------------------------------------------------------------------
+ */
+static void
+nmea_timer(
+ int unit,
+ struct peer * peer
+ )
+{
+#if NMEA_WRITE_SUPPORT
+
+ struct refclockproc * const pp = peer->procptr;
+
+ UNUSED_ARG(unit);
+
+ if (-1 != pp->io.fd) /* any mode bits to evaluate here? */
+ gps_send(pp->io.fd, "$PMOTG,RMC,0000*1D\r\n", peer);
+#else
+
+ UNUSED_ARG(unit);
+ UNUSED_ARG(peer);
+
+#endif /* NMEA_WRITE_SUPPORT */
+}
+
+#ifdef HAVE_PPSAPI
+/*
+ * -------------------------------------------------------------------
+ * refclock_ppsrelate(...) -- correlate with PPS edge
+ *
+ * This function is used to correlate a receive time stamp and a
+ * reference time with a PPS edge time stamp. It applies the necessary
+ * fudges (fudge1 for PPS, fudge2 for receive time) and then tries to
+ * move the receive time stamp to the corresponding edge. This can warp
+ * into future, if a transmission delay of more than 500ms is not
+ * compensated with a corresponding fudge time2 value, because then the
+ * next PPS edge is nearer than the last. (Similiar to what the PPS ATOM
+ * driver does, but we deal with full time stamps here, not just phase
+ * shift information.) Likewise, a negative fudge time2 value must be
+ * used if the reference time stamp correlates with the *following* PPS
+ * pulse.
+ *
+ * Note that the receive time fudge value only needs to move the receive
+ * stamp near a PPS edge but that close proximity is not required;
+ * +/-100ms precision should be enough. But since the fudge value will
+ * probably also be used to compensate the transmission delay when no
+ * PPS edge can be related to the time stamp, it's best to get it as
+ * close as possible.
+ *
+ * It should also be noted that the typical use case is matching to the
+ * preceeding edge, as most units relate their sentences to the current
+ * second.
+ *
+ * The function returns PPS_RELATE_NONE (0) if no PPS edge correlation
+ * can be fixed; PPS_RELATE_EDGE (1) when a PPS edge could be fixed, but
+ * the distance to the reference time stamp is too big (exceeds
+ * +/-400ms) and the ATOM driver PLL cannot be used to fix the phase;
+ * and PPS_RELATE_PHASE (2) when the ATOM driver PLL code can be used.
+ *
+ * On output, the receive time stamp is replaced with the corresponding
+ * PPS edge time if a fix could be made; the PPS fudge is updated to
+ * reflect the proper fudge time to apply. (This implies that
+ * 'refclock_process_offset()' must be used!)
+ * -------------------------------------------------------------------
+ */
+#define PPS_RELATE_NONE 0 /* no pps correlation possible */
+#define PPS_RELATE_EDGE 1 /* recv time fixed, no phase lock */
+#define PPS_RELATE_PHASE 2 /* recv time fixed, phase lock ok */
+
+static int
+refclock_ppsrelate(
+ const struct refclockproc * pp , /* for sanity */
+ const struct refclock_atom * ap , /* for PPS io */
+ const l_fp * reftime ,
+ l_fp * rd_stamp, /* i/o read stamp */
+ double pp_fudge, /* pps fudge */
+ double * rd_fudge /* i/o read fudge */
+ )
+{
+ pps_info_t pps_info;
+ struct timespec timeout;
+ l_fp pp_stamp, pp_delta;
+ double delta, idelta;
+
+ if (pp->leap == LEAP_NOTINSYNC)
+ return PPS_RELATE_NONE; /* clock is insane, no chance */
+
+ ZERO(timeout);
+ ZERO(pps_info);
+ if (time_pps_fetch(ap->handle, PPS_TSFMT_TSPEC,
+ &pps_info, &timeout) < 0)
+ return PPS_RELATE_NONE; /* can't get time stamps */
+
+ /* get last active PPS edge before receive */
+ if (ap->pps_params.mode & PPS_CAPTUREASSERT)
+ timeout = pps_info.assert_timestamp;
+ else if (ap->pps_params.mode & PPS_CAPTURECLEAR)
+ timeout = pps_info.clear_timestamp;
+ else
+ return PPS_RELATE_NONE; /* WHICH edge, please?!? */
+
+ /* get delta between receive time and PPS time */
+ pp_stamp = tspec_stamp_to_lfp(timeout);
+ pp_delta = *rd_stamp;
+ L_SUB(&pp_delta, &pp_stamp);
+ LFPTOD(&pp_delta, delta);
+ delta += pp_fudge - *rd_fudge;
+ if (fabs(delta) > 1.5)
+ return PPS_RELATE_NONE; /* PPS timeout control */
+
+ /* eventually warp edges, check phase */
+ idelta = floor(delta + 0.5);
+ pp_fudge -= idelta;
+ delta -= idelta;
+ if (fabs(delta) > 0.45)
+ return PPS_RELATE_NONE; /* dead band control */
+
+ /* we actually have a PPS edge to relate with! */
+ *rd_stamp = pp_stamp;
+ *rd_fudge = pp_fudge;
+
+ /* if whole system out-of-sync, do not try to PLL */
+ if (sys_leap == LEAP_NOTINSYNC)
+ return PPS_RELATE_EDGE; /* cannot PLL with atom code */
+
+ /* check against reftime if ATOM PLL can be used */
+ pp_delta = *reftime;
+ L_SUB(&pp_delta, &pp_stamp);
+ LFPTOD(&pp_delta, delta);
+ delta += pp_fudge;
+ if (fabs(delta) > 0.45)
+ return PPS_RELATE_EDGE; /* cannot PLL with atom code */
+
+ /* all checks passed, gets an AAA rating here! */
+ return PPS_RELATE_PHASE; /* can PLL with atom code */
+}
+#endif /* HAVE_PPSAPI */
+
+/*
+ * -------------------------------------------------------------------
+ * nmea_receive - receive data from the serial interface
+ *
+ * This is the workhorse for NMEA data evaluation:
+ *
+ * + it checks all NMEA data, and rejects sentences that are not valid
+ * NMEA sentences
+ * + it checks whether a sentence is known and to be used
+ * + it parses the time and date data from the NMEA data string and
+ * augments the missing bits. (century in dat, whole date, ...)
+ * + it rejects data that is not from the first accepted sentence in a
+ * burst
+ * + it eventually replaces the receive time with the PPS edge time.
+ * + it feeds the data to the internal processing stages.
+ * -------------------------------------------------------------------
+ */
+static void
+nmea_receive(
+ struct recvbuf * rbufp
+ )
+{
+ /* declare & init control structure ptrs */
+ struct peer * const peer = rbufp->recv_peer;
+ struct refclockproc * const pp = peer->procptr;
+ nmea_unit * const up = (nmea_unit*)pp->unitptr;
+
+ /* Use these variables to hold data until we decide its worth keeping */
+ nmea_data rdata;
+ char rd_lastcode[BMAX];
+ l_fp rd_timestamp, rd_reftime;
+ int rd_lencode;
+ double rd_fudge;
+
+ /* working stuff */
+ struct calendar date; /* to keep & convert the time stamp */
+ struct timespec tofs; /* offset to full-second reftime */
+ gps_weektm gpsw; /* week time storage */
+ /* results of sentence/date/time parsing */
+ u_char sentence; /* sentence tag */
+ int checkres;
+ char * cp;
+ int rc_date;
+ int rc_time;
+
+ /* make sure data has defined pristine state */
+ ZERO(tofs);
+ ZERO(date);
+ ZERO(gpsw);
+ sentence = 0;
+ rc_date = 0;
+ rc_time = 0;
+ /*
+ * Read the timecode and timestamp, then initialise field
+ * processing. The <CR><LF> at the NMEA line end is translated
+ * to <LF><LF> by the terminal input routines on most systems,
+ * and this gives us one spurious empty read per record which we
+ * better ignore silently.
+ */
+ rd_lencode = refclock_gtlin(rbufp, rd_lastcode,
+ sizeof(rd_lastcode), &rd_timestamp);
+ checkres = field_init(&rdata, rd_lastcode, rd_lencode);
+ switch (checkres) {
+
+ case CHECK_INVALID:
+ DPRINTF(1, ("%s invalid data: '%s'\n",
+ refnumtoa(&peer->srcadr), rd_lastcode));
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+
+ case CHECK_EMPTY:
+ return;
+
+ default:
+ DPRINTF(1, ("%s gpsread: %d '%s'\n",
+ refnumtoa(&peer->srcadr), rd_lencode,
+ rd_lastcode));
+ break;
+ }
+ up->tally.total++;
+
+ /*
+ * --> below this point we have a valid NMEA sentence <--
+ *
+ * Check sentence name. Skip first 2 chars (talker ID) in most
+ * cases, to allow for $GLGGA and $GPGGA etc. Since the name
+ * field has at least 5 chars we can simply shift the field
+ * start.
+ */
+ cp = field_parse(&rdata, 0);
+ if (strncmp(cp + 2, "RMC,", 4) == 0)
+ sentence = NMEA_GPRMC;
+ else if (strncmp(cp + 2, "GGA,", 4) == 0)
+ sentence = NMEA_GPGGA;
+ else if (strncmp(cp + 2, "GLL,", 4) == 0)
+ sentence = NMEA_GPGLL;
+ else if (strncmp(cp + 2, "ZDA,", 4) == 0)
+ sentence = NMEA_GPZDA;
+ else if (strncmp(cp + 2, "ZDG,", 4) == 0)
+ sentence = NMEA_GPZDG;
+ else if (strncmp(cp, "PGRMF,", 6) == 0)
+ sentence = NMEA_PGRMF;
+ else
+ return; /* not something we know about */
+
+ /* Eventually output delay measurement now. */
+ if (peer->ttl & NMEA_DELAYMEAS_MASK) {
+ mprintf_clock_stats(&peer->srcadr, "delay %0.6f %.*s",
+ ldexp(rd_timestamp.l_uf, -32),
+ (int)(strchr(rd_lastcode, ',') - rd_lastcode),
+ rd_lastcode);
+ }
+
+ /* See if I want to process this message type */
+ if ((peer->ttl & NMEA_MESSAGE_MASK) &&
+ !(peer->ttl & sentence_mode[sentence])) {
+ up->tally.filtered++;
+ return;
+ }
+
+ /*
+ * make sure it came in clean
+ *
+ * Apparently, older NMEA specifications (which are expensive)
+ * did not require the checksum for all sentences. $GPMRC is
+ * the only one so far identified which has always been required
+ * to include a checksum.
+ *
+ * Today, most NMEA GPS receivers checksum every sentence. To
+ * preserve its error-detection capabilities with modern GPSes
+ * while allowing operation without checksums on all but $GPMRC,
+ * we keep track of whether we've ever seen a valid checksum on
+ * a given sentence, and if so, reject future instances without
+ * checksum. ('up->cksum_type[NMEA_GPRMC]' is set in
+ * 'nmea_start()' to enforce checksums for $GPRMC right from the
+ * start.)
+ */
+ if (up->cksum_type[sentence] <= (u_char)checkres) {
+ up->cksum_type[sentence] = (u_char)checkres;
+ } else {
+ DPRINTF(1, ("%s checksum missing: '%s'\n",
+ refnumtoa(&peer->srcadr), rd_lastcode));
+ refclock_report(peer, CEVNT_BADREPLY);
+ up->tally.malformed++;
+ return;
+ }
+
+ /*
+ * $GPZDG provides GPS time not UTC, and the two mix poorly.
+ * Once have processed a $GPZDG, do not process any further UTC
+ * sentences (all but $GPZDG currently).
+ */
+ if (up->gps_time && NMEA_GPZDG != sentence) {
+ up->tally.filtered++;
+ return;
+ }
+
+ DPRINTF(1, ("%s processing %d bytes, timecode '%s'\n",
+ refnumtoa(&peer->srcadr), rd_lencode, rd_lastcode));
+
+ /*
+ * Grab fields depending on clock string type and possibly wipe
+ * sensitive data from the last timecode.
+ */
+ switch (sentence) {
+
+ case NMEA_GPRMC:
+ /* Check quality byte, fetch data & time */
+ rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 1);
+ pp->leap = parse_qual(&rdata, 2, 'A', 0);
+ rc_date = parse_date(&date, &rdata, 9, DATE_1_DDMMYY)
+ && unfold_century(&date, rd_timestamp.l_ui);
+ if (CLK_FLAG4 & pp->sloppyclockflag)
+ field_wipe(&rdata, 3, 4, 5, 6, -1);
+ break;
+
+ case NMEA_GPGGA:
+ /* Check quality byte, fetch time only */
+ rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 1);
+ pp->leap = parse_qual(&rdata, 6, '0', 1);
+ rc_date = unfold_day(&date, rd_timestamp.l_ui);
+ if (CLK_FLAG4 & pp->sloppyclockflag)
+ field_wipe(&rdata, 2, 4, -1);
+ break;
+
+ case NMEA_GPGLL:
+ /* Check quality byte, fetch time only */
+ rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 5);
+ pp->leap = parse_qual(&rdata, 6, 'A', 0);
+ rc_date = unfold_day(&date, rd_timestamp.l_ui);
+ if (CLK_FLAG4 & pp->sloppyclockflag)
+ field_wipe(&rdata, 1, 3, -1);
+ break;
+
+ case NMEA_GPZDA:
+ /* No quality. Assume best, fetch time & full date */
+ pp->leap = LEAP_NOWARNING;
+ rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 1);
+ rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY);
+ break;
+
+ case NMEA_GPZDG:
+ /* Check quality byte, fetch time & full date */
+ rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 1);
+ rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY);
+ pp->leap = parse_qual(&rdata, 4, '0', 1);
+ tofs.tv_sec = -1; /* GPZDG is following second */
+ break;
+
+ case NMEA_PGRMF:
+ /* get date, time, qualifier and GPS weektime. We need
+ * date and time-of-day for the century fix, so we read
+ * them first.
+ */
+ rc_date = parse_weekdata(&gpsw, &rdata, 1, 2, 5)
+ && parse_date(&date, &rdata, 3, DATE_1_DDMMYY);
+ rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 4);
+ pp->leap = parse_qual(&rdata, 11, '0', 1);
+ rc_date = rc_date
+ && gpsfix_century(&date, &gpsw, &up->century_cache);
+ if (CLK_FLAG4 & pp->sloppyclockflag)
+ field_wipe(&rdata, 6, 8, -1);
+ break;
+
+ default:
+ INVARIANT(0); /* Coverity 97123 */
+ return;
+ }
+
+ /* Check sanity of time-of-day. */
+ if (rc_time == 0) { /* no time or conversion error? */
+ checkres = CEVNT_BADTIME;
+ up->tally.malformed++;
+ }
+ /* Check sanity of date. */
+ else if (rc_date == 0) {/* no date or conversion error? */
+ checkres = CEVNT_BADDATE;
+ up->tally.malformed++;
+ }
+ /* check clock sanity; [bug 2143] */
+ else if (pp->leap == LEAP_NOTINSYNC) { /* no good status? */
+ checkres = CEVNT_BADREPLY;
+ up->tally.rejected++;
+ }
+ else
+ checkres = -1;
+
+ if (checkres != -1) {
+ save_ltc(pp, rd_lastcode, rd_lencode);
+ refclock_report(peer, checkres);
+ return;
+ }
+
+ DPRINTF(1, ("%s effective timecode: %04u-%02u-%02u %02d:%02d:%02d\n",
+ refnumtoa(&peer->srcadr),
+ date.year, date.month, date.monthday,
+ date.hour, date.minute, date.second));
+
+ /* Check if we must enter GPS time mode; log so if we do */
+ if (!up->gps_time && (sentence == NMEA_GPZDG)) {
+ msyslog(LOG_INFO, "%s using GPS time as if it were UTC",
+ refnumtoa(&peer->srcadr));
+ up->gps_time = 1;
+ }
+
+ /*
+ * Get the reference time stamp from the calendar buffer.
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp, but only if the PPS is not in control.
+ * Discard sentence if reference time did not change.
+ */
+ rd_reftime = eval_gps_time(peer, &date, &tofs, &rd_timestamp);
+ if (L_ISEQU(&up->last_reftime, &rd_reftime)) {
+ /* Do not touch pp->a_lastcode on purpose! */
+ up->tally.filtered++;
+ return;
+ }
+ up->last_reftime = rd_reftime;
+ rd_fudge = pp->fudgetime2;
+
+ DPRINTF(1, ("%s using '%s'\n",
+ refnumtoa(&peer->srcadr), rd_lastcode));
+
+ /* Data will be accepted. Update stats & log data. */
+ up->tally.accepted++;
+ save_ltc(pp, rd_lastcode, rd_lencode);
+ pp->lastrec = rd_timestamp;
+
+#ifdef HAVE_PPSAPI
+ /*
+ * If we have PPS running, we try to associate the sentence
+ * with the last active edge of the PPS signal.
+ */
+ if (up->ppsapi_lit)
+ switch (refclock_ppsrelate(
+ pp, &up->atom, &rd_reftime, &rd_timestamp,
+ pp->fudgetime1, &rd_fudge))
+ {
+ case PPS_RELATE_PHASE:
+ up->ppsapi_gate = TRUE;
+ peer->precision = PPS_PRECISION;
+ peer->flags |= FLAG_PPS;
+ DPRINTF(2, ("%s PPS_RELATE_PHASE\n",
+ refnumtoa(&peer->srcadr)));
+ up->tally.pps_used++;
+ break;
+
+ case PPS_RELATE_EDGE:
+ up->ppsapi_gate = TRUE;
+ peer->precision = PPS_PRECISION;
+ DPRINTF(2, ("%s PPS_RELATE_EDGE\n",
+ refnumtoa(&peer->srcadr)));
+ break;
+
+ case PPS_RELATE_NONE:
+ default:
+ /*
+ * Resetting precision and PPS flag is done in
+ * 'nmea_poll', since it might be a glitch. But
+ * at the end of the poll cycle we know...
+ */
+ DPRINTF(2, ("%s PPS_RELATE_NONE\n",
+ refnumtoa(&peer->srcadr)));
+ break;
+ }
+#endif /* HAVE_PPSAPI */
+
+ refclock_process_offset(pp, rd_reftime, rd_timestamp, rd_fudge);
+}
+
+
+/*
+ * -------------------------------------------------------------------
+ * nmea_poll - called by the transmit procedure
+ *
+ * Does the necessary bookkeeping stuff to keep the reported state of
+ * the clock in sync with reality.
+ *
+ * We go to great pains to avoid changing state here, since there may
+ * be more than one eavesdropper receiving the same timecode.
+ * -------------------------------------------------------------------
+ */
+static void
+nmea_poll(
+ int unit,
+ struct peer * peer
+ )
+{
+ struct refclockproc * const pp = peer->procptr;
+ nmea_unit * const up = (nmea_unit *)pp->unitptr;
+
+ /*
+ * Process median filter samples. If none received, declare a
+ * timeout and keep going.
+ */
+#ifdef HAVE_PPSAPI
+ /*
+ * If we don't have PPS pulses and time stamps, turn PPS down
+ * for now.
+ */
+ if (!up->ppsapi_gate) {
+ peer->flags &= ~FLAG_PPS;
+ peer->precision = PRECISION;
+ } else {
+ up->ppsapi_gate = FALSE;
+ }
+#endif /* HAVE_PPSAPI */
+
+ /*
+ * If the median filter is empty, claim a timeout. Else process
+ * the input data and keep the stats going.
+ */
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ } else {
+ pp->polls++;
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ }
+
+ /*
+ * If extended logging is required, write the tally stats to the
+ * clockstats file; otherwise just do a normal clock stats
+ * record. Clear the tally stats anyway.
+ */
+ if (peer->ttl & NMEA_EXTLOG_MASK) {
+ /* Log & reset counters with extended logging */
+ char *nmea = pp->a_lastcode;
+ if (*nmea == '\0') nmea = "(none)";
+ mprintf_clock_stats(
+ &peer->srcadr, "%s %u %u %u %u %u %u",
+ nmea,
+ up->tally.total, up->tally.accepted,
+ up->tally.rejected, up->tally.malformed,
+ up->tally.filtered, up->tally.pps_used);
+ } else {
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ }
+ ZERO(up->tally);
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Save the last timecode string, making sure it's properly truncated
+ * if necessary and NUL terminated in any case.
+ */
+static void
+save_ltc(
+ struct refclockproc * const pp,
+ const char * const tc,
+ size_t len
+ )
+{
+ if (len >= sizeof(pp->a_lastcode))
+ len = sizeof(pp->a_lastcode) - 1;
+ pp->lencode = (u_short)len;
+ memcpy(pp->a_lastcode, tc, len);
+ pp->a_lastcode[len] = '\0';
+}
+
+
+#if NMEA_WRITE_SUPPORT
+/*
+ * -------------------------------------------------------------------
+ * gps_send(fd, cmd, peer) Sends a command to the GPS receiver.
+ * as in gps_send(fd, "rqts,u", peer);
+ *
+ * If 'cmd' starts with a '$' it is assumed that this command is in raw
+ * format, that is, starts with '$', ends with '<cr><lf>' and that any
+ * checksum is correctly provided; the command will be send 'as is' in
+ * that case. Otherwise the function will create the necessary frame
+ * (start char, chksum, final CRLF) on the fly.
+ *
+ * We don't currently send any data, but would like to send RTCM SC104
+ * messages for differential positioning. It should also give us better
+ * time. Without a PPS output, we're Just fooling ourselves because of
+ * the serial code paths
+ * -------------------------------------------------------------------
+ */
+static void
+gps_send(
+ int fd,
+ const char * cmd,
+ struct peer * peer
+ )
+{
+ /* $...*xy<CR><LF><NUL> add 7 */
+ char buf[NMEA_PROTO_MAXLEN + 7];
+ int len;
+ u_char dcs;
+ const u_char *beg, *end;
+
+ if (*cmd != '$') {
+ /* get checksum and length */
+ beg = end = (const u_char*)cmd;
+ dcs = 0;
+ while (*end >= ' ' && *end != '*')
+ dcs ^= *end++;
+ len = end - beg;
+ /* format into output buffer with overflow check */
+ len = snprintf(buf, sizeof(buf), "$%.*s*%02X\r\n",
+ len, beg, dcs);
+ if ((size_t)len >= sizeof(buf)) {
+ DPRINTF(1, ("%s gps_send: buffer overflow for command '%s'\n",
+ refnumtoa(&peer->srcadr), cmd));
+ return; /* game over player 1 */
+ }
+ cmd = buf;
+ } else {
+ len = strlen(cmd);
+ }
+
+ DPRINTF(1, ("%s gps_send: '%.*s'\n", refnumtoa(&peer->srcadr),
+ len - 2, cmd));
+
+ /* send out the whole stuff */
+ if (write(fd, cmd, len) == -1)
+ refclock_report(peer, CEVNT_FAULT);
+}
+#endif /* NMEA_WRITE_SUPPORT */
+
+/*
+ * -------------------------------------------------------------------
+ * helpers for faster field splitting
+ * -------------------------------------------------------------------
+ *
+ * set up a field record, check syntax and verify checksum
+ *
+ * format is $XXXXX,1,2,3,4*ML
+ *
+ * 8-bit XOR of characters between $ and * noninclusive is transmitted
+ * in last two chars M and L holding most and least significant nibbles
+ * in hex representation such as:
+ *
+ * $GPGLL,5057.970,N,00146.110,E,142451,A*27
+ * $GPVTG,089.0,T,,,15.2,N,,*7F
+ *
+ * Some other constraints:
+ * + The field name must at least 5 upcase characters or digits and must
+ * start with a character.
+ * + The checksum (if present) must be uppercase hex digits.
+ * + The length of a sentence is limited to 80 characters (not including
+ * the final CR/LF nor the checksum, but including the leading '$')
+ *
+ * Return values:
+ * + CHECK_INVALID
+ * The data does not form a valid NMEA sentence or a checksum error
+ * occurred.
+ * + CHECK_VALID
+ * The data is a valid NMEA sentence but contains no checksum.
+ * + CHECK_CSVALID
+ * The data is a valid NMEA sentence and passed the checksum test.
+ * -------------------------------------------------------------------
+ */
+static int
+field_init(
+ nmea_data * data, /* context structure */
+ char * cptr, /* start of raw data */
+ int dlen /* data len, not counting trailing NUL */
+ )
+{
+ u_char cs_l; /* checksum local computed */
+ u_char cs_r; /* checksum remote given */
+ char * eptr; /* buffer end end pointer */
+ char tmp; /* char buffer */
+
+ cs_l = 0;
+ cs_r = 0;
+ /* some basic input constraints */
+ if (dlen < 0)
+ dlen = 0;
+ eptr = cptr + dlen;
+ *eptr = '\0';
+
+ /* load data context */
+ data->base = cptr;
+ data->cptr = cptr;
+ data->cidx = 0;
+ data->blen = dlen;
+
+ /* syntax check follows here. check allowed character
+ * sequences, updating the local computed checksum as we go.
+ *
+ * regex equiv: '^\$[A-Z][A-Z0-9]{4,}[^*]*(\*[0-9A-F]{2})?$'
+ */
+
+ /* -*- start character: '^\$' */
+ if (*cptr == '\0')
+ return CHECK_EMPTY;
+ if (*cptr++ != '$')
+ return CHECK_INVALID;
+
+ /* -*- advance context beyond start character */
+ data->base++;
+ data->cptr++;
+ data->blen--;
+
+ /* -*- field name: '[A-Z][A-Z0-9]{4,},' */
+ if (*cptr < 'A' || *cptr > 'Z')
+ return CHECK_INVALID;
+ cs_l ^= *cptr++;
+ while ((*cptr >= 'A' && *cptr <= 'Z') ||
+ (*cptr >= '0' && *cptr <= '9') )
+ cs_l ^= *cptr++;
+ if (*cptr != ',' || (cptr - data->base) < NMEA_PROTO_IDLEN)
+ return CHECK_INVALID;
+ cs_l ^= *cptr++;
+
+ /* -*- data: '[^*]*' */
+ while (*cptr && *cptr != '*')
+ cs_l ^= *cptr++;
+
+ /* -*- checksum field: (\*[0-9A-F]{2})?$ */
+ if (*cptr == '\0')
+ return CHECK_VALID;
+ if (*cptr != '*' || cptr != eptr - 3 ||
+ (cptr - data->base) >= NMEA_PROTO_MAXLEN)
+ return CHECK_INVALID;
+
+ for (cptr++; (tmp = *cptr) != '\0'; cptr++) {
+ if (tmp >= '0' && tmp <= '9')
+ cs_r = (cs_r << 4) + (tmp - '0');
+ else if (tmp >= 'A' && tmp <= 'F')
+ cs_r = (cs_r << 4) + (tmp - 'A' + 10);
+ else
+ break;
+ }
+
+ /* -*- make sure we are at end of string and csum matches */
+ if (cptr != eptr || cs_l != cs_r)
+ return CHECK_INVALID;
+
+ return CHECK_CSVALID;
+}
+
+/*
+ * -------------------------------------------------------------------
+ * fetch a data field by index, zero being the name field. If this
+ * function is called repeatedly with increasing indices, the total load
+ * is O(n), n being the length of the string; if it is called with
+ * decreasing indices, the total load is O(n^2). Try not to go backwards
+ * too often.
+ * -------------------------------------------------------------------
+ */
+static char *
+field_parse(
+ nmea_data * data,
+ int fn
+ )
+{
+ char tmp;
+
+ if (fn < data->cidx) {
+ data->cidx = 0;
+ data->cptr = data->base;
+ }
+ while ((fn > data->cidx) && (tmp = *data->cptr) != '\0') {
+ data->cidx += (tmp == ',');
+ data->cptr++;
+ }
+ return data->cptr;
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Wipe (that is, overwrite with '_') data fields and the checksum in
+ * the last timecode. The list of field indices is given as integers
+ * in a varargs list, preferrably in ascending order, in any case
+ * terminated by a negative field index.
+ *
+ * A maximum number of 8 fields can be overwritten at once to guard
+ * against runaway (that is, unterminated) argument lists.
+ *
+ * This function affects what a remote user can see with
+ *
+ * ntpq -c clockvar <server>
+ *
+ * Note that this also removes the wiped fields from any clockstats
+ * log. Some NTP operators monitor their NMEA GPS using the change in
+ * location in clockstats over time as as a proxy for the quality of
+ * GPS reception and thereby time reported.
+ * -------------------------------------------------------------------
+ */
+static void
+field_wipe(
+ nmea_data * data,
+ ...
+ )
+{
+ va_list va; /* vararg index list */
+ int fcnt; /* safeguard against runaway arglist */
+ int fidx; /* field to nuke, or -1 for checksum */
+ char * cp; /* overwrite destination */
+
+ fcnt = 8;
+ cp = NULL;
+ va_start(va, data);
+ do {
+ fidx = va_arg(va, int);
+ if (fidx >= 0 && fidx <= NMEA_PROTO_FIELDS) {
+ cp = field_parse(data, fidx);
+ } else {
+ cp = data->base + data->blen;
+ if (data->blen >= 3 && cp[-3] == '*')
+ cp -= 2;
+ }
+ for ( ; '\0' != *cp && '*' != *cp && ',' != *cp; cp++)
+ if ('.' != *cp)
+ *cp = '_';
+ } while (fcnt-- && fidx >= 0);
+ va_end(va);
+}
+
+/*
+ * -------------------------------------------------------------------
+ * PARSING HELPERS
+ * -------------------------------------------------------------------
+ *
+ * Check sync status
+ *
+ * If the character at the data field start matches the tag value,
+ * return LEAP_NOWARNING and LEAP_NOTINSYNC otherwise. If the 'inverted'
+ * flag is given, just the opposite value is returned. If there is no
+ * data field (*cp points to the NUL byte) the result is LEAP_NOTINSYNC.
+ * -------------------------------------------------------------------
+ */
+static u_char
+parse_qual(
+ nmea_data * rd,
+ int idx,
+ char tag,
+ int inv
+ )
+{
+ static const u_char table[2] =
+ { LEAP_NOTINSYNC, LEAP_NOWARNING };
+ char * dp;
+
+ dp = field_parse(rd, idx);
+
+ return table[ *dp && ((*dp == tag) == !inv) ];
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Parse a time stamp in HHMMSS[.sss] format with error checking.
+ *
+ * returns 1 on success, 0 on failure
+ * -------------------------------------------------------------------
+ */
+static int
+parse_time(
+ struct calendar * jd, /* result calendar pointer */
+ long * ns, /* storage for nsec fraction */
+ nmea_data * rd,
+ int idx
+ )
+{
+ static const unsigned long weight[4] = {
+ 0, 100000000, 10000000, 1000000
+ };
+
+ int rc;
+ u_int h;
+ u_int m;
+ u_int s;
+ int p1;
+ int p2;
+ u_long f;
+ char * dp;
+
+ dp = field_parse(rd, idx);
+ rc = sscanf(dp, "%2u%2u%2u%n.%3lu%n", &h, &m, &s, &p1, &f, &p2);
+ if (rc < 3 || p1 != 6) {
+ DPRINTF(1, ("nmea: invalid time code: '%.6s'\n", dp));
+ return FALSE;
+ }
+
+ /* value sanity check */
+ if (h > 23 || m > 59 || s > 60) {
+ DPRINTF(1, ("nmea: invalid time spec %02u:%02u:%02u\n",
+ h, m, s));
+ return FALSE;
+ }
+
+ jd->hour = (u_char)h;
+ jd->minute = (u_char)m;
+ jd->second = (u_char)s;
+ /* if we have a fraction, scale it up to nanoseconds. */
+ if (rc == 4)
+ *ns = f * weight[p2 - p1 - 1];
+ else
+ *ns = 0;
+
+ return TRUE;
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Parse a date string from an NMEA sentence. This could either be a
+ * partial date in DDMMYY format in one field, or DD,MM,YYYY full date
+ * spec spanning three fields. This function does some extensive error
+ * checking to make sure the date string was consistent.
+ *
+ * returns 1 on success, 0 on failure
+ * -------------------------------------------------------------------
+ */
+static int
+parse_date(
+ struct calendar * jd, /* result pointer */
+ nmea_data * rd,
+ int idx,
+ enum date_fmt fmt
+ )
+{
+ int rc;
+ u_int y;
+ u_int m;
+ u_int d;
+ int p;
+ char * dp;
+
+ dp = field_parse(rd, idx);
+ switch (fmt) {
+
+ case DATE_1_DDMMYY:
+ rc = sscanf(dp, "%2u%2u%2u%n", &d, &m, &y, &p);
+ if (rc != 3 || p != 6) {
+ DPRINTF(1, ("nmea: invalid date code: '%.6s'\n",
+ dp));
+ return FALSE;
+ }
+ break;
+
+ case DATE_3_DDMMYYYY:
+ rc = sscanf(dp, "%2u,%2u,%4u%n", &d, &m, &y, &p);
+ if (rc != 3 || p != 10) {
+ DPRINTF(1, ("nmea: invalid date code: '%.10s'\n",
+ dp));
+ return FALSE;
+ }
+ break;
+
+ default:
+ DPRINTF(1, ("nmea: invalid parse format: %d\n", fmt));
+ return FALSE;
+ }
+
+ /* value sanity check */
+ if (d < 1 || d > 31 || m < 1 || m > 12) {
+ DPRINTF(1, ("nmea: invalid date spec (YMD) %04u:%02u:%02u\n",
+ y, m, d));
+ return FALSE;
+ }
+
+ /* store results */
+ jd->monthday = (u_char)d;
+ jd->month = (u_char)m;
+ jd->year = (u_short)y;
+
+ return TRUE;
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Parse GPS week time info from an NMEA sentence. This info contains
+ * the GPS week number, the GPS time-of-week and the leap seconds GPS
+ * to UTC.
+ *
+ * returns 1 on success, 0 on failure
+ * -------------------------------------------------------------------
+ */
+static int
+parse_weekdata(
+ gps_weektm * wd,
+ nmea_data * rd,
+ int weekidx,
+ int timeidx,
+ int leapidx
+ )
+{
+ u_long secs;
+ int fcnt;
+
+ /* parse fields and count success */
+ fcnt = sscanf(field_parse(rd, weekidx), "%hu", &wd->wt_week);
+ fcnt += sscanf(field_parse(rd, timeidx), "%lu", &secs);
+ fcnt += sscanf(field_parse(rd, leapidx), "%hd", &wd->wt_leap);
+ if (fcnt != 3 || wd->wt_week >= 1024 || secs >= 7*SECSPERDAY) {
+ DPRINTF(1, ("nmea: parse_weekdata: invalid weektime spec\n"));
+ return FALSE;
+ }
+ wd->wt_time = (u_int32)secs;
+
+ return TRUE;
+}
+
+/*
+ * -------------------------------------------------------------------
+ * funny calendar-oriented stuff -- perhaps a bit hard to grok.
+ * -------------------------------------------------------------------
+ *
+ * Unfold a time-of-day (seconds since midnight) around the current
+ * system time in a manner that guarantees an absolute difference of
+ * less than 12hrs.
+ *
+ * This function is used for NMEA sentences that contain no date
+ * information. This requires the system clock to be in +/-12hrs
+ * around the true time, or the clock will synchronize the system 1day
+ * off if not augmented with a time sources that also provide the
+ * necessary date information.
+ *
+ * The function updates the calendar structure it also uses as
+ * input to fetch the time from.
+ *
+ * returns 1 on success, 0 on failure
+ * -------------------------------------------------------------------
+ */
+static int
+unfold_day(
+ struct calendar * jd,
+ u_int32 rec_ui
+ )
+{
+ vint64 rec_qw;
+ ntpcal_split rec_ds;
+
+ /*
+ * basically this is the peridiodic extension of the receive
+ * time - 12hrs to the time-of-day with a period of 1 day.
+ * But we would have to execute this in 64bit arithmetic, and we
+ * cannot assume we can do this; therefore this is done
+ * in split representation.
+ */
+ rec_qw = ntpcal_ntp_to_ntp(rec_ui - SECSPERDAY/2, NULL);
+ rec_ds = ntpcal_daysplit(&rec_qw);
+ rec_ds.lo = ntpcal_periodic_extend(rec_ds.lo,
+ ntpcal_date_to_daysec(jd),
+ SECSPERDAY);
+ rec_ds.hi += ntpcal_daysec_to_date(jd, rec_ds.lo);
+ return (ntpcal_rd_to_date(jd, rec_ds.hi + DAY_NTP_STARTS) >= 0);
+}
+
+/*
+ * -------------------------------------------------------------------
+ * A 2-digit year is expanded into full year spec around the year found
+ * in 'jd->year'. This should be in +79/-19 years around the system time,
+ * or the result will be off by 100 years. The assymetric behaviour was
+ * chosen to enable inital sync for systems that do not have a
+ * battery-backup clock and start with a date that is typically years in
+ * the past.
+ *
+ * Since the GPS epoch starts at 1980-01-06, the resulting year will be
+ * not be before 1980 in any case.
+ *
+ * returns 1 on success, 0 on failure
+ * -------------------------------------------------------------------
+ */
+static int
+unfold_century(
+ struct calendar * jd,
+ u_int32 rec_ui
+ )
+{
+ struct calendar rec;
+ int32 baseyear;
+
+ ntpcal_ntp_to_date(&rec, rec_ui, NULL);
+ baseyear = rec.year - 20;
+ if (baseyear < g_gpsMinYear)
+ baseyear = g_gpsMinYear;
+ jd->year = (u_short)ntpcal_periodic_extend(baseyear, jd->year,
+ 100);
+
+ return ((baseyear <= jd->year) && (baseyear + 100 > jd->year));
+}
+
+/*
+ * -------------------------------------------------------------------
+ * A 2-digit year is expanded into a full year spec by correlation with
+ * a GPS week number and the current leap second count.
+ *
+ * The GPS week time scale counts weeks since Sunday, 1980-01-06, modulo
+ * 1024 and seconds since start of the week. The GPS time scale is based
+ * on international atomic time (TAI), so the leap second difference to
+ * UTC is also needed for a proper conversion.
+ *
+ * A brute-force analysis (that is, test for every date) shows that a
+ * wrong assignment of the century can not happen between the years 1900
+ * to 2399 when comparing the week signatures for different
+ * centuries. (I *think* that will not happen for 400*1024 years, but I
+ * have no valid proof. -*-perlinger@ntp.org-*-)
+ *
+ * This function is bound to to work between years 1980 and 2399
+ * (inclusive), which should suffice for now ;-)
+ *
+ * Note: This function needs a full date&time spec on input due to the
+ * necessary leap second corrections!
+ *
+ * returns 1 on success, 0 on failure
+ * -------------------------------------------------------------------
+ */
+static int
+gpsfix_century(
+ struct calendar * jd,
+ const gps_weektm * wd,
+ u_short * century
+ )
+{
+ int32 days;
+ int32 doff;
+ u_short week;
+ u_short year;
+ int loop;
+
+ /* Get day offset. Assumes that the input time is in range and
+ * that the leap seconds do not shift more than +/-1 day.
+ */
+ doff = ntpcal_date_to_daysec(jd) + wd->wt_leap;
+ doff = (doff >= SECSPERDAY) - (doff < 0);
+
+ /*
+ * Loop over centuries to get a match, starting with the last
+ * successful one. (Or with the 19th century if the cached value
+ * is out of range...)
+ */
+ year = jd->year % 100;
+ for (loop = 5; loop > 0; loop--,(*century)++) {
+ if (*century < 19 || *century >= 24)
+ *century = 19;
+ /* Get days and week in GPS epoch */
+ jd->year = year + *century * 100;
+ days = ntpcal_date_to_rd(jd) - DAY_GPS_STARTS + doff;
+ week = (days / 7) % 1024;
+ if (days >= 0 && wd->wt_week == week)
+ return TRUE; /* matched... */
+ }
+
+ jd->year = year;
+ return FALSE; /* match failed... */
+}
+
+/*
+ * -------------------------------------------------------------------
+ * And now the final execise: Considering the fact that many (most?)
+ * GPS receivers cannot handle a GPS epoch wrap well, we try to
+ * compensate for that problem by unwrapping a GPS epoch around the
+ * receive stamp. Another execise in periodic unfolding, of course,
+ * but with enough points to take care of.
+ *
+ * Note: The integral part of 'tofs' is intended to handle small(!)
+ * systematic offsets, as -1 for handling $GPZDG, which gives the
+ * following second. (sigh...) The absolute value shall be less than a
+ * day (86400 seconds).
+ * -------------------------------------------------------------------
+ */
+static l_fp
+eval_gps_time(
+ struct peer * peer, /* for logging etc */
+ const struct calendar * gpst, /* GPS time stamp */
+ const struct timespec * tofs, /* GPS frac second & offset */
+ const l_fp * recv /* receive time stamp */
+ )
+{
+ struct refclockproc * const pp = peer->procptr;
+ nmea_unit * const up = (nmea_unit *)pp->unitptr;
+
+ l_fp retv;
+
+ /* components of calculation */
+ int32_t rcv_sec, rcv_day; /* receive ToD and day */
+ int32_t gps_sec, gps_day; /* GPS ToD and day in NTP epoch */
+ int32_t adj_day, weeks; /* adjusted GPS day and week shift */
+
+ /* some temporaries to shuffle data */
+ vint64 vi64;
+ ntpcal_split rs64;
+
+ /* evaluate time stamp from receiver. */
+ gps_sec = ntpcal_date_to_daysec(gpst);
+ gps_day = ntpcal_date_to_rd(gpst) - DAY_NTP_STARTS;
+
+ /* merge in fractional offset */
+ retv = tspec_intv_to_lfp(*tofs);
+ gps_sec += retv.l_i;
+
+ /* If we fully trust the GPS receiver, just combine days and
+ * seconds and be done. */
+ if (peer->ttl & NMEA_DATETRUST_MASK) {
+ retv.l_ui = ntpcal_dayjoin(gps_day, gps_sec).D_s.lo;
+ return retv;
+ }
+
+ /* So we do not trust the GPS receiver to deliver a correct date
+ * due to the GPS epoch changes. We map the date from the
+ * receiver into the +/-512 week interval around the receive
+ * time in that case. This would be a tad easier with 64bit
+ * calculations, but again, we restrict the code to 32bit ops
+ * when possible. */
+
+ /* - make sure the GPS fractional day is normalised
+ * Applying the offset value might have put us slightly over the
+ * edge of the allowed range for seconds-of-day. Doing a full
+ * division with floor correction is overkill here; a simple
+ * addition or subtraction step is sufficient. Using WHILE loops
+ * gives the right result even if the offset exceeds one day,
+ * which is NOT what it's intented for! */
+ while (gps_sec >= SECSPERDAY) {
+ gps_sec -= SECSPERDAY;
+ gps_day += 1;
+ }
+ while (gps_sec < 0) {
+ gps_sec += SECSPERDAY;
+ gps_day -= 1;
+ }
+
+ /* - get unfold base: day of full recv time - 512 weeks */
+ vi64 = ntpcal_ntp_to_ntp(recv->l_ui, NULL);
+ rs64 = ntpcal_daysplit(&vi64);
+ rcv_sec = rs64.lo;
+ rcv_day = rs64.hi - 512 * 7;
+
+ /* - take the fractional days into account
+ * If the fractional day of the GPS time is smaller than the
+ * fractional day of the receive time, we shift the base day for
+ * the unfold by 1. */
+ if ( gps_sec < rcv_sec
+ || (gps_sec == rcv_sec && retv.l_uf < recv->l_uf))
+ rcv_day += 1;
+
+ /* - don't warp ahead of GPS invention! */
+ if (rcv_day < g_gpsMinBase)
+ rcv_day = g_gpsMinBase;
+
+ /* - let the magic happen: */
+ adj_day = ntpcal_periodic_extend(rcv_day, gps_day, 1024*7);
+
+ /* - check if we should log a GPS epoch warp */
+ weeks = (adj_day - gps_day) / 7;
+ if (weeks != up->epoch_warp) {
+ up->epoch_warp = weeks;
+ LOGIF(CLOCKINFO, (LOG_INFO,
+ "%s Changed GPS epoch warp to %d weeks",
+ refnumtoa(&peer->srcadr), weeks));
+ }
+
+ /* - build result and be done */
+ retv.l_ui = ntpcal_dayjoin(adj_day, gps_sec).D_s.lo;
+ return retv;
+}
+
+/*
+ * ===================================================================
+ *
+ * NMEAD support
+ *
+ * original nmead support added by Jon Miner (cp_n18@yahoo.com)
+ *
+ * See http://home.hiwaay.net/~taylorc/gps/nmea-server/
+ * for information about nmead
+ *
+ * To use this, you need to create a link from /dev/gpsX to
+ * the server:port where nmead is running. Something like this:
+ *
+ * ln -s server:port /dev/gps1
+ *
+ * Split into separate function by Juergen Perlinger
+ * (perlinger-at-ntp-dot-org)
+ *
+ * ===================================================================
+ */
+static int
+nmead_open(
+ const char * device
+ )
+{
+ int fd = -1; /* result file descriptor */
+
+#ifdef HAVE_READLINK
+ char host[80]; /* link target buffer */
+ char * port; /* port name or number */
+ int rc; /* result code (several)*/
+ int sh; /* socket handle */
+ struct addrinfo ai_hint; /* resolution hint */
+ struct addrinfo *ai_list; /* resolution result */
+ struct addrinfo *ai; /* result scan ptr */
+
+ fd = -1;
+
+ /* try to read as link, make sure no overflow occurs */
+ rc = readlink(device, host, sizeof(host));
+ if ((size_t)rc >= sizeof(host))
+ return fd; /* error / overflow / truncation */
+ host[rc] = '\0'; /* readlink does not place NUL */
+
+ /* get port */
+ port = strchr(host, ':');
+ if (!port)
+ return fd; /* not 'host:port' syntax ? */
+ *port++ = '\0'; /* put in separator */
+
+ /* get address infos and try to open socket
+ *
+ * This getaddrinfo() is naughty in ntpd's nonblocking main
+ * thread, but you have to go out of your wary to use this code
+ * and typically the blocking is at startup where its impact is
+ * reduced. The same holds for the 'connect()', as it is
+ * blocking, too...
+ */
+ ZERO(ai_hint);
+ ai_hint.ai_protocol = IPPROTO_TCP;
+ ai_hint.ai_socktype = SOCK_STREAM;
+ if (getaddrinfo(host, port, &ai_hint, &ai_list))
+ return fd;
+
+ for (ai = ai_list; ai && (fd == -1); ai = ai->ai_next) {
+ sh = socket(ai->ai_family, ai->ai_socktype,
+ ai->ai_protocol);
+ if (INVALID_SOCKET == sh)
+ continue;
+ rc = connect(sh, ai->ai_addr, ai->ai_addrlen);
+ if (-1 != rc)
+ fd = sh;
+ else
+ close(sh);
+ }
+ freeaddrinfo(ai_list);
+#else
+ fd = -1;
+#endif
+
+ return fd;
+}
+#else
+NONEMPTY_TRANSLATION_UNIT
+#endif /* REFCLOCK && CLOCK_NMEA */
diff --git a/ntpd/refclock_oncore.c b/ntpd/refclock_oncore.c
new file mode 100644
index 0000000..3bc60bf
--- /dev/null
+++ b/ntpd/refclock_oncore.c
@@ -0,0 +1,4083 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * refclock_oncore.c
+ *
+ * Driver for some of the various the Motorola Oncore GPS receivers.
+ * should work with Basic, PVT6, VP, UT, UT+, GT, GT+, SL, M12, M12+T
+ * The receivers with TRAIM (VP, UT, UT+, M12+T), will be more accurate
+ * than the others.
+ * The receivers without position hold (GT, GT+) will be less accurate.
+ *
+ * Tested with:
+ *
+ * (UT) (VP)
+ * COPYRIGHT 1991-1997 MOTOROLA INC. COPYRIGHT 1991-1996 MOTOROLA INC.
+ * SFTW P/N # 98-P36848P SFTW P/N # 98-P36830P
+ * SOFTWARE VER # 2 SOFTWARE VER # 8
+ * SOFTWARE REV # 2 SOFTWARE REV # 8
+ * SOFTWARE DATE APR 24 1998 SOFTWARE DATE 06 Aug 1996
+ * MODEL # R1121N1114 MODEL # B4121P1155
+ * HWDR P/N # 1 HDWR P/N # _
+ * SERIAL # R0010A SERIAL # SSG0226478
+ * MANUFACTUR DATE 6H07 MANUFACTUR DATE 7E02
+ * OPTIONS LIST IB
+ *
+ * (Basic) (M12)
+ * COPYRIGHT 1991-1994 MOTOROLA INC. COPYRIGHT 1991-2000 MOTOROLA INC.
+ * SFTW P/N # 98-P39949M SFTW P/N # 61-G10002A
+ * SOFTWARE VER # 5 SOFTWARE VER # 1
+ * SOFTWARE REV # 0 SOFTWARE REV # 3
+ * SOFTWARE DATE 20 JAN 1994 SOFTWARE DATE Mar 13 2000
+ * MODEL # A11121P116 MODEL # P143T12NR1
+ * HDWR P/N # _ HWDR P/N # 1
+ * SERIAL # SSG0049809 SERIAL # P003UD
+ * MANUFACTUR DATE 417AMA199 MANUFACTUR DATE 0C27
+ * OPTIONS LIST AB
+ *
+ * (M12+T) (M12+T later version)
+ * COPYRIGHT 1991-2002 MOTOROLA INC. COPYRIGHT 1991-2003 MOTOROLA INC.
+ * SFTW P/N # 61-G10268A SFTW P/N # 61-G10268A
+ * SOFTWARE VER # 2 SOFTWARE VER # 2
+ * SOFTWARE REV # 0 SOFTWARE REV # 1
+ * SOFTWARE DATE AUG 14 2002 SOFTWARE DATE APR 16 2003
+ * MODEL # P283T12T11 MODEL # P273T12T12
+ * HWDR P/N # 2 HWDR P/N # 2
+ * SERIAL # P04DC2 SERIAL # P05Z7Z
+ * MANUFACTUR DATE 2J17 MANUFACTUR DATE 3G15
+ *
+ * --------------------------------------------------------------------------
+ * Reg Clemens (June 2009)
+ * BUG[1220] OK, big patch, but mostly done mechanically. Change direct calls to write
+ * to clockstats to a call to oncore_log, which now calls the old routine plus msyslog.
+ * Have to set the LOG_LEVELS of the calls for msyslog, and this was done by hand. New
+ * routine oncore_log.
+ * --------------------------------------------------------------------------
+ * Reg Clemens (June 2009)
+ * BUG[1218] The comment on where the oncore driver gets its input file does not
+ * agree with the code. Change the comment.
+ * --------------------------------------------------------------------------
+ * Reg Clemens (June 2009)
+ * change exit statements to return(0) in main program. I had assumed that if the
+ * PPS driver did not start for some reason, we shuould stop NTPD itelf. Others
+ * disagree. We now give an ERR log message and stop this driver.
+ * --------------------------------------------------------------------------
+ * Reg Clemens (June 2009)
+ * A bytes available message for the input subsystem (Debug message).
+ * --------------------------------------------------------------------------
+ * Reg Clemens (Nov 2008)
+ * This code adds a message for TRAIM messages. Users often worry about the
+ * driver not starting up, and it is often because of signal strength being low.
+ * Low signal strength will give TRAIM messages.
+ * --------------------------------------------------------------------------
+ * Reg Clemens (Nov 2008)
+ * Add waiting on Almanac Message.
+ * --------------------------------------------------------------------------
+ * Reg Clemens (Nov 2008)
+ * Add back in @@Bl code to do the @@Bj/@@Gj that is in later ONCOREs
+ * LEAP SECONDS: All of the ONCORE receivers, VP -> M12T have the @@Bj command
+ * that says 'Leap Pending'. As documented it only becomes true in the month
+ * before the leap second is to be applied, but in practice at least some of
+ * the receivers turn this indicator on as soon as the message is posted, which
+ * can be 6months early. As such, we use the Bj command to turn on the
+ * instance->pp->leap indicator but only run this test in December and June for
+ * updates on 1Jan and 1July.
+ *
+ * The @@Gj command exists in later ONCOREs, and it gives the exact date
+ * and size of the Leap Update. It can be emulated in the VP using the @@Bl
+ * command which reads the raw Satellite Broadcast Messages.
+ * We use these two commands to print informative messages in the clockstats
+ * file once per day as soon as the message appears on the satellites.
+ * --------------------------------------------------------------------------
+ * Reg Clemens (Feb 2006)
+ * Fix some gcc4 compiler complaints
+ * Fix possible segfault in oncore_init_shmem
+ * change all (possible) fprintf(stderr, to record_clock_stats
+ * Apply patch from Russell J. Yount <rjy@cmu.edu> Fixed (new) MT12+T UTC not correct
+ * immediately after new Almanac Read.
+ * Apply patch for new PPS implementation by Rodolfo Giometti <giometti@linux.it>
+ * now code can use old Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de> or
+ * the new one. Compiles depending on timepps.h seen.
+ * --------------------------------------------------------------------------
+ * Luis Batanero Guerrero <luisba@rao.es> (Dec 2005) Patch for leap seconds
+ * (the oncore driver was setting the wrong ntpd variable)
+ * --------------------------------------------------------------------------
+ * Reg.Clemens (Mar 2004)
+ * Support for interfaces other than PPSAPI removed, for Solaris, SunOS,
+ * SCO, you now need to use one of the timepps.h files in the root dir.
+ * this driver will 'grab' it for you if you dont have one in /usr/include
+ * --------------------------------------------------------------------------
+ * This code uses the two devices
+ * /dev/oncore.serial.n
+ * /dev/oncore.pps.n
+ * which may be linked to the same device.
+ * and can read initialization data from the file
+ * /etc/ntp.oncoreN, /etc/ntp.oncore.N, or /etc/ntp.oncore, where
+ * n or N are the unit number, viz 127.127.30.N.
+ * --------------------------------------------------------------------------
+ * Reg.Clemens <reg@dwf.com> Sep98.
+ * Original code written for FreeBSD.
+ * With these mods it works on FreeBSD, SunOS, Solaris and Linux
+ * (SunOS 4.1.3 + ppsclock)
+ * (Solaris7 + MU4)
+ * (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + or later).
+ *
+ * Lat,Long,Ht, cable-delay, offset, and the ReceiverID (along with the
+ * state machine state) are printed to CLOCKSTATS if that file is enabled
+ * in /etc/ntp.conf.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * According to the ONCORE manual (TRM0003, Rev 3.2, June 1998, page 3.13)
+ * doing an average of 10000 valid 2D and 3D fixes is what the automatic
+ * site survey mode does. Looking at the output from the receiver
+ * it seems like it is only using 3D fixes.
+ * When we do it ourselves, take 10000 3D fixes.
+ */
+
+#define POS_HOLD_AVERAGE 10000 /* nb, 10000s ~= 2h45m */
+
+/*
+ * ONCORE_SHMEM_STATUS will create a mmap(2)'ed file named according to a
+ * "STATUS" line in the oncore config file, which contains the most recent
+ * copy of all types of messages we recognize. This file can be mmap(2)'ed
+ * by monitoring and statistics programs.
+ *
+ * See separate HTML documentation for this option.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ONCORE)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#ifdef ONCORE_SHMEM_STATUS
+# ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+# ifndef MAP_FAILED
+# define MAP_FAILED ((u_char *) -1)
+# endif /* MAP_FAILED */
+# endif /* HAVE_SYS_MMAN_H */
+#endif /* ONCORE_SHMEM_STATUS */
+
+#ifdef HAVE_PPSAPI
+# include "ppsapi_timepps.h"
+#endif
+
+struct Bl {
+ int dt_ls;
+ int dt_lsf;
+ int WN;
+ int DN;
+ int WN_lsf;
+ int DN_lsf;
+ int wn_flg;
+ int lsf_flg;
+ int Bl_day;
+} Bl;
+
+enum receive_state {
+ ONCORE_NO_IDEA,
+ ONCORE_CHECK_ID,
+ ONCORE_CHECK_CHAN,
+ ONCORE_HAVE_CHAN,
+ ONCORE_RESET_SENT,
+ ONCORE_TEST_SENT,
+ ONCORE_INIT,
+ ONCORE_ALMANAC,
+ ONCORE_RUN
+};
+
+enum site_survey_state {
+ ONCORE_SS_UNKNOWN,
+ ONCORE_SS_TESTING,
+ ONCORE_SS_HW,
+ ONCORE_SS_SW,
+ ONCORE_SS_DONE
+};
+
+enum antenna_state {
+ ONCORE_ANTENNA_UNKNOWN = -1,
+ ONCORE_ANTENNA_OK = 0,
+ ONCORE_ANTENNA_OC = 1,
+ ONCORE_ANTENNA_UC = 2,
+ ONCORE_ANTENNA_NV = 3
+};
+
+/* Model Name, derived from the @@Cj message.
+ * Used to initialize some variables.
+ */
+
+enum oncore_model {
+ ONCORE_BASIC,
+ ONCORE_PVT6,
+ ONCORE_VP,
+ ONCORE_UT,
+ ONCORE_UTPLUS,
+ ONCORE_GT,
+ ONCORE_GTPLUS,
+ ONCORE_SL,
+ ONCORE_M12,
+ ONCORE_UNKNOWN
+};
+
+/* the bits that describe these properties are in the same place
+ * on the VP/UT, but have moved on the M12. As such we extract
+ * them, and use them from this struct.
+ *
+ */
+
+struct RSM {
+ u_char posn0D;
+ u_char posn2D;
+ u_char posn3D;
+ u_char bad_almanac;
+ u_char bad_fix;
+};
+
+/* It is possible to test the VP/UT each cycle (@@Ea or equivalent) to
+ * see what mode it is in. The bits on the M12 are multiplexed with
+ * other messages, so we have to 'keep' the last known mode here.
+ */
+
+enum posn_mode {
+ MODE_UNKNOWN,
+ MODE_0D,
+ MODE_2D,
+ MODE_3D
+};
+
+struct instance {
+ int unit; /* 127.127.30.unit */
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ int ttyfd; /* TTY file descriptor */
+ int ppsfd; /* PPS file descriptor */
+ int shmemfd; /* Status shm descriptor */
+ pps_handle_t pps_h;
+ pps_params_t pps_p;
+ enum receive_state o_state; /* Receive state */
+ enum posn_mode mode; /* 0D, 2D, 3D */
+ enum site_survey_state site_survey; /* Site Survey state */
+ enum antenna_state ant_state; /* antenna state */
+
+ int Bj_day;
+
+ u_long delay; /* ns */
+ long offset; /* ns */
+
+ u_char *shmem;
+ char *shmem_fname;
+ u_int shmem_Cb;
+ u_int shmem_Ba;
+ u_int shmem_Ea;
+ u_int shmem_Ha;
+ u_char shmem_reset;
+ u_char shmem_Posn;
+ u_char shmem_bad_Ea;
+ u_char almanac_from_shmem;
+
+ double ss_lat;
+ double ss_long;
+ double ss_ht;
+ double dH;
+ int ss_count;
+ u_char posn_set;
+
+ enum oncore_model model;
+ u_int version;
+ u_int revision;
+
+ u_char chan; /* 6 for PVT6 or BASIC, 8 for UT/VP, 12 for m12, 0 if unknown */
+ s_char traim; /* do we have traim? yes UT/VP, M12+T, no BASIC, GT, M12, -1 unknown, 0 no, +1 yes */
+ /* the following 7 are all timing counters */
+ u_char traim_delay; /* seconds counter, waiting for reply */
+ u_char count; /* cycles thru Ea before starting */
+ u_char count1; /* cycles thru Ea after SS_TESTING, waiting for SS_HW */
+ u_char count2; /* cycles thru Ea after count, to check for @@Ea */
+ u_char count3; /* cycles thru Ea checking for # channels */
+ u_char count4; /* cycles thru leap after Gj to issue Bj */
+ u_char count5; /* cycles thru get_timestamp waiting for valid UTC correction */
+ u_char count5_set; /* only set count5 once */
+ u_char counta; /* count for waiting on almanac message */
+ u_char pollcnt;
+ u_char timeout; /* count to retry Cj after Fa self-test */
+ u_char max_len; /* max length message seen by oncore_log, for debugging */
+ u_char max_count; /* count for message statistics */
+
+ struct RSM rsm; /* bits extracted from Receiver Status Msg in @@Ea */
+ struct Bl Bl; /* Satellite Broadcast Data Message */
+ u_char printed;
+ u_char polled;
+ u_long ev_serial;
+ int Rcvptr;
+ u_char Rcvbuf[500];
+ u_char BEHa[160]; /* Ba, Ea or Ha */
+ u_char BEHn[80]; /* Bn , En , or Hn */
+ u_char Cj[300];
+ u_char Ag; /* Satellite mask angle */
+ u_char saw_At;
+ u_char saw_Ay;
+ u_char saw_Az;
+ s_char saw_Bj;
+ s_char saw_Gj;
+ u_char have_dH;
+ u_char init_type;
+ s_char saw_tooth;
+ s_char chan_in; /* chan number from INPUT, will always use it */
+ u_char chan_id; /* chan number determined from part number */
+ u_char chan_ck; /* chan number determined by sending commands to hardware */
+ s_char traim_in; /* TRAIM from INPUT, will always use ON/OFF specified */
+ s_char traim_id; /* TRAIM determined from part number */
+ u_char traim_ck; /* TRAIM determined by sending commands to hardware */
+ u_char once; /* one pass code at top of BaEaHa */
+ s_char assert;
+ u_char hardpps;
+ s_char pps_control; /* PPS control, M12 only */
+ s_char pps_control_msg_seen;
+};
+
+#define rcvbuf instance->Rcvbuf
+#define rcvptr instance->Rcvptr
+
+static int oncore_start (int, struct peer *);
+static void oncore_poll (int, struct peer *);
+static void oncore_shutdown (int, struct peer *);
+static void oncore_consume (struct instance *);
+static void oncore_read_config (struct instance *);
+static void oncore_receive (struct recvbuf *);
+static int oncore_ppsapi (struct instance *);
+static void oncore_get_timestamp (struct instance *, long, long);
+static void oncore_init_shmem (struct instance *);
+
+static void oncore_antenna_report (struct instance *, enum antenna_state);
+static void oncore_chan_test (struct instance *);
+static void oncore_check_almanac (struct instance *);
+static void oncore_check_antenna (struct instance *);
+static void oncore_check_leap_sec (struct instance *);
+static int oncore_checksum_ok (u_char *, int);
+static void oncore_compute_dH (struct instance *);
+static void oncore_load_almanac (struct instance *);
+static void oncore_log (struct instance *, int, const char *);
+static int oncore_log_f (struct instance *, int, const char *, ...)
+ NTP_PRINTF(3, 4);
+static void oncore_print_Cb (struct instance *, u_char *);
+/* static void oncore_print_array (u_char *, int); */
+static void oncore_print_posn (struct instance *);
+static void oncore_sendmsg (struct instance *, u_char *, size_t);
+static void oncore_set_posn (struct instance *);
+static void oncore_set_traim (struct instance *);
+static void oncore_shmem_get_3D (struct instance *);
+static void oncore_ss (struct instance *);
+static int oncore_wait_almanac (struct instance *);
+
+static void oncore_msg_any (struct instance *, u_char *, size_t, int);
+static void oncore_msg_Adef (struct instance *, u_char *, size_t);
+static void oncore_msg_Ag (struct instance *, u_char *, size_t);
+static void oncore_msg_As (struct instance *, u_char *, size_t);
+static void oncore_msg_At (struct instance *, u_char *, size_t);
+static void oncore_msg_Ay (struct instance *, u_char *, size_t);
+static void oncore_msg_Az (struct instance *, u_char *, size_t);
+static void oncore_msg_BaEaHa (struct instance *, u_char *, size_t);
+static void oncore_msg_Bd (struct instance *, u_char *, size_t);
+static void oncore_msg_Bj (struct instance *, u_char *, size_t);
+static void oncore_msg_Bl (struct instance *, u_char *, size_t);
+static void oncore_msg_BnEnHn (struct instance *, u_char *, size_t);
+static void oncore_msg_CaFaIa (struct instance *, u_char *, size_t);
+static void oncore_msg_Cb (struct instance *, u_char *, size_t);
+static void oncore_msg_Cf (struct instance *, u_char *, size_t);
+static void oncore_msg_Cj (struct instance *, u_char *, size_t);
+static void oncore_msg_Cj_id (struct instance *, u_char *, size_t);
+static void oncore_msg_Cj_init (struct instance *, u_char *, size_t);
+static void oncore_msg_Ga (struct instance *, u_char *, size_t);
+static void oncore_msg_Gb (struct instance *, u_char *, size_t);
+static void oncore_msg_Gc (struct instance *, u_char *, size_t);
+static void oncore_msg_Gj (struct instance *, u_char *, size_t);
+static void oncore_msg_Sz (struct instance *, u_char *, size_t);
+
+struct refclock refclock_oncore = {
+ oncore_start, /* start up driver */
+ oncore_shutdown, /* shut down driver */
+ oncore_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * Understanding the next bit here is not easy unless you have a manual
+ * for the the various Oncore Models.
+ */
+
+static struct msg_desc {
+ const char flag[3];
+ const int len;
+ void (*handler) (struct instance *, u_char *, size_t);
+ const char *fmt;
+ int shmem;
+} oncore_messages[] = {
+ /* Ea and En first since they're most common */
+ { "Ea", 76, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC" },
+ { "Ba", 68, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdsC" },
+ { "Ha", 154, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmaaaaoooohhhhmmmmVVvvhhddntimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddssrrccooooTTushmvvvvvvC" },
+ { "Bn", 59, oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffC" },
+ { "En", 69, oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC" },
+ { "Hn", 78, oncore_msg_BnEnHn, "" },
+ { "Ab", 10, 0, "" },
+ { "Ac", 11, 0, "" },
+ { "Ad", 11, oncore_msg_Adef, "" },
+ { "Ae", 11, oncore_msg_Adef, "" },
+ { "Af", 15, oncore_msg_Adef, "" },
+ { "Ag", 8, oncore_msg_Ag, "" }, /* Satellite mask angle */
+ { "As", 20, oncore_msg_As, "" },
+ { "At", 8, oncore_msg_At, "" },
+ { "Au", 12, 0, "" },
+ { "Av", 8, 0, "" },
+ { "Aw", 8, 0, "" },
+ { "Ay", 11, oncore_msg_Ay, "" },
+ { "Az", 11, oncore_msg_Az, "" },
+ { "AB", 8, 0, "" },
+ { "Bb", 92, 0, "" },
+ { "Bd", 23, oncore_msg_Bd, "" },
+ { "Bj", 8, oncore_msg_Bj, "" },
+ { "Bl", 41, oncore_msg_Bl, "" },
+ { "Ca", 9, oncore_msg_CaFaIa, "" },
+ { "Cb", 33, oncore_msg_Cb, "" },
+ { "Cf", 7, oncore_msg_Cf, "" },
+ { "Cg", 8, 0, "" },
+ { "Ch", 9, 0, "" },
+ { "Cj", 294, oncore_msg_Cj, "" },
+ { "Ek", 71, 0, "" },
+ { "Fa", 9, oncore_msg_CaFaIa, "" },
+ { "Ga", 20, oncore_msg_Ga, "" },
+ { "Gb", 17, oncore_msg_Gb, "" },
+ { "Gc", 8, oncore_msg_Gc, "" },
+ { "Gd", 8, 0, "" },
+ { "Ge", 8, 0, "" },
+ { "Gj", 21, oncore_msg_Gj, "" },
+ { "Ia", 10, oncore_msg_CaFaIa, "" },
+ { "Sz", 8, oncore_msg_Sz, "" },
+ { {0}, 7, 0, "" }
+};
+
+
+static u_char oncore_cmd_Aa[] = { 'A', 'a', 0, 0, 0 }; /* 6/8 Time of Day */
+static u_char oncore_cmd_Ab[] = { 'A', 'b', 0, 0, 0 }; /* 6/8 GMT Correction */
+static u_char oncore_cmd_AB[] = { 'A', 'B', 4 }; /* VP Application Type: Static */
+static u_char oncore_cmd_Ac[] = { 'A', 'c', 0, 0, 0, 0 }; /* 6/8 Date */
+static u_char oncore_cmd_Ad[] = { 'A', 'd', 0,0,0,0 }; /* 6/8 Latitude */
+static u_char oncore_cmd_Ae[] = { 'A', 'e', 0,0,0,0 }; /* 6/8 Longitude */
+static u_char oncore_cmd_Af[] = { 'A', 'f', 0,0,0,0, 0 }; /* 6/8 Height */
+static u_char oncore_cmd_Ag[] = { 'A', 'g', 0 }; /* 6/8/12 Satellite Mask Angle */
+static u_char oncore_cmd_Agx[] = { 'A', 'g', 0xff }; /* 6/8/12 Satellite Mask Angle: read */
+static u_char oncore_cmd_As[] = { 'A', 's', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; /* 6/8/12 Posn Hold Parameters */
+static u_char oncore_cmd_Asx[] = { 'A', 's', 0x7f,0xff,0xff,0xff, /* 6/8/12 Posn Hold Readback */
+ 0x7f,0xff,0xff,0xff, /* on UT+ this doesnt work with 0xff */
+ 0x7f,0xff,0xff,0xff, 0xff }; /* but does work with 0x7f (sigh). */
+static u_char oncore_cmd_At0[] = { 'A', 't', 0 }; /* 6/8 Posn Hold: off */
+static u_char oncore_cmd_At1[] = { 'A', 't', 1 }; /* 6/8 Posn Hold: on */
+static u_char oncore_cmd_At2[] = { 'A', 't', 2 }; /* 6/8 Posn Hold: Start Site Survey */
+static u_char oncore_cmd_Atx[] = { 'A', 't', 0xff }; /* 6/8 Posn Hold: Read Back */
+static u_char oncore_cmd_Au[] = { 'A', 'u', 0,0,0,0, 0 }; /* GT/M12 Altitude Hold Ht. */
+static u_char oncore_cmd_Av0[] = { 'A', 'v', 0 }; /* VP/GT Altitude Hold: off */
+static u_char oncore_cmd_Av1[] = { 'A', 'v', 1 }; /* VP/GT Altitude Hold: on */
+static u_char oncore_cmd_Aw[] = { 'A', 'w', 1 }; /* 6/8/12 UTC/GPS time selection */
+static u_char oncore_cmd_Ay[] = { 'A', 'y', 0, 0, 0, 0 }; /* Timing 1PPS time offset: set */
+static u_char oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff }; /* Timing 1PPS time offset: Read */
+static u_char oncore_cmd_Az[] = { 'A', 'z', 0, 0, 0, 0 }; /* 6/8UT/12 1PPS Cable Delay: set */
+static u_char oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff }; /* 6/8UT/12 1PPS Cable Delay: Read */
+static u_char oncore_cmd_Ba0[] = { 'B', 'a', 0 }; /* 6 Position/Data/Status: off */
+static u_char oncore_cmd_Ba[] = { 'B', 'a', 1 }; /* 6 Position/Data/Status: on */
+static u_char oncore_cmd_Bb[] = { 'B', 'b', 1 }; /* 6/8/12 Visible Satellites */
+static u_char oncore_cmd_Bd[] = { 'B', 'd', 1 }; /* 6/8/12? Almanac Status Msg. */
+static u_char oncore_cmd_Be[] = { 'B', 'e', 1 }; /* 6/8/12 Request Almanac Data */
+static u_char oncore_cmd_Bj[] = { 'B', 'j', 0 }; /* 6/8 Leap Second Pending */
+static u_char oncore_cmd_Bl[] = { 'B', 'l', 1 }; /* VP Satellite Broadcast Data Msg */
+static u_char oncore_cmd_Bn0[] = { 'B', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg off, traim on */
+static u_char oncore_cmd_Bn[] = { 'B', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg on, traim on */
+static u_char oncore_cmd_Bnx[] = { 'B', 'n', 0, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg off, traim off */
+static u_char oncore_cmd_Ca[] = { 'C', 'a' }; /* 6 Self Test */
+static u_char oncore_cmd_Cf[] = { 'C', 'f' }; /* 6/8/12 Set to Defaults */
+static u_char oncore_cmd_Cg[] = { 'C', 'g', 1 }; /* VP Posn Fix/Idle Mode */
+static u_char oncore_cmd_Cj[] = { 'C', 'j' }; /* 6/8/12 Receiver ID */
+static u_char oncore_cmd_Ea0[] = { 'E', 'a', 0 }; /* 8 Position/Data/Status: off */
+static u_char oncore_cmd_Ea[] = { 'E', 'a', 1 }; /* 8 Position/Data/Status: on */
+static u_char oncore_cmd_Ek[] = { 'E', 'k', 0 }; /* just turn off */ /* 8 Posn/Status/Data - extension */
+static u_char oncore_cmd_En0[] = { 'E', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg off, traim on */
+static u_char oncore_cmd_En[] = { 'E', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg on, traim on */
+static u_char oncore_cmd_Enx[] = { 'E', 'n', 0, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg off, traim off */
+static u_char oncore_cmd_Fa[] = { 'F', 'a' }; /* 8 Self Test */
+static u_char oncore_cmd_Ga[] = { 'G', 'a', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; /* 12 Position Set */
+static u_char oncore_cmd_Gax[] = { 'G', 'a', 0xff, 0xff, 0xff, 0xff, /* 12 Position Set: Read */
+ 0xff, 0xff, 0xff, 0xff, /* */
+ 0xff, 0xff, 0xff, 0xff, 0xff }; /* */
+static u_char oncore_cmd_Gb[] = { 'G', 'b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* 12 set Date/Time */
+static u_char oncore_cmd_Gc[] = { 'G', 'c', 0 }; /* 12 PPS Control: Off, On, 1+satellite,TRAIM */
+static u_char oncore_cmd_Gd0[] = { 'G', 'd', 0 }; /* 12 Position Control: 3D (no hold) */
+static u_char oncore_cmd_Gd1[] = { 'G', 'd', 1 }; /* 12 Position Control: 0D (3D hold) */
+static u_char oncore_cmd_Gd2[] = { 'G', 'd', 2 }; /* 12 Position Control: 2D (Alt Hold) */
+static u_char oncore_cmd_Gd3[] = { 'G', 'd', 3 }; /* 12 Position Coltrol: Start Site Survey */
+static u_char oncore_cmd_Ge0[] = { 'G', 'e', 0 }; /* M12+T TRAIM: off */
+static u_char oncore_cmd_Ge[] = { 'G', 'e', 1 }; /* M12+T TRAIM: on */
+static u_char oncore_cmd_Gj[] = { 'G', 'j' }; /* 8?/12 Leap Second Pending */
+static u_char oncore_cmd_Ha0[] = { 'H', 'a', 0 }; /* 12 Position/Data/Status: off */
+static u_char oncore_cmd_Ha[] = { 'H', 'a', 1 }; /* 12 Position/Data/Status: on */
+static u_char oncore_cmd_Hn0[] = { 'H', 'n', 0 }; /* 12 TRAIM Status: off */
+static u_char oncore_cmd_Hn[] = { 'H', 'n', 1 }; /* 12 TRAIM Status: on */
+static u_char oncore_cmd_Ia[] = { 'I', 'a' }; /* 12 Self Test */
+
+/* it appears that as of 1997/1998, the UT had As,At, but not Au,Av
+ * the GT had Au,Av, but not As,At
+ * This was as of v2.0 of both firmware sets. possibly 1.3 for UT.
+ * Bj in UT at v1.3
+ * dont see Bd in UT/GT thru 1999
+ * Gj in UT as of 3.0, 1999 , Bj as of 1.3
+ */
+
+#define DEVICE1 "/dev/oncore.serial.%d" /* name of serial device */
+#define DEVICE2 "/dev/oncore.pps.%d" /* name of pps device */
+
+#define SPEED B9600 /* Oncore Binary speed (9600 bps) */
+
+/*
+ * Assemble and disassemble 32bit signed quantities from a buffer.
+ *
+ */
+
+ /* to buffer, int w, u_char *buf */
+#define w32_buf(buf,w) { u_int i_tmp; \
+ i_tmp = (w<0) ? (~(-w)+1) : (w); \
+ (buf)[0] = (i_tmp >> 24) & 0xff; \
+ (buf)[1] = (i_tmp >> 16) & 0xff; \
+ (buf)[2] = (i_tmp >> 8) & 0xff; \
+ (buf)[3] = (i_tmp ) & 0xff; \
+ }
+
+#define w32(buf) (((buf)[0]&0xff) << 24 | \
+ ((buf)[1]&0xff) << 16 | \
+ ((buf)[2]&0xff) << 8 | \
+ ((buf)[3]&0xff) )
+
+ /* from buffer, char *buf, result to an int */
+#define buf_w32(buf) (((buf)[0]&0200) ? (-(~w32(buf)+1)) : w32(buf))
+
+
+/*
+ * oncore_start - initialize data for processing
+ */
+
+static int
+oncore_start(
+ int unit,
+ struct peer *peer
+ )
+{
+#define STRING_LEN 32
+ register struct instance *instance;
+ struct refclockproc *pp;
+ int fd1, fd2;
+ char device1[STRING_LEN], device2[STRING_LEN];
+#ifndef SYS_WINNT
+ struct stat stat1, stat2;
+#endif
+
+ /* create instance structure for this unit */
+
+ instance = emalloc(sizeof(*instance));
+ memset(instance, 0, sizeof(*instance));
+
+ /* initialize miscellaneous variables */
+
+ pp = peer->procptr;
+ instance->pp = pp;
+ instance->unit = unit;
+ instance->peer = peer;
+ instance->assert = 1;
+ instance->once = 1;
+
+ instance->Bj_day = -1;
+ instance->traim = -1;
+ instance->traim_in = -1;
+ instance->chan_in = -1;
+ instance->pps_control = -1; /* PPS control, M12 only */
+ instance->pps_control_msg_seen = -1; /* Have seen response to Gc msg */
+ instance->model = ONCORE_UNKNOWN;
+ instance->mode = MODE_UNKNOWN;
+ instance->site_survey = ONCORE_SS_UNKNOWN;
+ instance->Ag = 0xff; /* Satellite mask angle, unset by user */
+ instance->ant_state = ONCORE_ANTENNA_UNKNOWN;
+
+ peer->flags &= ~FLAG_PPS; /* PPS not active yet */
+ peer->precision = -26;
+ peer->minpoll = 4;
+ peer->maxpoll = 4;
+ pp->clockdesc = "Motorola Oncore GPS Receiver";
+ memcpy((char *)&pp->refid, "GPS\0", (size_t) 4);
+
+ oncore_log(instance, LOG_NOTICE, "ONCORE DRIVER -- CONFIGURING");
+ instance->o_state = ONCORE_NO_IDEA;
+ oncore_log(instance, LOG_NOTICE, "state = ONCORE_NO_IDEA");
+
+ /* Now open files.
+ * This is a bit complicated, a we dont want to open the same file twice
+ * (its a problem on some OS), and device2 may not exist for the new PPS
+ */
+
+ (void)snprintf(device1, sizeof(device1), DEVICE1, unit);
+ (void)snprintf(device2, sizeof(device2), DEVICE2, unit);
+
+ /* OPEN DEVICES */
+ /* opening different devices for fd1 and fd2 presents no problems */
+ /* opening the SAME device twice, seems to be OS dependent.
+ (a) on Linux (no streams) no problem
+ (b) on SunOS (and possibly Solaris, untested), (streams)
+ never see the line discipline.
+ Since things ALWAYS work if we only open the device once, we check
+ to see if the two devices are in fact the same, then proceed to
+ do one open or two.
+
+ For use with linuxPPS we assume that the N_TTY file has been opened
+ and that the line discipline has been changed to N_PPS by another
+ program (say ppsldisc) so that the two files expected by the oncore
+ driver can be opened.
+
+ Note that the linuxPPS N_PPS file is just like a N_TTY, so we can do
+ the stat below without error even though the file has already had its
+ line discipline changed by another process.
+
+ The Windows port of ntpd arranges to return duplicate handles for
+ multiple opens of the same serial device, and doesn't have inodes
+ for serial handles, so we just open both on Windows.
+ */
+#ifndef SYS_WINNT
+ if (stat(device1, &stat1)) {
+ oncore_log_f(instance, LOG_ERR, "Can't stat fd1 (%s)",
+ device1);
+ return(0); /* exit, no file, can't start driver */
+ }
+
+ if (stat(device2, &stat2)) {
+ stat2.st_dev = stat2.st_ino = -2;
+ oncore_log_f(instance, LOG_ERR, "Can't stat fd2 (%s) %d %m",
+ device2, errno);
+ }
+#endif /* !SYS_WINNT */
+
+ fd1 = refclock_open(device1, SPEED, LDISC_RAW);
+ if (fd1 <= 0) {
+ oncore_log_f(instance, LOG_ERR, "Can't open fd1 (%s)",
+ device1);
+ return(0); /* exit, can't open file, can't start driver */
+ }
+
+ /* for LINUX the PPS device is the result of a line discipline.
+ It seems simplest to let an external program create the appropriate
+ /dev/pps<n> file, and only check (carefully) for its existance here
+ */
+
+#ifndef SYS_WINNT
+ if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) /* same device here */
+ fd2 = fd1;
+ else
+#endif /* !SYS_WINNT */
+ { /* different devices here */
+ if ((fd2=tty_open(device2, O_RDWR, 0777)) < 0) {
+ oncore_log_f(instance, LOG_ERR,
+ "Can't open fd2 (%s)", device2);
+ return(0); /* exit, can't open PPS file, can't start driver */
+ }
+ }
+
+ /* open ppsapi source */
+
+ if (time_pps_create(fd2, &instance->pps_h) < 0) {
+ oncore_log(instance, LOG_ERR, "exit, PPSAPI not found in kernel");
+ return(0); /* exit, don't find PPSAPI in kernel */
+ }
+
+ /* continue initialization */
+
+ instance->ttyfd = fd1;
+ instance->ppsfd = fd2;
+
+ /* go read any input data in /etc/ntp.oncoreX or /etc/ntp/oncore.X */
+
+ oncore_read_config(instance);
+
+ if (!oncore_ppsapi(instance))
+ return(0);
+
+ pp->io.clock_recv = oncore_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd1;
+ if (!io_addclock(&pp->io)) {
+ oncore_log(instance, LOG_ERR, "can't do io_addclock");
+ close(fd1);
+ pp->io.fd = -1;
+ free(instance);
+ return (0);
+ }
+ pp->unitptr = instance;
+
+#ifdef ONCORE_SHMEM_STATUS
+ /*
+ * Before starting ONCORE, lets setup SHMEM
+ * This will include merging an old SHMEM into the new one if
+ * an old one is found.
+ */
+
+ oncore_init_shmem(instance);
+#endif
+
+ /*
+ * This will return the Model of the Oncore receiver.
+ * and start the Initialization loop in oncore_msg_Cj.
+ */
+
+ instance->o_state = ONCORE_CHECK_ID;
+ oncore_log(instance, LOG_NOTICE, "state = ONCORE_CHECK_ID");
+
+ instance->timeout = 4;
+ oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Set Posn Fix mode (not Idle (VP)) */
+ oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+
+ instance->pollcnt = 2;
+ return (1);
+}
+
+
+/*
+ * oncore_shutdown - shut down the clock
+ */
+
+static void
+oncore_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct instance *instance;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ instance = pp->unitptr;
+
+ if (pp->io.fd != -1)
+ io_closeclock(&pp->io);
+
+ if (instance != NULL) {
+ time_pps_destroy (instance->pps_h);
+
+ close(instance->ttyfd);
+
+ if ((instance->ppsfd != -1) && (instance->ppsfd != instance->ttyfd))
+ close(instance->ppsfd);
+
+ if (instance->shmemfd)
+ close(instance->shmemfd);
+
+ free(instance);
+ }
+}
+
+
+
+/*
+ * oncore_poll - called by the transmit procedure
+ */
+
+static void
+oncore_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct instance *instance;
+
+ instance = peer->procptr->unitptr;
+ if (instance->timeout) {
+ instance->timeout--;
+ if (instance->timeout == 0) {
+ oncore_log(instance, LOG_ERR,
+ "Oncore: No response from @@Cj, shutting down driver");
+ oncore_shutdown(unit, peer);
+ } else {
+ oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+ oncore_log(instance, LOG_WARNING, "Oncore: Resend @@Cj");
+ }
+ return;
+ }
+
+ if (!instance->pollcnt)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ instance->pollcnt--;
+ peer->procptr->polls++;
+ instance->polled = 1;
+}
+
+
+
+/*
+ * Initialize PPSAPI
+ */
+
+static int
+oncore_ppsapi(
+ struct instance *instance
+ )
+{
+ int cap, mode, mode1;
+ char *cp;
+
+ if (time_pps_getcap(instance->pps_h, &cap) < 0) {
+ oncore_log_f(instance, LOG_ERR, "time_pps_getcap failed: %m");
+ return (0);
+ }
+
+ if (time_pps_getparams(instance->pps_h, &instance->pps_p) < 0) {
+ oncore_log_f(instance, LOG_ERR, "time_pps_getparams failed: %m");
+ return (0);
+ }
+
+ /* nb. only turn things on, if someone else has turned something
+ * on before we get here, leave it alone!
+ */
+
+ if (instance->assert) {
+ cp = "Assert";
+ mode = PPS_CAPTUREASSERT;
+ mode1 = PPS_OFFSETASSERT;
+ } else {
+ cp = "Clear";
+ mode = PPS_CAPTURECLEAR;
+ mode1 = PPS_OFFSETCLEAR;
+ }
+ oncore_log_f(instance, LOG_INFO, "Initializing timing to %s.",
+ cp);
+
+ if (!(mode & cap)) {
+ oncore_log_f(instance, LOG_ERR,
+ "Can't set timing to %s, exiting...", cp);
+ return(0);
+ }
+
+ if (!(mode1 & cap)) {
+ oncore_log_f(instance, LOG_NOTICE,
+ "Can't set %s, this will increase jitter.",
+ cp);
+ mode1 = 0;
+ }
+
+ /* only set what is legal */
+
+ instance->pps_p.mode = (mode | mode1 | PPS_TSFMT_TSPEC) & cap;
+
+ if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) {
+ oncore_log_f(instance, LOG_ERR, "ONCORE: time_pps_setparams fails %m");
+ return(0); /* exit, can't do time_pps_setparans on PPS file */
+ }
+
+ /* If HARDPPS is on, we tell kernel */
+
+ if (instance->hardpps) {
+ int i;
+
+ oncore_log(instance, LOG_INFO, "HARDPPS Set.");
+
+ if (instance->assert)
+ i = PPS_CAPTUREASSERT;
+ else
+ i = PPS_CAPTURECLEAR;
+
+ /* we know that 'i' is legal from above */
+
+ if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS, i,
+ PPS_TSFMT_TSPEC) < 0) {
+ oncore_log_f(instance, LOG_ERR, "time_pps_kcbind failed: %m");
+ oncore_log(instance, LOG_ERR, "HARDPPS failed, abort...");
+ return (0);
+ }
+
+ hardpps_enable = 1;
+ }
+ return(1);
+}
+
+
+
+#ifdef ONCORE_SHMEM_STATUS
+static void
+oncore_init_shmem(
+ struct instance *instance
+ )
+{
+ int i, l, n, fd, shmem_old_size, n1;
+ u_char *cp, *cp1, *buf, *shmem_old;
+ struct msg_desc *mp;
+ struct stat sbuf;
+ size_t shmem_length;
+
+ /*
+ * The first thing we do is see if there is an instance->shmem_fname file (still)
+ * out there from a previous run. If so, we copy it in and use it to initialize
+ * shmem (so we won't lose our almanac if we need it).
+ */
+
+ shmem_old = 0;
+ shmem_old_size = 0;
+ if ((fd = open(instance->shmem_fname, O_RDONLY)) < 0)
+ oncore_log(instance, LOG_WARNING, "ONCORE: Can't open SHMEM file");
+ else {
+ fstat(fd, &sbuf);
+ shmem_old_size = sbuf.st_size;
+ if (shmem_old_size != 0) {
+ shmem_old = emalloc((unsigned) sbuf.st_size);
+ read(fd, shmem_old, shmem_old_size);
+ }
+ close(fd);
+ }
+
+ /* OK, we now create the NEW SHMEM. */
+
+ if ((instance->shmemfd = open(instance->shmem_fname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) {
+ oncore_log(instance, LOG_WARNING, "ONCORE: Can't open shmem");
+ if (shmem_old)
+ free(shmem_old);
+
+ return;
+ }
+
+ /* see how big it needs to be */
+
+ n = 1;
+ for (mp=oncore_messages; mp->flag[0]; mp++) {
+ mp->shmem = n;
+ /* Allocate space for multiplexed almanac, and 0D/2D/3D @@Ea records */
+ if (!strcmp(mp->flag, "Cb")) {
+ instance->shmem_Cb = n;
+ n += (mp->len + 3) * 34;
+ }
+ if (!strcmp(mp->flag, "Ba")) {
+ instance->shmem_Ba = n;
+ n += (mp->len + 3) * 3;
+ }
+ if (!strcmp(mp->flag, "Ea")) {
+ instance->shmem_Ea = n;
+ n += (mp->len + 3) * 3;
+ }
+ if (!strcmp(mp->flag, "Ha")) {
+ instance->shmem_Ha = n;
+ n += (mp->len + 3) * 3;
+ }
+ n += (mp->len + 3);
+ }
+ shmem_length = n + 2;
+
+ buf = emalloc(shmem_length);
+ memset(buf, 0, shmem_length);
+
+ /* next build the new SHMEM buffer in memory */
+
+ for (mp=oncore_messages; mp->flag[0]; mp++) {
+ l = mp->shmem;
+ buf[l + 0] = mp->len >> 8;
+ buf[l + 1] = mp->len & 0xff;
+ buf[l + 2] = 0;
+ buf[l + 3] = '@';
+ buf[l + 4] = '@';
+ buf[l + 5] = mp->flag[0];
+ buf[l + 6] = mp->flag[1];
+ if (!strcmp(mp->flag, "Cb") || !strcmp(mp->flag, "Ba") || !strcmp(mp->flag, "Ea") || !strcmp(mp->flag, "Ha")) {
+ if (!strcmp(mp->flag, "Cb"))
+ n = 35;
+ else
+ n = 4;
+ for (i=1; i<n; i++) {
+ buf[l + i * (mp->len+3) + 0] = mp->len >> 8;
+ buf[l + i * (mp->len+3) + 1] = mp->len & 0xff;
+ buf[l + i * (mp->len+3) + 2] = 0;
+ buf[l + i * (mp->len+3) + 3] = '@';
+ buf[l + i * (mp->len+3) + 4] = '@';
+ buf[l + i * (mp->len+3) + 5] = mp->flag[0];
+ buf[l + i * (mp->len+3) + 6] = mp->flag[1];
+ }
+ }
+ }
+
+ /* we now walk thru the two buffers (shmem_old and buf, soon to become shmem)
+ * copying the data in shmem_old to buf.
+ * When we are done we write it out and free both buffers.
+ * If the structure sizes dont agree, I will not copy.
+ * This could be due to an addition/deletion or a problem with the disk file.
+ */
+
+ if (shmem_old) {
+ if (shmem_old_size == shmem_length) {
+ for (cp=buf+4, cp1=shmem_old+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3), cp1+=(n+3)) {
+ n1 = 256*(*(cp1-3)) + *(cp1-2);
+ if (n == 0 || n1 != n || strncmp((char *) cp, (char *) cp1, 4))
+ break;
+
+ memcpy(cp, cp1, (size_t) n);
+ }
+ }
+ free(shmem_old);
+ }
+
+ i = write(instance->shmemfd, buf, shmem_length);
+ free(buf);
+
+ if (i != shmem_length) {
+ oncore_log(instance, LOG_ERR, "ONCORE: error writing shmem");
+ close(instance->shmemfd);
+ return;
+ }
+
+ instance->shmem = (u_char *) mmap(0, shmem_length,
+ PROT_READ | PROT_WRITE,
+#ifdef MAP_HASSEMAPHORE
+ MAP_HASSEMAPHORE |
+#endif
+ MAP_SHARED, instance->shmemfd, (off_t)0);
+
+ if (instance->shmem == (u_char *)MAP_FAILED) {
+ instance->shmem = 0;
+ close(instance->shmemfd);
+ return;
+ }
+
+ oncore_log_f(instance, LOG_NOTICE,
+ "SHMEM (size = %ld) is CONFIGURED and available as %s",
+ (u_long) shmem_length, instance->shmem_fname);
+}
+#endif /* ONCORE_SHMEM_STATUS */
+
+
+
+/*
+ * Read Input file if it exists.
+ */
+
+static void
+oncore_read_config(
+ struct instance *instance
+ )
+{
+/*
+ * First we try to open the configuration file
+ * /etc/ntp.oncore.N
+ * where N is the unit number viz 127.127.30.N.
+ * If we don't find it we try
+ * /etc/ntp.oncoreN
+ * and then
+ * /etc/ntp.oncore
+ *
+ * If we don't find any then we don't have the cable delay or PPS offset
+ * and we choose MODE (4) below.
+ *
+ * Five Choices for MODE
+ * (0) ONCORE is preinitialized, don't do anything to change it.
+ * nb, DON'T set 0D mode, DON'T set Delay, position...
+ * (1) NO RESET, Read Position, delays from data file, lock it in, go to 0D mode.
+ * (2) NO RESET, Read Delays from data file, do SITE SURVEY to get position,
+ * lock this in, go to 0D mode.
+ * (3) HARD RESET, Read Position, delays from data file, lock it in, go to 0D mode.
+ * (4) HARD RESET, Read Delays from data file, do SITE SURVEY to get position,
+ * lock this in, go to 0D mode.
+ * NB. If a POSITION is specified in the config file with mode=(2,4) [SITE SURVEY]
+ * then this position is set as the INITIAL position of the ONCORE.
+ * This can reduce the time to first fix.
+ * -------------------------------------------------------------------------------
+ * Note that an Oncore UT without a battery backup retains NO information if it is
+ * power cycled, with a Battery Backup it remembers the almanac, etc.
+ * For an Oncore VP, there is an eeprom that will contain this data, along with the
+ * option of Battery Backup.
+ * So a UT without Battery Backup is equivalent to doing a HARD RESET on each
+ * power cycle, since there is nowhere to store the data.
+ * -------------------------------------------------------------------------------
+ *
+ * If we open one or the other of the files, we read it looking for
+ * MODE, LAT, LON, (HT, HTGPS, HTMSL), DELAY, OFFSET, ASSERT, CLEAR, HARDPPS,
+ * STATUS, POSN3D, POSN2D, CHAN, TRAIM
+ * then initialize using method MODE. For Mode = (1,3) all of (LAT, LON, HT) must
+ * be present or mode reverts to (2,4).
+ *
+ * Read input file.
+ *
+ * # is comment to end of line
+ * = allowed between 1st and 2nd fields.
+ *
+ * Expect to see one line with 'MODE' as first field, followed by an integer
+ * in the range 0-4 (default = 4).
+ *
+ * Expect to see two lines with 'LONG', 'LAT' followed by 1-3 fields.
+ * All numbers are floating point.
+ * DDD.ddd
+ * DDD MMM.mmm
+ * DDD MMM SSS.sss
+ *
+ * Expect to see one line with 'HT' as first field,
+ * followed by 1-2 fields. First is a number, the second is 'FT' or 'M'
+ * for feet or meters. HT is the height above the GPS ellipsoid.
+ * If the receiver reports height in both GPS and MSL, then we will report
+ * the difference GPS-MSL on the clockstats file.
+ *
+ * There is an optional line, starting with DELAY, followed
+ * by 1 or two fields. The first is a number (a time) the second is
+ * 'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
+ * DELAY is cable delay, typically a few tens of ns.
+ *
+ * There is an optional line, starting with OFFSET, followed
+ * by 1 or two fields. The first is a number (a time) the second is
+ * 'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
+ * OFFSET is the offset of the PPS pulse from 0. (only fully implemented
+ * with the PPSAPI, we need to be able to tell the Kernel about this
+ * offset if the Kernel PLL is in use, but can only do this presently
+ * when using the PPSAPI interface. If not using the Kernel PLL,
+ * then there is no problem.
+ *
+ * There is an optional line, with either ASSERT or CLEAR on it, which
+ * determine which transition of the PPS signal is used for timing by the
+ * PPSAPI. If neither is present, then ASSERT is assumed.
+ * ASSERT/CLEAR can also be set with FLAG2 of the ntp.conf input.
+ * For Flag2, ASSERT=0, and hence is default.
+ *
+ * There is an optional line, with HARDPPS on it. Including this line causes
+ * the PPS signal to control the kernel PLL.
+ * HARDPPS can also be set with FLAG3 of the ntp.conf input.
+ * For Flag3, 0 is disabled, and the default.
+ *
+ * There are three options that have to do with using the shared memory option.
+ * First, to enable the option there must be a SHMEM line with a file name.
+ * The file name is the file associated with the shared memory.
+ *
+ * In shared memory, there is one 'record' for each returned variable.
+ * For the @@Ea data there are three 'records' containing position data.
+ * There will always be data in the record corresponding to the '0D' @@Ea record,
+ * and the user has a choice of filling the '3D' record by specifying POSN3D,
+ * or the '2D' record by specifying POSN2D. In either case the '2D' or '3D'
+ * record is filled once every 15s.
+ *
+ * Two additional variables that can be set are CHAN and TRAIM. These should be
+ * set correctly by the code examining the @@Cj record, but we bring them out here
+ * to allow the user to override either the # of channels, or the existence of TRAIM.
+ * CHAN expects to be followed by in integer: 6, 8, or 12. TRAIM expects to be
+ * followed by YES or NO.
+ *
+ * There is an optional line with MASK on it followed by one integer field in the
+ * range 0 to 89. This sets the satellite mask angle and will determine the minimum
+ * elevation angle for satellites to be tracked by the receiver. The default value
+ * is 10 deg for the VP and 0 deg for all other receivers.
+ *
+ * There is an optional line with PPSCONTROL on it (only valid for M12 or M12+T
+ * receivers, the option is read, but ignored for all others)
+ * and it is followed by:
+ * ON Turn PPS on. This is the default and the default for other
+ * oncore receivers. The PPS is on even if not tracking
+ * any satellites.
+ * SATELLITE Turns PPS on if tracking at least 1 satellite, else off.
+ * TRAIM Turns PPS on or off controlled by TRAIM.
+ * The OFF option is NOT implemented, since the Oncore driver will not work
+ * without the PPS signal.
+ *
+ * So acceptable input would be
+ * # these are my coordinates (RWC)
+ * LON -106 34.610
+ * LAT 35 08.999
+ * HT 1589 # could equally well say HT 5215 FT
+ * DELAY 60 ns
+ */
+
+ FILE *fd;
+ char *cp, *cc, *ca, line[100], units[2], device[64], **cpp;
+ char *dirs[] = { "/etc/ntp", "/etc", 0 };
+ int i, sign, lat_flg, long_flg, ht_flg, mode, mask;
+ double f1, f2, f3;
+
+ fd = NULL; /* just to shutup gcc complaint */
+ for (cpp=dirs; *cpp; cpp++) {
+ cp = *cpp;
+ snprintf(device, sizeof(device), "%s/ntp.oncore.%d",
+ cp, instance->unit); /* try "ntp.oncore.0 */
+ if ((fd=fopen(device, "r")))
+ break;
+ snprintf(device, sizeof(device), "%s/ntp.oncore%d",
+ cp, instance->unit); /* try "ntp.oncore0" */
+ if ((fd=fopen(device, "r")))
+ break;
+ snprintf(device, sizeof(device), "%s/ntp.oncore", cp);
+ if ((fd=fopen(device, "r"))) /* last try "ntp.oncore" */
+ break;
+ }
+
+ if (!fd) { /* no inputfile, default to the works ... */
+ instance->init_type = 4;
+ return;
+ }
+
+ mode = mask = 0;
+ lat_flg = long_flg = ht_flg = 0;
+ while (fgets(line, 100, fd)) {
+
+ /* Remove comments */
+ if ((cp = strchr(line, '#')))
+ *cp = '\0';
+
+ /* Remove trailing space */
+ for (i = strlen(line);
+ i > 0 && isascii((int)line[i - 1]) && isspace((int)line[i - 1]);
+ )
+ line[--i] = '\0';
+
+ /* Remove leading space */
+ for (cc = line; *cc && isascii((int)*cc) && isspace((int)*cc); cc++)
+ continue;
+
+ /* Stop if nothing left */
+ if (!*cc)
+ continue;
+
+ /* Uppercase the command and find the arg */
+ for (ca = cc; *ca; ca++) {
+ if (isascii((int)*ca)) {
+ if (islower((int)*ca)) {
+ *ca = toupper(*ca);
+ } else if (isspace((int)*ca) || (*ca == '='))
+ break;
+ }
+ }
+
+ /* Remove space (and possible =) leading the arg */
+ for (; *ca && isascii((int)*ca) && (isspace((int)*ca) || (*ca == '=')); ca++)
+ continue;
+
+ if (!strncmp(cc, "STATUS", (size_t) 6) || !strncmp(cc, "SHMEM", (size_t) 5)) {
+ instance->shmem_fname = estrdup(ca);
+ continue;
+ }
+
+ /* Uppercase argument as well */
+ for (cp = ca; *cp; cp++)
+ if (isascii((int)*cp) && islower((int)*cp))
+ *cp = toupper(*cp);
+
+ if (!strncmp(cc, "LAT", (size_t) 3)) {
+ f1 = f2 = f3 = 0;
+ sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
+ sign = 1;
+ if (f1 < 0) {
+ f1 = -f1;
+ sign = -1;
+ }
+ instance->ss_lat = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
+ lat_flg++;
+ } else if (!strncmp(cc, "LON", (size_t) 3)) {
+ f1 = f2 = f3 = 0;
+ sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
+ sign = 1;
+ if (f1 < 0) {
+ f1 = -f1;
+ sign = -1;
+ }
+ instance->ss_long = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
+ long_flg++;
+ } else if (!strncmp(cc, "HT", (size_t) 2)) {
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(ca, "%lf %1s", &f1, units);
+ if (units[0] == 'F')
+ f1 = 0.3048 * f1;
+ instance->ss_ht = 100 * f1; /* cm */
+ ht_flg++;
+ } else if (!strncmp(cc, "DELAY", (size_t) 5)) {
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(ca, "%lf %1s", &f1, units);
+ if (units[0] == 'N')
+ ;
+ else if (units[0] == 'U')
+ f1 = 1000 * f1;
+ else if (units[0] == 'M')
+ f1 = 1000000 * f1;
+ else
+ f1 = 1000000000 * f1;
+ if (f1 < 0 || f1 > 1.e9)
+ f1 = 0;
+ if (f1 < 0 || f1 > 999999)
+ oncore_log_f(instance, LOG_WARNING,
+ "PPS Cable delay of %fns out of Range, ignored",
+ f1);
+ else
+ instance->delay = f1; /* delay in ns */
+ } else if (!strncmp(cc, "OFFSET", (size_t) 6)) {
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(ca, "%lf %1s", &f1, units);
+ if (units[0] == 'N')
+ ;
+ else if (units[0] == 'U')
+ f1 = 1000 * f1;
+ else if (units[0] == 'M')
+ f1 = 1000000 * f1;
+ else
+ f1 = 1000000000 * f1;
+ if (f1 < 0 || f1 > 1.e9)
+ f1 = 0;
+ if (f1 < 0 || f1 > 999999999.)
+ oncore_log_f(instance, LOG_WARNING,
+ "PPS Offset of %fns out of Range, ignored",
+ f1);
+ else
+ instance->offset = f1; /* offset in ns */
+ } else if (!strncmp(cc, "MODE", (size_t) 4)) {
+ sscanf(ca, "%d", &mode);
+ if (mode < 0 || mode > 4)
+ mode = 4;
+ } else if (!strncmp(cc, "ASSERT", (size_t) 6)) {
+ instance->assert = 1;
+ } else if (!strncmp(cc, "CLEAR", (size_t) 5)) {
+ instance->assert = 0;
+ } else if (!strncmp(cc, "HARDPPS", (size_t) 7)) {
+ instance->hardpps = 1;
+ } else if (!strncmp(cc, "POSN2D", (size_t) 6)) {
+ instance->shmem_Posn = 2;
+ } else if (!strncmp(cc, "POSN3D", (size_t) 6)) {
+ instance->shmem_Posn = 3;
+ } else if (!strncmp(cc, "CHAN", (size_t) 4)) {
+ sscanf(ca, "%d", &i);
+ if ((i == 6) || (i == 8) || (i == 12))
+ instance->chan_in = i;
+ } else if (!strncmp(cc, "TRAIM", (size_t) 5)) {
+ instance->traim_in = 1; /* so TRAIM alone is YES */
+ if (!strcmp(ca, "NO") || !strcmp(ca, "OFF")) /* Yes/No, On/Off */
+ instance->traim_in = 0;
+ } else if (!strncmp(cc, "MASK", (size_t) 4)) {
+ sscanf(ca, "%d", &mask);
+ if (mask > -1 && mask < 90)
+ instance->Ag = mask; /* Satellite mask angle */
+ } else if (!strncmp(cc,"PPSCONTROL",10)) { /* pps control M12 only */
+ if (!strcmp(ca,"ON") || !strcmp(ca, "CONTINUOUS")) {
+ instance->pps_control = 1; /* PPS always on */
+ } else if (!strcmp(ca,"SATELLITE")) {
+ instance->pps_control = 2; /* PPS on when satellite is available */
+ } else if (!strcmp(ca,"TRAIM")) {
+ instance->pps_control = 3; /* PPS on when TRAIM status is OK */
+ } else {
+ oncore_log_f(instance, LOG_WARNING,
+ "Unknown value \"%s\" for PPSCONTROL, ignored",
+ cc);
+ }
+ }
+ }
+ fclose(fd);
+
+ /*
+ * OK, have read all of data file, and extracted the good stuff.
+ * If lat/long/ht specified they ALL must be specified for mode = (1,3).
+ */
+
+ instance->posn_set = 1;
+ if (!( lat_flg && long_flg && ht_flg )) {
+ oncore_log_f(instance, LOG_WARNING,
+ "ONCORE: incomplete data on %s", device);
+ instance->posn_set = 0;
+ if (mode == 1 || mode == 3) {
+ oncore_log_f(instance, LOG_WARNING,
+ "Input Mode = %d, but no/incomplete position, mode set to %d",
+ mode, mode+1);
+ mode++;
+ }
+ }
+ instance->init_type = mode;
+
+ oncore_log_f(instance, LOG_INFO, "Input mode = %d", mode);
+}
+
+
+
+/*
+ * move data from NTP to buffer (toss the extra in the unlikely case it won't fit)
+ */
+
+static void
+oncore_receive(
+ struct recvbuf *rbufp
+ )
+{
+ size_t i;
+ u_char *p;
+ struct peer *peer;
+ struct instance *instance;
+
+ peer = rbufp->recv_peer;
+ instance = peer->procptr->unitptr;
+ p = (u_char *) &rbufp->recv_space;
+
+#ifdef ONCORE_VERBOSE_RECEIVE
+ if (debug > 4) {
+ int i;
+ char Msg[120], Msg2[10];
+
+ oncore_log_f(instance, LOG_DEBUG,
+ ">>> %d bytes available",
+ rbufp->recv_length);
+ strlcpy(Msg, ">>>", sizeof(Msg));
+ for (i = 0; i < rbufp->recv_length; i++) {
+ snprintf(Msg2, sizeof(Msg2), "%02x ", p[i]);
+ strlcat(Msg, Msg2, sizeof(Msg));
+ }
+ oncore_log(instance, LOG_DEBUG, Msg);
+
+ strlcpy(Msg, ">>>", sizeof(Msg));
+ for (i = 0; i < rbufp->recv_length; i++) {
+ snprintf(Msg2, sizeof(Msg2), "%03o ", p[i]);
+ strlcat(Msg, Msg2, sizeof(Msg));
+ }
+ oncore_log(instance, LOG_DEBUG, Msg);
+ }
+#endif
+
+ i = rbufp->recv_length;
+ if (rcvbuf+rcvptr+i > &rcvbuf[sizeof rcvbuf])
+ i = sizeof(rcvbuf) - rcvptr; /* and some char will be lost */
+ memcpy(rcvbuf+rcvptr, p, i);
+ rcvptr += i;
+ oncore_consume(instance);
+}
+
+
+
+/*
+ * Deal with any complete messages
+ */
+
+static void
+oncore_consume(
+ struct instance *instance
+ )
+{
+ int i, m;
+ unsigned l;
+
+ while (rcvptr >= 7) {
+ if (rcvbuf[0] != '@' || rcvbuf[1] != '@') {
+ /* We're not in sync, lets try to get there */
+ for (i=1; i < rcvptr-1; i++)
+ if (rcvbuf[i] == '@' && rcvbuf[i+1] == '@')
+ break;
+#ifdef ONCORE_VERBOSE_CONSUME
+ if (debug > 4)
+ oncore_log_f(instance, LOG_DEBUG,
+ ">>> skipping %d chars",
+ i);
+#endif
+ if (i != rcvptr)
+ memcpy(rcvbuf, rcvbuf+i, (size_t)(rcvptr-i));
+ rcvptr -= i;
+ continue;
+ }
+
+ /* Ok, we have a header now */
+ l = sizeof(oncore_messages)/sizeof(oncore_messages[0]) -1;
+ for(m=0; m<l; m++)
+ if (!strncmp(oncore_messages[m].flag, (char *)(rcvbuf+2), (size_t) 2))
+ break;
+ if (m == l) {
+#ifdef ONCORE_VERBOSE_CONSUME
+ if (debug > 4)
+ oncore_log_f(instance, LOG_DEBUG,
+ ">>> Unknown MSG, skipping 4 (%c%c)",
+ rcvbuf[2], rcvbuf[3]);
+#endif
+ memcpy(rcvbuf, rcvbuf+4, (size_t) 4);
+ rcvptr -= 4;
+ continue;
+ }
+
+ l = oncore_messages[m].len;
+#ifdef ONCORE_VERBOSE_CONSUME
+ if (debug > 3)
+ oncore_log_f(instance, LOG_DEBUG,
+ "GOT: %c%c %d of %d entry %d",
+ instance->unit, rcvbuf[2],
+ rcvbuf[3], rcvptr, l, m);
+#endif
+ /* Got the entire message ? */
+
+ if (rcvptr < l)
+ return;
+
+ /* are we at the end of message? should be <Cksum><CR><LF> */
+
+ if (rcvbuf[l-2] != '\r' || rcvbuf[l-1] != '\n') {
+#ifdef ONCORE_VERBOSE_CONSUME
+ if (debug)
+ oncore_log(instance, LOG_DEBUG, "NO <CR><LF> at end of message");
+#endif
+ } else { /* check the CheckSum */
+ if (oncore_checksum_ok(rcvbuf, l)) {
+ if (instance->shmem != NULL) {
+ instance->shmem[oncore_messages[m].shmem + 2]++;
+ memcpy(instance->shmem + oncore_messages[m].shmem + 3,
+ rcvbuf, (size_t) l);
+ }
+ oncore_msg_any(instance, rcvbuf, (size_t) (l-3), m);
+ if (oncore_messages[m].handler)
+ oncore_messages[m].handler(instance, rcvbuf, (size_t) (l-3));
+ }
+#ifdef ONCORE_VERBOSE_CONSUME
+ else if (debug) {
+ char Msg[120], Msg2[10];
+
+ oncore_log(instance, LOG_ERR, "Checksum mismatch!");
+ snprintf(Msg, sizeof(Msg), "@@%c%c ", rcvbuf[2], rcvbuf[3]);
+ for (i = 4; i < l; i++) {
+ snprintf(Msg2, sizeof(Msg2),
+ "%03o ", rcvbuf[i]);
+ strlcat(Msg, Msg2, sizeof(Msg));
+ }
+ oncore_log(instance, LOG_DEBUG, Msg);
+ }
+#endif
+ }
+
+ if (l != rcvptr)
+ memcpy(rcvbuf, rcvbuf+l, (size_t) (rcvptr-l));
+ rcvptr -= l;
+ }
+}
+
+
+
+static void
+oncore_get_timestamp(
+ struct instance *instance,
+ long dt1, /* tick offset THIS time step */
+ long dt2 /* tick offset NEXT time step */
+ )
+{
+ int Rsm;
+ u_long j;
+ l_fp ts, ts_tmp;
+ double dmy;
+#ifdef HAVE_STRUCT_TIMESPEC
+ struct timespec *tsp = 0;
+#else
+ struct timeval *tsp = 0;
+#endif
+ int current_mode;
+ pps_params_t current_params;
+ struct timespec timeout;
+ struct peer *peer;
+ pps_info_t pps_i;
+ char Msg[160];
+
+ peer = instance->peer;
+
+#if 1
+ /* If we are in SiteSurvey mode, then we are in 3D mode, and we fall thru.
+ * If we have Finished the SiteSurvey, then we fall thru for the 14/15
+ * times we get here in 0D mode (the 1/15 is in 3D for SHMEM).
+ * This gives good time, which gets better when the SS is done.
+ */
+
+ if ((instance->site_survey == ONCORE_SS_DONE) && (instance->mode != MODE_0D)) {
+#else
+ /* old check, only fall thru for SS_DONE and 0D mode, 2h45m wait for ticks */
+
+ if ((instance->site_survey != ONCORE_SS_DONE) || (instance->mode != MODE_0D)) {
+#endif
+ peer->flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */
+ return;
+ }
+
+ /* Don't do anything without an almanac to define the GPS->UTC delta */
+
+ if (instance->rsm.bad_almanac) {
+ peer->flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */
+ return;
+ }
+
+ /* Once the Almanac is valid, the M12+T does not produce valid UTC
+ * immediately.
+ * Wait for UTC offset decode valid, then wait one message more
+ * so we are not off by 13 seconds after reset.
+ */
+
+ if (instance->count5) {
+ instance->count5--;
+ peer->flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */
+ return;
+ }
+
+ j = instance->ev_serial;
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ if (time_pps_fetch(instance->pps_h, PPS_TSFMT_TSPEC, &pps_i,
+ &timeout) < 0) {
+ oncore_log_f(instance, LOG_ERR,
+ "time_pps_fetch failed %m");
+ peer->flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */
+ return;
+ }
+
+ if (instance->assert) {
+ tsp = &pps_i.assert_timestamp;
+
+#ifdef ONCORE_VERBOSE_GET_TIMESTAMP
+ if (debug > 2) {
+ u_long i;
+
+ i = (u_long) pps_i.assert_sequence;
+# ifdef HAVE_STRUCT_TIMESPEC
+ oncore_log_f(instance, LOG_DEBUG,
+ "serial/j (%lu, %lu) %ld.%09ld", i,
+ j, (long)tsp->tv_sec,
+ (long)tsp->tv_nsec);
+# else
+ oncore_log_f(instance, LOG_DEBUG,
+ "serial/j (%lu, %lu) %ld.%06ld", i,
+ j, (long)tsp->tv_sec,
+ (long)tsp->tv_usec);
+# endif
+ }
+#endif
+
+ if (pps_i.assert_sequence == j) {
+ oncore_log(instance, LOG_NOTICE, "ONCORE: oncore_get_timestamp, error serial pps");
+ peer->flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */
+ return;
+ }
+
+ instance->ev_serial = pps_i.assert_sequence;
+ } else {
+ tsp = &pps_i.clear_timestamp;
+
+#if 0
+ if (debug > 2) {
+ u_long i;
+
+ i = (u_long) pps_i.clear_sequence;
+# ifdef HAVE_STRUCT_TIMESPEC
+ oncore_log_f(instance, LOG_DEBUG,
+ "serial/j (%lu, %lu) %ld.%09ld", i,
+ j, (long)tsp->tv_sec,
+ (long)tsp->tv_nsec);
+# else
+ oncore_log_f(instance, LOG_DEBUG,
+ "serial/j (%lu, %lu) %ld.%06ld", i,
+ j, (long)tsp->tv_sec,
+ (long)tsp->tv_usec);
+# endif
+ }
+#endif
+
+ if (pps_i.clear_sequence == j) {
+ oncore_log(instance, LOG_ERR, "oncore_get_timestamp, error serial pps");
+ peer->flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */
+ return;
+ }
+ instance->ev_serial = pps_i.clear_sequence;
+ }
+
+ /* convert timespec -> ntp l_fp */
+
+ dmy = tsp->tv_nsec;
+ dmy /= 1e9;
+ ts.l_uf = dmy * 4294967296.0;
+ ts.l_ui = tsp->tv_sec;
+
+#if 0
+ alternate code for previous 4 lines is
+ dmy = 1.0e-9*tsp->tv_nsec; /* fractional part */
+ DTOLFP(dmy, &ts);
+ dmy = tsp->tv_sec; /* integer part */
+ DTOLFP(dmy, &ts_tmp);
+ L_ADD(&ts, &ts_tmp);
+ or more simply
+ dmy = 1.0e-9*tsp->tv_nsec; /* fractional part */
+ DTOLFP(dmy, &ts);
+ ts.l_ui = tsp->tv_sec;
+#endif /* 0 */
+
+ /* now have timestamp in ts */
+ /* add in saw_tooth and offset, these will be ZERO if no TRAIM */
+ /* they will be IGNORED if the PPSAPI cant do PPS_OFFSET/ASSERT/CLEAR */
+ /* we just try to add them in and dont test for that here */
+
+ /* saw_tooth not really necessary if using TIMEVAL */
+ /* since its only precise to us, but do it anyway. */
+
+ /* offset in ns, and is positive (late), we subtract */
+ /* to put the PPS time transition back where it belongs */
+
+ /* must hand the offset for the NEXT sec off to the Kernel to do */
+ /* the addition, so that the Kernel PLL sees the offset too */
+
+ if (instance->assert)
+ instance->pps_p.assert_offset.tv_nsec = -dt2;
+ else
+ instance->pps_p.clear_offset.tv_nsec = -dt2;
+
+ /* The following code is necessary, and not just a time_pps_setparams,
+ * using the saved instance->pps_p, since some other process on the
+ * machine may have diddled with the mode bits (say adding something
+ * that it needs). We take what is there and ADD what we need.
+ * [[ The results from the time_pps_getcap is unlikely to change so
+ * we could probably just save it, but I choose to do the call ]]
+ * Unfortunately, there is only ONE set of mode bits in the kernel per
+ * interface, and not one set for each open handle.
+ *
+ * There is still a race condition here where we might mess up someone
+ * elses mode, but if he is being careful too, he should survive.
+ */
+
+ if (time_pps_getcap(instance->pps_h, &current_mode) < 0) {
+ oncore_log_f(instance, LOG_ERR,
+ "time_pps_getcap failed: %m");
+ peer->flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */
+ return;
+ }
+
+ if (time_pps_getparams(instance->pps_h, &current_params) < 0) {
+ oncore_log_f(instance, LOG_ERR,
+ "time_pps_getparams failed: %m");
+ peer->flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */
+ return;
+ }
+
+ /* or current and mine */
+ current_params.mode |= instance->pps_p.mode;
+ /* but only set whats legal */
+ current_params.mode &= current_mode;
+
+ current_params.assert_offset.tv_sec = 0;
+ current_params.assert_offset.tv_nsec = -dt2;
+ current_params.clear_offset.tv_sec = 0;
+ current_params.clear_offset.tv_nsec = -dt2;
+
+ if (time_pps_setparams(instance->pps_h, &current_params))
+ oncore_log(instance, LOG_ERR, "ONCORE: Error doing time_pps_setparams");
+
+ /* have time from UNIX origin, convert to NTP origin. */
+
+ ts.l_ui += JAN_1970;
+ instance->pp->lastrec = ts;
+
+ /* print out information about this timestamp (long line) */
+
+ ts_tmp = ts;
+ ts_tmp.l_ui = 0; /* zero integer part */
+ LFPTOD(&ts_tmp, dmy); /* convert fractional part to a double */
+ j = 1.0e9*dmy; /* then to integer ns */
+
+ Rsm = 0;
+ if (instance->chan == 6)
+ Rsm = instance->BEHa[64];
+ else if (instance->chan == 8)
+ Rsm = instance->BEHa[72];
+ else if (instance->chan == 12)
+ Rsm = ((instance->BEHa[129]<<8) | instance->BEHa[130]);
+
+ if (instance->chan == 6 || instance->chan == 8) {
+ char f1[5], f2[5], f3[5], f4[5];
+ if (instance->traim) {
+ snprintf(f1, sizeof(f1), "%d",
+ instance->BEHn[21]);
+ snprintf(f2, sizeof(f2), "%d",
+ instance->BEHn[22]);
+ snprintf(f3, sizeof(f3), "%2d",
+ instance->BEHn[23] * 256 +
+ instance->BEHn[24]);
+ snprintf(f4, sizeof(f4), "%3d",
+ (s_char)instance->BEHn[25]);
+ } else {
+ strlcpy(f1, "x", sizeof(f1));
+ strlcpy(f2, "x", sizeof(f2));
+ strlcpy(f3, "xx", sizeof(f3));
+ strlcpy(f4, "xxx", sizeof(f4));
+ }
+ snprintf(Msg, sizeof(Msg), /* MAX length 128, currently at 127 */
+ "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d,%s,%s sigma %s neg-sawtooth %s sat %d%d%d%d%d%d%d%d",
+ ts.l_ui, j,
+ instance->pp->year, instance->pp->day,
+ instance->pp->hour, instance->pp->minute, instance->pp->second,
+ (long) tsp->tv_sec % 60,
+ Rsm, 0.1*(256*instance->BEHa[35]+instance->BEHa[36]),
+ /*rsat dop */
+ instance->BEHa[38], instance->BEHa[39], instance->traim, f1, f2,
+ /* nsat visible, nsat tracked, traim,traim,traim */
+ f3, f4,
+ /* sigma neg-sawtooth */
+ /*sat*/ instance->BEHa[41], instance->BEHa[45], instance->BEHa[49], instance->BEHa[53],
+ instance->BEHa[57], instance->BEHa[61], instance->BEHa[65], instance->BEHa[69]
+ ); /* will be 0 for 6 chan */
+ } else if (instance->chan == 12) {
+ char f1[5], f2[5], f3[5], f4[5];
+ if (instance->traim) {
+ snprintf(f1, sizeof(f1), "%d",
+ instance->BEHn[6]);
+ snprintf(f2, sizeof(f2), "%d",
+ instance->BEHn[7]);
+ snprintf(f3, sizeof(f3), "%d",
+ instance->BEHn[12] * 256 +
+ instance->BEHn[13]);
+ snprintf(f4, sizeof(f4), "%3d",
+ (s_char)instance->BEHn[14]);
+ } else {
+ strlcpy(f1, "x", sizeof(f1));
+ strlcpy(f2, "x", sizeof(f2));
+ strlcpy(f3, "xx", sizeof(f3));
+ strlcpy(f4, "xxx", sizeof(f4));
+ }
+ snprintf(Msg, sizeof(Msg),
+ "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d,%s,%s sigma %s neg-sawtooth %s sat %d%d%d%d%d%d%d%d%d%d%d%d",
+ ts.l_ui, j,
+ instance->pp->year, instance->pp->day,
+ instance->pp->hour, instance->pp->minute, instance->pp->second,
+ (long) tsp->tv_sec % 60,
+ Rsm, 0.1*(256*instance->BEHa[53]+instance->BEHa[54]),
+ /*rsat dop */
+ instance->BEHa[55], instance->BEHa[56], instance->traim, f1, f2,
+ /* nsat visible, nsat tracked traim,traim,traim */
+ f3, f4,
+ /* sigma neg-sawtooth */
+ /*sat*/ instance->BEHa[58], instance->BEHa[64], instance->BEHa[70], instance->BEHa[76],
+ instance->BEHa[82], instance->BEHa[88], instance->BEHa[94], instance->BEHa[100],
+ instance->BEHa[106], instance->BEHa[112], instance->BEHa[118], instance->BEHa[124]
+ );
+ }
+
+ /* and some things I dont understand (magic ntp things) */
+
+ if (!refclock_process(instance->pp)) {
+ refclock_report(instance->peer, CEVNT_BADTIME);
+ peer->flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */
+ return;
+ }
+
+ oncore_log(instance, LOG_INFO, Msg); /* this is long message above */
+ instance->pollcnt = 2;
+
+ if (instance->polled) {
+ instance->polled = 0;
+ /* instance->pp->dispersion = instance->pp->skew = 0; */
+ instance->pp->lastref = instance->pp->lastrec;
+ refclock_receive(instance->peer);
+ }
+ peer->flags |= FLAG_PPS;
+}
+
+
+/*************** oncore_msg_XX routines start here *******************/
+
+
+/*
+ * print Oncore response message.
+ */
+
+static void
+oncore_msg_any(
+ struct instance *instance,
+ u_char *buf,
+ size_t len,
+ int idx
+ )
+{
+#ifdef ONCORE_VERBOSE_MSG_ANY
+ int i;
+ const char *fmt = oncore_messages[idx].fmt;
+ const char *p;
+ char *q;
+ char *qlim;
+#ifdef HAVE_GETCLOCK
+ struct timespec ts;
+#endif
+ struct timeval tv;
+ char Msg[120], Msg2[10];
+
+ if (debug > 3) {
+# ifdef HAVE_GETCLOCK
+ (void) getclock(TIMEOFDAY, &ts);
+ tv.tv_sec = ts.tv_sec;
+ tv.tv_usec = ts.tv_nsec / 1000;
+# else
+ GETTIMEOFDAY(&tv, 0);
+# endif
+ oncore_log(instance, LOG_DEBUG, "%ld.%06ld",
+ (long)tv.tv_sec, (long)tv.tv_usec);
+
+ if (!*fmt) {
+ snprintf(Msg, sizeof(Msg), ">>@@%c%c ", buf[2],
+ buf[3]);
+ for(i = 2; i < len && i < 2400 ; i++) {
+ snprintf(Msg2, sizeof(Msg2), "%02x",
+ buf[i]);
+ strlcat(Msg, Msg2, sizeof(Msg));
+ }
+ oncore_log(instance, LOG_DEBUG, Msg);
+ return;
+ } else {
+ strlcpy(Msg, "##", sizeof(Msg));
+ qlim = Msg + sizeof(Msg) - 3;
+ for (p = fmt, q = Msg + 2; q < qlim && *p; ) {
+ *q++ = *p++;
+ *q++ = '_';
+ }
+ *q = '\0';
+ oncore_log(instance, LOG_DEBUG, Msg);
+ snprintf(Msg, sizeof(Msg), "%c%c", buf[2],
+ buf[3]);
+ i = 4;
+ for (p = fmt; *p; p++) {
+ snprintf(Msg2, "%02x", buf[i++]);
+ strlcat(Msg, Msg2, sizeof(Msg));
+ }
+ oncore_log(instance, LOG_DEBUG, Msg);
+ }
+ }
+#endif
+}
+
+
+
+/* Latitude, Longitude, Height */
+
+static void
+oncore_msg_Adef(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+}
+
+
+
+/* Mask Angle */
+
+static void
+oncore_msg_Ag(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char *cp;
+
+ cp = "set to";
+ if (instance->o_state == ONCORE_RUN)
+ cp = "is";
+
+ instance->Ag = buf[4];
+ oncore_log_f(instance, LOG_INFO,
+ "Satellite mask angle %s %d degrees", cp,
+ (int)instance->Ag);
+}
+
+
+
+/*
+ * get Position hold position
+ */
+
+static void
+oncore_msg_As(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ instance->ss_lat = buf_w32(&buf[4]);
+ instance->ss_long = buf_w32(&buf[8]);
+ instance->ss_ht = buf_w32(&buf[12]);
+
+ /* Print out Position */
+ oncore_print_posn(instance);
+}
+
+
+
+/*
+ * Try to use Oncore UT+ Auto Survey Feature
+ * If its not there (VP), set flag to do it ourselves.
+ */
+
+static void
+oncore_msg_At(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ instance->saw_At = 1;
+ if (instance->site_survey == ONCORE_SS_TESTING) {
+ if (buf[4] == 2) {
+ oncore_log(instance, LOG_NOTICE,
+ "Initiating hardware 3D site survey");
+
+ oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_HW");
+ instance->site_survey = ONCORE_SS_HW;
+ }
+ }
+}
+
+
+
+/*
+ * get PPS Offset
+ * Nb. @@Ay is not supported for early UT (no plus) model
+ */
+
+static void
+oncore_msg_Ay(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ if (instance->saw_Ay)
+ return;
+
+ instance->saw_Ay = 1;
+
+ instance->offset = buf_w32(&buf[4]);
+
+ oncore_log_f(instance, LOG_INFO, "PPS Offset is set to %ld ns",
+ instance->offset);
+}
+
+
+
+/*
+ * get Cable Delay
+ */
+
+static void
+oncore_msg_Az(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ if (instance->saw_Az)
+ return;
+
+ instance->saw_Az = 1;
+
+ instance->delay = buf_w32(&buf[4]);
+
+ oncore_log_f(instance, LOG_INFO, "Cable delay is set to %ld ns",
+ instance->delay);
+}
+
+
+
+/* Ba, Ea and Ha come here, these contain Position */
+
+static void
+oncore_msg_BaEaHa(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ const char *cp;
+ int mode;
+
+ /* OK, we are close to the RUN state now.
+ * But we have a few more items to initialize first.
+ *
+ * At the beginning of this routine there are several 'timers'.
+ * We enter this routine 1/sec, and since the upper levels of NTP have usurped
+ * the use of timers, we use the 1/sec entry to do things that
+ * we would normally do with timers...
+ */
+
+ if (instance->o_state == ONCORE_CHECK_CHAN) { /* here while checking for the # chan */
+ if (buf[2] == 'B') { /* 6chan */
+ if (instance->chan_ck < 6) instance->chan_ck = 6;
+ } else if (buf[2] == 'E') { /* 8chan */
+ if (instance->chan_ck < 8) instance->chan_ck = 8;
+ } else if (buf[2] == 'H') { /* 12chan */
+ if (instance->chan_ck < 12) instance->chan_ck = 12;
+ }
+
+ if (instance->count3++ < 5)
+ return;
+
+ instance->count3 = 0;
+
+ if (instance->chan_in != -1) /* set in Input */
+ instance->chan = instance->chan_in;
+ else /* set from test */
+ instance->chan = instance->chan_ck;
+
+ oncore_log_f(instance, LOG_INFO, "Input says chan = %d",
+ instance->chan_in);
+ oncore_log_f(instance, LOG_INFO, "Model # says chan = %d",
+ instance->chan_id);
+ oncore_log_f(instance, LOG_INFO, "Testing says chan = %d",
+ instance->chan_ck);
+ oncore_log_f(instance, LOG_INFO, "Using chan = %d",
+ instance->chan);
+
+ instance->o_state = ONCORE_HAVE_CHAN;
+ oncore_log(instance, LOG_NOTICE, "state = ONCORE_HAVE_CHAN");
+
+ instance->timeout = 4;
+ oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+ return;
+ }
+
+ if (instance->o_state != ONCORE_ALMANAC && instance->o_state != ONCORE_RUN)
+ return;
+
+ /* PAUSE 5sec - make sure results are stable, before using position */
+
+ if (instance->count) {
+ if (instance->count++ < 5)
+ return;
+ instance->count = 0;
+ }
+
+ memcpy(instance->BEHa, buf, (size_t) (len+3)); /* Ba, Ea or Ha */
+
+ /* check if we saw a response to Gc (M12 or M12+T */
+
+ if (instance->pps_control_msg_seen != -2) {
+ if ((instance->pps_control_msg_seen == -1) && (instance->pps_control != -1)) {
+ oncore_log(instance, LOG_INFO, "PPSCONTROL set, but not implemented (not M12)");
+ }
+ instance->pps_control_msg_seen = -2;
+ }
+
+ /* check the antenna (did it get unplugged) and almanac (is it ready) for changes. */
+
+ oncore_check_almanac(instance);
+ oncore_check_antenna(instance);
+
+ /* If we are in Almanac mode, waiting for Almanac, we can't do anything till we have it */
+ /* When we have an almanac, we will start the Bn/En/@@Hn messages */
+
+ if (instance->o_state == ONCORE_ALMANAC)
+ if (oncore_wait_almanac(instance))
+ return;
+
+ /* do some things once when we get this far in BaEaHa */
+
+ if (instance->once) {
+ instance->once = 0;
+ instance->count2 = 1;
+
+ /* Have we seen an @@At (position hold) command response */
+ /* if not, message out */
+
+ if (instance->chan != 12 && !instance->saw_At) {
+ oncore_log(instance, LOG_NOTICE,
+ "Not Good, no @@At command (no Position Hold), must be a GT/GT+");
+ oncore_sendmsg(instance, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
+ }
+
+ /* have an Almanac, can start the SiteSurvey
+ * (actually only need to get past the almanac_load where we diddle with At
+ * command,- we can't change it after we start the HW_SS below
+ */
+
+ mode = instance->init_type;
+ switch (mode) {
+ case 0: /* NO initialization, don't change anything */
+ case 1: /* Use given Position */
+ case 3:
+ instance->site_survey = ONCORE_SS_DONE;
+ oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_DONE");
+ break;
+
+ case 2:
+ case 4: /* Site Survey */
+ oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_TESTING");
+ instance->site_survey = ONCORE_SS_TESTING;
+ instance->count1 = 1;
+ if (instance->chan == 12)
+ oncore_sendmsg(instance, oncore_cmd_Gd3, sizeof(oncore_cmd_Gd3)); /* M12+T */
+ else
+ oncore_sendmsg(instance, oncore_cmd_At2, sizeof(oncore_cmd_At2)); /* not GT, arg not VP */
+ break;
+ }
+
+ /* Read back PPS Offset for Output */
+ /* Nb. This will fail silently for early UT (no plus) and M12 models */
+
+ oncore_sendmsg(instance, oncore_cmd_Ayx, sizeof(oncore_cmd_Ayx));
+
+ /* Read back Cable Delay for Output */
+
+ oncore_sendmsg(instance, oncore_cmd_Azx, sizeof(oncore_cmd_Azx));
+
+ /* Read back Satellite Mask Angle for Output */
+
+ oncore_sendmsg(instance, oncore_cmd_Agx, sizeof(oncore_cmd_Agx));
+ }
+
+
+ /* Unfortunately, the Gd3 command returns '3' for the M12 v1.3 firmware where it is
+ * out-of-range and it should return 0-2. (v1.3 can't do a HW Site Survey)
+ * We must do the Gd3, and then wait a cycle or two for things to settle,
+ * then check Ha[130]&0x10 to see if a SS is in progress.
+ * We will set SW if HW has not been set after an appropriate delay.
+ */
+
+ if (instance->site_survey == ONCORE_SS_TESTING) {
+ if (instance->chan == 12) {
+ if (instance->count1) {
+ if (instance->count1++ > 5 || instance->BEHa[130]&0x10) {
+ instance->count1 = 0;
+ if (instance->BEHa[130]&0x10) {
+ oncore_log(instance, LOG_NOTICE,
+ "Initiating hardware 3D site survey");
+
+ oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_HW");
+ instance->site_survey = ONCORE_SS_HW;
+ } else {
+ oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_SW");
+ instance->site_survey = ONCORE_SS_SW;
+ }
+ }
+ }
+ } else {
+ if (instance->count1) {
+ if (instance->count1++ > 5) {
+ instance->count1 = 0;
+ /*
+ * For instance->site_survey to still be ONCORE_SS_TESTING, then after a 5sec
+ * wait after the @@At2/@@Gd3 command we have not changed the state to
+ * ONCORE_SS_HW. If the Hardware is capable of doing a Site Survey, then
+ * the variable would have been changed by now.
+ * There are three possibilities:
+ * 6/8chan
+ * (a) We did not get a response to the @@At0 or @@At2 commands,
+ * and it must be a GT/GT+/SL with no position hold mode.
+ * We will have to do it ourselves.
+ * (b) We saw the @@At0, @@At2 commands, but @@At2 failed,
+ * must be a VP or older UT which doesn't have Site Survey mode.
+ * We will have to do it ourselves.
+ * 12chan
+ * (c) We saw the @@Gd command, and saw H[13]*0x10
+ * We will have to do it ourselves (done above)
+ */
+
+ oncore_log_f(instance, LOG_INFO,
+ "Initiating software 3D site survey (%d samples)",
+ POS_HOLD_AVERAGE);
+
+ oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_SW");
+ instance->site_survey = ONCORE_SS_SW;
+
+ instance->ss_lat = instance->ss_long = instance->ss_ht = 0;
+ if (instance->chan == 12)
+ oncore_sendmsg(instance, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* disable */
+ else {
+ oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* disable */
+ oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* disable */
+ }
+ }
+ }
+ }
+ }
+
+ /* check the mode we are in 0/2/3D */
+
+ if (instance->chan == 6) {
+ if (instance->BEHa[64]&0x8)
+ instance->mode = MODE_0D;
+ else if (instance->BEHa[64]&0x10)
+ instance->mode = MODE_2D;
+ else if (instance->BEHa[64]&0x20)
+ instance->mode = MODE_3D;
+ } else if (instance->chan == 8) {
+ if (instance->BEHa[72]&0x8)
+ instance->mode = MODE_0D;
+ else if (instance->BEHa[72]&0x10)
+ instance->mode = MODE_2D;
+ else if (instance->BEHa[72]&0x20)
+ instance->mode = MODE_3D;
+ } else if (instance->chan == 12) {
+ int bits;
+
+ bits = (instance->BEHa[129]>>5) & 0x7; /* actually Ha */
+ if (bits == 0x4)
+ instance->mode = MODE_0D;
+ else if (bits == 0x6)
+ instance->mode = MODE_2D;
+ else if (bits == 0x7)
+ instance->mode = MODE_3D;
+ }
+
+ /* copy the record to the (extra) location in SHMEM */
+
+ if (instance->shmem) {
+ int i;
+ u_char *smp; /* pointer to start of shared mem for Ba/Ea/Ha */
+
+ switch(instance->chan) {
+ case 6: smp = &instance->shmem[instance->shmem_Ba]; break;
+ case 8: smp = &instance->shmem[instance->shmem_Ea]; break;
+ case 12: smp = &instance->shmem[instance->shmem_Ha]; break;
+ default: smp = (u_char *) NULL; break;
+ }
+
+ switch (instance->mode) {
+ case MODE_0D: i = 1; break; /* 0D, Position Hold */
+ case MODE_2D: i = 2; break; /* 2D, Altitude Hold */
+ case MODE_3D: i = 3; break; /* 3D fix */
+ default: i = 0; break;
+ }
+
+ if (i && smp != NULL) {
+ i *= (len+6);
+ smp[i + 2]++;
+ memcpy(&smp[i+3], buf, (size_t) (len+3));
+ }
+ }
+
+ /*
+ * check if traim timer active
+ * if it hasn't been cleared, then @@Bn/@@En/@@Hn did not respond
+ */
+
+ if (instance->traim_delay) {
+ if (instance->traim_delay++ > 5) {
+ instance->traim = 0;
+ instance->traim_delay = 0;
+ cp = "ONCORE: Did not detect TRAIM response, TRAIM = OFF";
+ oncore_log(instance, LOG_INFO, cp);
+
+ oncore_set_traim(instance);
+ } else
+ return;
+
+ }
+
+ /* by now should have a @@Ba/@@Ea/@@Ha with good data in it */
+
+ if (!instance->have_dH && !instance->traim_delay)
+ oncore_compute_dH(instance);
+
+ /*
+ * must be ONCORE_RUN if we are here.
+ * Have # chan and TRAIM by now.
+ */
+
+ instance->pp->year = buf[6]*256+buf[7];
+ instance->pp->day = ymd2yd(buf[6]*256+buf[7], buf[4], buf[5]);
+ instance->pp->hour = buf[8];
+ instance->pp->minute = buf[9];
+ instance->pp->second = buf[10];
+
+ /*
+ * Are we doing a Hardware or Software Site Survey?
+ */
+
+ if (instance->site_survey == ONCORE_SS_HW || instance->site_survey == ONCORE_SS_SW)
+ oncore_ss(instance);
+
+ /* see if we ever saw a response from the @@Ayx above */
+
+ if (instance->count2) {
+ if (instance->count2++ > 5) { /* this delay to check on @@Ay command */
+ instance->count2 = 0;
+
+ /* Have we seen an Ay (1PPS time offset) command response */
+ /* if not, and non-zero offset, zero the offset, and send message */
+
+ if (!instance->saw_Ay && instance->offset) {
+ oncore_log(instance, LOG_INFO, "No @@Ay command, PPS OFFSET ignored");
+ instance->offset = 0;
+ }
+ }
+ }
+
+ /*
+ * Check the leap second status once per day.
+ */
+
+ oncore_check_leap_sec(instance);
+
+ /*
+ * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn.
+ */
+
+ if (instance->shmem && !instance->shmem_bad_Ea && instance->shmem_Posn && (instance->site_survey == ONCORE_SS_DONE))
+ oncore_shmem_get_3D(instance);
+
+ if (!instance->traim) /* NO traim, no BnEnHn, go get tick */
+ oncore_get_timestamp(instance, instance->offset, instance->offset);
+}
+
+
+
+/* Almanac Status */
+
+static void
+oncore_msg_Bd(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ oncore_log_f(instance, LOG_NOTICE,
+ "Bd: Almanac %s, week = %d, t = %d, %d SVs: %x",
+ ((buf[4]) ? "LOADED" : "(NONE)"), buf[5], buf[6],
+ buf[7], w32(&buf[8]));
+}
+
+
+
+/* get leap-second warning message */
+
+/*
+ * @@Bj does NOT behave as documented in current Oncore firmware.
+ * It turns on the LEAP indicator when the data is set, and does not,
+ * as documented, wait until the beginning of the month when the
+ * leap second will occur.
+ * Since this firmware bug will never be fixed in all the outstanding Oncore receivers
+ * @@Bj is only called in June/December.
+ */
+
+static void
+oncore_msg_Bj(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ const char *cp;
+
+ instance->saw_Bj = 1;
+
+ switch(buf[4]) {
+ case 1:
+ instance->pp->leap = LEAP_ADDSECOND;
+ cp = "Set pp.leap to LEAP_ADDSECOND";
+ break;
+ case 2:
+ instance->pp->leap = LEAP_DELSECOND;
+ cp = "Set pp.leap to LEAP_DELSECOND";
+ break;
+ case 0:
+ default:
+ instance->pp->leap = LEAP_NOWARNING;
+ cp = "Set pp.leap to LEAP_NOWARNING";
+ break;
+ }
+ oncore_log(instance, LOG_NOTICE, cp);
+}
+
+
+
+static void
+oncore_msg_Bl(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ int chan, id, subframe, valid, page, i, j, tow;
+ int day_now, day_lsf;
+ char *cp;
+ enum {
+ WARN_NOT_YET,
+ WARN_0,
+ WARN_PLUS,
+ WARN_MINUS
+ } warn;
+
+ day_now = day_lsf = 0;
+ cp = NULL; /* keep gcc happy */
+
+ chan = buf[4] & 0377;
+ id = buf[5] & 0377;
+ subframe = buf[6] & 017;
+ valid = (buf[6] >> 4) & 017;
+ page = buf[7];
+
+ if ((!instance->Bl.lsf_flg && !instance->Bl.wn_flg) && (subframe == 4 && page == 18 && valid == 10)) {
+ instance->Bl.dt_ls = buf[32];
+ instance->Bl.WN_lsf = buf[33];
+ instance->Bl.DN_lsf = buf[34];
+ instance->Bl.dt_lsf = buf[35];
+ instance->Bl.lsf_flg++;
+ }
+ if ((instance->Bl.lsf_flg && !instance->Bl.wn_flg) && (subframe == 1 && valid == 10)) {
+ i = (buf[7+7]<<8) + buf[7+8];
+ instance->Bl.WN = i >> 6;
+ tow = (buf[7+4]<<16) + (buf[7+5]<<8) + buf[7+6];
+ tow >>= 7;
+ tow = tow & 0377777;
+ tow <<= 2;
+ instance->Bl.DN = tow/57600L + 1;
+ instance->Bl.wn_flg++;
+ }
+ if (instance->Bl.wn_flg && instance->Bl.lsf_flg) {
+ instance->Bl.wn_flg = instance->Bl.lsf_flg = 0;
+ oncore_cmd_Bl[2] = 0;
+ oncore_sendmsg(instance, oncore_cmd_Bl, sizeof oncore_cmd_Bl);
+ oncore_cmd_Bl[2] = 1;
+
+ i = instance->Bl.WN&01400;
+ instance->Bl.WN_lsf |= i;
+
+ /* have everything I need, doit */
+
+ i = (instance->Bl.WN_lsf - instance->Bl.WN);
+ if (i < 0)
+ i += 1024;
+ day_now = instance->Bl.DN;
+ day_lsf = 7*i + instance->Bl.DN_lsf;
+
+ /* ignore if in past or more than a month in future */
+
+ warn = WARN_NOT_YET;
+ if (day_lsf >= day_now && day_lsf - day_now < 32) {
+ /* if < 28d, doit, if 28-31, ck day-of-month < 20 (not at end of prev month) */
+ if (day_lsf - day_now < 28 || instance->BEHa[5] < 20) {
+ i = instance->Bl.dt_lsf - instance->Bl.dt_ls;
+ switch (i) {
+ case -1:
+ warn = WARN_MINUS;
+ break;
+ case 0:
+ warn = WARN_0;
+ break;
+ case 1:
+ warn = WARN_PLUS;
+ break;
+ }
+ }
+ }
+
+ switch (warn) {
+ case WARN_0:
+ case WARN_NOT_YET:
+ instance->peer->leap = LEAP_NOWARNING;
+ cp = "Set peer.leap to LEAP_NOWARNING";
+ break;
+ case WARN_MINUS:
+ instance->peer->leap = LEAP_DELSECOND;
+ cp = "Set peer.leap to LEAP_DELSECOND";
+ break;
+ case WARN_PLUS:
+ instance->peer->leap = LEAP_ADDSECOND;
+ cp = "Set peer.leap to LEAP_ADDSECOND";
+ break;
+ }
+ oncore_log(instance, LOG_NOTICE, cp);
+
+ i = instance->Bl.dt_lsf-instance->Bl.dt_ls;
+ if (i) {
+ j = (i >= 0) ? i : -i; /* abs(i) */
+ oncore_log_f(instance, LOG_NOTICE,
+ "see Leap_Second (%c%d) in %d days",
+ ((i >= 0) ? '+' : '-'), j,
+ day_lsf-day_now);
+ }
+ }
+
+/*
+ * Reg only wants the following output for "deeper" driver debugging.
+ * See Bug 2142 and Bug 1866
+ */
+#if 0
+ oncore_log_f(instance, LOG_DEBUG,
+ "dt_ls = %d dt_lsf = %d WN = %d DN = %d WN_lsf = %d DNlsf = %d wn_flg = %d lsf_flg = %d Bl_day = %d",
+ instance->Bl.dt_ls, instance->Bl.dt_lsf,
+ instance->Bl.WN, instance->Bl.DN,
+ instance->Bl.WN_lsf, instance->Bl.DN_lsf,
+ instance->Bl.wn_flg, instance->Bl.lsf_flg,
+ instance->Bl.Bl_day);
+#endif
+}
+
+
+static void
+oncore_msg_BnEnHn(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ long dt1, dt2;
+
+ if (instance->o_state != ONCORE_RUN)
+ return;
+
+ if (instance->traim_delay) { /* flag that @@Bn/@@En/Hn returned */
+ instance->traim_ck = 1;
+ instance->traim_delay = 0;
+ oncore_log(instance, LOG_NOTICE, "ONCORE: Detected TRAIM, TRAIM = ON");
+
+ oncore_set_traim(instance);
+ }
+
+ memcpy(instance->BEHn, buf, (size_t) len); /* Bn or En or Hn */
+
+ if (!instance->traim) /* BnEnHn will be turned off in any case */
+ return;
+
+ /* If Time RAIM doesn't like it, don't trust it */
+
+ if (buf[2] == 'H') {
+ if (instance->BEHn[6]) { /* bad TRAIM */
+ oncore_log(instance, LOG_WARNING, "BAD TRAIM");
+ return;
+ }
+
+ dt1 = instance->saw_tooth + instance->offset; /* dt this time step */
+ instance->saw_tooth = (s_char) instance->BEHn[14]; /* update for next time Hn[14] */
+ dt2 = instance->saw_tooth + instance->offset; /* dt next time step */
+ } else {
+ if (instance->BEHn[21]) /* bad TRAIM */
+ return;
+
+ dt1 = instance->saw_tooth + instance->offset; /* dt this time step */
+ instance->saw_tooth = (s_char) instance->BEHn[25]; /* update for next time Bn[25], En[25] */
+ dt2 = instance->saw_tooth + instance->offset; /* dt next time step */
+ }
+
+ oncore_get_timestamp(instance, dt1, dt2);
+}
+
+
+
+/* Here for @@Ca, @@Fa and @@Ia messages */
+
+/* These are Self test Commands for 6, 8, and 12 chan receivers.
+ * There are good reasons NOT to do a @@Ca, @@Fa or @@Ia command with the ONCORE.
+ * It was found that under some circumstances the following
+ * command would fail if issued immediately after the return from the
+ * @@Fa, but a 2sec delay seemed to fix things. Since simply calling
+ * sleep(2) is wasteful, and may cause trouble for some OS's, repeating
+ * itimer, we set a flag, and test it at the next POLL. If it hasn't
+ * been cleared, we reissue the @@Cj that is issued below.
+ * Note that we do a @@Cj at the beginning, and again here.
+ * The first is to get the info, the 2nd is just used as a safe command
+ * after the @@Fa for all Oncores (and it was in this posn in the
+ * original code).
+ */
+
+static void
+oncore_msg_CaFaIa(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ int i;
+
+ if (instance->o_state == ONCORE_TEST_SENT) {
+ enum antenna_state antenna;
+
+ instance->timeout = 0;
+
+#if ONCORE_VERBOSE_SELF_TEST
+ if (debug > 2) {
+ if (buf[2] == 'I')
+ oncore_log_f(instance, LOG_DEBUG,
+ ">>@@%ca %x %x %x", buf[2],
+ buf[4], buf[5], buf[6]);
+ else
+ oncore_log_f(instance, LOG_DEBUG,
+ ">>@@%ca %x %x", buf[2],
+ buf[4], buf[5]);
+ }
+#endif
+
+ antenna = (buf[4] & 0xc0) >> 6;
+ buf[4] &= ~0xc0;
+
+ i = buf[4] || buf[5];
+ if (buf[2] == 'I') i = i || buf[6];
+ if (i) {
+ if (buf[2] == 'I')
+ oncore_log_f(instance, LOG_ERR,
+ "self test failed: result %02x %02x %02x",
+ buf[4], buf[5], buf[6]);
+ else
+ oncore_log_f(instance, LOG_ERR,
+ "self test failed: result %02x %02x",
+ buf[4], buf[5]);
+
+ oncore_log(instance, LOG_ERR,
+ "ONCORE: self test failed, shutting down driver");
+
+ refclock_report(instance->peer, CEVNT_FAULT);
+ oncore_shutdown(instance->unit, instance->peer);
+ return;
+ }
+
+ /* report the current antenna state */
+
+ oncore_antenna_report(instance, antenna);
+
+ instance->o_state = ONCORE_INIT;
+ oncore_log(instance, LOG_NOTICE, "state = ONCORE_INIT");
+
+ instance->timeout = 4;
+ oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+ }
+}
+
+
+
+/*
+ * Demultiplex the almanac into shmem
+ */
+
+static void
+oncore_msg_Cb(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ int i;
+
+ if (instance->shmem == NULL)
+ return;
+
+ if (buf[4] == 5 && buf[5] > 0 && buf[5] < 26)
+ i = buf[5];
+ else if (buf[4] == 4 && buf[5] <= 5)
+ i = buf[5] + 24;
+ else if (buf[4] == 4 && buf[5] <= 10)
+ i = buf[5] + 23;
+ else if (buf[4] == 4 && buf[5] == 25)
+ i = 34;
+ else {
+ oncore_log(instance, LOG_NOTICE, "Cb: Response is NO ALMANAC");
+ return;
+ }
+
+ i *= 36;
+ instance->shmem[instance->shmem_Cb + i + 2]++;
+ memcpy(instance->shmem + instance->shmem_Cb + i + 3, buf, (size_t) (len + 3));
+
+#ifdef ONCORE_VERBOSE_MSG_CB
+ oncore_log_f(instance, LOG_DEBUG, "See Cb [%d,%d]", buf[4],
+ buf[5]);
+#endif
+}
+
+
+
+/*
+ * Set to Factory Defaults (Reasonable for UT w/ no Battery Backup
+ * not so for VP (eeprom) or any unit with a battery
+ */
+
+static void
+oncore_msg_Cf(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ if (instance->o_state == ONCORE_RESET_SENT) {
+ oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to Posn Fix mode */
+ /* Reset set VP to IDLE */
+ instance->o_state = ONCORE_TEST_SENT;
+ oncore_log(instance, LOG_NOTICE, "state = ONCORE_TEST_SENT");
+
+ oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+ }
+}
+
+
+
+/*
+ * This is the Grand Central Station for the Preliminary Initialization.
+ * Once done here we move on to oncore_msg_BaEaHa for final Initialization and Running.
+ *
+ * We do an @@Cj whenever we need a safe command for all Oncores.
+ * The @@Cj gets us back here where we can switch to the next phase of setup.
+ *
+ * o Once at the very beginning (in start) to get the Model number.
+ * This info is printed, but no longer used.
+ * o Again after we have determined the number of Channels in the receiver.
+ * o And once later after we have done a reset and test, (which may hang),
+ * as we are about to initialize the Oncore and start it running.
+ * o We have one routine below for each case.
+ */
+
+static void
+oncore_msg_Cj(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ int mode;
+
+ memcpy(instance->Cj, buf, len);
+
+ instance->timeout = 0;
+ if (instance->o_state == ONCORE_CHECK_ID) {
+ oncore_msg_Cj_id(instance, buf, len);
+ oncore_chan_test(instance);
+ } else if (instance->o_state == ONCORE_HAVE_CHAN) {
+ mode = instance->init_type;
+ if (mode == 3 || mode == 4) { /* Cf will return here to check for TEST */
+ instance->o_state = ONCORE_RESET_SENT;
+ oncore_log(instance, LOG_NOTICE, "state = ONCORE_RESET_SENT");
+ oncore_sendmsg(instance, oncore_cmd_Cf, sizeof(oncore_cmd_Cf));
+ } else {
+ instance->o_state = ONCORE_TEST_SENT;
+ oncore_log(instance, LOG_NOTICE, "state = ONCORE_TEST_SENT");
+ }
+ }
+
+ if (instance->o_state == ONCORE_TEST_SENT) {
+ if (instance->chan == 6)
+ oncore_sendmsg(instance, oncore_cmd_Ca, sizeof(oncore_cmd_Ca));
+ else if (instance->chan == 8)
+ oncore_sendmsg(instance, oncore_cmd_Fa, sizeof(oncore_cmd_Fa));
+ else if (instance->chan == 12)
+ oncore_sendmsg(instance, oncore_cmd_Ia, sizeof(oncore_cmd_Ia));
+ } else if (instance->o_state == ONCORE_INIT)
+ oncore_msg_Cj_init(instance, buf, len);
+}
+
+
+
+/* The information on determining a Oncore 'Model', viz VP, UT, etc, from
+ * the Model Number comes from "Richard M. Hambly" <rick@cnssys.com>
+ * and from Motorola. Until recently Rick was the only source of
+ * this information as Motorola didn't give the information out.
+ *
+ * Determine the Type from the Model #, this determines #chan and if TRAIM is
+ * available.
+ *
+ * The Information from this routine is NO LONGER USED.
+ * The RESULTS are PRINTED, BUT NOT USED, and the routine COULD BE DELETED
+ */
+
+static void
+oncore_msg_Cj_id(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char *cp, *cp1, *cp2, Model[21];
+
+ /* Write Receiver ID message to clockstats file */
+
+ instance->Cj[294] = '\0';
+ for (cp=(char *)instance->Cj; cp< (char *) &instance->Cj[294]; ) {
+ cp1 = strchr(cp, '\r');
+ if (!cp1)
+ cp1 = (char *)&instance->Cj[294];
+ *cp1 = '\0';
+ oncore_log(instance, LOG_NOTICE, cp);
+ *cp1 = '\r';
+ cp = cp1+2;
+ }
+
+ /* next, the Firmware Version and Revision numbers */
+
+ instance->version = atoi((char *) &instance->Cj[83]);
+ instance->revision = atoi((char *) &instance->Cj[111]);
+
+ /* from model number decide which Oncore this is,
+ and then the number of channels */
+
+ for (cp= (char *) &instance->Cj[160]; *cp == ' '; cp++) /* start right after 'Model #' */
+ ;
+ cp1 = cp;
+ cp2 = Model;
+ for (; !isspace((int)*cp) && cp-cp1 < 20; cp++, cp2++)
+ *cp2 = *cp;
+ *cp2 = '\0';
+
+ cp = 0;
+ if (!strncmp(Model, "PVT6", (size_t) 4)) {
+ cp = "PVT6";
+ instance->model = ONCORE_PVT6;
+ } else if (Model[0] == 'A') {
+ cp = "Basic";
+ instance->model = ONCORE_BASIC;
+ } else if (Model[0] == 'B' || !strncmp(Model, "T8", (size_t) 2)) {
+ cp = "VP";
+ instance->model = ONCORE_VP;
+ } else if (Model[0] == 'P') {
+ cp = "M12";
+ instance->model = ONCORE_M12;
+ } else if (Model[0] == 'R' || Model[0] == 'D' || Model[0] == 'S') {
+ if (Model[5] == 'N') {
+ cp = "GT";
+ instance->model = ONCORE_GT;
+ } else if ((Model[1] == '3' || Model[1] == '4') && Model[5] == 'G') {
+ cp = "GT+";
+ instance->model = ONCORE_GTPLUS;
+ } else if ((Model[1] == '5' && Model[5] == 'U') || (Model[1] == '1' && Model[5] == 'A')) {
+ cp = "UT";
+ instance->model = ONCORE_UT;
+ } else if (Model[1] == '5' && Model[5] == 'G') {
+ cp = "UT+";
+ instance->model = ONCORE_UTPLUS;
+ } else if (Model[1] == '6' && Model[5] == 'G') {
+ cp = "SL";
+ instance->model = ONCORE_SL;
+ } else {
+ cp = "Unknown";
+ instance->model = ONCORE_UNKNOWN;
+ }
+ } else {
+ cp = "Unknown";
+ instance->model = ONCORE_UNKNOWN;
+ }
+
+ /* use MODEL to set CHAN and TRAIM and possibly zero SHMEM */
+
+ oncore_log_f(instance, LOG_INFO,
+ "This looks like an Oncore %s with version %d.%d firmware.",
+ cp, instance->version, instance->revision);
+
+ instance->chan_id = 8; /* default */
+ if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6)
+ instance->chan_id = 6;
+ else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS)
+ instance->chan_id = 8;
+ else if (instance->model == ONCORE_M12)
+ instance->chan_id = 12;
+
+ instance->traim_id = 0; /* default */
+ if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6)
+ instance->traim_id = 0;
+ else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS)
+ instance->traim_id = 1;
+ else if (instance->model == ONCORE_M12)
+ instance->traim_id = -1;
+
+ oncore_log_f(instance, LOG_INFO, "Channels = %d, TRAIM = %s",
+ instance->chan_id,
+ ((instance->traim_id < 0)
+ ? "UNKNOWN"
+ : (instance->traim_id > 0)
+ ? "ON"
+ : "OFF"));
+}
+
+
+
+/* OK, know type of Oncore, have possibly reset it, and have tested it.
+ * We know the number of channels.
+ * We will determine whether we have TRAIM before we actually start.
+ * Now initialize.
+ */
+
+static void
+oncore_msg_Cj_init(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ u_char Cmd[20];
+ int mode;
+
+
+ /* The M12 with 1.3 or 2.0 Firmware, loses track of all Satellites and has to
+ * start again if we go from 0D -> 3D, then loses them again when we
+ * go from 3D -> 0D. We do this to get a @@Ea message for SHMEM.
+ * For NOW we will turn this aspect of filling SHMEM off for the M12
+ */
+
+ if (instance->chan == 12) {
+ instance->shmem_bad_Ea = 1;
+ oncore_log_f(instance, LOG_NOTICE,
+ "*** SHMEM partially enabled for ONCORE M12 s/w v%d.%d ***",
+ instance->version, instance->revision);
+ }
+
+ oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to Posn Fix mode */
+ oncore_sendmsg(instance, oncore_cmd_Bb, sizeof(oncore_cmd_Bb)); /* turn on for shmem (6/8/12) */
+ oncore_sendmsg(instance, oncore_cmd_Ek, sizeof(oncore_cmd_Ek)); /* turn off (VP) */
+ oncore_sendmsg(instance, oncore_cmd_Aw, sizeof(oncore_cmd_Aw)); /* UTC time (6/8/12) */
+ oncore_sendmsg(instance, oncore_cmd_AB, sizeof(oncore_cmd_AB)); /* Appl type static (VP) */
+ oncore_sendmsg(instance, oncore_cmd_Be, sizeof(oncore_cmd_Be)); /* Tell us the Almanac for shmem (6/8/12) */
+ oncore_sendmsg(instance, oncore_cmd_Bd, sizeof(oncore_cmd_Bd)); /* Tell us when Almanac changes */
+
+ mode = instance->init_type;
+
+ /* If there is Position input in the Config file
+ * and mode = (1,3) set it as posn hold posn, goto 0D mode.
+ * or mode = (2,4) set it as INITIAL position, and do Site Survey.
+ */
+
+ if (instance->posn_set) {
+ oncore_log(instance, LOG_INFO, "Setting Posn from input data");
+ oncore_set_posn(instance); /* this should print posn indirectly thru the As cmd */
+ } else /* must issue an @@At here to check on 6/8 Position Hold, set_posn would have */
+ if (instance->chan != 12)
+ oncore_sendmsg(instance, oncore_cmd_Atx, sizeof(oncore_cmd_Atx));
+
+ if (mode != 0) {
+ /* cable delay in ns */
+ memcpy(Cmd, oncore_cmd_Az, (size_t) sizeof(oncore_cmd_Az));
+ w32_buf(&Cmd[-2+4], instance->delay);
+ oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Az)); /* 6,8,12 */
+
+ /* PPS offset in ns */
+ memcpy(Cmd, oncore_cmd_Ay, (size_t) sizeof(oncore_cmd_Ay)); /* some have it, some don't */
+ w32_buf(&Cmd[-2+4], instance->offset); /* will check for hw response */
+ oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ay));
+
+ /* Satellite mask angle */
+
+ if (instance->Ag != 0xff) { /* will have 0xff in it if not set by user */
+ memcpy(Cmd, oncore_cmd_Ag, (size_t) sizeof(oncore_cmd_Ag));
+ Cmd[-2+4] = instance->Ag;
+ oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ag));
+ }
+ }
+
+ /* 6, 8 12 chan - Position/Status/Data Output Message, 1/s
+ * now we're really running
+ * these were ALL started in the chan test,
+ * However, if we had mode=3,4 then commands got turned off, so we turn
+ * them on again here just in case
+ */
+
+ if (instance->chan == 6) { /* start 6chan, kill 8,12chan commands, possibly testing VP in 6chan mode */
+ oncore_sendmsg(instance, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0));
+ oncore_sendmsg(instance, oncore_cmd_En0, sizeof(oncore_cmd_En0));
+ oncore_sendmsg(instance, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0));
+ oncore_sendmsg(instance, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
+ oncore_sendmsg(instance, oncore_cmd_Ba, sizeof(oncore_cmd_Ba ));
+ } else if (instance->chan == 8) { /* start 8chan, kill 6,12chan commands */
+ oncore_sendmsg(instance, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0));
+ oncore_sendmsg(instance, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0));
+ oncore_sendmsg(instance, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0));
+ oncore_sendmsg(instance, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
+ oncore_sendmsg(instance, oncore_cmd_Ea, sizeof(oncore_cmd_Ea ));
+ } else if (instance->chan == 12){ /* start 12chan, kill 6,12chan commands */
+ oncore_sendmsg(instance, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0));
+ oncore_sendmsg(instance, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0));
+ oncore_sendmsg(instance, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0));
+ oncore_sendmsg(instance, oncore_cmd_En0, sizeof(oncore_cmd_En0));
+ oncore_sendmsg(instance, oncore_cmd_Ha, sizeof(oncore_cmd_Ha ));
+ oncore_cmd_Gc[2] = (instance->pps_control < 0) ? 1 : instance->pps_control;
+ oncore_sendmsg(instance, oncore_cmd_Gc, sizeof(oncore_cmd_Gc)); /* PPS off/continuous/Tracking 1+sat/TRAIM */
+ }
+
+ instance->count = 1;
+ instance->o_state = ONCORE_ALMANAC;
+ oncore_log(instance, LOG_NOTICE, "state = ONCORE_ALMANAC");
+}
+
+
+
+/* 12chan position */
+
+static void
+oncore_msg_Ga(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ long lat, lon, ht;
+ double Lat, Lon, Ht;
+
+
+ lat = buf_w32(&buf[4]);
+ lon = buf_w32(&buf[8]);
+ ht = buf_w32(&buf[12]); /* GPS ellipsoid */
+
+ Lat = lat;
+ Lon = lon;
+ Ht = ht;
+
+ Lat /= 3600000;
+ Lon /= 3600000;
+ Ht /= 100;
+
+ oncore_log_f(instance, LOG_NOTICE,
+ "Ga Posn Lat = %.7f, Lon = %.7f, Ht = %.2f", Lat,
+ Lon, Ht);
+
+ instance->ss_lat = lat;
+ instance->ss_long = lon;
+ instance->ss_ht = ht;
+
+ oncore_print_posn(instance);
+}
+
+
+
+/* 12 chan time/date */
+
+static void
+oncore_msg_Gb(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char * gmts;
+ int mo, d, y, h, m, s, gmth, gmtm;
+
+ mo = buf[4];
+ d = buf[5];
+ y = 256*buf[6]+buf[7];
+
+ h = buf[8];
+ m = buf[9];
+ s = buf[10];
+
+ gmts = ((buf[11] == 0) ? "+" : "-");
+ gmth = buf[12];
+ gmtm = buf[13];
+
+ oncore_log_f(instance, LOG_NOTICE,
+ "Date/Time set to: %d%s%d %2d:%02d:%02d GMT (GMT offset is %s%02d:%02d)",
+ d, months[mo-1], y, h, m, s, gmts, gmth, gmtm);
+}
+
+
+
+/* Response to PPS Control message (M12 and M12+T only ) */
+
+static void
+oncore_msg_Gc(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char *tbl[] = {"OFF", "ON", "SATELLITE", "TRAIM" };
+
+ instance->pps_control_msg_seen = 1;
+ oncore_log_f(instance, LOG_INFO, "PPS Control set to %s",
+ tbl[buf[4]]);
+}
+
+
+
+/* Leap Second for M12, gives all info from satellite message */
+/* also in UT v3.0 */
+
+static void
+oncore_msg_Gj(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ static const char * insrem[2] = {
+ "removed",
+ "inserted"
+ };
+
+ int dt;
+ char *cp;
+
+ instance->saw_Gj = 1; /* flag, saw_Gj, dont need to try Bj in check_leap */
+
+ /* print the message to verify whats there */
+
+ dt = buf[5] - buf[4];
+
+ oncore_log_f(instance, LOG_INFO,
+ "Leap Sec Msg: %d %d %d %d %d %d %d %d %d %d",
+ buf[4], buf[5], 256 * buf[6] + buf[7], buf[8],
+ buf[9], buf[10],
+ (buf[14] + 256 *
+ (buf[13] + 256 * (buf[12] + 256 * buf[11]))),
+ buf[15], buf[16], buf[17]);
+
+ /* There seems to be eternal confusion about when a leap second
+ * takes place. It's the second *before* the new TAI offset
+ * becomes effective. But since the ONCORE receiver tells us
+ * just that, we would have to do some time/date calculations to
+ * get the actual leap second -- that is, the one that is
+ * deleted or inserted.
+ *
+ * Going through all this for a simple log is probably overkill,
+ * so for fixing bug#1050 the message output is changed to
+ * reflect the fact that it tells the second after the leap
+ * second.
+ */
+ if (dt)
+ oncore_log_f(instance, LOG_NOTICE,
+ "Leap second %s (%d) before %04u-%02u-%02u/%02u:%02u:%02u",
+ insrem[(dt > 0)], dt,
+ 256u * buf[6] + buf[7], buf[8], buf[9],
+ buf[15], buf[16], buf[17]);
+
+ /* Only raise warning within a month of the leap second */
+
+ instance->pp->leap = LEAP_NOWARNING;
+ cp = "Set pp.leap to LEAP_NOWARNING";
+
+ if (buf[6] == instance->BEHa[6] && buf[7] == instance->BEHa[7] && /* year */
+ buf[8] == instance->BEHa[4]) { /* month */
+ if (dt) {
+ if (dt < 0) {
+ instance->pp->leap = LEAP_DELSECOND;
+ cp = "Set pp.leap to LEAP_DELSECOND";
+ } else {
+ instance->pp->leap = LEAP_ADDSECOND;
+ cp = "Set pp.leap to LEAP_ADDSECOND";
+ }
+ }
+ }
+ oncore_log(instance, LOG_INFO, cp);
+}
+
+
+
+/* Power on failure */
+
+static void
+oncore_msg_Sz(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ if (instance && instance->peer) {
+ oncore_log(instance, LOG_ERR, "Oncore: System Failure at Power On");
+ oncore_shutdown(instance->unit, instance->peer);
+ }
+}
+
+/************** Small Subroutines ***************/
+
+
+static void
+oncore_antenna_report(
+ struct instance *instance,
+ enum antenna_state new_state)
+{
+ char *cp;
+
+ if (instance->ant_state == new_state)
+ return;
+
+ switch (new_state) {
+ case ONCORE_ANTENNA_OK: cp = "GPS antenna: OK"; break;
+ case ONCORE_ANTENNA_OC: cp = "GPS antenna: short (overcurrent)"; break;
+ case ONCORE_ANTENNA_UC: cp = "GPS antenna: open (not connected)"; break;
+ case ONCORE_ANTENNA_NV: cp = "GPS antenna: short (no voltage)"; break;
+ default: cp = "GPS antenna: ?"; break;
+ }
+
+ instance->ant_state = new_state;
+ oncore_log(instance, LOG_NOTICE, cp);
+}
+
+
+
+static void
+oncore_chan_test(
+ struct instance *instance
+ )
+{
+ /* subroutine oncore_Cj_id has determined the number of channels from the
+ * model number of the attached oncore. This is not always correct since
+ * the oncore could have non-standard firmware. Here we check (independently) by
+ * trying a 6, 8, and 12 chan command, and see which responds.
+ * Caution: more than one CAN respond.
+ *
+ * This #chan is used by the code rather than that calculated from the model number.
+ */
+
+ instance->o_state = ONCORE_CHECK_CHAN;
+ oncore_log(instance, LOG_NOTICE, "state = ONCORE_CHECK_CHAN");
+
+ instance->count3 = 1;
+ oncore_sendmsg(instance, oncore_cmd_Ba, sizeof(oncore_cmd_Ba));
+ oncore_sendmsg(instance, oncore_cmd_Ea, sizeof(oncore_cmd_Ea));
+ oncore_sendmsg(instance, oncore_cmd_Ha, sizeof(oncore_cmd_Ha));
+}
+
+
+
+/* check for a GOOD Almanac, have we got one yet? */
+
+static void
+oncore_check_almanac(
+ struct instance *instance
+ )
+{
+ if (instance->chan == 6) {
+ instance->rsm.bad_almanac = instance->BEHa[64]&0x1;
+ instance->rsm.bad_fix = instance->BEHa[64]&0x52;
+ } else if (instance->chan == 8) {
+ instance->rsm.bad_almanac = instance->BEHa[72]&0x1;
+ instance->rsm.bad_fix = instance->BEHa[72]&0x52;
+ } else if (instance->chan == 12) {
+ int bits1, bits2, bits3;
+
+ bits1 = (instance->BEHa[129]>>5) & 0x7; /* actually Ha */
+ bits2 = instance->BEHa[130];
+ instance->rsm.bad_almanac = (bits2 & 0x80);
+ instance->rsm.bad_fix = (bits2 & 0x8) || (bits1 == 0x2);
+ /* too few sat Bad Geom */
+
+ bits3 = instance->BEHa[141]; /* UTC parameters */
+ if (!instance->count5_set && (bits3 & 0xC0)) {
+ instance->count5 = 4; /* was 2 [Bug 1766] */
+ instance->count5_set = 1;
+ }
+#ifdef ONCORE_VERBOSE_CHECK_ALMANAC
+ oncore_log_f(instance, LOG_DEBUG,
+ "DEBUG BITS: (%x %x), (%x %x %x), %x %x %x %x %x",
+ instance->BEHa[129], instance->BEHa[130],
+ bits1, bits2, bits3,
+ instance->mode == MODE_0D,
+ instance->mode == MODE_2D,
+ instance->mode == MODE_3D,
+ instance->rsm.bad_almanac,
+ instance->rsm.bad_fix);
+ }
+#endif
+ }
+}
+
+
+
+/* check the antenna for changes (did it get unplugged?) */
+
+static void
+oncore_check_antenna(
+ struct instance *instance
+ )
+{
+ enum antenna_state antenna; /* antenna state */
+
+ antenna = instance->ant_state;
+ if (instance->chan == 12)
+ antenna = (instance->BEHa[130] & 0x6 ) >> 1;
+ else
+ antenna = (instance->BEHa[37] & 0xc0) >> 6; /* prob unset 6, set GT, UT unset VP */
+
+ oncore_antenna_report (instance, antenna);
+}
+
+
+
+/*
+ * Check the leap second status once per day.
+ *
+ * Note that the ONCORE firmware for the Bj command is wrong at
+ * least in the VP.
+ * It starts advertising a LEAP SECOND as soon as the GPS satellite
+ * data message (page 18, subframe 4) is updated to a date in the
+ * future, and does not wait for the month that it will occur.
+ * The event will usually be advertised several months in advance.
+ * Since there is a one bit flag, there is no way to tell if it is
+ * this month, or when...
+ *
+ * As such, we have the workaround below, of only checking for leap
+ * seconds with the Bj command in June/December.
+ *
+ * The Gj command gives more information, and we can tell in which
+ * month to apply the correction.
+ *
+ * Note that with the VP we COULD read the raw data message, and
+ * interpret it ourselves, but since this is specific to this receiver
+ * only, and the above workaround is adequate, we don't bother.
+ */
+
+static void
+oncore_check_leap_sec(
+ struct instance *instance
+ )
+{
+ oncore_cmd_Bl[2] = 1; /* just to be sure */
+ if (instance->Bj_day != instance->BEHa[5]) { /* do this 1/day */
+ instance->Bj_day = instance->BEHa[5];
+
+ if (instance->saw_Gj < 0) { /* -1 DONT have Gj use Bj */
+ if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12))
+ oncore_sendmsg(instance, oncore_cmd_Bj, sizeof(oncore_cmd_Bj));
+ oncore_sendmsg(instance, oncore_cmd_Bl, sizeof(oncore_cmd_Bl));
+ return;
+ }
+
+ if (instance->saw_Gj == 0) /* 0 is dont know if we have Gj */
+ instance->count4 = 1;
+
+ oncore_sendmsg(instance, oncore_cmd_Gj, sizeof(oncore_cmd_Gj));
+ return;
+ }
+
+ /* Gj works for some 6/8 chan UT and the M12 */
+ /* if no response from Gj in 5 sec, we try Bj */
+ /* which isnt implemented in all the GT/UT either */
+
+ if (instance->count4) { /* delay, waiting for Gj response */
+ if (instance->saw_Gj == 1)
+ instance->count4 = 0;
+ else if (instance->count4++ > 5) { /* delay, waiting for Gj response */
+ instance->saw_Gj = -1; /* didnt see it, will use Bj */
+ instance->count4 = 0;
+ if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12)) {
+ oncore_sendmsg(instance, oncore_cmd_Bj, sizeof(oncore_cmd_Bj));
+ oncore_sendmsg(instance, oncore_cmd_Bl, sizeof(oncore_cmd_Bl));
+ }
+ }
+ }
+}
+
+
+
+/* check the message checksum,
+ * buf points to START of message ( @@ )
+ * len is length WITH CR/LF.
+ */
+
+static int
+oncore_checksum_ok(
+ u_char *buf,
+ int len
+ )
+{
+ int i, j;
+
+ j = 0;
+ for (i = 2; i < len-3; i++)
+ j ^= buf[i];
+
+ return(j == buf[len-3]);
+}
+
+
+
+static void
+oncore_compute_dH(
+ struct instance *instance
+ )
+{
+ int GPS, MSL;
+
+ /* Here calculate dH = GPS - MSL for output message */
+ /* also set Altitude Hold mode if GT */
+
+ instance->have_dH = 1;
+ if (instance->chan == 12) {
+ GPS = buf_w32(&instance->BEHa[39]);
+ MSL = buf_w32(&instance->BEHa[43]);
+ } else {
+ GPS = buf_w32(&instance->BEHa[23]);
+ MSL = buf_w32(&instance->BEHa[27]);
+ }
+ instance->dH = GPS - MSL;
+ instance->dH /= 100.;
+
+ /* if MSL is not set, the calculation is meaningless */
+
+ if (MSL) /* not set ! */
+ oncore_log_f(instance, LOG_INFO,
+ "dH = (GPS - MSL) = %.2fm", instance->dH);
+}
+
+
+
+/*
+ * try loading Almanac from shmem (where it was copied from shmem_old
+ */
+
+static void
+oncore_load_almanac(
+ struct instance *instance
+ )
+{
+ u_char *cp, Cmd[20];
+ int n;
+ struct timeval tv;
+ struct tm *tm;
+
+ if (!instance->shmem)
+ return;
+
+#ifndef ONCORE_VERBOSE_LOAD_ALMANAC
+ for (cp = instance->shmem + 4; (n = 256 * (*(cp-3)) + *(cp-2));
+ cp += (n + 3)) {
+ if (!strncmp((char *) cp, "@@Cb", 4) &&
+ oncore_checksum_ok(cp, 33) &&
+ (*(cp+4) == 4 || *(cp+4) == 5)) {
+ write(instance->ttyfd, cp, n);
+ oncore_print_Cb(instance, cp);
+ }
+ }
+#else /* ONCORE_VERBOSE_LOAD_ALMANAC follows */
+ for (cp = instance->shmem + 4; (n = 256 * (*(cp-3)) + *(cp-2));
+ cp += (n+3)) {
+ oncore_log_f(instance, LOG_DEBUG, "See %c%c%c%c %d",
+ *(cp), *(cp+1), *(cp+2), *(cp+3), *(cp+4));
+
+ if (!strncmp(cp, "@@Cb", 4)) {
+ oncore_print_Cb(instance, cp);
+ if (oncore_checksum_ok(cp, 33)) {
+ if (*(cp+4) == 4 || *(cp+4) == 5) {
+ oncore_log(instance, LOG_DEBUG, "GOOD SF");
+ write(instance->ttyfd, cp, n);
+ } else
+ oncore_log(instance, LOG_DEBUG, "BAD SF");
+ } else
+ oncore_log(instance, LOG_DEBUG, "BAD CHECKSUM");
+ }
+ }
+#endif
+
+ /* Must load position and time or the Almanac doesn't do us any good */
+
+ if (!instance->posn_set) { /* if we input a posn use it, else from SHMEM */
+ oncore_log(instance, LOG_NOTICE, "Loading Posn from SHMEM");
+ for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3)) {
+ if ((instance->chan == 6 && (!strncmp((char *) cp, "@@Ba", 4) && oncore_checksum_ok(cp, 68))) ||
+ (instance->chan == 8 && (!strncmp((char *) cp, "@@Ea", 4) && oncore_checksum_ok(cp, 76))) ||
+ (instance->chan == 12 && (!strncmp((char *) cp, "@@Ha", 4) && oncore_checksum_ok(cp, 154)))) {
+ int ii, jj, kk;
+
+ instance->posn_set = 1;
+ ii = buf_w32(cp + 15);
+ jj = buf_w32(cp + 19);
+ kk = buf_w32(cp + 23);
+#ifdef ONCORE_VERBOSE_LOAD_ALMANAC
+ oncore_log_f(instance, LOG_DEBUG,
+ "SHMEM posn = %ld (%d, %d, %d)",
+ (long)(cp-instance->shmem),
+ ii, jj, kk);
+#endif
+ if (ii != 0 || jj != 0 || kk != 0) { /* phk asked for this test */
+ instance->ss_lat = ii;
+ instance->ss_long = jj;
+ instance->ss_ht = kk;
+ }
+ }
+ }
+ }
+ oncore_set_posn(instance);
+
+ /* and set time to time from Computer clock */
+
+ GETTIMEOFDAY(&tv, 0);
+ tm = gmtime((const time_t *) &tv.tv_sec);
+
+#ifdef ONCORE_VERBOSE_LOAD_ALMANAC
+ oncore_log_f(instance, LOG_DEBUG, "DATE %d %d %d, %d %d %d",
+ 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+#endif
+ if (instance->chan == 12) {
+ memcpy(Cmd, oncore_cmd_Gb, (size_t) sizeof(oncore_cmd_Gb));
+ Cmd[-2+4] = tm->tm_mon + 1;
+ Cmd[-2+5] = tm->tm_mday;
+ Cmd[-2+6] = (1900+tm->tm_year)/256;
+ Cmd[-2+7] = (1900+tm->tm_year)%256;
+ Cmd[-2+8] = tm->tm_hour;
+ Cmd[-2+9] = tm->tm_min;
+ Cmd[-2+10] = tm->tm_sec;
+ Cmd[-2+11] = 0;
+ Cmd[-2+12] = 0;
+ Cmd[-2+13] = 0;
+ oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Gb));
+ } else {
+ /* First set GMT offset to zero */
+
+ oncore_sendmsg(instance, oncore_cmd_Ab, sizeof(oncore_cmd_Ab));
+
+ memcpy(Cmd, oncore_cmd_Ac, (size_t) sizeof(oncore_cmd_Ac));
+ Cmd[-2+4] = tm->tm_mon + 1;
+ Cmd[-2+5] = tm->tm_mday;
+ Cmd[-2+6] = (1900+tm->tm_year)/256;
+ Cmd[-2+7] = (1900+tm->tm_year)%256;
+ oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ac));
+
+ memcpy(Cmd, oncore_cmd_Aa, (size_t) sizeof(oncore_cmd_Aa));
+ Cmd[-2+4] = tm->tm_hour;
+ Cmd[-2+5] = tm->tm_min;
+ Cmd[-2+6] = tm->tm_sec;
+ oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Aa));
+ }
+
+ oncore_log(instance, LOG_INFO, "Setting Posn and Time after Loading Almanac");
+}
+
+
+
+/* Almanac data input */
+
+static void
+oncore_print_Cb(
+ struct instance *instance,
+ u_char *cp
+ )
+{
+#ifdef ONCORE_VERBOSE_CB
+ int ii;
+ char Msg[160], Msg2[10];
+
+ oncore_log_f(instance, LOG_DEBUG, "DEBUG: See: %c%c%c%c", *(cp),
+ *(cp+1), *(cp+2), *(cp+3));
+
+ snprintf(Msg, sizeof(Msg), "DEBUG: Cb: [%d,%d]", *(cp+4),
+ *(cp+5));
+ for (ii = 0; ii < 33; ii++) {
+ snprintf(Msg2, sizeof(Msg2), " %d", *(cp+ii));
+ strlcat(Msg, Msg2, sizeof(Msg));
+ }
+ oncore_log(instance, LOG_DEBUG, Msg);
+
+ oncore_log_f(instance, LOG_DEBUG, "Debug: Cb: [%d,%d]", *(cp+4),
+ *(cp+5));
+#endif
+}
+
+
+#if 0
+static void
+oncore_print_array(
+ u_char *cp,
+ int n
+ )
+{
+ int jj, i, j, nn;
+
+ nn = 0;
+ printf("\nTOP\n");
+ jj = n/16;
+ for (j=0; j<jj; j++) {
+ printf("%4d: ", nn);
+ nn += 16;
+ for (i=0; i<16; i++)
+ printf(" %o", *cp++);
+ printf("\n");
+ }
+}
+#endif
+
+
+static void
+oncore_print_posn(
+ struct instance *instance
+ )
+{
+ char ew, ns;
+ double xd, xm, xs, yd, ym, ys, hm, hft;
+ int idx, idy, is, imx, imy;
+ long lat, lon;
+
+ oncore_log(instance, LOG_INFO, "Posn:");
+ ew = 'E';
+ lon = instance->ss_long;
+ if (lon < 0) {
+ ew = 'W';
+ lon = -lon;
+ }
+
+ ns = 'N';
+ lat = instance->ss_lat;
+ if (lat < 0) {
+ ns = 'S';
+ lat = -lat;
+ }
+
+ hm = instance->ss_ht/100.;
+ hft= hm/0.3048;
+
+ xd = lat/3600000.; /* lat, lon in int msec arc, ht in cm. */
+ yd = lon/3600000.;
+ oncore_log_f(instance, LOG_INFO,
+ "Lat = %c %11.7fdeg, Long = %c %11.7fdeg, Alt = %5.2fm (%5.2fft) GPS",
+ ns, xd, ew, yd, hm, hft);
+
+ idx = xd;
+ idy = yd;
+ imx = lat%3600000;
+ imy = lon%3600000;
+ xm = imx/60000.;
+ ym = imy/60000.;
+ oncore_log_f(instance, LOG_INFO,
+ "Lat = %c %3ddeg %7.4fm, Long = %c %3ddeg %8.5fm, Alt = %7.2fm (%7.2fft) GPS",
+ ns, idx, xm, ew, idy, ym, hm, hft);
+
+ imx = xm;
+ imy = ym;
+ is = lat%60000;
+ xs = is/1000.;
+ is = lon%60000;
+ ys = is/1000.;
+ oncore_log_f(instance, LOG_INFO,
+ "Lat = %c %3ddeg %2dm %5.2fs, Long = %c %3ddeg %2dm %5.2fs, Alt = %7.2fm (%7.2fft) GPS",
+ ns, idx, imx, xs, ew, idy, imy, ys, hm, hft);
+}
+
+
+
+/*
+ * write message to Oncore.
+ */
+
+static void
+oncore_sendmsg(
+ struct instance *instance,
+ u_char *ptr,
+ size_t len
+ )
+{
+ int fd;
+ u_char cs = 0;
+
+ fd = instance->ttyfd;
+#ifdef ONCORE_VERBOSE_SENDMSG
+ if (debug > 4) {
+ oncore_log_f(instance, LOG_DEBUG, "ONCORE: Send @@%c%c %d",
+ ptr[0], ptr[1], (int)len);
+ }
+#endif
+ write(fd, "@@", (size_t) 2);
+ write(fd, ptr, len);
+ while (len--)
+ cs ^= *ptr++;
+ write(fd, &cs, (size_t) 1);
+ write(fd, "\r\n", (size_t) 2);
+}
+
+
+
+static void
+oncore_set_posn(
+ struct instance *instance
+ )
+{
+ int mode;
+ u_char Cmd[20];
+
+ /* Turn OFF position hold, it needs to be off to set position (for some units),
+ will get set ON in @@Ea later */
+
+ if (instance->chan == 12)
+ oncore_sendmsg(instance, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* (12) */
+ else {
+ oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* (6/8) */
+ oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* (6/8) */
+ }
+
+ mode = instance->init_type;
+
+ if (mode != 0) { /* first set posn hold position */
+ memcpy(Cmd, oncore_cmd_As, (size_t) sizeof(oncore_cmd_As)); /* don't modify static variables */
+ w32_buf(&Cmd[-2+4], (int) instance->ss_lat);
+ w32_buf(&Cmd[-2+8], (int) instance->ss_long);
+ w32_buf(&Cmd[-2+12], (int) instance->ss_ht);
+ Cmd[-2+16] = 0;
+ oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_As)); /* posn hold 3D posn (6/8/12) */
+
+ memcpy(Cmd, oncore_cmd_Au, (size_t) sizeof(oncore_cmd_Au));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_ht);
+ Cmd[-2+8] = 0;
+ oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Au)); /* altitude hold (6/8/12 not UT, M12T) */
+
+ /* next set current position */
+
+ if (instance->chan == 12) {
+ memcpy(Cmd, oncore_cmd_Ga, (size_t) sizeof(oncore_cmd_Ga));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_lat);
+ w32_buf(&Cmd[-2+8], (int) instance->ss_long);
+ w32_buf(&Cmd[-2+12],(int) instance->ss_ht);
+ Cmd[-2+16] = 0;
+ oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ga)); /* 3d posn (12) */
+ } else {
+ memcpy(Cmd, oncore_cmd_Ad, (size_t) sizeof(oncore_cmd_Ad));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_lat);
+ oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ad)); /* lat (6/8) */
+
+ memcpy(Cmd, oncore_cmd_Ae, (size_t) sizeof(oncore_cmd_Ae));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_long);
+ oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ae)); /* long (6/8) */
+
+ memcpy(Cmd, oncore_cmd_Af, (size_t) sizeof(oncore_cmd_Af));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_ht);
+ Cmd[-2+8] = 0;
+ oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Af)); /* ht (6/8) */
+ }
+
+ /* Finally, turn on position hold */
+
+ if (instance->chan == 12)
+ oncore_sendmsg(instance, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1));
+ else
+ oncore_sendmsg(instance, oncore_cmd_At1, sizeof(oncore_cmd_At1));
+ }
+}
+
+
+
+static void
+oncore_set_traim(
+ struct instance *instance
+ )
+{
+ if (instance->traim_in != -1) /* set in Input */
+ instance->traim = instance->traim_in;
+ else
+ instance->traim = instance->traim_ck;
+
+ oncore_log_f(instance, LOG_INFO, "Input says TRAIM = %d",
+ instance->traim_in);
+ oncore_log_f(instance, LOG_INFO, "Model # says TRAIM = %d",
+ instance->traim_id);
+ oncore_log_f(instance, LOG_INFO, "Testing says TRAIM = %d",
+ instance->traim_ck);
+ oncore_log_f(instance, LOG_INFO, "Using TRAIM = %d",
+ instance->traim);
+
+ if (instance->traim_ck == 1 && instance->traim == 0) {
+ /* if it should be off, and I turned it on during testing,
+ then turn it off again */
+ if (instance->chan == 6)
+ oncore_sendmsg(instance, oncore_cmd_Bnx, sizeof(oncore_cmd_Bnx));
+ else if (instance->chan == 8)
+ oncore_sendmsg(instance, oncore_cmd_Enx, sizeof(oncore_cmd_Enx));
+ else /* chan == 12 */
+ oncore_sendmsg(instance, oncore_cmd_Ge0, sizeof(oncore_cmd_Ge0));
+ oncore_sendmsg(instance, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
+ }
+}
+
+
+
+/*
+ * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn.
+ */
+
+static void
+oncore_shmem_get_3D(
+ struct instance *instance
+ )
+{
+ if (instance->pp->second%15 == 3) { /* start the sequence */ /* by changing mode */
+ instance->shmem_reset = 1;
+ if (instance->chan == 12) {
+ if (instance->shmem_Posn == 2)
+ oncore_sendmsg(instance, oncore_cmd_Gd2, sizeof(oncore_cmd_Gd2)); /* 2D */
+ else
+ oncore_sendmsg(instance, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* 3D */
+ } else {
+ if (instance->saw_At) { /* out of 0D -> 3D mode */
+ oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0));
+ if (instance->shmem_Posn == 2) /* 3D -> 2D mode */
+ oncore_sendmsg(instance, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
+ } else
+ oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0));
+ }
+ } else if (instance->shmem_reset || (instance->mode != MODE_0D)) {
+ instance->shmem_reset = 0;
+ if (instance->chan == 12)
+ oncore_sendmsg(instance, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1)); /* 0D */
+ else {
+ if (instance->saw_At) {
+ if (instance->mode == MODE_2D) /* 2D -> 3D or 0D mode */
+ oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0));
+ oncore_sendmsg(instance, oncore_cmd_At1, sizeof(oncore_cmd_At1)); /* to 0D mode */
+ } else
+ oncore_sendmsg(instance, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
+ }
+ }
+}
+
+
+
+/*
+ * Here we do the Software SiteSurvey.
+ * We have to average our own position for the Position Hold Mode
+ * We use Heights from the GPS ellipsoid.
+ * We check for the END of either HW or SW SiteSurvey.
+ */
+
+static void
+oncore_ss(
+ struct instance *instance
+ )
+{
+ double lat, lon, ht;
+
+
+ if (instance->site_survey == ONCORE_SS_HW) {
+ /*
+ * Check to see if Hardware SiteSurvey has Finished.
+ */
+
+ if ((instance->chan == 8 && !(instance->BEHa[37] & 0x20)) ||
+ (instance->chan == 12 && !(instance->BEHa[130] & 0x10))) {
+ oncore_log(instance, LOG_INFO, "Now in 0D mode");
+
+ if (instance->chan == 12)
+ oncore_sendmsg(instance, oncore_cmd_Gax, sizeof(oncore_cmd_Gax));
+ else
+ oncore_sendmsg(instance, oncore_cmd_Asx, sizeof(oncore_cmd_Asx));
+
+ oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_DONE");
+ instance->site_survey = ONCORE_SS_DONE;
+ }
+ } else {
+ /*
+ * Must be a Software Site Survey.
+ */
+
+ if (instance->rsm.bad_fix) /* Not if poor geometry or less than 3 sats */
+ return;
+
+ if (instance->mode != MODE_3D) /* Use only 3D Fixes */
+ return;
+
+ instance->ss_lat += buf_w32(&instance->BEHa[15]);
+ instance->ss_long += buf_w32(&instance->BEHa[19]);
+ instance->ss_ht += buf_w32(&instance->BEHa[23]); /* GPS ellipsoid */
+ instance->ss_count++;
+
+ if (instance->ss_count != POS_HOLD_AVERAGE)
+ return;
+
+ instance->ss_lat /= POS_HOLD_AVERAGE;
+ instance->ss_long /= POS_HOLD_AVERAGE;
+ instance->ss_ht /= POS_HOLD_AVERAGE;
+
+ oncore_log_f(instance, LOG_NOTICE,
+ "Surveyed posn: lat %.3f (mas) long %.3f (mas) ht %.3f (cm)",
+ instance->ss_lat, instance->ss_long,
+ instance->ss_ht);
+ lat = instance->ss_lat/3600000.;
+ lon = instance->ss_long/3600000.;
+ ht = instance->ss_ht/100;
+ oncore_log_f(instance, LOG_NOTICE,
+ "Surveyed posn: lat %.7f (deg) long %.7f (deg) ht %.2f (m)",
+ lat, lon, ht);
+
+ oncore_set_posn(instance);
+
+ oncore_log(instance, LOG_INFO, "Now in 0D mode");
+
+ oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_DONE");
+ instance->site_survey = ONCORE_SS_DONE;
+ }
+}
+
+
+
+static int
+oncore_wait_almanac(
+ struct instance *instance
+ )
+{
+ if (instance->rsm.bad_almanac) {
+ instance->counta++;
+ if (instance->counta%5 == 0)
+ oncore_log(instance, LOG_INFO, "Waiting for Almanac");
+
+ /*
+ * If we get here (first time) then we don't have an almanac in memory.
+ * Check if we have a SHMEM, and if so try to load whatever is there.
+ */
+
+ if (!instance->almanac_from_shmem) {
+ instance->almanac_from_shmem = 1;
+ oncore_load_almanac(instance);
+ }
+ return(1);
+ } else { /* Here we have the Almanac, we will be starting the @@Bn/@@En/@@Hn
+ commands, and can finally check for TRAIM. Again, we set a delay
+ (5sec) and wait for things to settle down */
+
+ if (instance->chan == 6)
+ oncore_sendmsg(instance, oncore_cmd_Bn, sizeof(oncore_cmd_Bn));
+ else if (instance->chan == 8)
+ oncore_sendmsg(instance, oncore_cmd_En, sizeof(oncore_cmd_En));
+ else if (instance->chan == 12) {
+ oncore_sendmsg(instance, oncore_cmd_Gc, sizeof(oncore_cmd_Gc)); /* 1PPS on, continuous */
+ oncore_sendmsg(instance, oncore_cmd_Ge, sizeof(oncore_cmd_Ge)); /* TRAIM on */
+ oncore_sendmsg(instance, oncore_cmd_Hn, sizeof(oncore_cmd_Hn)); /* TRAIM status 1/s */
+ }
+ instance->traim_delay = 1;
+
+ oncore_log(instance, LOG_NOTICE, "Have now loaded an ALMANAC");
+
+ instance->o_state = ONCORE_RUN;
+ oncore_log(instance, LOG_NOTICE, "state = ONCORE_RUN");
+ }
+ return(0);
+}
+
+
+
+static void
+oncore_log (
+ struct instance *instance,
+ int log_level,
+ const char *msg
+ )
+{
+ msyslog(log_level, "ONCORE[%d]: %s", instance->unit, msg);
+ mprintf_clock_stats(&instance->peer->srcadr, "ONCORE[%d]: %s",
+ instance->unit, msg);
+}
+
+
+static int
+oncore_log_f(
+ struct instance * instance,
+ int log_level,
+ const char * fmt,
+ ...
+ )
+{
+ va_list ap;
+ int rc;
+ char msg[512];
+
+ va_start(ap, fmt);
+ rc = mvsnprintf(msg, sizeof(msg), fmt, ap);
+ va_end(ap);
+ oncore_log(instance, log_level, msg);
+
+#ifdef ONCORE_VERBOSE_ONCORE_LOG
+ instance->max_len = max(strlen(msg), instance->max_len);
+ instance->max_count++;
+ if (instance->max_count % 100 == 0)
+ oncore_log_f(instance, LOG_INFO,
+ "Max Message Length so far is %d",
+ instance->max_len);
+#endif
+ return rc;
+}
+
+#else
+int refclock_oncore_bs;
+#endif /* REFCLOCK && CLOCK_ONCORE */
diff --git a/ntpd/refclock_palisade.c b/ntpd/refclock_palisade.c
new file mode 100644
index 0000000..2b396d8
--- /dev/null
+++ b/ntpd/refclock_palisade.c
@@ -0,0 +1,1253 @@
+/*
+ * This software was developed by the Software and Component Technologies
+ * group of Trimble Navigation, Ltd.
+ *
+ * Copyright (c) 1997, 1998, 1999, 2000 Trimble Navigation Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Trimble Navigation, Ltd.
+ * 4. The name of Trimble Navigation Ltd. may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TRIMBLE NAVIGATION LTD. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL TRIMBLE NAVIGATION LTD. BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * refclock_palisade - clock driver for the Trimble Palisade GPS
+ * timing receiver
+ *
+ * For detailed information on this program, please refer to the html
+ * Refclock 29 page accompanying the NTP distribution.
+ *
+ * for questions / bugs / comments, contact:
+ * sven_dietrich@trimble.com
+ *
+ * Sven-Thorsten Dietrich
+ * 645 North Mary Avenue
+ * Post Office Box 3642
+ * Sunnyvale, CA 94088-3642
+ *
+ * Version 2.45; July 14, 1999
+ *
+ *
+ *
+ * 31/03/06: Added support for Thunderbolt GPS Disciplined Clock.
+ * Contact: Fernando Pablo Hauscarriaga
+ * E-mail: fernandoph@iar.unlp.edu.ar
+ * Home page: www.iar.unlp.edu.ar/~fernandoph
+ * Instituto Argentino de Radioastronomia
+ * www.iar.unlp.edu.ar
+ *
+ * 14/01/07: Conditinal compilation for Thunderbolt support no longer needed
+ * now we use mode 2 for decode thunderbolt packets.
+ * Fernando P. Hauscarriaga
+ *
+ * 30/08/09: Added support for Trimble Acutime Gold Receiver.
+ * Fernando P. Hauscarriaga (fernandoph@iar.unlp.edu.ar)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_PALISADE)
+
+#ifdef SYS_WINNT
+extern int async_write(int, const void *, unsigned int);
+#undef write
+#define write(fd, data, octets) async_write(fd, data, octets)
+#endif
+
+#include "refclock_palisade.h"
+/* Table to get from month to day of the year */
+const int days_of_year [12] = {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+};
+
+#ifdef DEBUG
+const char * Tracking_Status[15][15] = {
+ { "Doing Fixes\0" }, { "Good 1SV\0" }, { "Approx. 1SV\0" },
+ {"Need Time\0" }, { "Need INIT\0" }, { "PDOP too High\0" },
+ { "Bad 1SV\0" }, { "0SV Usable\0" }, { "1SV Usable\0" },
+ { "2SV Usable\0" }, { "3SV Usable\0" }, { "No Integrity\0" },
+ { "Diff Corr\0" }, { "Overdet Clock\0" }, { "Invalid\0" } };
+#endif
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_palisade = {
+ palisade_start, /* start up driver */
+ palisade_shutdown, /* shut down driver */
+ palisade_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+int day_of_year (char *dt);
+
+/* Extract the clock type from the mode setting */
+#define CLK_TYPE(x) ((int)(((x)->ttl) & 0x7F))
+
+/* Supported clock types */
+#define CLK_TRIMBLE 0 /* Trimble Palisade */
+#define CLK_PRAECIS 1 /* Endrun Technologies Praecis */
+#define CLK_THUNDERBOLT 2 /* Trimble Thunderbolt GPS Receiver */
+#define CLK_ACUTIME 3 /* Trimble Acutime Gold */
+#define CLK_ACUTIMEB 4 /* Trimble Actutime Gold Port B */
+
+int praecis_msg;
+static void praecis_parse(struct recvbuf *rbufp, struct peer *peer);
+
+/* These routines are for sending packets to the Thunderbolt receiver
+ * They are taken from Markus Prosch
+ */
+
+#ifdef PALISADE_SENDCMD_RESURRECTED
+/*
+ * sendcmd - Build data packet for sending
+ */
+static void
+sendcmd (
+ struct packettx *buffer,
+ int c
+ )
+{
+ *buffer->data = DLE;
+ *(buffer->data + 1) = (unsigned char)c;
+ buffer->size = 2;
+}
+#endif /* PALISADE_SENDCMD_RESURRECTED */
+
+/*
+ * sendsupercmd - Build super data packet for sending
+ */
+static void
+sendsupercmd (
+ struct packettx *buffer,
+ int c1,
+ int c2
+ )
+{
+ *buffer->data = DLE;
+ *(buffer->data + 1) = (unsigned char)c1;
+ *(buffer->data + 2) = (unsigned char)c2;
+ buffer->size = 3;
+}
+
+/*
+ * sendbyte -
+ */
+static void
+sendbyte (
+ struct packettx *buffer,
+ int b
+ )
+{
+ if (b == DLE)
+ *(buffer->data+buffer->size++) = DLE;
+ *(buffer->data+buffer->size++) = (unsigned char)b;
+}
+
+/*
+ * sendint -
+ */
+static void
+sendint (
+ struct packettx *buffer,
+ int a
+ )
+{
+ sendbyte(buffer, (unsigned char)((a>>8) & 0xff));
+ sendbyte(buffer, (unsigned char)(a & 0xff));
+}
+
+/*
+ * sendetx - Send packet or super packet to the device
+ */
+static int
+sendetx (
+ struct packettx *buffer,
+ int fd
+ )
+{
+ int result;
+
+ *(buffer->data+buffer->size++) = DLE;
+ *(buffer->data+buffer->size++) = ETX;
+ result = write(fd, buffer->data, (unsigned long)buffer->size);
+
+ if (result != -1)
+ return (result);
+ else
+ return (-1);
+}
+
+/*
+ * init_thunderbolt - Prepares Thunderbolt receiver to be used with
+ * NTP (also taken from Markus Prosch).
+ */
+static void
+init_thunderbolt (
+ int fd
+ )
+{
+ struct packettx tx;
+
+ tx.size = 0;
+ tx.data = (u_char *) malloc(100);
+
+ /* set UTC time */
+ sendsupercmd (&tx, 0x8E, 0xA2);
+ sendbyte (&tx, 0x3);
+ sendetx (&tx, fd);
+
+ /* activate packets 0x8F-AB and 0x8F-AC */
+ sendsupercmd (&tx, 0x8F, 0xA5);
+ sendint (&tx, 0x5);
+ sendetx (&tx, fd);
+
+ free(tx.data);
+}
+
+/*
+ * init_acutime - Prepares Acutime Receiver to be used with NTP
+ */
+static void
+init_acutime (
+ int fd
+ )
+{
+ /* Disable all outputs, Enable Event-Polling on PortA so
+ we can ask for time packets */
+ struct packettx tx;
+
+ tx.size = 0;
+ tx.data = (u_char *) malloc(100);
+
+ sendsupercmd(&tx, 0x8E, 0xA5);
+ sendbyte(&tx, 0x02);
+ sendbyte(&tx, 0x00);
+ sendbyte(&tx, 0x00);
+ sendbyte(&tx, 0x00);
+ sendetx(&tx, fd);
+
+ free(tx.data);
+}
+
+/*
+ * palisade_start - open the devices and initialize data for processing
+ */
+static int
+palisade_start (
+ int unit,
+ struct peer *peer
+ )
+{
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char gpsdev[20];
+ struct termios tio;
+
+ snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit);
+
+ /*
+ * Open serial port.
+ */
+ fd = refclock_open(gpsdev, SPEED232, LDISC_RAW);
+ if (fd <= 0) {
+#ifdef DEBUG
+ printf("Palisade(%d) start: open %s failed\n", unit, gpsdev);
+#endif
+ return 0;
+ }
+
+ msyslog(LOG_NOTICE, "Palisade(%d) fd: %d dev: %s", unit, fd,
+ gpsdev);
+
+ if (tcgetattr(fd, &tio) < 0) {
+ msyslog(LOG_ERR,
+ "Palisade(%d) tcgetattr(fd, &tio): %m",unit);
+#ifdef DEBUG
+ printf("Palisade(%d) tcgetattr(fd, &tio)\n",unit);
+#endif
+ close(fd);
+ return (0);
+ }
+
+ tio.c_cflag |= (PARENB|PARODD);
+ tio.c_iflag &= ~ICRNL;
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+
+ up->type = CLK_TYPE(peer);
+ switch (up->type) {
+ case CLK_TRIMBLE:
+ /* Normal mode, do nothing */
+ break;
+ case CLK_PRAECIS:
+ msyslog(LOG_NOTICE, "Palisade(%d) Praecis mode enabled"
+ ,unit);
+ break;
+ case CLK_THUNDERBOLT:
+ msyslog(LOG_NOTICE, "Palisade(%d) Thunderbolt mode enabled"
+ ,unit);
+ tio.c_cflag = (CS8|CLOCAL|CREAD);
+ break;
+ case CLK_ACUTIME:
+ msyslog(LOG_NOTICE, "Palisade(%d) Acutime Gold mode enabled"
+ ,unit);
+ break;
+ default:
+ msyslog(LOG_NOTICE, "Palisade(%d) mode unknown",unit);
+ break;
+ }
+ if (tcsetattr(fd, TCSANOW, &tio) == -1) {
+ msyslog(LOG_ERR, "Palisade(%d) tcsetattr(fd, &tio): %m",unit);
+#ifdef DEBUG
+ printf("Palisade(%d) tcsetattr(fd, &tio)\n",unit);
+#endif
+ close(fd);
+ free(up);
+ return 0;
+ }
+
+ pp = peer->procptr;
+ pp->io.clock_recv = palisade_io;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+#ifdef DEBUG
+ printf("Palisade(%d) io_addclock\n",unit);
+#endif
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ pp->unitptr = up;
+ pp->clockdesc = DESCRIPTION;
+
+ peer->precision = PRECISION;
+ peer->sstclktype = CTL_SST_TS_UHF;
+ peer->minpoll = TRMB_MINPOLL;
+ peer->maxpoll = TRMB_MAXPOLL;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+ up->leap_status = 0;
+ up->unit = (short) unit;
+ up->rpt_status = TSIP_PARSED_EMPTY;
+ up->rpt_cnt = 0;
+
+ if (up->type == CLK_THUNDERBOLT)
+ init_thunderbolt(fd);
+ if (up->type == CLK_ACUTIME)
+ init_acutime(fd);
+
+ return 1;
+}
+
+
+/*
+ * palisade_shutdown - shut down the clock
+ */
+static void
+palisade_shutdown (
+ int unit,
+ struct peer *peer
+ )
+{
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ if (NULL != up)
+ free(up);
+}
+
+
+
+/*
+ * unpack_date - get day and year from date
+ */
+int
+day_of_year (
+ char * dt
+ )
+{
+ int day, mon, year;
+
+ mon = dt[1];
+ /* Check month is inside array bounds */
+ if ((mon < 1) || (mon > 12))
+ return -1;
+
+ day = dt[0] + days_of_year[mon - 1];
+ year = getint((u_char *) (dt + 2));
+
+ if ( !(year % 4) && ((year % 100) ||
+ (!(year % 100) && !(year%400)))
+ &&(mon > 2))
+ day ++; /* leap year and March or later */
+
+ return day;
+}
+
+
+/*
+ * TSIP_decode - decode the TSIP data packets
+ */
+int
+TSIP_decode (
+ struct peer *peer
+ )
+{
+ int st;
+ long secint;
+ double secs;
+ double secfrac;
+ unsigned short event = 0;
+
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Check the time packet, decode its contents.
+ * If the timecode has invalid length or is not in
+ * proper format, declare bad format and exit.
+ */
+
+ if ((up->type != CLK_THUNDERBOLT) & (up->type != CLK_ACUTIME)){
+ if ((up->rpt_buf[0] == (char) 0x41) ||
+ (up->rpt_buf[0] == (char) 0x46) ||
+ (up->rpt_buf[0] == (char) 0x54) ||
+ (up->rpt_buf[0] == (char) 0x4B) ||
+ (up->rpt_buf[0] == (char) 0x6D)) {
+
+ /* standard time packet - GPS time and GPS week number */
+#ifdef DEBUG
+ printf("Palisade Port B packets detected. Connect to Port A\n");
+#endif
+
+ return 0;
+ }
+ }
+
+ /*
+ * We cast both to u_char to as 0x8f uses the sign bit on a char
+ */
+ if ((u_char) up->rpt_buf[0] == (u_char) 0x8f) {
+ /*
+ * Superpackets
+ */
+ event = (unsigned short) (getint((u_char *) &mb(1)) & 0xffff);
+ if (!((pp->sloppyclockflag & CLK_FLAG2) || event))
+ /* Ignore Packet */
+ return 0;
+
+ switch (mb(0) & 0xff) {
+ int GPS_UTC_Offset;
+ long tow;
+
+ case PACKET_8F0B:
+
+ if (up->polled <= 0)
+ return 0;
+
+ if (up->rpt_cnt != LENCODE_8F0B) /* check length */
+ break;
+
+#ifdef DEBUG
+ if (debug > 1) {
+ int ts;
+ double lat, lon, alt;
+ lat = getdbl((u_char *) &mb(42)) * R2D;
+ lon = getdbl((u_char *) &mb(50)) * R2D;
+ alt = getdbl((u_char *) &mb(58));
+
+ printf("TSIP_decode: unit %d: Latitude: %03.4f Longitude: %03.4f Alt: %05.2f m\n",
+ up->unit, lat,lon,alt);
+ printf("TSIP_decode: unit %d: Sats:",
+ up->unit);
+ for (st = 66, ts = 0; st <= 73; st++)
+ if (mb(st)) {
+ if (mb(st) > 0) ts++;
+ printf(" %02d", mb(st));
+ }
+ printf(" : Tracking %d\n", ts);
+ }
+#endif
+
+ GPS_UTC_Offset = getint((u_char *) &mb(16));
+ if (GPS_UTC_Offset == 0) { /* Check UTC offset */
+#ifdef DEBUG
+ printf("TSIP_decode: UTC Offset Unknown\n");
+#endif
+ break;
+ }
+
+ secs = getdbl((u_char *) &mb(3));
+ secint = (long) secs;
+ secfrac = secs - secint; /* 0.0 <= secfrac < 1.0 */
+
+ pp->nsec = (long) (secfrac * 1000000000);
+
+ secint %= 86400; /* Only care about today */
+ pp->hour = secint / 3600;
+ secint %= 3600;
+ pp->minute = secint / 60;
+ secint %= 60;
+ pp->second = secint % 60;
+
+ if ((pp->day = day_of_year(&mb(11))) < 0) break;
+
+ pp->year = getint((u_char *) &mb(13));
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%09ld %02d/%02d/%04d UTC %02d\n",
+ up->unit, mb(0) & 0xff, event, pp->hour, pp->minute,
+ pp->second, pp->nsec, mb(12), mb(11), pp->year, GPS_UTC_Offset);
+#endif
+ /* Only use this packet when no
+ * 8F-AD's are being received
+ */
+
+ if (up->leap_status) {
+ up->leap_status = 0;
+ return 0;
+ }
+
+ return 2;
+ break;
+
+ case PACKET_NTP:
+ /* Palisade-NTP Packet */
+
+ if (up->rpt_cnt != LENCODE_NTP) /* check length */
+ break;
+
+ up->leap_status = mb(19);
+
+ if (up->polled <= 0)
+ return 0;
+
+ /* Check Tracking Status */
+ st = mb(18);
+ if (st < 0 || st > 14)
+ st = 14;
+ if ((st >= 2 && st <= 7) || st == 11 || st == 12) {
+#ifdef DEBUG
+ printf("TSIP_decode: Not Tracking Sats : %s\n",
+ *Tracking_Status[st]);
+#endif
+ refclock_report(peer, CEVNT_BADTIME);
+ up->polled = -1;
+ return 0;
+ break;
+ }
+
+ if (up->leap_status & PALISADE_LEAP_PENDING) {
+ if (up->leap_status & PALISADE_UTC_TIME)
+ pp->leap = LEAP_ADDSECOND;
+ else
+ pp->leap = LEAP_DELSECOND;
+ }
+ else if (up->leap_status)
+ pp->leap = LEAP_NOWARNING;
+
+ else { /* UTC flag is not set:
+ * Receiver may have been reset, and lost
+ * its UTC almanac data */
+ pp->leap = LEAP_NOTINSYNC;
+#ifdef DEBUG
+ printf("TSIP_decode: UTC Almanac unavailable: %d\n",
+ mb(19));
+#endif
+ refclock_report(peer, CEVNT_BADTIME);
+ up->polled = -1;
+ return 0;
+ }
+
+ pp->nsec = (long) (getdbl((u_char *) &mb(3))
+ * 1000000000);
+
+ if ((pp->day = day_of_year(&mb(14))) < 0)
+ break;
+ pp->year = getint((u_char *) &mb(16));
+ pp->hour = mb(11);
+ pp->minute = mb(12);
+ pp->second = mb(13);
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%09ld %02d/%02d/%04d UTC %02x %s\n",
+ up->unit, mb(0) & 0xff, event, pp->hour, pp->minute,
+ pp->second, pp->nsec, mb(15), mb(14), pp->year,
+ mb(19), *Tracking_Status[st]);
+#endif
+ return 1;
+ break;
+
+ case PACKET_8FAC:
+ if (up->polled <= 0)
+ return 0;
+
+ if (up->rpt_cnt != LENCODE_8FAC)/* check length */
+ break;
+
+#ifdef DEBUG
+ if (debug > 1) {
+ double lat, lon, alt;
+ lat = getdbl((u_char *) &mb(36)) * R2D;
+ lon = getdbl((u_char *) &mb(44)) * R2D;
+ alt = getdbl((u_char *) &mb(52));
+
+ printf("TSIP_decode: unit %d: Latitude: %03.4f Longitude: %03.4f Alt: %05.2f m\n",
+ up->unit, lat,lon,alt);
+ printf("TSIP_decode: unit %d\n", up->unit);
+ }
+#endif
+ if (getint((u_char *) &mb(10)) & 0x80)
+ pp->leap = LEAP_ADDSECOND; /* we ASSUME addsecond */
+ else
+ pp->leap = LEAP_NOWARNING;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("TSIP_decode: unit %d: 0x%02x leap %d\n",
+ up->unit, mb(0) & 0xff, pp->leap);
+ if (debug > 1) {
+ printf("Receiver MODE: 0x%02X\n", (u_char)mb(1));
+ if (mb(1) == 0x00)
+ printf(" AUTOMATIC\n");
+ if (mb(1) == 0x01)
+ printf(" SINGLE SATELLITE\n");
+ if (mb(1) == 0x03)
+ printf(" HORIZONTAL(2D)\n");
+ if (mb(1) == 0x04)
+ printf(" FULL POSITION(3D)\n");
+ if (mb(1) == 0x05)
+ printf(" DGPR REFERENCE\n");
+ if (mb(1) == 0x06)
+ printf(" CLOCK HOLD(2D)\n");
+ if (mb(1) == 0x07)
+ printf(" OVERDETERMINED CLOCK\n");
+
+ printf("\n** Disciplining MODE 0x%02X:\n", (u_char)mb(2));
+ if (mb(2) == 0x00)
+ printf(" NORMAL\n");
+ if (mb(2) == 0x01)
+ printf(" POWER-UP\n");
+ if (mb(2) == 0x02)
+ printf(" AUTO HOLDOVER\n");
+ if (mb(2) == 0x03)
+ printf(" MANUAL HOLDOVER\n");
+ if (mb(2) == 0x04)
+ printf(" RECOVERY\n");
+ if (mb(2) == 0x06)
+ printf(" DISCIPLINING DISABLED\n");
+ }
+#endif
+ return 0;
+ break;
+
+ case PACKET_8FAB:
+ /* Thunderbolt Primary Timing Packet */
+
+ if (up->rpt_cnt != LENCODE_8FAB) /* check length */
+ break;
+
+ if (up->polled <= 0)
+ return 0;
+
+ GPS_UTC_Offset = getint((u_char *) &mb(7));
+
+ if (GPS_UTC_Offset == 0){ /* Check UTC Offset */
+#ifdef DEBUG
+ printf("TSIP_decode: UTC Offset Unknown\n");
+#endif
+ break;
+ }
+
+
+ if ((mb(9) & 0x1d) == 0x0) {
+ /* if we know the GPS time and the UTC offset,
+ we expect UTC timing information !!! */
+
+ pp->leap = LEAP_NOTINSYNC;
+ refclock_report(peer, CEVNT_BADTIME);
+ up->polled = -1;
+ return 0;
+ }
+
+ pp->nsec = 0;
+#ifdef DEBUG
+ printf("\nTiming Flags are:\n");
+ printf("Timing flag value is: 0x%X\n", mb(9));
+ if ((mb(9) & 0x01) != 0)
+ printf (" Getting UTC time\n");
+ else
+ printf (" Getting GPS time\n");
+ if ((mb(9) & 0x02) != 0)
+ printf (" PPS is from UTC\n");
+ else
+ printf (" PPS is from GPS\n");
+ if ((mb(9) & 0x04) != 0)
+ printf (" Time is not Set\n");
+ else
+ printf (" Time is Set\n");
+ if ((mb(9) & 0x08) != 0)
+ printf(" I dont have UTC info\n");
+ else
+ printf (" I have UTC info\n");
+ if ((mb(9) & 0x10) != 0)
+ printf (" Time is from USER\n\n");
+ else
+ printf (" Time is from GPS\n\n");
+#endif
+
+ if ((pp->day = day_of_year(&mb(13))) < 0)
+ break;
+ tow = getlong((u_char *) &mb(1));
+#ifdef DEBUG
+ if (debug > 1) {
+ printf("pp->day: %d\n", pp->day);
+ printf("TOW: %ld\n", tow);
+ printf("DAY: %d\n", mb(13));
+ }
+#endif
+ pp->year = getint((u_char *) &mb(15));
+ pp->hour = mb(12);
+ pp->minute = mb(11);
+ pp->second = mb(10);
+
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%09ld %02d/%02d/%04d ",up->unit, mb(0) & 0xff, event, pp->hour, pp->minute, pp->second, pp->nsec, mb(14), mb(13), pp->year);
+#endif
+ return 1;
+ break;
+
+ default:
+ /* Ignore Packet */
+ return 0;
+ } /* switch */
+ } /* if 8F packets */
+
+ else if (up->rpt_buf[0] == (u_char)0x42) {
+ printf("0x42\n");
+ return 0;
+ }
+ else if (up->rpt_buf[0] == (u_char)0x43) {
+ printf("0x43\n");
+ return 0;
+ }
+ else if ((up->rpt_buf[0] == PACKET_41) & (up->type == CLK_THUNDERBOLT)){
+ printf("Undocumented 0x41 packet on Thunderbolt\n");
+ return 0;
+ }
+ else if ((up->rpt_buf[0] == PACKET_41A) & (up->type == CLK_ACUTIME)) {
+#ifdef DEBUG
+ printf("GPS TOW: %ld\n", (long)getlong((u_char *) &mb(0)));
+ printf("GPS WN: %d\n", getint((u_char *) &mb(4)));
+ printf("GPS UTC-GPS Offser: %ld\n", (long)getlong((u_char *) &mb(6)));
+#endif
+ return 0;
+ }
+
+ /* Health Status for Acutime Receiver */
+ else if ((up->rpt_buf[0] == PACKET_46) & (up->type == CLK_ACUTIME)) {
+#ifdef DEBUG
+ if (debug > 1)
+ /* Status Codes */
+ switch (mb(0)) {
+ case 0x00:
+ printf ("Doing Position Fixes\n");
+ break;
+ case 0x01:
+ printf ("Do no have GPS time yet\n");
+ break;
+ case 0x03:
+ printf ("PDOP is too high\n");
+ break;
+ case 0x08:
+ printf ("No usable satellites\n");
+ break;
+ case 0x09:
+ printf ("Only 1 usable satellite\n");
+ break;
+ case 0x0A:
+ printf ("Only 2 usable satellites\n");
+ break;
+ case 0x0B:
+ printf ("Only 3 usable satellites\n");
+ break;
+ case 0x0C:
+ printf("The Chosen satellite is unusable\n");
+ break;
+ }
+#endif
+ /* Error Codes */
+ if (mb(1) != 0) {
+
+ refclock_report(peer, CEVNT_BADTIME);
+ up->polled = -1;
+#ifdef DEBUG
+ if (debug > 1) {
+ if (mb(1) & 0x01)
+ printf ("Signal Processor Error, reset unit.\n");
+ if (mb(1) & 0x02)
+ printf ("Alignment error, channel or chip 1, reset unit.\n");
+ if (mb(1) & 0x03)
+ printf ("Alignment error, channel or chip 2, reset unit.\n");
+ if (mb(1) & 0x04)
+ printf ("Antenna feed line fault (open or short)\n");
+ if (mb(1) & 0x05)
+ printf ("Excessive reference frequency error, refer to packet 0x2D and packet 0x4D documentation for further information\n");
+ }
+#endif
+
+ return 0;
+ }
+ }
+ else if (up->rpt_buf[0] == 0x54)
+ return 0;
+
+ else if (up->rpt_buf[0] == PACKET_6D) {
+#ifdef DEBUG
+ int sats;
+
+ if ((mb(0) & 0x01) && (mb(0) & 0x02))
+ printf("2d Fix Dimension\n");
+ if (mb(0) & 0x04)
+ printf("3d Fix Dimension\n");
+
+ if (mb(0) & 0x08)
+ printf("Fix Mode is MANUAL\n");
+ else
+ printf("Fix Mode is AUTO\n");
+
+ sats = mb(0) & 0xF0;
+ sats = sats >> 4;
+ printf("Tracking %d Satellites\n", sats);
+#endif
+ return 0;
+ } /* else if not super packet */
+ refclock_report(peer, CEVNT_BADREPLY);
+ up->polled = -1;
+#ifdef DEBUG
+ printf("TSIP_decode: unit %d: bad packet %02x-%02x event %d len %d\n",
+ up->unit, up->rpt_buf[0] & 0xff, mb(0) & 0xff,
+ event, up->rpt_cnt);
+#endif
+ return 0;
+}
+
+/*
+ * palisade__receive - receive data from the serial interface
+ */
+
+static void
+palisade_receive (
+ struct peer * peer
+ )
+{
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ if (! TSIP_decode(peer)) return;
+
+ if (up->polled <= 0)
+ return; /* no poll pending, already received or timeout */
+
+ up->polled = 0; /* Poll reply received */
+ pp->lencode = 0; /* clear time code */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "palisade_receive: unit %d: %4d %03d %02d:%02d:%02d.%09ld\n",
+ up->unit, pp->year, pp->day, pp->hour, pp->minute,
+ pp->second, pp->nsec);
+#endif
+
+ /*
+ * Process the sample
+ * Generate timecode: YYYY DoY HH:MM:SS.microsec
+ * report and process
+ */
+
+ snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
+ "%4d %03d %02d:%02d:%02d.%09ld",
+ pp->year, pp->day,
+ pp->hour,pp->minute, pp->second, pp->nsec);
+ pp->lencode = 24;
+
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+
+#ifdef DEBUG
+ printf("palisade_receive: unit %d: refclock_process failed!\n",
+ up->unit);
+#endif
+ return;
+ }
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+#ifdef DEBUG
+ if (debug)
+ printf("palisade_receive: unit %d: %s\n",
+ up->unit, prettydate(&pp->lastrec));
+#endif
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+}
+
+
+/*
+ * palisade_poll - called by the transmit procedure
+ *
+ */
+static void
+palisade_poll (
+ int unit,
+ struct peer *peer
+ )
+{
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ pp->polls++;
+ if (up->polled > 0) /* last reply never arrived or error */
+ refclock_report(peer, CEVNT_TIMEOUT);
+
+ up->polled = 2; /* synchronous packet + 1 event */
+
+#ifdef DEBUG
+ if (debug)
+ printf("palisade_poll: unit %d: polling %s\n", unit,
+ (pp->sloppyclockflag & CLK_FLAG2) ?
+ "synchronous packet" : "event");
+#endif
+
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ return; /* using synchronous packet input */
+
+ if(up->type == CLK_PRAECIS) {
+ if(write(peer->procptr->io.fd,"SPSTAT\r\n",8) < 0)
+ msyslog(LOG_ERR, "Palisade(%d) write: %m:",unit);
+ else {
+ praecis_msg = 1;
+ return;
+ }
+ }
+
+ if (HW_poll(pp) < 0)
+ refclock_report(peer, CEVNT_FAULT);
+}
+
+static void
+praecis_parse (
+ struct recvbuf *rbufp,
+ struct peer *peer
+ )
+{
+ static char buf[100];
+ static int p = 0;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ memcpy(buf+p,rbufp->recv_space.X_recv_buffer, rbufp->recv_length);
+ p += rbufp->recv_length;
+
+ if(buf[p-2] == '\r' && buf[p-1] == '\n') {
+ buf[p-2] = '\0';
+ record_clock_stats(&peer->srcadr, buf);
+
+ p = 0;
+ praecis_msg = 0;
+
+ if (HW_poll(pp) < 0)
+ refclock_report(peer, CEVNT_FAULT);
+
+ }
+}
+
+static void
+palisade_io (
+ struct recvbuf *rbufp
+ )
+{
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ char * c, * d;
+
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ if(up->type == CLK_PRAECIS) {
+ if(praecis_msg) {
+ praecis_parse(rbufp,peer);
+ return;
+ }
+ }
+
+ c = (char *) &rbufp->recv_space;
+ d = c + rbufp->recv_length;
+
+ while (c != d) {
+
+ /* Build time packet */
+ switch (up->rpt_status) {
+
+ case TSIP_PARSED_DLE_1:
+ switch (*c)
+ {
+ case 0:
+ case DLE:
+ case ETX:
+ up->rpt_status = TSIP_PARSED_EMPTY;
+ break;
+
+ default:
+ up->rpt_status = TSIP_PARSED_DATA;
+ /* save packet ID */
+ up->rpt_buf[0] = *c;
+ break;
+ }
+ break;
+
+ case TSIP_PARSED_DATA:
+ if (*c == DLE)
+ up->rpt_status = TSIP_PARSED_DLE_2;
+ else
+ mb(up->rpt_cnt++) = *c;
+ break;
+
+ case TSIP_PARSED_DLE_2:
+ if (*c == DLE) {
+ up->rpt_status = TSIP_PARSED_DATA;
+ mb(up->rpt_cnt++) =
+ *c;
+ }
+ else if (*c == ETX)
+ up->rpt_status = TSIP_PARSED_FULL;
+ else {
+ /* error: start new report packet */
+ up->rpt_status = TSIP_PARSED_DLE_1;
+ up->rpt_buf[0] = *c;
+ }
+ break;
+
+ case TSIP_PARSED_FULL:
+ case TSIP_PARSED_EMPTY:
+ default:
+ if ( *c != DLE)
+ up->rpt_status = TSIP_PARSED_EMPTY;
+ else
+ up->rpt_status = TSIP_PARSED_DLE_1;
+ break;
+ }
+
+ c++;
+
+ if (up->rpt_status == TSIP_PARSED_DLE_1) {
+ up->rpt_cnt = 0;
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ /* stamp it */
+ get_systime(&pp->lastrec);
+ }
+ else if (up->rpt_status == TSIP_PARSED_EMPTY)
+ up->rpt_cnt = 0;
+
+ else if (up->rpt_cnt > BMAX)
+ up->rpt_status =TSIP_PARSED_EMPTY;
+
+ if (up->rpt_status == TSIP_PARSED_FULL)
+ palisade_receive(peer);
+
+ } /* while chars in buffer */
+}
+
+
+/*
+ * Trigger the Palisade's event input, which is driven off the RTS
+ *
+ * Take a system time stamp to match the GPS time stamp.
+ *
+ */
+long
+HW_poll (
+ struct refclockproc * pp /* pointer to unit structure */
+ )
+{
+ int x; /* state before & after RTS set */
+ struct palisade_unit *up;
+
+ up = pp->unitptr;
+
+ /* read the current status, so we put things back right */
+ if (ioctl(pp->io.fd, TIOCMGET, &x) < 0) {
+ DPRINTF(1, ("Palisade HW_poll: unit %d: GET %m\n",
+ up->unit));
+ msyslog(LOG_ERR, "Palisade(%d) HW_poll: ioctl(fd,GET): %m",
+ up->unit);
+ return -1;
+ }
+
+ x |= TIOCM_RTS; /* turn on RTS */
+
+ /* Edge trigger */
+ if (up->type == CLK_ACUTIME)
+ write (pp->io.fd, "", 1);
+
+ if (ioctl(pp->io.fd, TIOCMSET, &x) < 0) {
+#ifdef DEBUG
+ if (debug)
+ printf("Palisade HW_poll: unit %d: SET \n", up->unit);
+#endif
+ msyslog(LOG_ERR,
+ "Palisade(%d) HW_poll: ioctl(fd, SET, RTS_on): %m",
+ up->unit);
+ return -1;
+ }
+
+ x &= ~TIOCM_RTS; /* turn off RTS */
+
+ /* poll timestamp */
+ get_systime(&pp->lastrec);
+
+ if (ioctl(pp->io.fd, TIOCMSET, &x) == -1) {
+#ifdef DEBUG
+ if (debug)
+ printf("Palisade HW_poll: unit %d: UNSET \n", up->unit);
+#endif
+ msyslog(LOG_ERR,
+ "Palisade(%d) HW_poll: ioctl(fd, UNSET, RTS_off): %m",
+ up->unit);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * copy/swap a big-endian palisade double into a host double
+ */
+static double
+getdbl (
+ u_char *bp
+ )
+{
+#ifdef WORDS_BIGENDIAN
+ double out;
+
+ memcpy(&out, bp, sizeof(out));
+ return out;
+#else
+ union {
+ u_char ch[8];
+ u_int32 u32[2];
+ } ui;
+
+ union {
+ double out;
+ u_int32 u32[2];
+ } uo;
+
+ memcpy(ui.ch, bp, sizeof(ui.ch));
+ /* least-significant 32 bits of double from swapped bp[4] to bp[7] */
+ uo.u32[0] = ntohl(ui.u32[1]);
+ /* most-significant 32 bits from swapped bp[0] to bp[3] */
+ uo.u32[1] = ntohl(ui.u32[0]);
+
+ return uo.out;
+#endif
+}
+
+/*
+ * copy/swap a big-endian palisade short into a host short
+ */
+static short
+getint (
+ u_char *bp
+ )
+{
+ u_short us;
+
+ memcpy(&us, bp, sizeof(us));
+ return (short)ntohs(us);
+}
+
+/*
+ * copy/swap a big-endian palisade 32-bit int into a host 32-bit int
+ */
+static int32
+getlong(
+ u_char *bp
+ )
+{
+ u_int32 u32;
+
+ memcpy(&u32, bp, sizeof(u32));
+ return (int32)(u_int32)ntohl(u32);
+}
+
+#else /* REFCLOCK && CLOCK_PALISADE*/
+int refclock_palisade_c_notempty;
+#endif
diff --git a/ntpd/refclock_palisade.h b/ntpd/refclock_palisade.h
new file mode 100644
index 0000000..4f1ab26
--- /dev/null
+++ b/ntpd/refclock_palisade.h
@@ -0,0 +1,201 @@
+/*
+ * This software was developed by the Software and Component Technologies
+ * group of Trimble Navigation, Ltd.
+ *
+ * Copyright (c) 1997, 1998, 1999, 2000 Trimble Navigation Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Trimble Navigation, Ltd.
+ * 4. The name of Trimble Navigation Ltd. may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TRIMBLE NAVIGATION LTD. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL TRIMBLE NAVIGATION LTD. BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * refclock_palisade - clock driver for the Trimble Palisade GPS
+ * timing receiver
+ *
+ * For detailed information on this program, please refer to the html
+ * Refclock 29 page accompanying the NTP distribution.
+ *
+ * for questions / bugs / comments, contact:
+ * sven_dietrich@trimble.com
+ *
+ * Sven-Thorsten Dietrich
+ * 645 North Mary Avenue
+ * Post Office Box 3642
+ * Sunnyvale, CA 94088-3642
+ *
+ */
+
+#ifndef REFCLOCK_PALISADE_H
+#define REFCLOCK_PALISADE_H
+
+#if defined HAVE_SYS_MODEM_H
+#include <sys/modem.h>
+#ifndef __QNXNTO__
+#define TIOCMSET MCSETA
+#define TIOCMGET MCGETA
+#define TIOCM_RTS MRTS
+#endif
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# ifdef TERMIOS_NEEDS__SVID3
+# define _SVID3
+# endif
+# include <termios.h>
+# include <sys/stat.h>
+# ifdef TERMIOS_NEEDS__SVID3
+# undef _SVID3
+# endif
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+/*
+ * GPS Definitions
+ */
+#define DESCRIPTION "Trimble Palisade GPS" /* Long name */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference ID */
+#define TRMB_MINPOLL 4 /* 16 seconds */
+#define TRMB_MAXPOLL 5 /* 32 seconds */
+
+/*
+ * I/O Definitions
+ */
+#define DEVICE "/dev/palisade%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+
+/*
+ * TSIP Report Definitions
+ */
+#define LENCODE_8F0B 74 /* Length of TSIP 8F-0B Packet & header */
+#define LENCODE_NTP 22 /* Length of Palisade NTP Packet */
+
+#define LENCODE_8FAC 68 /* Length of Thunderbolt 8F-AC Position Packet*/
+#define LENCODE_8FAB 17 /* Length of Thunderbolt Primary Timing Packet*/
+
+/* Allowed Sub-Packet ID's */
+#define PACKET_8F0B 0x0B
+#define PACKET_NTP 0xAD
+
+/* Thunderbolt Packets */
+#define PACKET_8FAC 0xAC /* Supplementary Thunderbolt Time Packet */
+#define PACKET_8FAB 0xAB /* Primary Thunderbolt Time Packet */
+#define PACKET_6D 0x6D /* Supplementary Thunderbolt Tracking Stats */
+#define PACKET_41 0x41 /* Thunderbolt I dont know what this packet is, it's not documented on my manual*/
+
+/* Acutime Packets */
+#define PACKET_41A 0x41 /* GPS time */
+#define PACKET_46 0x46 /* Receiver Health */
+
+#define DLE 0x10
+#define ETX 0x03
+
+/* parse states */
+#define TSIP_PARSED_EMPTY 0
+#define TSIP_PARSED_FULL 1
+#define TSIP_PARSED_DLE_1 2
+#define TSIP_PARSED_DATA 3
+#define TSIP_PARSED_DLE_2 4
+
+/*
+ * Leap-Insert and Leap-Delete are encoded as follows:
+ * PALISADE_UTC_TIME set and PALISADE_LEAP_PENDING set: INSERT leap
+ */
+
+#define PALISADE_LEAP_INPROGRESS 0x08 /* This is the leap flag */
+#define PALISADE_LEAP_WARNING 0x04 /* GPS Leap Warning (see ICD-200) */
+#define PALISADE_LEAP_PENDING 0x02 /* Leap Pending (24 hours) */
+#define PALISADE_UTC_TIME 0x01 /* UTC time available */
+
+#define mb(_X_) (up->rpt_buf[(_X_ + 1)]) /* shortcut for buffer access */
+
+/* Conversion Definitions */
+#define GPS_PI (3.1415926535898)
+#define R2D (180.0/GPS_PI)
+
+/*
+ * Structure for build data packets for send (thunderbolt uses it only)
+ * taken from Markus Prosch
+ */
+struct packettx
+{
+ short size;
+ u_char *data;
+};
+
+/*
+ * Palisade unit control structure.
+ */
+struct palisade_unit {
+ short unit; /* NTP refclock unit number */
+ int polled; /* flag to detect noreplies */
+ char leap_status; /* leap second flag */
+ char rpt_status; /* TSIP Parser State */
+ short rpt_cnt; /* TSIP packet length so far */
+ char rpt_buf[BMAX]; /* packet assembly buffer */
+ int type; /* Clock mode type */
+};
+
+/*
+ * Function prototypes
+ */
+
+static int palisade_start (int, struct peer *);
+static void palisade_shutdown (int, struct peer *);
+static void palisade_receive (struct peer *);
+static void palisade_poll (int, struct peer *);
+static void palisade_io (struct recvbuf *);
+int palisade_configure (int, struct peer *);
+int TSIP_decode (struct peer *);
+long HW_poll (struct refclockproc *);
+static double getdbl (u_char *);
+static short getint (u_char *);
+static int32 getlong (u_char *);
+
+
+#ifdef PALISADE_SENDCMD_RESURRECTED
+static void sendcmd (struct packettx *buffer, int c);
+#endif
+static void sendsupercmd (struct packettx *buffer, int c1, int c2);
+static void sendbyte (struct packettx *buffer, int b);
+static void sendint (struct packettx *buffer, int a);
+static int sendetx (struct packettx *buffer, int fd);
+static void init_thunderbolt (int fd);
+static void init_acutime (int fd);
+
+#endif /* REFCLOCK_PALISADE_H */
diff --git a/ntpd/refclock_parse.c b/ntpd/refclock_parse.c
new file mode 100644
index 0000000..052ec3a
--- /dev/null
+++ b/ntpd/refclock_parse.c
@@ -0,0 +1,6204 @@
+/*
+ * /src/NTP/REPOSITORY/ntp4-dev/ntpd/refclock_parse.c,v 4.81 2009/05/01 10:15:29 kardel RELEASE_20090105_A
+ *
+ * refclock_parse.c,v 4.81 2009/05/01 10:15:29 kardel RELEASE_20090105_A
+ *
+ * generic reference clock driver for several DCF/GPS/MSF/... receivers
+ *
+ * PPS notes:
+ * On systems that support PPSAPI (RFC2783) PPSAPI is the
+ * preferred interface.
+ *
+ * Optionally make use of a STREAMS module for input processing where
+ * available and configured. This STREAMS module reduces the time
+ * stamp latency for serial and PPS events.
+ * Currently the STREAMS module is only available for Suns running
+ * SunOS 4.x and SunOS5.x.
+ *
+ * Copyright (c) 1995-2009 by Frank Kardel <kardel <AT> ntp.org>
+ * Copyright (c) 1989-1994 by Frank Kardel, Friedrich-Alexander Universität Erlangen-Nürnberg, Germany
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "ntp_types.h"
+
+#if defined(REFCLOCK) && defined(CLOCK_PARSE)
+
+/*
+ * This driver currently provides the support for
+ * - Meinberg receiver DCF77 PZF 535 (TCXO version) (DCF)
+ * - Meinberg receiver DCF77 PZF 535 (OCXO version) (DCF)
+ * - Meinberg receiver DCF77 PZF 509 (DCF)
+ * - Meinberg receiver DCF77 AM receivers (e.g. C51) (DCF)
+ * - IGEL CLOCK (DCF)
+ * - ELV DCF7000 (DCF)
+ * - Schmid clock (DCF)
+ * - Conrad DCF77 receiver module (DCF)
+ * - FAU DCF77 NTP receiver (TimeBrick) (DCF)
+ * - WHARTON 400A Series clock (DCF)
+ *
+ * - Meinberg GPS166/GPS167 (GPS)
+ * - Trimble (TSIP and TAIP protocol) (GPS)
+ *
+ * - RCC8000 MSF Receiver (MSF)
+ * - VARITEXT clock (MSF)
+ */
+
+/*
+ * Meinberg receivers are usually connected via a
+ * 9600 baud serial line
+ *
+ * The Meinberg GPS receivers also have a special NTP time stamp
+ * format. The firmware release is Uni-Erlangen.
+ *
+ * Meinberg generic receiver setup:
+ * output time code every second
+ * Baud rate 9600 7E2S
+ *
+ * Meinberg GPS16x setup:
+ * output time code every second
+ * Baudrate 19200 8N1
+ *
+ * This software supports the standard data formats used
+ * in Meinberg receivers.
+ *
+ * Special software versions are only sensible for the
+ * GPS 16x family of receivers.
+ *
+ * Meinberg can be reached via: http://www.meinberg.de/
+ */
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "timevalops.h" /* includes <sys/time.h> */
+#include "ntp_control.h"
+#include "ntp_string.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#ifndef TM_IN_SYS_TIME
+# include <time.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if !defined(STREAM) && !defined(HAVE_SYSV_TTYS) && !defined(HAVE_BSD_TTYS) && !defined(HAVE_TERMIOS)
+# include "Bletch: Define one of {STREAM,HAVE_SYSV_TTYS,HAVE_TERMIOS}"
+#endif
+
+#ifdef STREAM
+# include <sys/stream.h>
+# include <sys/stropts.h>
+#endif
+
+#ifdef HAVE_TERMIOS
+# include <termios.h>
+# define TTY_GETATTR(_FD_, _ARG_) tcgetattr((_FD_), (_ARG_))
+# define TTY_SETATTR(_FD_, _ARG_) tcsetattr((_FD_), TCSANOW, (_ARG_))
+# undef HAVE_SYSV_TTYS
+#endif
+
+#ifdef HAVE_SYSV_TTYS
+# define TTY_GETATTR(_FD_, _ARG_) ioctl((_FD_), TCGETA, (_ARG_))
+# define TTY_SETATTR(_FD_, _ARG_) ioctl((_FD_), TCSETAW, (_ARG_))
+#endif
+
+#ifdef HAVE_BSD_TTYS
+/* #error CURRENTLY NO BSD TTY SUPPORT */
+# include "Bletch: BSD TTY not currently supported"
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_PPSAPI
+# include "ppsapi_timepps.h"
+# include "refclock_atom.h"
+#endif
+
+#ifdef PPS
+# ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+# endif
+# ifdef HAVE_TIO_SERIAL_STUFF
+# include <linux/serial.h>
+# endif
+#endif
+
+#define BUFFER_SIZE(_BUF, _PTR) ((_BUF) + sizeof(_BUF) - (_PTR))
+#define BUFFER_SIZES(_BUF, _PTR, _SZ) ((_BUF) + (_SZ) - (_PTR))
+
+/*
+ * document type of PPS interfacing - copy of ifdef mechanism in local_input()
+ */
+#undef PPS_METHOD
+
+#ifdef HAVE_PPSAPI
+#define PPS_METHOD "PPS API"
+#else
+#ifdef TIOCDCDTIMESTAMP
+#define PPS_METHOD "TIOCDCDTIMESTAMP"
+#else /* TIOCDCDTIMESTAMP */
+#if defined(HAVE_STRUCT_PPSCLOCKEV) && (defined(HAVE_CIOGETEV) || defined(HAVE_TIOCGPPSEV))
+#ifdef HAVE_CIOGETEV
+#define PPS_METHOD "CIOGETEV"
+#endif
+#ifdef HAVE_TIOCGPPSEV
+#define PPS_METHOD "TIOCGPPSEV"
+#endif
+#endif
+#endif /* TIOCDCDTIMESTAMP */
+#endif /* HAVE_PPSAPI */
+
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+
+#include "parse.h"
+#include "mbg_gps166.h"
+#include "trimble.h"
+#include "binio.h"
+#include "ascii.h"
+#include "ieee754io.h"
+#include "recvbuff.h"
+
+static char rcsid[] = "refclock_parse.c,v 4.81 2009/05/01 10:15:29 kardel RELEASE_20090105_A+POWERUPTRUST";
+
+/**===========================================================================
+ ** external interface to ntp mechanism
+ **/
+
+static int parse_start (int, struct peer *);
+static void parse_shutdown (int, struct peer *);
+static void parse_poll (int, struct peer *);
+static void parse_control (int, const struct refclockstat *, struct refclockstat *, struct peer *);
+
+struct refclock refclock_parse = {
+ parse_start,
+ parse_shutdown,
+ parse_poll,
+ parse_control,
+ noentry,
+ noentry,
+ NOFLAGS
+};
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 4 /* maximum number of "PARSE" units permitted */
+#define PARSEDEVICE "/dev/refclock-%d" /* device to open %d is unit number */
+#define PARSEPPSDEVICE "/dev/refclockpps-%d" /* optional pps device to open %d is unit number */
+
+#undef ABS
+#define ABS(_X_) (((_X_) < 0) ? -(_X_) : (_X_))
+
+#define PARSE_HARDPPS_DISABLE 0
+#define PARSE_HARDPPS_ENABLE 1
+
+/**===========================================================================
+ ** function vector for dynamically binding io handling mechanism
+ **/
+
+struct parseunit; /* to keep inquiring minds happy */
+
+typedef struct bind
+{
+ const char *bd_description; /* name of type of binding */
+ int (*bd_init) (struct parseunit *); /* initialize */
+ void (*bd_end) (struct parseunit *); /* end */
+ int (*bd_setcs) (struct parseunit *, parsectl_t *); /* set character size */
+ int (*bd_disable) (struct parseunit *); /* disable */
+ int (*bd_enable) (struct parseunit *); /* enable */
+ int (*bd_getfmt) (struct parseunit *, parsectl_t *); /* get format */
+ int (*bd_setfmt) (struct parseunit *, parsectl_t *); /* setfmt */
+ int (*bd_timecode) (struct parseunit *, parsectl_t *); /* get time code */
+ void (*bd_receive) (struct recvbuf *); /* receive operation */
+ int (*bd_io_input) (struct recvbuf *); /* input operation */
+} bind_t;
+
+#define PARSE_END(_X_) (*(_X_)->binding->bd_end)(_X_)
+#define PARSE_SETCS(_X_, _CS_) (*(_X_)->binding->bd_setcs)(_X_, _CS_)
+#define PARSE_ENABLE(_X_) (*(_X_)->binding->bd_enable)(_X_)
+#define PARSE_DISABLE(_X_) (*(_X_)->binding->bd_disable)(_X_)
+#define PARSE_GETFMT(_X_, _DCT_) (*(_X_)->binding->bd_getfmt)(_X_, _DCT_)
+#define PARSE_SETFMT(_X_, _DCT_) (*(_X_)->binding->bd_setfmt)(_X_, _DCT_)
+#define PARSE_GETTIMECODE(_X_, _DCT_) (*(_X_)->binding->bd_timecode)(_X_, _DCT_)
+
+/*
+ * special handling flags
+ */
+#define PARSE_F_PPSONSECOND 0x00000001 /* PPS pulses are on second */
+#define PARSE_F_POWERUPTRUST 0x00000100 /* POWERUP state ist trusted for */
+ /* trusttime after SYNC was seen */
+/**===========================================================================
+ ** error message regression handling
+ **
+ ** there are quite a few errors that can occur in rapid succession such as
+ ** noisy input data or no data at all. in order to reduce the amount of
+ ** syslog messages in such case, we are using a backoff algorithm. We limit
+ ** the number of error messages of a certain class to 1 per time unit. if a
+ ** configurable number of messages is displayed that way, we move on to the
+ ** next time unit / count for that class. a count of messages that have been
+ ** suppressed is held and displayed whenever a corresponding message is
+ ** displayed. the time units for a message class will also be displayed.
+ ** whenever an error condition clears we reset the error message state,
+ ** thus we would still generate much output on pathological conditions
+ ** where the system oscillates between OK and NOT OK states. coping
+ ** with that condition is currently considered too complicated.
+ **/
+
+#define ERR_ALL (unsigned)~0 /* "all" errors */
+#define ERR_BADDATA (unsigned)0 /* unusable input data/conversion errors */
+#define ERR_NODATA (unsigned)1 /* no input data */
+#define ERR_BADIO (unsigned)2 /* read/write/select errors */
+#define ERR_BADSTATUS (unsigned)3 /* unsync states */
+#define ERR_BADEVENT (unsigned)4 /* non nominal events */
+#define ERR_INTERNAL (unsigned)5 /* internal error */
+#define ERR_CNT (unsigned)(ERR_INTERNAL+1)
+
+#define ERR(_X_) if (list_err(parse, (_X_)))
+
+struct errorregression
+{
+ u_long err_count; /* number of repititions per class */
+ u_long err_delay; /* minimum delay between messages */
+};
+
+static struct errorregression
+err_baddata[] = /* error messages for bad input data */
+{
+ { 1, 0 }, /* output first message immediately */
+ { 5, 60 }, /* output next five messages in 60 second intervals */
+ { 3, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_nodata[] = /* error messages for missing input data */
+{
+ { 1, 0 }, /* output first message immediately */
+ { 5, 60 }, /* output next five messages in 60 second intervals */
+ { 3, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_badstatus[] = /* unsynchronized state messages */
+{
+ { 1, 0 }, /* output first message immediately */
+ { 5, 60 }, /* output next five messages in 60 second intervals */
+ { 3, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_badio[] = /* io failures (bad reads, selects, ...) */
+{
+ { 1, 0 }, /* output first message immediately */
+ { 5, 60 }, /* output next five messages in 60 second intervals */
+ { 5, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_badevent[] = /* non nominal events */
+{
+ { 20, 0 }, /* output first message immediately */
+ { 6, 60 }, /* output next five messages in 60 second intervals */
+ { 5, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_internal[] = /* really bad things - basically coding/OS errors */
+{
+ { 0, 0 }, /* output all messages immediately */
+};
+
+static struct errorregression *
+err_tbl[] =
+{
+ err_baddata,
+ err_nodata,
+ err_badio,
+ err_badstatus,
+ err_badevent,
+ err_internal
+};
+
+struct errorinfo
+{
+ u_long err_started; /* begin time (ntp) of error condition */
+ u_long err_last; /* last time (ntp) error occurred */
+ u_long err_cnt; /* number of error repititions */
+ u_long err_suppressed; /* number of suppressed messages */
+ struct errorregression *err_stage; /* current error stage */
+};
+
+/**===========================================================================
+ ** refclock instance data
+ **/
+
+struct parseunit
+{
+ /*
+ * NTP management
+ */
+ struct peer *peer; /* backlink to peer structure - refclock inactive if 0 */
+ struct refclockproc *generic; /* backlink to refclockproc structure */
+
+ /*
+ * PARSE io
+ */
+ bind_t *binding; /* io handling binding */
+
+ /*
+ * parse state
+ */
+ parse_t parseio; /* io handling structure (user level parsing) */
+
+ /*
+ * type specific parameters
+ */
+ struct parse_clockinfo *parse_type; /* link to clock description */
+
+ /*
+ * clock state handling/reporting
+ */
+ u_char flags; /* flags (leap_control) */
+ u_long lastchange; /* time (ntp) when last state change accured */
+ u_long statetime[CEVNT_MAX+1]; /* accumulated time of clock states */
+ u_long pollneeddata; /* current_time(!=0) for receive sample expected in PPS mode */
+ u_short lastformat; /* last format used */
+ u_long lastsync; /* time (ntp) when clock was last seen fully synchronized */
+ u_long maxunsync; /* max time in seconds a receiver is trusted after loosing synchronisation */
+ double ppsphaseadjust; /* phase adjustment of PPS time stamp */
+ u_long lastmissed; /* time (ntp) when poll didn't get data (powerup heuristic) */
+ u_long ppsserial; /* magic cookie for ppsclock serials (avoids stale ppsclock data) */
+ int ppsfd; /* fd to ise for PPS io */
+#ifdef HAVE_PPSAPI
+ int hardppsstate; /* current hard pps state */
+ struct refclock_atom atom; /* PPSAPI structure */
+#endif
+ parsetime_t timedata; /* last (parse module) data */
+ void *localdata; /* optional local, receiver-specific data */
+ unsigned long localstate; /* private local state */
+ struct errorinfo errors[ERR_CNT]; /* error state table for suppressing excessive error messages */
+ struct ctl_var *kv; /* additional pseudo variables */
+ u_long laststatistic; /* time when staticstics where output */
+};
+
+
+/**===========================================================================
+ ** Clockinfo section all parameter for specific clock types
+ ** includes NTP parameters, TTY parameters and IO handling parameters
+ **/
+
+static void poll_dpoll (struct parseunit *);
+static void poll_poll (struct peer *);
+static int poll_init (struct parseunit *);
+
+typedef struct poll_info
+{
+ u_long rate; /* poll rate - once every "rate" seconds - 0 off */
+ const char *string; /* string to send for polling */
+ u_long count; /* number of characters in string */
+} poll_info_t;
+
+#define NO_CL_FLAGS 0
+#define NO_POLL 0
+#define NO_INIT 0
+#define NO_END 0
+#define NO_EVENT 0
+#define NO_LCLDATA 0
+#define NO_MESSAGE 0
+#define NO_PPSDELAY 0
+
+#define DCF_ID "DCF" /* generic DCF */
+#define DCF_A_ID "DCFa" /* AM demodulation */
+#define DCF_P_ID "DCFp" /* psuedo random phase shift */
+#define GPS_ID "GPS" /* GPS receiver */
+
+#define NOCLOCK_ROOTDELAY 0.0
+#define NOCLOCK_BASEDELAY 0.0
+#define NOCLOCK_DESCRIPTION 0
+#define NOCLOCK_MAXUNSYNC 0
+#define NOCLOCK_CFLAG 0
+#define NOCLOCK_IFLAG 0
+#define NOCLOCK_OFLAG 0
+#define NOCLOCK_LFLAG 0
+#define NOCLOCK_ID "TILT"
+#define NOCLOCK_POLL NO_POLL
+#define NOCLOCK_INIT NO_INIT
+#define NOCLOCK_END NO_END
+#define NOCLOCK_DATA NO_LCLDATA
+#define NOCLOCK_FORMAT ""
+#define NOCLOCK_TYPE CTL_SST_TS_UNSPEC
+#define NOCLOCK_SAMPLES 0
+#define NOCLOCK_KEEP 0
+
+#define DCF_TYPE CTL_SST_TS_LF
+#define GPS_TYPE CTL_SST_TS_UHF
+
+/*
+ * receiver specific constants
+ */
+#define MBG_SPEED (B9600)
+#define MBG_CFLAG (CS7|PARENB|CREAD|CLOCAL|HUPCL|CSTOPB)
+#define MBG_IFLAG (IGNBRK|IGNPAR|ISTRIP)
+#define MBG_OFLAG 0
+#define MBG_LFLAG 0
+#define MBG_FLAGS PARSE_F_PPSONSECOND
+
+/*
+ * Meinberg DCF77 receivers
+ */
+#define DCFUA31_ROOTDELAY 0.0 /* 0 */
+#define DCFUA31_BASEDELAY 0.010 /* 10.7421875ms: 10 ms (+/- 3 ms) */
+#define DCFUA31_DESCRIPTION "Meinberg DCF77 C51 or compatible"
+#define DCFUA31_MAXUNSYNC 60*30 /* only trust clock for 1/2 hour */
+#define DCFUA31_SPEED MBG_SPEED
+#define DCFUA31_CFLAG MBG_CFLAG
+#define DCFUA31_IFLAG MBG_IFLAG
+#define DCFUA31_OFLAG MBG_OFLAG
+#define DCFUA31_LFLAG MBG_LFLAG
+#define DCFUA31_SAMPLES 5
+#define DCFUA31_KEEP 3
+#define DCFUA31_FORMAT "Meinberg Standard"
+
+/*
+ * Meinberg DCF PZF535/TCXO (FM/PZF) receiver
+ */
+#define DCFPZF535_ROOTDELAY 0.0
+#define DCFPZF535_BASEDELAY 0.001968 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define DCFPZF535_DESCRIPTION "Meinberg DCF PZF 535/509 / TCXO"
+#define DCFPZF535_MAXUNSYNC 60*60*12 /* only trust clock for 12 hours
+ * @ 5e-8df/f we have accumulated
+ * at most 2.16 ms (thus we move to
+ * NTP synchronisation */
+#define DCFPZF535_SPEED MBG_SPEED
+#define DCFPZF535_CFLAG MBG_CFLAG
+#define DCFPZF535_IFLAG MBG_IFLAG
+#define DCFPZF535_OFLAG MBG_OFLAG
+#define DCFPZF535_LFLAG MBG_LFLAG
+#define DCFPZF535_SAMPLES 5
+#define DCFPZF535_KEEP 3
+#define DCFPZF535_FORMAT "Meinberg Standard"
+
+/*
+ * Meinberg DCF PZF535/OCXO receiver
+ */
+#define DCFPZF535OCXO_ROOTDELAY 0.0
+#define DCFPZF535OCXO_BASEDELAY 0.001968 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define DCFPZF535OCXO_DESCRIPTION "Meinberg DCF PZF 535/509 / OCXO"
+#define DCFPZF535OCXO_MAXUNSYNC 60*60*96 /* only trust clock for 4 days
+ * @ 5e-9df/f we have accumulated
+ * at most an error of 1.73 ms
+ * (thus we move to NTP synchronisation) */
+#define DCFPZF535OCXO_SPEED MBG_SPEED
+#define DCFPZF535OCXO_CFLAG MBG_CFLAG
+#define DCFPZF535OCXO_IFLAG MBG_IFLAG
+#define DCFPZF535OCXO_OFLAG MBG_OFLAG
+#define DCFPZF535OCXO_LFLAG MBG_LFLAG
+#define DCFPZF535OCXO_SAMPLES 5
+#define DCFPZF535OCXO_KEEP 3
+#define DCFPZF535OCXO_FORMAT "Meinberg Standard"
+
+/*
+ * Meinberg GPS16X receiver
+ */
+static void gps16x_message (struct parseunit *, parsetime_t *);
+static int gps16x_poll_init (struct parseunit *);
+
+#define GPS16X_ROOTDELAY 0.0 /* nothing here */
+#define GPS16X_BASEDELAY 0.001968 /* XXX to be fixed ! 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define GPS16X_DESCRIPTION "Meinberg GPS16x receiver"
+#define GPS16X_MAXUNSYNC 60*60*96 /* only trust clock for 4 days
+ * @ 5e-9df/f we have accumulated
+ * at most an error of 1.73 ms
+ * (thus we move to NTP synchronisation) */
+#define GPS16X_SPEED B19200
+#define GPS16X_CFLAG (CS8|CREAD|CLOCAL|HUPCL)
+#define GPS16X_IFLAG (IGNBRK|IGNPAR)
+#define GPS16X_OFLAG MBG_OFLAG
+#define GPS16X_LFLAG MBG_LFLAG
+#define GPS16X_POLLRATE 6
+#define GPS16X_POLLCMD ""
+#define GPS16X_CMDSIZE 0
+
+static poll_info_t gps16x_pollinfo = { GPS16X_POLLRATE, GPS16X_POLLCMD, GPS16X_CMDSIZE };
+
+#define GPS16X_INIT gps16x_poll_init
+#define GPS16X_POLL 0
+#define GPS16X_END 0
+#define GPS16X_DATA ((void *)(&gps16x_pollinfo))
+#define GPS16X_MESSAGE gps16x_message
+#define GPS16X_ID GPS_ID
+#define GPS16X_FORMAT "Meinberg GPS Extended"
+#define GPS16X_SAMPLES 5
+#define GPS16X_KEEP 3
+
+/*
+ * ELV DCF7000 Wallclock-Receiver/Switching Clock (Kit)
+ *
+ * This is really not the hottest clock - but before you have nothing ...
+ */
+#define DCF7000_ROOTDELAY 0.0 /* 0 */
+#define DCF7000_BASEDELAY 0.405 /* slow blow */
+#define DCF7000_DESCRIPTION "ELV DCF7000"
+#define DCF7000_MAXUNSYNC (60*5) /* sorry - but it just was not build as a clock */
+#define DCF7000_SPEED (B9600)
+#define DCF7000_CFLAG (CS8|CREAD|PARENB|PARODD|CLOCAL|HUPCL)
+#define DCF7000_IFLAG (IGNBRK)
+#define DCF7000_OFLAG 0
+#define DCF7000_LFLAG 0
+#define DCF7000_SAMPLES 5
+#define DCF7000_KEEP 3
+#define DCF7000_FORMAT "ELV DCF7000"
+
+/*
+ * Schmid DCF Receiver Kit
+ *
+ * When the WSDCF clock is operating optimally we want the primary clock
+ * distance to come out at 300 ms. Thus, peer.distance in the WSDCF peer
+ * structure is set to 290 ms and we compute delays which are at least
+ * 10 ms long. The following are 290 ms and 10 ms expressed in u_fp format
+ */
+#define WS_POLLRATE 1 /* every second - watch interdependency with poll routine */
+#define WS_POLLCMD "\163"
+#define WS_CMDSIZE 1
+
+static poll_info_t wsdcf_pollinfo = { WS_POLLRATE, WS_POLLCMD, WS_CMDSIZE };
+
+#define WSDCF_INIT poll_init
+#define WSDCF_POLL poll_dpoll
+#define WSDCF_END 0
+#define WSDCF_DATA ((void *)(&wsdcf_pollinfo))
+#define WSDCF_ROOTDELAY 0.0 /* 0 */
+#define WSDCF_BASEDELAY 0.010 /* ~ 10ms */
+#define WSDCF_DESCRIPTION "WS/DCF Receiver"
+#define WSDCF_FORMAT "Schmid"
+#define WSDCF_MAXUNSYNC (60*60) /* assume this beast hold at 1 h better than 2 ms XXX-must verify */
+#define WSDCF_SPEED (B1200)
+#define WSDCF_CFLAG (CS8|CREAD|CLOCAL)
+#define WSDCF_IFLAG 0
+#define WSDCF_OFLAG 0
+#define WSDCF_LFLAG 0
+#define WSDCF_SAMPLES 5
+#define WSDCF_KEEP 3
+
+/*
+ * RAW DCF77 - input of DCF marks via RS232 - many variants
+ */
+#define RAWDCF_FLAGS 0
+#define RAWDCF_ROOTDELAY 0.0 /* 0 */
+#define RAWDCF_BASEDELAY 0.258
+#define RAWDCF_FORMAT "RAW DCF77 Timecode"
+#define RAWDCF_MAXUNSYNC (0) /* sorry - its a true receiver - no signal - no time */
+#define RAWDCF_SPEED (B50)
+#ifdef NO_PARENB_IGNPAR /* Was: defined(SYS_IRIX4) || defined(SYS_IRIX5) */
+/* somehow doesn't grok PARENB & IGNPAR (mj) */
+# define RAWDCF_CFLAG (CS8|CREAD|CLOCAL)
+#else
+# define RAWDCF_CFLAG (CS8|CREAD|CLOCAL|PARENB)
+#endif
+#ifdef RAWDCF_NO_IGNPAR /* Was: defined(SYS_LINUX) && defined(CLOCK_RAWDCF) */
+# define RAWDCF_IFLAG 0
+#else
+# define RAWDCF_IFLAG (IGNPAR)
+#endif
+#define RAWDCF_OFLAG 0
+#define RAWDCF_LFLAG 0
+#define RAWDCF_SAMPLES 20
+#define RAWDCF_KEEP 12
+#define RAWDCF_INIT 0
+
+/*
+ * RAW DCF variants
+ */
+/*
+ * Conrad receiver
+ *
+ * simplest (cheapest) DCF clock - e. g. DCF77 receiver by Conrad
+ * (~40DM - roughly $30 ) followed by a level converter for RS232
+ */
+#define CONRAD_BASEDELAY 0.292 /* Conrad receiver @ 50 Baud on a Sun */
+#define CONRAD_DESCRIPTION "RAW DCF77 CODE (Conrad DCF77 receiver module)"
+
+/* Gude Analog- und Digitalsystem GmbH 'Expert mouseCLOCK USB v2.0' */
+#define GUDE_EMC_USB_V20_SPEED (B4800)
+#define GUDE_EMC_USB_V20_BASEDELAY 0.425 /* USB serial<->USB converter FTDI232R */
+#define GUDE_EMC_USB_V20_DESCRIPTION "RAW DCF77 CODE (Expert mouseCLOCK USB v2.0)"
+
+/*
+ * TimeBrick receiver
+ */
+#define TIMEBRICK_BASEDELAY 0.210 /* TimeBrick @ 50 Baud on a Sun */
+#define TIMEBRICK_DESCRIPTION "RAW DCF77 CODE (TimeBrick)"
+
+/*
+ * IGEL:clock receiver
+ */
+#define IGELCLOCK_BASEDELAY 0.258 /* IGEL:clock receiver */
+#define IGELCLOCK_DESCRIPTION "RAW DCF77 CODE (IGEL:clock)"
+#define IGELCLOCK_SPEED (B1200)
+#define IGELCLOCK_CFLAG (CS8|CREAD|HUPCL|CLOCAL)
+
+/*
+ * RAWDCF receivers that need to be powered from DTR
+ * (like Expert mouse clock)
+ */
+static int rawdcf_init_1 (struct parseunit *);
+#define RAWDCFDTRSET_DESCRIPTION "RAW DCF77 CODE (DTR SET/RTS CLR)"
+#define RAWDCFDTRSET75_DESCRIPTION "RAW DCF77 CODE (DTR SET/RTS CLR @ 75 baud)"
+#define RAWDCFDTRSET_INIT rawdcf_init_1
+
+/*
+ * RAWDCF receivers that need to be powered from
+ * DTR CLR and RTS SET
+ */
+static int rawdcf_init_2 (struct parseunit *);
+#define RAWDCFDTRCLRRTSSET_DESCRIPTION "RAW DCF77 CODE (DTR CLR/RTS SET)"
+#define RAWDCFDTRCLRRTSSET75_DESCRIPTION "RAW DCF77 CODE (DTR CLR/RTS SET @ 75 baud)"
+#define RAWDCFDTRCLRRTSSET_INIT rawdcf_init_2
+
+/*
+ * Trimble GPS receivers (TAIP and TSIP protocols)
+ */
+#ifndef TRIM_POLLRATE
+#define TRIM_POLLRATE 0 /* only true direct polling */
+#endif
+
+#define TRIM_TAIPPOLLCMD ">SRM;FR_FLAG=F;EC_FLAG=F<>QTM<"
+#define TRIM_TAIPCMDSIZE (sizeof(TRIM_TAIPPOLLCMD)-1)
+
+static poll_info_t trimbletaip_pollinfo = { TRIM_POLLRATE, TRIM_TAIPPOLLCMD, TRIM_TAIPCMDSIZE };
+static int trimbletaip_init (struct parseunit *);
+static void trimbletaip_event (struct parseunit *, int);
+
+/* query time & UTC correction data */
+static char tsipquery[] = { DLE, 0x21, DLE, ETX, DLE, 0x2F, DLE, ETX };
+
+static poll_info_t trimbletsip_pollinfo = { TRIM_POLLRATE, tsipquery, sizeof(tsipquery) };
+static int trimbletsip_init (struct parseunit *);
+static void trimbletsip_end (struct parseunit *);
+static void trimbletsip_message (struct parseunit *, parsetime_t *);
+static void trimbletsip_event (struct parseunit *, int);
+
+#define TRIMBLETSIP_IDLE_TIME (300) /* 5 minutes silence at most */
+#define TRIMBLE_RESET_HOLDOFF TRIMBLETSIP_IDLE_TIME
+
+#define TRIMBLETAIP_SPEED (B4800)
+#define TRIMBLETAIP_CFLAG (CS8|CREAD|CLOCAL)
+#define TRIMBLETAIP_IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON)
+#define TRIMBLETAIP_OFLAG (OPOST|ONLCR)
+#define TRIMBLETAIP_LFLAG (0)
+
+#define TRIMBLETSIP_SPEED (B9600)
+#define TRIMBLETSIP_CFLAG (CS8|CLOCAL|CREAD|PARENB|PARODD)
+#define TRIMBLETSIP_IFLAG (IGNBRK)
+#define TRIMBLETSIP_OFLAG (0)
+#define TRIMBLETSIP_LFLAG (ICANON)
+
+#define TRIMBLETSIP_SAMPLES 5
+#define TRIMBLETSIP_KEEP 3
+#define TRIMBLETAIP_SAMPLES 5
+#define TRIMBLETAIP_KEEP 3
+
+#define TRIMBLETAIP_FLAGS (PARSE_F_PPSONSECOND)
+#define TRIMBLETSIP_FLAGS (TRIMBLETAIP_FLAGS)
+
+#define TRIMBLETAIP_POLL poll_dpoll
+#define TRIMBLETSIP_POLL poll_dpoll
+
+#define TRIMBLETAIP_INIT trimbletaip_init
+#define TRIMBLETSIP_INIT trimbletsip_init
+
+#define TRIMBLETAIP_EVENT trimbletaip_event
+
+#define TRIMBLETSIP_EVENT trimbletsip_event
+#define TRIMBLETSIP_MESSAGE trimbletsip_message
+
+#define TRIMBLETAIP_END 0
+#define TRIMBLETSIP_END trimbletsip_end
+
+#define TRIMBLETAIP_DATA ((void *)(&trimbletaip_pollinfo))
+#define TRIMBLETSIP_DATA ((void *)(&trimbletsip_pollinfo))
+
+#define TRIMBLETAIP_ID GPS_ID
+#define TRIMBLETSIP_ID GPS_ID
+
+#define TRIMBLETAIP_FORMAT "Trimble TAIP"
+#define TRIMBLETSIP_FORMAT "Trimble TSIP"
+
+#define TRIMBLETAIP_ROOTDELAY 0x0
+#define TRIMBLETSIP_ROOTDELAY 0x0
+
+#define TRIMBLETAIP_BASEDELAY 0.0
+#define TRIMBLETSIP_BASEDELAY 0.020 /* GPS time message latency */
+
+#define TRIMBLETAIP_DESCRIPTION "Trimble GPS (TAIP) receiver"
+#define TRIMBLETSIP_DESCRIPTION "Trimble GPS (TSIP) receiver"
+
+#define TRIMBLETAIP_MAXUNSYNC 0
+#define TRIMBLETSIP_MAXUNSYNC 0
+
+#define TRIMBLETAIP_EOL '<'
+
+/*
+ * RadioCode Clocks RCC 800 receiver
+ */
+#define RCC_POLLRATE 0 /* only true direct polling */
+#define RCC_POLLCMD "\r"
+#define RCC_CMDSIZE 1
+
+static poll_info_t rcc8000_pollinfo = { RCC_POLLRATE, RCC_POLLCMD, RCC_CMDSIZE };
+#define RCC8000_FLAGS 0
+#define RCC8000_POLL poll_dpoll
+#define RCC8000_INIT poll_init
+#define RCC8000_END 0
+#define RCC8000_DATA ((void *)(&rcc8000_pollinfo))
+#define RCC8000_ROOTDELAY 0.0
+#define RCC8000_BASEDELAY 0.0
+#define RCC8000_ID "MSF"
+#define RCC8000_DESCRIPTION "RCC 8000 MSF Receiver"
+#define RCC8000_FORMAT "Radiocode RCC8000"
+#define RCC8000_MAXUNSYNC (60*60) /* should be ok for an hour */
+#define RCC8000_SPEED (B2400)
+#define RCC8000_CFLAG (CS8|CREAD|CLOCAL)
+#define RCC8000_IFLAG (IGNBRK|IGNPAR)
+#define RCC8000_OFLAG 0
+#define RCC8000_LFLAG 0
+#define RCC8000_SAMPLES 5
+#define RCC8000_KEEP 3
+
+/*
+ * Hopf Radio clock 6021 Format
+ *
+ */
+#define HOPF6021_ROOTDELAY 0.0
+#define HOPF6021_BASEDELAY 0.0
+#define HOPF6021_DESCRIPTION "HOPF 6021"
+#define HOPF6021_FORMAT "hopf Funkuhr 6021"
+#define HOPF6021_MAXUNSYNC (60*60) /* should be ok for an hour */
+#define HOPF6021_SPEED (B9600)
+#define HOPF6021_CFLAG (CS8|CREAD|CLOCAL)
+#define HOPF6021_IFLAG (IGNBRK|ISTRIP)
+#define HOPF6021_OFLAG 0
+#define HOPF6021_LFLAG 0
+#define HOPF6021_FLAGS 0
+#define HOPF6021_SAMPLES 5
+#define HOPF6021_KEEP 3
+
+/*
+ * Diem's Computime Radio Clock Receiver
+ */
+#define COMPUTIME_FLAGS 0
+#define COMPUTIME_ROOTDELAY 0.0
+#define COMPUTIME_BASEDELAY 0.0
+#define COMPUTIME_ID DCF_ID
+#define COMPUTIME_DESCRIPTION "Diem's Computime receiver"
+#define COMPUTIME_FORMAT "Diem's Computime Radio Clock"
+#define COMPUTIME_TYPE DCF_TYPE
+#define COMPUTIME_MAXUNSYNC (60*60) /* only trust clock for 1 hour */
+#define COMPUTIME_SPEED (B9600)
+#define COMPUTIME_CFLAG (CSTOPB|CS7|CREAD|CLOCAL)
+#define COMPUTIME_IFLAG (IGNBRK|IGNPAR|ISTRIP)
+#define COMPUTIME_OFLAG 0
+#define COMPUTIME_LFLAG 0
+#define COMPUTIME_SAMPLES 5
+#define COMPUTIME_KEEP 3
+
+/*
+ * Varitext Radio Clock Receiver
+ */
+#define VARITEXT_FLAGS 0
+#define VARITEXT_ROOTDELAY 0.0
+#define VARITEXT_BASEDELAY 0.0
+#define VARITEXT_ID "MSF"
+#define VARITEXT_DESCRIPTION "Varitext receiver"
+#define VARITEXT_FORMAT "Varitext Radio Clock"
+#define VARITEXT_TYPE DCF_TYPE
+#define VARITEXT_MAXUNSYNC (60*60) /* only trust clock for 1 hour */
+#define VARITEXT_SPEED (B9600)
+#define VARITEXT_CFLAG (CS7|CREAD|CLOCAL|PARENB|PARODD)
+#define VARITEXT_IFLAG (IGNPAR|IGNBRK|INPCK) /*|ISTRIP)*/
+#define VARITEXT_OFLAG 0
+#define VARITEXT_LFLAG 0
+#define VARITEXT_SAMPLES 32
+#define VARITEXT_KEEP 20
+
+/*
+ * SEL240x Satellite Sychronized Clock
+ */
+#define SEL240X_POLLRATE 0 /* only true direct polling */
+#define SEL240X_POLLCMD "BUB8"
+#define SEL240X_CMDSIZE 4
+
+static poll_info_t sel240x_pollinfo = { SEL240X_POLLRATE,
+ SEL240X_POLLCMD,
+ SEL240X_CMDSIZE };
+#define SEL240X_FLAGS (PARSE_F_PPSONSECOND)
+#define SEL240X_POLL poll_dpoll
+#define SEL240X_INIT poll_init
+#define SEL240X_END 0
+#define SEL240X_DATA ((void *)(&sel240x_pollinfo))
+#define SEL240X_ROOTDELAY 0.0
+#define SEL240X_BASEDELAY 0.0
+#define SEL240X_ID GPS_ID
+#define SEL240X_DESCRIPTION "SEL240x Satellite Synchronized Clock"
+#define SEL240X_FORMAT "SEL B8"
+#define SEL240X_MAXUNSYNC 60*60*12 /* only trust clock for 12 hours */
+#define SEL240X_SPEED (B9600)
+#define SEL240X_CFLAG (CS8|CREAD|CLOCAL)
+#define SEL240X_IFLAG (IGNBRK|IGNPAR)
+#define SEL240X_OFLAG (0)
+#define SEL240X_LFLAG (0)
+#define SEL240X_SAMPLES 5
+#define SEL240X_KEEP 3
+
+static struct parse_clockinfo
+{
+ u_long cl_flags; /* operation flags (PPS interpretation, trust handling) */
+ void (*cl_poll) (struct parseunit *); /* active poll routine */
+ int (*cl_init) (struct parseunit *); /* active poll init routine */
+ void (*cl_event) (struct parseunit *, int); /* special event handling (e.g. reset clock) */
+ void (*cl_end) (struct parseunit *); /* active poll end routine */
+ void (*cl_message) (struct parseunit *, parsetime_t *); /* process a lower layer message */
+ void *cl_data; /* local data area for "poll" mechanism */
+ double cl_rootdelay; /* rootdelay */
+ double cl_basedelay; /* current offset by which the RS232
+ time code is delayed from the actual time */
+ const char *cl_id; /* ID code */
+ const char *cl_description; /* device name */
+ const char *cl_format; /* fixed format */
+ u_char cl_type; /* clock type (ntp control) */
+ u_long cl_maxunsync; /* time to trust oscillator after losing synch */
+ u_long cl_speed; /* terminal input & output baudrate */
+ u_long cl_cflag; /* terminal control flags */
+ u_long cl_iflag; /* terminal input flags */
+ u_long cl_oflag; /* terminal output flags */
+ u_long cl_lflag; /* terminal local flags */
+ u_long cl_samples; /* samples for median filter */
+ u_long cl_keep; /* samples for median filter to keep */
+} parse_clockinfo[] =
+{
+ { /* mode 0 */
+ MBG_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ DCFPZF535_ROOTDELAY,
+ DCFPZF535_BASEDELAY,
+ DCF_P_ID,
+ DCFPZF535_DESCRIPTION,
+ DCFPZF535_FORMAT,
+ DCF_TYPE,
+ DCFPZF535_MAXUNSYNC,
+ DCFPZF535_SPEED,
+ DCFPZF535_CFLAG,
+ DCFPZF535_IFLAG,
+ DCFPZF535_OFLAG,
+ DCFPZF535_LFLAG,
+ DCFPZF535_SAMPLES,
+ DCFPZF535_KEEP
+ },
+ { /* mode 1 */
+ MBG_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ DCFPZF535OCXO_ROOTDELAY,
+ DCFPZF535OCXO_BASEDELAY,
+ DCF_P_ID,
+ DCFPZF535OCXO_DESCRIPTION,
+ DCFPZF535OCXO_FORMAT,
+ DCF_TYPE,
+ DCFPZF535OCXO_MAXUNSYNC,
+ DCFPZF535OCXO_SPEED,
+ DCFPZF535OCXO_CFLAG,
+ DCFPZF535OCXO_IFLAG,
+ DCFPZF535OCXO_OFLAG,
+ DCFPZF535OCXO_LFLAG,
+ DCFPZF535OCXO_SAMPLES,
+ DCFPZF535OCXO_KEEP
+ },
+ { /* mode 2 */
+ MBG_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ DCFUA31_ROOTDELAY,
+ DCFUA31_BASEDELAY,
+ DCF_A_ID,
+ DCFUA31_DESCRIPTION,
+ DCFUA31_FORMAT,
+ DCF_TYPE,
+ DCFUA31_MAXUNSYNC,
+ DCFUA31_SPEED,
+ DCFUA31_CFLAG,
+ DCFUA31_IFLAG,
+ DCFUA31_OFLAG,
+ DCFUA31_LFLAG,
+ DCFUA31_SAMPLES,
+ DCFUA31_KEEP
+ },
+ { /* mode 3 */
+ MBG_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ DCF7000_ROOTDELAY,
+ DCF7000_BASEDELAY,
+ DCF_A_ID,
+ DCF7000_DESCRIPTION,
+ DCF7000_FORMAT,
+ DCF_TYPE,
+ DCF7000_MAXUNSYNC,
+ DCF7000_SPEED,
+ DCF7000_CFLAG,
+ DCF7000_IFLAG,
+ DCF7000_OFLAG,
+ DCF7000_LFLAG,
+ DCF7000_SAMPLES,
+ DCF7000_KEEP
+ },
+ { /* mode 4 */
+ NO_CL_FLAGS,
+ WSDCF_POLL,
+ WSDCF_INIT,
+ NO_EVENT,
+ WSDCF_END,
+ NO_MESSAGE,
+ WSDCF_DATA,
+ WSDCF_ROOTDELAY,
+ WSDCF_BASEDELAY,
+ DCF_A_ID,
+ WSDCF_DESCRIPTION,
+ WSDCF_FORMAT,
+ DCF_TYPE,
+ WSDCF_MAXUNSYNC,
+ WSDCF_SPEED,
+ WSDCF_CFLAG,
+ WSDCF_IFLAG,
+ WSDCF_OFLAG,
+ WSDCF_LFLAG,
+ WSDCF_SAMPLES,
+ WSDCF_KEEP
+ },
+ { /* mode 5 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCF_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ RAWDCF_ROOTDELAY,
+ CONRAD_BASEDELAY,
+ DCF_A_ID,
+ CONRAD_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 6 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCF_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ RAWDCF_ROOTDELAY,
+ TIMEBRICK_BASEDELAY,
+ DCF_A_ID,
+ TIMEBRICK_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 7 */
+ MBG_FLAGS,
+ GPS16X_POLL,
+ GPS16X_INIT,
+ NO_EVENT,
+ GPS16X_END,
+ GPS16X_MESSAGE,
+ GPS16X_DATA,
+ GPS16X_ROOTDELAY,
+ GPS16X_BASEDELAY,
+ GPS16X_ID,
+ GPS16X_DESCRIPTION,
+ GPS16X_FORMAT,
+ GPS_TYPE,
+ GPS16X_MAXUNSYNC,
+ GPS16X_SPEED,
+ GPS16X_CFLAG,
+ GPS16X_IFLAG,
+ GPS16X_OFLAG,
+ GPS16X_LFLAG,
+ GPS16X_SAMPLES,
+ GPS16X_KEEP
+ },
+ { /* mode 8 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ RAWDCF_ROOTDELAY,
+ IGELCLOCK_BASEDELAY,
+ DCF_A_ID,
+ IGELCLOCK_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ IGELCLOCK_SPEED,
+ IGELCLOCK_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 9 */
+ TRIMBLETAIP_FLAGS,
+#if TRIM_POLLRATE /* DHD940515: Allow user config */
+ NO_POLL,
+#else
+ TRIMBLETAIP_POLL,
+#endif
+ TRIMBLETAIP_INIT,
+ TRIMBLETAIP_EVENT,
+ TRIMBLETAIP_END,
+ NO_MESSAGE,
+ TRIMBLETAIP_DATA,
+ TRIMBLETAIP_ROOTDELAY,
+ TRIMBLETAIP_BASEDELAY,
+ TRIMBLETAIP_ID,
+ TRIMBLETAIP_DESCRIPTION,
+ TRIMBLETAIP_FORMAT,
+ GPS_TYPE,
+ TRIMBLETAIP_MAXUNSYNC,
+ TRIMBLETAIP_SPEED,
+ TRIMBLETAIP_CFLAG,
+ TRIMBLETAIP_IFLAG,
+ TRIMBLETAIP_OFLAG,
+ TRIMBLETAIP_LFLAG,
+ TRIMBLETAIP_SAMPLES,
+ TRIMBLETAIP_KEEP
+ },
+ { /* mode 10 */
+ TRIMBLETSIP_FLAGS,
+#if TRIM_POLLRATE /* DHD940515: Allow user config */
+ NO_POLL,
+#else
+ TRIMBLETSIP_POLL,
+#endif
+ TRIMBLETSIP_INIT,
+ TRIMBLETSIP_EVENT,
+ TRIMBLETSIP_END,
+ TRIMBLETSIP_MESSAGE,
+ TRIMBLETSIP_DATA,
+ TRIMBLETSIP_ROOTDELAY,
+ TRIMBLETSIP_BASEDELAY,
+ TRIMBLETSIP_ID,
+ TRIMBLETSIP_DESCRIPTION,
+ TRIMBLETSIP_FORMAT,
+ GPS_TYPE,
+ TRIMBLETSIP_MAXUNSYNC,
+ TRIMBLETSIP_SPEED,
+ TRIMBLETSIP_CFLAG,
+ TRIMBLETSIP_IFLAG,
+ TRIMBLETSIP_OFLAG,
+ TRIMBLETSIP_LFLAG,
+ TRIMBLETSIP_SAMPLES,
+ TRIMBLETSIP_KEEP
+ },
+ { /* mode 11 */
+ NO_CL_FLAGS,
+ RCC8000_POLL,
+ RCC8000_INIT,
+ NO_EVENT,
+ RCC8000_END,
+ NO_MESSAGE,
+ RCC8000_DATA,
+ RCC8000_ROOTDELAY,
+ RCC8000_BASEDELAY,
+ RCC8000_ID,
+ RCC8000_DESCRIPTION,
+ RCC8000_FORMAT,
+ DCF_TYPE,
+ RCC8000_MAXUNSYNC,
+ RCC8000_SPEED,
+ RCC8000_CFLAG,
+ RCC8000_IFLAG,
+ RCC8000_OFLAG,
+ RCC8000_LFLAG,
+ RCC8000_SAMPLES,
+ RCC8000_KEEP
+ },
+ { /* mode 12 */
+ HOPF6021_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ HOPF6021_ROOTDELAY,
+ HOPF6021_BASEDELAY,
+ DCF_ID,
+ HOPF6021_DESCRIPTION,
+ HOPF6021_FORMAT,
+ DCF_TYPE,
+ HOPF6021_MAXUNSYNC,
+ HOPF6021_SPEED,
+ HOPF6021_CFLAG,
+ HOPF6021_IFLAG,
+ HOPF6021_OFLAG,
+ HOPF6021_LFLAG,
+ HOPF6021_SAMPLES,
+ HOPF6021_KEEP
+ },
+ { /* mode 13 */
+ COMPUTIME_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ COMPUTIME_ROOTDELAY,
+ COMPUTIME_BASEDELAY,
+ COMPUTIME_ID,
+ COMPUTIME_DESCRIPTION,
+ COMPUTIME_FORMAT,
+ COMPUTIME_TYPE,
+ COMPUTIME_MAXUNSYNC,
+ COMPUTIME_SPEED,
+ COMPUTIME_CFLAG,
+ COMPUTIME_IFLAG,
+ COMPUTIME_OFLAG,
+ COMPUTIME_LFLAG,
+ COMPUTIME_SAMPLES,
+ COMPUTIME_KEEP
+ },
+ { /* mode 14 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCFDTRSET_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ RAWDCF_ROOTDELAY,
+ RAWDCF_BASEDELAY,
+ DCF_A_ID,
+ RAWDCFDTRSET_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 15 */
+ 0, /* operation flags (io modes) */
+ NO_POLL, /* active poll routine */
+ NO_INIT, /* active poll init routine */
+ NO_EVENT, /* special event handling (e.g. reset clock) */
+ NO_END, /* active poll end routine */
+ NO_MESSAGE, /* process a lower layer message */
+ NO_LCLDATA, /* local data area for "poll" mechanism */
+ 0, /* rootdelay */
+ 11.0 /* bits */ / 9600, /* current offset by which the RS232
+ time code is delayed from the actual time */
+ DCF_ID, /* ID code */
+ "WHARTON 400A Series clock", /* device name */
+ "WHARTON 400A Series clock Output Format 1", /* fixed format */
+ /* Must match a format-name in a libparse/clk_xxx.c file */
+ DCF_TYPE, /* clock type (ntp control) */
+ (1*60*60), /* time to trust oscillator after losing synch */
+ B9600, /* terminal input & output baudrate */
+ (CS8|CREAD|PARENB|CLOCAL|HUPCL),/* terminal control flags */
+ 0, /* terminal input flags */
+ 0, /* terminal output flags */
+ 0, /* terminal local flags */
+ 5, /* samples for median filter */
+ 3, /* samples for median filter to keep */
+ },
+ { /* mode 16 - RAWDCF RTS set, DTR clr */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCFDTRCLRRTSSET_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ RAWDCF_ROOTDELAY,
+ RAWDCF_BASEDELAY,
+ DCF_A_ID,
+ RAWDCFDTRCLRRTSSET_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 17 */
+ VARITEXT_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ VARITEXT_ROOTDELAY,
+ VARITEXT_BASEDELAY,
+ VARITEXT_ID,
+ VARITEXT_DESCRIPTION,
+ VARITEXT_FORMAT,
+ VARITEXT_TYPE,
+ VARITEXT_MAXUNSYNC,
+ VARITEXT_SPEED,
+ VARITEXT_CFLAG,
+ VARITEXT_IFLAG,
+ VARITEXT_OFLAG,
+ VARITEXT_LFLAG,
+ VARITEXT_SAMPLES,
+ VARITEXT_KEEP
+ },
+ { /* mode 18 */
+ MBG_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ GPS16X_END,
+ GPS16X_MESSAGE,
+ GPS16X_DATA,
+ GPS16X_ROOTDELAY,
+ GPS16X_BASEDELAY,
+ GPS16X_ID,
+ GPS16X_DESCRIPTION,
+ GPS16X_FORMAT,
+ GPS_TYPE,
+ GPS16X_MAXUNSYNC,
+ GPS16X_SPEED,
+ GPS16X_CFLAG,
+ GPS16X_IFLAG,
+ GPS16X_OFLAG,
+ GPS16X_LFLAG,
+ GPS16X_SAMPLES,
+ GPS16X_KEEP
+ },
+ { /* mode 19 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCF_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ RAWDCF_ROOTDELAY,
+ GUDE_EMC_USB_V20_BASEDELAY,
+ DCF_A_ID,
+ GUDE_EMC_USB_V20_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ GUDE_EMC_USB_V20_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 20, like mode 14 but driven by 75 baud */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCFDTRSET_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ RAWDCF_ROOTDELAY,
+ RAWDCF_BASEDELAY,
+ DCF_A_ID,
+ RAWDCFDTRSET75_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ B75,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 21, like mode 16 but driven by 75 baud
+ - RAWDCF RTS set, DTR clr */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCFDTRCLRRTSSET_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ RAWDCF_ROOTDELAY,
+ RAWDCF_BASEDELAY,
+ DCF_A_ID,
+ RAWDCFDTRCLRRTSSET75_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ B75,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 22 - like 2 with POWERUP trust */
+ MBG_FLAGS | PARSE_F_POWERUPTRUST,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ DCFUA31_ROOTDELAY,
+ DCFUA31_BASEDELAY,
+ DCF_A_ID,
+ DCFUA31_DESCRIPTION,
+ DCFUA31_FORMAT,
+ DCF_TYPE,
+ DCFUA31_MAXUNSYNC,
+ DCFUA31_SPEED,
+ DCFUA31_CFLAG,
+ DCFUA31_IFLAG,
+ DCFUA31_OFLAG,
+ DCFUA31_LFLAG,
+ DCFUA31_SAMPLES,
+ DCFUA31_KEEP
+ },
+ { /* mode 23 - like 7 with POWERUP trust */
+ MBG_FLAGS | PARSE_F_POWERUPTRUST,
+ GPS16X_POLL,
+ GPS16X_INIT,
+ NO_EVENT,
+ GPS16X_END,
+ GPS16X_MESSAGE,
+ GPS16X_DATA,
+ GPS16X_ROOTDELAY,
+ GPS16X_BASEDELAY,
+ GPS16X_ID,
+ GPS16X_DESCRIPTION,
+ GPS16X_FORMAT,
+ GPS_TYPE,
+ GPS16X_MAXUNSYNC,
+ GPS16X_SPEED,
+ GPS16X_CFLAG,
+ GPS16X_IFLAG,
+ GPS16X_OFLAG,
+ GPS16X_LFLAG,
+ GPS16X_SAMPLES,
+ GPS16X_KEEP
+ },
+ { /* mode 24 */
+ SEL240X_FLAGS,
+ SEL240X_POLL,
+ SEL240X_INIT,
+ NO_EVENT,
+ SEL240X_END,
+ NO_MESSAGE,
+ SEL240X_DATA,
+ SEL240X_ROOTDELAY,
+ SEL240X_BASEDELAY,
+ SEL240X_ID,
+ SEL240X_DESCRIPTION,
+ SEL240X_FORMAT,
+ GPS_TYPE,
+ SEL240X_MAXUNSYNC,
+ SEL240X_SPEED,
+ SEL240X_CFLAG,
+ SEL240X_IFLAG,
+ SEL240X_OFLAG,
+ SEL240X_LFLAG,
+ SEL240X_SAMPLES,
+ SEL240X_KEEP
+ },
+};
+
+static int ncltypes = sizeof(parse_clockinfo) / sizeof(struct parse_clockinfo);
+
+#define CLK_REALTYPE(x) ((int)(((x)->ttl) & 0x7F))
+#define CLK_TYPE(x) ((CLK_REALTYPE(x) >= ncltypes) ? ~0 : CLK_REALTYPE(x))
+#define CLK_UNIT(x) ((int)REFCLOCKUNIT(&(x)->srcadr))
+#define CLK_PPS(x) (((x)->ttl) & 0x80)
+
+/*
+ * Other constant stuff
+ */
+#define PARSEHSREFID 0x7f7f08ff /* 127.127.8.255 refid for hi strata */
+
+#define PARSESTATISTICS (60*60) /* output state statistics every hour */
+
+static int notice = 0;
+
+#define PARSE_STATETIME(parse, i) ((parse->generic->currentstatus == i) ? parse->statetime[i] + current_time - parse->lastchange : parse->statetime[i])
+
+static void parse_event (struct parseunit *, int);
+static void parse_process (struct parseunit *, parsetime_t *);
+static void clear_err (struct parseunit *, u_long);
+static int list_err (struct parseunit *, u_long);
+static char * l_mktime (u_long);
+
+/**===========================================================================
+ ** implementation error message regression module
+ **/
+static void
+clear_err(
+ struct parseunit *parse,
+ u_long lstate
+ )
+{
+ if (lstate == ERR_ALL)
+ {
+ int i;
+
+ for (i = 0; i < ERR_CNT; i++)
+ {
+ parse->errors[i].err_stage = err_tbl[i];
+ parse->errors[i].err_cnt = 0;
+ parse->errors[i].err_last = 0;
+ parse->errors[i].err_started = 0;
+ parse->errors[i].err_suppressed = 0;
+ }
+ }
+ else
+ {
+ parse->errors[lstate].err_stage = err_tbl[lstate];
+ parse->errors[lstate].err_cnt = 0;
+ parse->errors[lstate].err_last = 0;
+ parse->errors[lstate].err_started = 0;
+ parse->errors[lstate].err_suppressed = 0;
+ }
+}
+
+static int
+list_err(
+ struct parseunit *parse,
+ u_long lstate
+ )
+{
+ int do_it;
+ struct errorinfo *err = &parse->errors[lstate];
+
+ if (err->err_started == 0)
+ {
+ err->err_started = current_time;
+ }
+
+ do_it = (current_time - err->err_last) >= err->err_stage->err_delay;
+
+ if (do_it)
+ err->err_cnt++;
+
+ if (err->err_stage->err_count &&
+ (err->err_cnt >= err->err_stage->err_count))
+ {
+ err->err_stage++;
+ err->err_cnt = 0;
+ }
+
+ if (!err->err_cnt && do_it)
+ msyslog(LOG_INFO, "PARSE receiver #%d: interval for following error message class is at least %s",
+ CLK_UNIT(parse->peer), l_mktime(err->err_stage->err_delay));
+
+ if (!do_it)
+ err->err_suppressed++;
+ else
+ err->err_last = current_time;
+
+ if (do_it && err->err_suppressed)
+ {
+ msyslog(LOG_INFO, "PARSE receiver #%d: %ld message%s suppressed, error condition class persists for %s",
+ CLK_UNIT(parse->peer), err->err_suppressed, (err->err_suppressed == 1) ? " was" : "s where",
+ l_mktime(current_time - err->err_started));
+ err->err_suppressed = 0;
+ }
+
+ return do_it;
+}
+
+/*--------------------------------------------------
+ * mkreadable - make a printable ascii string (without
+ * embedded quotes so that the ntpq protocol isn't
+ * fooled
+ */
+#ifndef isprint
+#define isprint(_X_) (((_X_) > 0x1F) && ((_X_) < 0x7F))
+#endif
+
+static char *
+mkreadable(
+ char *buffer,
+ long blen,
+ const char *src,
+ u_long srclen,
+ int hex
+ )
+{
+ static const char ellipsis[] = "...";
+ char *b = buffer;
+ char *endb = NULL;
+
+ if (blen < 4)
+ return NULL; /* don't bother with mini buffers */
+
+ endb = buffer + blen - sizeof(ellipsis);
+
+ blen--; /* account for '\0' */
+
+ while (blen && srclen--)
+ {
+ if (!hex && /* no binary only */
+ (*src != '\\') && /* no plain \ */
+ (*src != '"') && /* no " */
+ isprint((int)*src)) /* only printables */
+ { /* they are easy... */
+ *buffer++ = *src++;
+ blen--;
+ }
+ else
+ {
+ if (blen < 4)
+ {
+ while (blen--)
+ {
+ *buffer++ = '.';
+ }
+ *buffer = '\0';
+ return b;
+ }
+ else
+ {
+ if (*src == '\\')
+ {
+ memcpy(buffer, "\\\\", 2);
+ buffer += 2;
+ blen -= 2;
+ src++;
+ }
+ else
+ {
+ snprintf(buffer, blen, "\\x%02x", *src++);
+ blen -= 4;
+ buffer += 4;
+ }
+ }
+ }
+ if (srclen && !blen && endb) /* overflow - set last chars to ... */
+ memcpy(endb, ellipsis, sizeof(ellipsis));
+ }
+
+ *buffer = '\0';
+ return b;
+}
+
+
+/*--------------------------------------------------
+ * mkascii - make a printable ascii string
+ * assumes (unless defined better) 7-bit ASCII
+ */
+static char *
+mkascii(
+ char *buffer,
+ long blen,
+ const char *src,
+ u_long srclen
+ )
+{
+ return mkreadable(buffer, blen, src, srclen, 0);
+}
+
+/**===========================================================================
+ ** implementation of i/o handling methods
+ ** (all STREAM, partial STREAM, user level)
+ **/
+
+/*
+ * define possible io handling methods
+ */
+#ifdef STREAM
+static int ppsclock_init (struct parseunit *);
+static int stream_init (struct parseunit *);
+static void stream_end (struct parseunit *);
+static int stream_enable (struct parseunit *);
+static int stream_disable (struct parseunit *);
+static int stream_setcs (struct parseunit *, parsectl_t *);
+static int stream_getfmt (struct parseunit *, parsectl_t *);
+static int stream_setfmt (struct parseunit *, parsectl_t *);
+static int stream_timecode (struct parseunit *, parsectl_t *);
+static void stream_receive (struct recvbuf *);
+#endif
+
+static int local_init (struct parseunit *);
+static void local_end (struct parseunit *);
+static int local_nop (struct parseunit *);
+static int local_setcs (struct parseunit *, parsectl_t *);
+static int local_getfmt (struct parseunit *, parsectl_t *);
+static int local_setfmt (struct parseunit *, parsectl_t *);
+static int local_timecode (struct parseunit *, parsectl_t *);
+static void local_receive (struct recvbuf *);
+static int local_input (struct recvbuf *);
+
+static bind_t io_bindings[] =
+{
+#ifdef STREAM
+ {
+ "parse STREAM",
+ stream_init,
+ stream_end,
+ stream_setcs,
+ stream_disable,
+ stream_enable,
+ stream_getfmt,
+ stream_setfmt,
+ stream_timecode,
+ stream_receive,
+ 0,
+ },
+ {
+ "ppsclock STREAM",
+ ppsclock_init,
+ local_end,
+ local_setcs,
+ local_nop,
+ local_nop,
+ local_getfmt,
+ local_setfmt,
+ local_timecode,
+ local_receive,
+ local_input,
+ },
+#endif
+ {
+ "normal",
+ local_init,
+ local_end,
+ local_setcs,
+ local_nop,
+ local_nop,
+ local_getfmt,
+ local_setfmt,
+ local_timecode,
+ local_receive,
+ local_input,
+ },
+ {
+ (char *)0,
+ }
+};
+
+#ifdef STREAM
+
+/*--------------------------------------------------
+ * ppsclock STREAM init
+ */
+static int
+ppsclock_init(
+ struct parseunit *parse
+ )
+{
+ static char m1[] = "ppsclocd";
+ static char m2[] = "ppsclock";
+
+ /*
+ * now push the parse streams module
+ * it will ensure exclusive access to the device
+ */
+ if (ioctl(parse->ppsfd, I_PUSH, (caddr_t)m1) == -1 &&
+ ioctl(parse->ppsfd, I_PUSH, (caddr_t)m2) == -1)
+ {
+ if (errno != EINVAL)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: ppsclock_init: ioctl(fd, I_PUSH, \"ppsclock\"): %m",
+ CLK_UNIT(parse->peer));
+ }
+ return 0;
+ }
+ if (!local_init(parse))
+ {
+ (void)ioctl(parse->ppsfd, I_POP, (caddr_t)0);
+ return 0;
+ }
+
+ parse->flags |= PARSE_PPSCLOCK;
+ return 1;
+}
+
+/*--------------------------------------------------
+ * parse STREAM init
+ */
+static int
+stream_init(
+ struct parseunit *parse
+ )
+{
+ static char m1[] = "parse";
+ /*
+ * now push the parse streams module
+ * to test whether it is there (neat interface 8-( )
+ */
+ if (ioctl(parse->generic->io.fd, I_PUSH, (caddr_t)m1) == -1)
+ {
+ if (errno != EINVAL) /* accept non-existence */
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CLK_UNIT(parse->peer));
+ }
+ return 0;
+ }
+ else
+ {
+ while(ioctl(parse->generic->io.fd, I_POP, (caddr_t)0) == 0)
+ /* empty loop */;
+
+ /*
+ * now push it a second time after we have removed all
+ * module garbage
+ */
+ if (ioctl(parse->generic->io.fd, I_PUSH, (caddr_t)m1) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+}
+
+/*--------------------------------------------------
+ * parse STREAM end
+ */
+static void
+stream_end(
+ struct parseunit *parse
+ )
+{
+ while(ioctl(parse->generic->io.fd, I_POP, (caddr_t)0) == 0)
+ /* empty loop */;
+}
+
+/*--------------------------------------------------
+ * STREAM setcs
+ */
+static int
+stream_setcs(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_SETCS;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_setcs: ioctl(fd, I_STR, PARSEIOC_SETCS): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM enable
+ */
+static int
+stream_enable(
+ struct parseunit *parse
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_ENABLE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)0;
+ strioc.ic_len = 0;
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_enable: ioctl(fd, I_STR, PARSEIOC_ENABLE): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ parse->generic->io.clock_recv = stream_receive; /* ok - parse input in kernel */
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM disable
+ */
+static int
+stream_disable(
+ struct parseunit *parse
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_DISABLE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)0;
+ strioc.ic_len = 0;
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_disable: ioctl(fd, I_STR, PARSEIOC_DISABLE): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ parse->generic->io.clock_recv = local_receive; /* ok - parse input in daemon */
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM getfmt
+ */
+static int
+stream_getfmt(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_GETFMT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: ioctl(fd, I_STR, PARSEIOC_GETFMT): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM setfmt
+ */
+static int
+stream_setfmt(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_SETFMT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_setfmt: ioctl(fd, I_STR, PARSEIOC_SETFMT): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ return 1;
+}
+
+
+/*--------------------------------------------------
+ * STREAM timecode
+ */
+static int
+stream_timecode(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_TIMECODE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_timecode: ioctl(fd, I_STR, PARSEIOC_TIMECODE): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ clear_err(parse, ERR_INTERNAL);
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM receive
+ */
+static void
+stream_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct parseunit * parse;
+ parsetime_t parsetime;
+
+ parse = (struct parseunit *)rbufp->recv_peer->procptr->unitptr;
+ if (!parse->peer)
+ return;
+
+ if (rbufp->recv_length != sizeof(parsetime_t))
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR,"PARSE receiver #%d: stream_receive: bad size (got %d expected %d)",
+ CLK_UNIT(parse->peer), rbufp->recv_length, (int)sizeof(parsetime_t));
+ parse_event(parse, CEVNT_BADREPLY);
+ return;
+ }
+ clear_err(parse, ERR_BADIO);
+
+ memmove((caddr_t)&parsetime,
+ (caddr_t)rbufp->recv_buffer,
+ sizeof(parsetime_t));
+
+#ifdef DEBUG
+ if (debug > 3)
+ {
+ printf("PARSE receiver #%d: status %06x, state %08x, time %lx.%08lx, stime %lx.%08lx, ptime %lx.%08lx\n",
+ CLK_UNIT(parse->peer),
+ (unsigned int)parsetime.parse_status,
+ (unsigned int)parsetime.parse_state,
+ (unsigned long)parsetime.parse_time.tv.tv_sec,
+ (unsigned long)parsetime.parse_time.tv.tv_usec,
+ (unsigned long)parsetime.parse_stime.tv.tv_sec,
+ (unsigned long)parsetime.parse_stime.tv.tv_usec,
+ (unsigned long)parsetime.parse_ptime.tv.tv_sec,
+ (unsigned long)parsetime.parse_ptime.tv.tv_usec);
+ }
+#endif
+
+ /*
+ * switch time stamp world - be sure to normalize small usec field
+ * errors.
+ */
+
+ parsetime.parse_stime.fp = tval_stamp_to_lfp(parsetime.parse_stime.tv);
+
+ if (PARSE_TIMECODE(parsetime.parse_state))
+ {
+ parsetime.parse_time.fp = tval_stamp_to_lfp(parsetime.parse_time.tv);
+ }
+
+ if (PARSE_PPS(parsetime.parse_state))
+ {
+ parsetime.parse_ptime.fp = tval_stamp_to_lfp(parsetime.parse_ptime.tv);
+ }
+
+ parse_process(parse, &parsetime);
+}
+#endif
+
+/*--------------------------------------------------
+ * local init
+ */
+static int
+local_init(
+ struct parseunit *parse
+ )
+{
+ return parse_ioinit(&parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local end
+ */
+static void
+local_end(
+ struct parseunit *parse
+ )
+{
+ parse_ioend(&parse->parseio);
+}
+
+
+/*--------------------------------------------------
+ * local nop
+ */
+static int
+local_nop(
+ struct parseunit *parse
+ )
+{
+ return 1;
+}
+
+/*--------------------------------------------------
+ * local setcs
+ */
+static int
+local_setcs(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ return parse_setcs(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local getfmt
+ */
+static int
+local_getfmt(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ return parse_getfmt(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local setfmt
+ */
+static int
+local_setfmt(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ return parse_setfmt(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local timecode
+ */
+static int
+local_timecode(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ return parse_timecode(tcl, &parse->parseio);
+}
+
+
+/*--------------------------------------------------
+ * local input
+ */
+static int
+local_input(
+ struct recvbuf *rbufp
+ )
+{
+ struct parseunit * parse;
+
+ int count;
+ unsigned char *s;
+ timestamp_t ts;
+
+ parse = (struct parseunit *)rbufp->recv_peer->procptr->unitptr;
+ if (!parse->peer)
+ return 0;
+
+ /*
+ * eat all characters, parsing then and feeding complete samples
+ */
+ count = rbufp->recv_length;
+ s = (unsigned char *)rbufp->recv_buffer;
+ ts.fp = rbufp->recv_time;
+
+ while (count--)
+ {
+ if (parse_ioread(&parse->parseio, (unsigned int)(*s++), &ts))
+ {
+ struct recvbuf *buf;
+
+ /*
+ * got something good to eat
+ */
+ if (!PARSE_PPS(parse->parseio.parse_dtime.parse_state))
+ {
+#ifdef HAVE_PPSAPI
+ if (parse->flags & PARSE_PPSCLOCK)
+ {
+ struct timespec pps_timeout;
+ pps_info_t pps_info;
+
+ pps_timeout.tv_sec = 0;
+ pps_timeout.tv_nsec = 0;
+
+ if (time_pps_fetch(parse->atom.handle, PPS_TSFMT_TSPEC, &pps_info,
+ &pps_timeout) == 0)
+ {
+ if (pps_info.assert_sequence + pps_info.clear_sequence != parse->ppsserial)
+ {
+ double dtemp;
+
+ struct timespec pts;
+ /*
+ * add PPS time stamp if available via ppsclock module
+ * and not supplied already.
+ */
+ if (parse->flags & PARSE_CLEAR)
+ pts = pps_info.clear_timestamp;
+ else
+ pts = pps_info.assert_timestamp;
+
+ parse->parseio.parse_dtime.parse_ptime.fp.l_ui = pts.tv_sec + JAN_1970;
+
+ dtemp = pts.tv_nsec / 1e9;
+ if (dtemp < 0.) {
+ dtemp += 1;
+ parse->parseio.parse_dtime.parse_ptime.fp.l_ui--;
+ }
+ if (dtemp > 1.) {
+ dtemp -= 1;
+ parse->parseio.parse_dtime.parse_ptime.fp.l_ui++;
+ }
+ parse->parseio.parse_dtime.parse_ptime.fp.l_uf = dtemp * FRAC;
+
+ parse->parseio.parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+#ifdef DEBUG
+ if (debug > 3)
+ {
+ printf(
+ "parse: local_receive: fd %d PPSAPI seq %ld - PPS %s\n",
+ rbufp->fd,
+ (long)pps_info.assert_sequence + (long)pps_info.clear_sequence,
+ lfptoa(&parse->parseio.parse_dtime.parse_ptime.fp, 6));
+ }
+#endif
+ }
+#ifdef DEBUG
+ else
+ {
+ if (debug > 3)
+ {
+ printf(
+ "parse: local_receive: fd %d PPSAPI seq assert %ld, seq clear %ld - NO PPS event\n",
+ rbufp->fd,
+ (long)pps_info.assert_sequence, (long)pps_info.clear_sequence);
+ }
+ }
+#endif
+ parse->ppsserial = pps_info.assert_sequence + pps_info.clear_sequence;
+ }
+#ifdef DEBUG
+ else
+ {
+ if (debug > 3)
+ {
+ printf(
+ "parse: local_receive: fd %d PPSAPI time_pps_fetch errno = %d\n",
+ rbufp->fd,
+ errno);
+ }
+ }
+#endif
+ }
+#else
+#ifdef TIOCDCDTIMESTAMP
+ struct timeval dcd_time;
+
+ if (ioctl(parse->ppsfd, TIOCDCDTIMESTAMP, &dcd_time) != -1)
+ {
+ l_fp tstmp;
+
+ TVTOTS(&dcd_time, &tstmp);
+ tstmp.l_ui += JAN_1970;
+ L_SUB(&ts.fp, &tstmp);
+ if (ts.fp.l_ui == 0)
+ {
+#ifdef DEBUG
+ if (debug)
+ {
+ printf(
+ "parse: local_receive: fd %d DCDTIMESTAMP %s\n",
+ parse->ppsfd,
+ lfptoa(&tstmp, 6));
+ printf(" sigio %s\n",
+ lfptoa(&ts.fp, 6));
+ }
+#endif
+ parse->parseio.parse_dtime.parse_ptime.fp = tstmp;
+ parse->parseio.parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+ }
+ }
+#else /* TIOCDCDTIMESTAMP */
+#if defined(HAVE_STRUCT_PPSCLOCKEV) && (defined(HAVE_CIOGETEV) || defined(HAVE_TIOCGPPSEV))
+ if (parse->flags & PARSE_PPSCLOCK)
+ {
+ l_fp tts;
+ struct ppsclockev ev;
+
+#ifdef HAVE_CIOGETEV
+ if (ioctl(parse->ppsfd, CIOGETEV, (caddr_t)&ev) == 0)
+#endif
+#ifdef HAVE_TIOCGPPSEV
+ if (ioctl(parse->ppsfd, TIOCGPPSEV, (caddr_t)&ev) == 0)
+#endif
+ {
+ if (ev.serial != parse->ppsserial)
+ {
+ /*
+ * add PPS time stamp if available via ppsclock module
+ * and not supplied already.
+ */
+ if (!buftvtots((const char *)&ev.tv, &tts))
+ {
+ ERR(ERR_BADDATA)
+ msyslog(LOG_ERR,"parse: local_receive: timestamp conversion error (buftvtots) (ppsclockev.tv)");
+ }
+ else
+ {
+ parse->parseio.parse_dtime.parse_ptime.fp = tts;
+ parse->parseio.parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+ }
+ }
+ parse->ppsserial = ev.serial;
+ }
+ }
+#endif
+#endif /* TIOCDCDTIMESTAMP */
+#endif /* !HAVE_PPSAPI */
+ }
+ if (count)
+ { /* simulate receive */
+ buf = get_free_recv_buffer();
+ if (buf != NULL) {
+ memmove((caddr_t)buf->recv_buffer,
+ (caddr_t)&parse->parseio.parse_dtime,
+ sizeof(parsetime_t));
+ buf->recv_length = sizeof(parsetime_t);
+ buf->recv_time = rbufp->recv_time;
+#ifndef HAVE_IO_COMPLETION_PORT
+ buf->srcadr = rbufp->srcadr;
+#endif
+ buf->dstadr = rbufp->dstadr;
+ buf->receiver = rbufp->receiver;
+ buf->fd = rbufp->fd;
+ buf->X_from_where = rbufp->X_from_where;
+ parse->generic->io.recvcount++;
+ packets_received++;
+ add_full_recv_buffer(buf);
+#ifdef HAVE_IO_COMPLETION_PORT
+ SetEvent(WaitableIoEventHandle);
+#endif
+ }
+ parse_iodone(&parse->parseio);
+ }
+ else
+ {
+ memmove((caddr_t)rbufp->recv_buffer,
+ (caddr_t)&parse->parseio.parse_dtime,
+ sizeof(parsetime_t));
+ parse_iodone(&parse->parseio);
+ rbufp->recv_length = sizeof(parsetime_t);
+ return 1; /* got something & in place return */
+ }
+ }
+ }
+ return 0; /* nothing to pass up */
+}
+
+/*--------------------------------------------------
+ * local receive
+ */
+static void
+local_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct parseunit * parse;
+ parsetime_t parsetime;
+
+ parse = (struct parseunit *)rbufp->recv_peer->procptr->unitptr;
+ if (!parse->peer)
+ return;
+
+ if (rbufp->recv_length != sizeof(parsetime_t))
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR,"PARSE receiver #%d: local_receive: bad size (got %d expected %d)",
+ CLK_UNIT(parse->peer), rbufp->recv_length, (int)sizeof(parsetime_t));
+ parse_event(parse, CEVNT_BADREPLY);
+ return;
+ }
+ clear_err(parse, ERR_BADIO);
+
+ memmove((caddr_t)&parsetime,
+ (caddr_t)rbufp->recv_buffer,
+ sizeof(parsetime_t));
+
+#ifdef DEBUG
+ if (debug > 3)
+ {
+ printf("PARSE receiver #%d: status %06x, state %08x, time(fp) %lx.%08lx, stime(fp) %lx.%08lx, ptime(fp) %lx.%08lx\n",
+ CLK_UNIT(parse->peer),
+ (unsigned int)parsetime.parse_status,
+ (unsigned int)parsetime.parse_state,
+ (unsigned long)parsetime.parse_time.fp.l_ui,
+ (unsigned long)parsetime.parse_time.fp.l_uf,
+ (unsigned long)parsetime.parse_stime.fp.l_ui,
+ (unsigned long)parsetime.parse_stime.fp.l_uf,
+ (unsigned long)parsetime.parse_ptime.fp.l_ui,
+ (unsigned long)parsetime.parse_ptime.fp.l_uf);
+ }
+#endif
+
+ parse_process(parse, &parsetime);
+}
+
+/*--------------------------------------------------
+ * init_iobinding - find and initialize lower layers
+ */
+static bind_t *
+init_iobinding(
+ struct parseunit *parse
+ )
+{
+ bind_t *b = io_bindings;
+
+ while (b->bd_description != (char *)0)
+ {
+ if ((*b->bd_init)(parse))
+ {
+ return b;
+ }
+ b++;
+ }
+ return (bind_t *)0;
+}
+
+/**===========================================================================
+ ** support routines
+ **/
+
+/*--------------------------------------------------
+ * convert a flag field to a string
+ */
+static char *
+parsestate(
+ u_long lstate,
+ char *buffer,
+ int size
+ )
+{
+ static struct bits
+ {
+ u_long bit;
+ const char *name;
+ } flagstrings[] =
+ {
+ { PARSEB_ANNOUNCE, "DST SWITCH WARNING" },
+ { PARSEB_POWERUP, "NOT SYNCHRONIZED" },
+ { PARSEB_NOSYNC, "TIME CODE NOT CONFIRMED" },
+ { PARSEB_DST, "DST" },
+ { PARSEB_UTC, "UTC DISPLAY" },
+ { PARSEB_LEAPADD, "LEAP ADD WARNING" },
+ { PARSEB_LEAPDEL, "LEAP DELETE WARNING" },
+ { PARSEB_LEAPSECOND, "LEAP SECOND" },
+ { PARSEB_ALTERNATE, "ALTERNATE ANTENNA" },
+ { PARSEB_TIMECODE, "TIME CODE" },
+ { PARSEB_PPS, "PPS" },
+ { PARSEB_POSITION, "POSITION" },
+ { 0 }
+ };
+
+ static struct sbits
+ {
+ u_long bit;
+ const char *name;
+ } sflagstrings[] =
+ {
+ { PARSEB_S_LEAP, "LEAP INDICATION" },
+ { PARSEB_S_PPS, "PPS SIGNAL" },
+ { PARSEB_S_ANTENNA, "ANTENNA" },
+ { PARSEB_S_POSITION, "POSITION" },
+ { 0 }
+ };
+ int i;
+ char *s, *t;
+
+
+ *buffer = '\0';
+ s = t = buffer;
+
+ i = 0;
+ while (flagstrings[i].bit)
+ {
+ if (flagstrings[i].bit & lstate)
+ {
+ if (s != t)
+ strlcpy(t, "; ", BUFFER_SIZES(buffer, t, size));
+ if (strlcat(t, flagstrings[i].name, BUFFER_SIZES(buffer, t, size)) <
+ BUFFER_SIZES(buffer, t, size))
+ t += strlen(t);
+ }
+ i++;
+ }
+
+ if (lstate & (PARSEB_S_LEAP|PARSEB_S_ANTENNA|PARSEB_S_PPS|PARSEB_S_POSITION))
+ {
+ if (s != t &&
+ strlcpy(t, "; ", BUFFER_SIZES(buffer, t, size)) <
+ BUFFER_SIZES(buffer, t, size))
+ t += strlen(t);
+
+ if (strlcpy(t, "(", BUFFER_SIZES(buffer, t, size)) <
+ BUFFER_SIZES(buffer, t, size))
+ s = t = t + strlen(t);
+
+ i = 0;
+ while (sflagstrings[i].bit)
+ {
+ if (sflagstrings[i].bit & lstate)
+ {
+ if (t != s &&
+ strlcpy(t, "; ", BUFFER_SIZES(buffer, t, size)) <
+ BUFFER_SIZES(buffer, t, size))
+ t += 2;
+
+ if (strlcpy(t, sflagstrings[i].name, BUFFER_SIZES(buffer, t, size)) <
+ BUFFER_SIZES(buffer, t, size))
+ t += strlen(t);
+ }
+ i++;
+ }
+ strlcpy(t, ")", BUFFER_SIZES(buffer, t, size));
+ }
+ return buffer;
+}
+
+/*--------------------------------------------------
+ * convert a status flag field to a string
+ */
+static char *
+parsestatus(
+ u_long lstate,
+ char *buffer,
+ int size
+ )
+{
+ static struct bits
+ {
+ u_long bit;
+ const char *name;
+ } flagstrings[] =
+ {
+ { CVT_OK, "CONVERSION SUCCESSFUL" },
+ { CVT_NONE, "NO CONVERSION" },
+ { CVT_FAIL, "CONVERSION FAILED" },
+ { CVT_BADFMT, "ILLEGAL FORMAT" },
+ { CVT_BADDATE, "DATE ILLEGAL" },
+ { CVT_BADTIME, "TIME ILLEGAL" },
+ { CVT_ADDITIONAL, "ADDITIONAL DATA" },
+ { 0 }
+ };
+ int i;
+
+ *buffer = '\0';
+
+ i = 0;
+ while (flagstrings[i].bit)
+ {
+ if (flagstrings[i].bit & lstate)
+ {
+ if (buffer[0])
+ strlcat(buffer, "; ", size);
+ strlcat(buffer, flagstrings[i].name, size);
+ }
+ i++;
+ }
+
+ return buffer;
+}
+
+/*--------------------------------------------------
+ * convert a clock status flag field to a string
+ */
+static const char *
+clockstatus(
+ u_long lstate
+ )
+{
+ static char buffer[20];
+ static struct status
+ {
+ u_long value;
+ const char *name;
+ } flagstrings[] =
+ {
+ { CEVNT_NOMINAL, "NOMINAL" },
+ { CEVNT_TIMEOUT, "NO RESPONSE" },
+ { CEVNT_BADREPLY,"BAD FORMAT" },
+ { CEVNT_FAULT, "FAULT" },
+ { CEVNT_PROP, "PROPAGATION DELAY" },
+ { CEVNT_BADDATE, "ILLEGAL DATE" },
+ { CEVNT_BADTIME, "ILLEGAL TIME" },
+ { (unsigned)~0L }
+ };
+ int i;
+
+ i = 0;
+ while (flagstrings[i].value != ~0)
+ {
+ if (flagstrings[i].value == lstate)
+ {
+ return flagstrings[i].name;
+ }
+ i++;
+ }
+
+ snprintf(buffer, sizeof(buffer), "unknown #%ld", (u_long)lstate);
+
+ return buffer;
+}
+
+
+/*--------------------------------------------------
+ * l_mktime - make representation of a relative time
+ */
+static char *
+l_mktime(
+ u_long delta
+ )
+{
+ u_long tmp, m, s;
+ static char buffer[40];
+ char *t;
+
+ buffer[0] = '\0';
+
+ if ((tmp = delta / (60*60*24)) != 0)
+ {
+ snprintf(buffer, BUFFER_SIZE(buffer, buffer), "%ldd+", (u_long)tmp);
+ delta -= tmp * 60*60*24;
+ }
+
+ s = delta % 60;
+ delta /= 60;
+ m = delta % 60;
+ delta /= 60;
+
+ t = buffer + strlen(buffer);
+
+ snprintf(t, BUFFER_SIZE(buffer, t), "%02d:%02d:%02d",
+ (int)delta, (int)m, (int)s);
+
+ return buffer;
+}
+
+
+/*--------------------------------------------------
+ * parse_statistics - list summary of clock states
+ */
+static void
+parse_statistics(
+ struct parseunit *parse
+ )
+{
+ int i;
+
+ NLOG(NLOG_CLOCKSTATIST) /* conditional if clause for conditional syslog */
+ {
+ msyslog(LOG_INFO, "PARSE receiver #%d: running time: %s",
+ CLK_UNIT(parse->peer),
+ l_mktime(current_time - parse->generic->timestarted));
+
+ msyslog(LOG_INFO, "PARSE receiver #%d: current status: %s",
+ CLK_UNIT(parse->peer),
+ clockstatus(parse->generic->currentstatus));
+
+ for (i = 0; i <= CEVNT_MAX; i++)
+ {
+ u_long s_time;
+ u_long percent, d = current_time - parse->generic->timestarted;
+
+ percent = s_time = PARSE_STATETIME(parse, i);
+
+ while (((u_long)(~0) / 10000) < percent)
+ {
+ percent /= 10;
+ d /= 10;
+ }
+
+ if (d)
+ percent = (percent * 10000) / d;
+ else
+ percent = 10000;
+
+ if (s_time)
+ msyslog(LOG_INFO, "PARSE receiver #%d: state %18s: %13s (%3ld.%02ld%%)",
+ CLK_UNIT(parse->peer),
+ clockstatus((unsigned int)i),
+ l_mktime(s_time),
+ percent / 100, percent % 100);
+ }
+ }
+}
+
+/*--------------------------------------------------
+ * cparse_statistics - wrapper for statistics call
+ */
+static void
+cparse_statistics(
+ struct parseunit *parse
+ )
+{
+ if (parse->laststatistic + PARSESTATISTICS < current_time)
+ parse_statistics(parse);
+ parse->laststatistic = current_time;
+}
+
+/**===========================================================================
+ ** ntp interface routines
+ **/
+
+/*--------------------------------------------------
+ * parse_shutdown - shut down a PARSE clock
+ */
+static void
+parse_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = NULL;
+
+ if (peer && peer->procptr)
+ parse = peer->procptr->unitptr;
+
+ if (!parse)
+ {
+ /* nothing to clean up */
+ return;
+ }
+
+ if (!parse->peer)
+ {
+ msyslog(LOG_INFO, "PARSE receiver #%d: INTERNAL ERROR - unit already inactive - shutdown ignored", unit);
+ return;
+ }
+
+#ifdef HAVE_PPSAPI
+ if (parse->flags & PARSE_PPSCLOCK)
+ {
+ (void)time_pps_destroy(parse->atom.handle);
+ }
+#endif
+ if (parse->generic->io.fd != parse->ppsfd && parse->ppsfd != -1)
+ (void)closeserial(parse->ppsfd); /* close separate PPS source */
+
+ /*
+ * print statistics a last time and
+ * stop statistics machine
+ */
+ parse_statistics(parse);
+
+ if (parse->parse_type->cl_end)
+ {
+ parse->parse_type->cl_end(parse);
+ }
+
+ /*
+ * cleanup before leaving this world
+ */
+ if (parse->binding)
+ PARSE_END(parse);
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ io_closeclock(&parse->generic->io);
+
+ free_varlist(parse->kv);
+
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" removed",
+ CLK_UNIT(parse->peer), parse->parse_type->cl_description);
+
+ parse->peer = (struct peer *)0; /* unused now */
+ peer->procptr->unitptr = (caddr_t)0;
+ free(parse);
+}
+
+#ifdef HAVE_PPSAPI
+/*----------------------------------------
+ * set up HARDPPS via PPSAPI
+ */
+static void
+parse_hardpps(
+ struct parseunit *parse,
+ int mode
+ )
+{
+ if (parse->hardppsstate == mode)
+ return;
+
+ if (CLK_PPS(parse->peer) && (parse->flags & PARSE_PPSKERNEL)) {
+ int i = 0;
+
+ if (mode == PARSE_HARDPPS_ENABLE)
+ {
+ if (parse->flags & PARSE_CLEAR)
+ i = PPS_CAPTURECLEAR;
+ else
+ i = PPS_CAPTUREASSERT;
+ }
+
+ if (time_pps_kcbind(parse->atom.handle, PPS_KC_HARDPPS, i,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR, "PARSE receiver #%d: time_pps_kcbind failed: %m",
+ CLK_UNIT(parse->peer));
+ } else {
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO, "PARSE receiver #%d: kernel PPS synchronisation %sabled",
+ CLK_UNIT(parse->peer), (mode == PARSE_HARDPPS_ENABLE) ? "en" : "dis");
+ /*
+ * tell the rest, that we have a kernel PPS source, iff we ever enable HARDPPS
+ */
+ if (mode == PARSE_HARDPPS_ENABLE)
+ hardpps_enable = 1;
+ }
+ }
+
+ parse->hardppsstate = mode;
+}
+
+/*----------------------------------------
+ * set up PPS via PPSAPI
+ */
+static int
+parse_ppsapi(
+ struct parseunit *parse
+ )
+{
+ int cap, mode_ppsoffset;
+ char *cp;
+
+ parse->flags &= ~PARSE_PPSCLOCK;
+
+ /*
+ * collect PPSAPI offset capability - should move into generic handling
+ */
+ if (time_pps_getcap(parse->atom.handle, &cap) < 0) {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_ppsapi: time_pps_getcap failed: %m",
+ CLK_UNIT(parse->peer));
+
+ return 0;
+ }
+
+ /*
+ * initialize generic PPSAPI interface
+ *
+ * we leave out CLK_FLAG3 as time_pps_kcbind()
+ * is handled here for now. Ideally this should also
+ * be part of the generic PPSAPI interface
+ */
+ if (!refclock_params(parse->flags & (CLK_FLAG1|CLK_FLAG2|CLK_FLAG4), &parse->atom))
+ return 0;
+
+ /* nb. only turn things on, if someone else has turned something
+ * on before we get here, leave it alone!
+ */
+
+ if (parse->flags & PARSE_CLEAR) {
+ cp = "CLEAR";
+ mode_ppsoffset = PPS_OFFSETCLEAR;
+ } else {
+ cp = "ASSERT";
+ mode_ppsoffset = PPS_OFFSETASSERT;
+ }
+
+ msyslog(LOG_INFO, "PARSE receiver #%d: initializing PPS to %s",
+ CLK_UNIT(parse->peer), cp);
+
+ if (!(mode_ppsoffset & cap)) {
+ msyslog(LOG_WARNING, "PARSE receiver #%d: Cannot set PPS_%sCLEAR, this will increase jitter (PPS API capabilities=0x%x)",
+ CLK_UNIT(parse->peer), cp, cap);
+ mode_ppsoffset = 0;
+ } else {
+ if (mode_ppsoffset == PPS_OFFSETCLEAR)
+ {
+ parse->atom.pps_params.clear_offset.tv_sec = -parse->ppsphaseadjust;
+ parse->atom.pps_params.clear_offset.tv_nsec = -1e9*(parse->ppsphaseadjust - (long)parse->ppsphaseadjust);
+ }
+
+ if (mode_ppsoffset == PPS_OFFSETASSERT)
+ {
+ parse->atom.pps_params.assert_offset.tv_sec = -parse->ppsphaseadjust;
+ parse->atom.pps_params.assert_offset.tv_nsec = -1e9*(parse->ppsphaseadjust - (long)parse->ppsphaseadjust);
+ }
+ }
+
+ parse->atom.pps_params.mode |= mode_ppsoffset;
+
+ if (time_pps_setparams(parse->atom.handle, &parse->atom.pps_params) < 0) {
+ msyslog(LOG_ERR, "PARSE receiver #%d: FAILED set PPS parameters: %m",
+ CLK_UNIT(parse->peer));
+ return 0;
+ }
+
+ parse->flags |= PARSE_PPSCLOCK;
+ return 1;
+}
+#else
+#define parse_hardpps(_PARSE_, _MODE_) /* empty */
+#endif
+
+/*--------------------------------------------------
+ * parse_start - open the PARSE devices and initialize data for processing
+ */
+static int
+parse_start(
+ int sysunit,
+ struct peer *peer
+ )
+{
+ u_int unit;
+ int fd232;
+#ifdef HAVE_TERMIOS
+ struct termios tio; /* NEEDED FOR A LONG TIME ! */
+#endif
+#ifdef HAVE_SYSV_TTYS
+ struct termio tio; /* NEEDED FOR A LONG TIME ! */
+#endif
+ struct parseunit * parse;
+ char parsedev[sizeof(PARSEDEVICE)+20];
+ char parseppsdev[sizeof(PARSEPPSDEVICE)+20];
+ parsectl_t tmp_ctl;
+ u_int type;
+
+ /*
+ * get out Copyright information once
+ */
+ if (!notice)
+ {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "NTP PARSE support: Copyright (c) 1989-2009, Frank Kardel");
+ notice = 1;
+ }
+
+ type = CLK_TYPE(peer);
+ unit = CLK_UNIT(peer);
+
+ if ((type == ~0) || (parse_clockinfo[type].cl_description == (char *)0))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: unsupported clock type %d (max %d)",
+ unit, CLK_REALTYPE(peer), ncltypes-1);
+ return 0;
+ }
+
+ /*
+ * Unit okay, attempt to open the device.
+ */
+ (void) snprintf(parsedev, sizeof(parsedev), PARSEDEVICE, unit);
+ (void) snprintf(parseppsdev, sizeof(parsedev), PARSEPPSDEVICE, unit);
+
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+#ifndef O_NONBLOCK
+#define O_NONBLOCK 0
+#endif
+
+ fd232 = tty_open(parsedev, O_RDWR | O_NOCTTY | O_NONBLOCK, 0777);
+
+ if (fd232 == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: open of %s failed: %m", unit, parsedev);
+ return 0;
+ }
+
+ parse = emalloc_zero(sizeof(*parse));
+
+ parse->generic = peer->procptr; /* link up */
+ parse->generic->unitptr = (caddr_t)parse; /* link down */
+
+ /*
+ * Set up the structures
+ */
+ parse->generic->timestarted = current_time;
+ parse->lastchange = current_time;
+
+ parse->flags = 0;
+ parse->pollneeddata = 0;
+ parse->laststatistic = current_time;
+ parse->lastformat = (unsigned short)~0; /* assume no format known */
+ parse->timedata.parse_status = (unsigned short)~0; /* be sure to mark initial status change */
+ parse->lastmissed = 0; /* assume got everything */
+ parse->ppsserial = 0;
+ parse->ppsfd = -1;
+ parse->localdata = (void *)0;
+ parse->localstate = 0;
+ parse->kv = (struct ctl_var *)0;
+
+ clear_err(parse, ERR_ALL);
+
+ parse->parse_type = &parse_clockinfo[type];
+
+ parse->maxunsync = parse->parse_type->cl_maxunsync;
+
+ parse->generic->fudgetime1 = parse->parse_type->cl_basedelay;
+
+ parse->generic->fudgetime2 = 0.0;
+ parse->ppsphaseadjust = parse->generic->fudgetime2;
+
+ parse->generic->clockdesc = parse->parse_type->cl_description;
+
+ peer->rootdelay = parse->parse_type->cl_rootdelay;
+ peer->sstclktype = parse->parse_type->cl_type;
+ peer->precision = sys_precision;
+
+ peer->stratum = STRATUM_REFCLOCK;
+
+ if (peer->stratum <= 1)
+ memmove((char *)&parse->generic->refid, parse->parse_type->cl_id, 4);
+ else
+ parse->generic->refid = htonl(PARSEHSREFID);
+
+ parse->generic->io.fd = fd232;
+
+ parse->peer = peer; /* marks it also as busy */
+
+ /*
+ * configure terminal line
+ */
+ if (TTY_GETATTR(fd232, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcgetattr(%d, &tio): %m", unit, fd232);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+ else
+ {
+#ifndef _PC_VDISABLE
+ memset((char *)tio.c_cc, 0, sizeof(tio.c_cc));
+#else
+ int disablec;
+ errno = 0; /* pathconf can deliver -1 without changing errno ! */
+
+ disablec = fpathconf(parse->generic->io.fd, _PC_VDISABLE);
+ if (disablec == -1 && errno)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: fpathconf(fd, _PC_VDISABLE): %m", CLK_UNIT(parse->peer));
+ memset((char *)tio.c_cc, 0, sizeof(tio.c_cc)); /* best guess */
+ }
+ else
+ if (disablec != -1)
+ memset((char *)tio.c_cc, disablec, sizeof(tio.c_cc));
+#endif
+
+#if defined (VMIN) || defined(VTIME)
+ if ((parse_clockinfo[type].cl_lflag & ICANON) == 0)
+ {
+#ifdef VMIN
+ tio.c_cc[VMIN] = 1;
+#endif
+#ifdef VTIME
+ tio.c_cc[VTIME] = 0;
+#endif
+ }
+#endif
+
+ tio.c_cflag = parse_clockinfo[type].cl_cflag;
+ tio.c_iflag = parse_clockinfo[type].cl_iflag;
+ tio.c_oflag = parse_clockinfo[type].cl_oflag;
+ tio.c_lflag = parse_clockinfo[type].cl_lflag;
+
+
+#ifdef HAVE_TERMIOS
+ if ((cfsetospeed(&tio, parse_clockinfo[type].cl_speed) == -1) ||
+ (cfsetispeed(&tio, parse_clockinfo[type].cl_speed) == -1))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcset{i,o}speed(&tio, speed): %m", unit);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+#else
+ tio.c_cflag |= parse_clockinfo[type].cl_speed;
+#endif
+
+ /*
+ * set up pps device
+ * if the PARSEPPSDEVICE can be opened that will be used
+ * for PPS else PARSEDEVICE will be used
+ */
+ parse->ppsfd = tty_open(parseppsdev, O_RDWR | O_NOCTTY | O_NONBLOCK, 0777);
+
+ if (parse->ppsfd == -1)
+ {
+ parse->ppsfd = fd232;
+ }
+
+/*
+ * Linux PPS - the old way
+ */
+#if defined(HAVE_TIO_SERIAL_STUFF) /* Linux hack: define PPS interface */
+ {
+ struct serial_struct ss;
+ if (ioctl(parse->ppsfd, TIOCGSERIAL, &ss) < 0 ||
+ (
+#ifdef ASYNC_LOW_LATENCY
+ ss.flags |= ASYNC_LOW_LATENCY,
+#endif
+#ifndef HAVE_PPSAPI
+#ifdef ASYNC_PPS_CD_NEG
+ ss.flags |= ASYNC_PPS_CD_NEG,
+#endif
+#endif
+ ioctl(parse->ppsfd, TIOCSSERIAL, &ss)) < 0) {
+ msyslog(LOG_NOTICE, "refclock_parse: TIOCSSERIAL fd %d, %m", parse->ppsfd);
+ msyslog(LOG_NOTICE,
+ "refclock_parse: optional PPS processing not available");
+ } else {
+ parse->flags |= PARSE_PPSCLOCK;
+#ifdef ASYNC_PPS_CD_NEG
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO,
+ "refclock_parse: PPS detection on");
+#endif
+ }
+ }
+#endif
+
+/*
+ * SUN the Solaris way
+ */
+#ifdef HAVE_TIOCSPPS /* SUN PPS support */
+ if (CLK_PPS(parse->peer))
+ {
+ int i = 1;
+
+ if (ioctl(parse->ppsfd, TIOCSPPS, (caddr_t)&i) == 0)
+ {
+ parse->flags |= PARSE_PPSCLOCK;
+ }
+ }
+#endif
+
+/*
+ * PPS via PPSAPI
+ */
+#if defined(HAVE_PPSAPI)
+ parse->hardppsstate = PARSE_HARDPPS_DISABLE;
+ if (CLK_PPS(parse->peer))
+ {
+ if (!refclock_ppsapi(parse->ppsfd, &parse->atom))
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: parse_start: could not set up PPS: %m", CLK_UNIT(parse->peer));
+ }
+ else
+ {
+ parse_ppsapi(parse);
+ }
+ }
+#endif
+
+ if (TTY_SETATTR(fd232, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcsetattr(%d, &tio): %m", unit, fd232);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+ }
+
+ /*
+ * pick correct input machine
+ */
+ parse->generic->io.srcclock = peer;
+ parse->generic->io.datalen = 0;
+
+ parse->binding = init_iobinding(parse);
+
+ if (parse->binding == (bind_t *)0)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: io sub system initialisation failed.", CLK_UNIT(parse->peer));
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+ parse->generic->io.clock_recv = parse->binding->bd_receive; /* pick correct receive routine */
+ parse->generic->io.io_input = parse->binding->bd_io_input; /* pick correct input routine */
+
+ /*
+ * as we always(?) get 8 bit chars we want to be
+ * sure, that the upper bits are zero for less
+ * than 8 bit I/O - so we pass that information on.
+ * note that there can be only one bit count format
+ * per file descriptor
+ */
+
+ switch (tio.c_cflag & CSIZE)
+ {
+ case CS5:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS5;
+ break;
+
+ case CS6:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS6;
+ break;
+
+ case CS7:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS7;
+ break;
+
+ case CS8:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS8;
+ break;
+ }
+
+ if (!PARSE_SETCS(parse, &tmp_ctl))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setcs() FAILED.", unit);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+ tmp_ctl.parseformat.parse_count = strlcpy(tmp_ctl.parseformat.parse_buffer,
+ parse->parse_type->cl_format,
+ sizeof(tmp_ctl.parseformat.parse_buffer));
+ if (tmp_ctl.parseformat.parse_count >= sizeof(tmp_ctl.parseformat.parse_buffer))
+ tmp_ctl.parseformat.parse_count = sizeof(tmp_ctl.parseformat.parse_buffer) - 1;
+
+ if (!PARSE_SETFMT(parse, &tmp_ctl))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setfmt() FAILED.", unit);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+ /*
+ * get rid of all IO accumulated so far
+ */
+#ifdef HAVE_TERMIOS
+ (void) tcflush(parse->generic->io.fd, TCIOFLUSH);
+#else
+#if defined(TCFLSH) && defined(TCIOFLUSH)
+ {
+ int flshcmd = TCIOFLUSH;
+
+ (void) ioctl(parse->generic->io.fd, TCFLSH, (caddr_t)&flshcmd);
+ }
+#endif
+#endif
+
+ /*
+ * try to do any special initializations
+ */
+ if (parse->parse_type->cl_init)
+ {
+ if (parse->parse_type->cl_init(parse))
+ {
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+ }
+
+ /*
+ * Insert in async io device list.
+ */
+ if (!io_addclock(&parse->generic->io))
+ {
+ msyslog(LOG_ERR,
+ "PARSE receiver #%d: parse_start: addclock %s fails (ABORT - clock type requires async io)", CLK_UNIT(parse->peer), parsedev);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+
+ /*
+ * print out configuration
+ */
+ NLOG(NLOG_CLOCKINFO)
+ {
+ /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" (I/O device %s, PPS device %s) added",
+ CLK_UNIT(parse->peer),
+ parse->parse_type->cl_description, parsedev,
+ (parse->ppsfd != parse->generic->io.fd) ? parseppsdev : parsedev);
+
+ msyslog(LOG_INFO, "PARSE receiver #%d: Stratum %d, trust time %s, precision %d",
+ CLK_UNIT(parse->peer),
+ parse->peer->stratum,
+ l_mktime(parse->maxunsync), parse->peer->precision);
+
+ msyslog(LOG_INFO, "PARSE receiver #%d: rootdelay %.6f s, phase adjustment %.6f s, PPS phase adjustment %.6f s, %s IO handling",
+ CLK_UNIT(parse->peer),
+ parse->parse_type->cl_rootdelay,
+ parse->generic->fudgetime1,
+ parse->ppsphaseadjust,
+ parse->binding->bd_description);
+
+ msyslog(LOG_INFO, "PARSE receiver #%d: Format recognition: %s", CLK_UNIT(parse->peer),
+ parse->parse_type->cl_format);
+ msyslog(LOG_INFO, "PARSE receiver #%d: %sPPS support%s", CLK_UNIT(parse->peer),
+ CLK_PPS(parse->peer) ? "" : "NO ",
+ CLK_PPS(parse->peer) ?
+#ifdef PPS_METHOD
+ " (implementation " PPS_METHOD ")"
+#else
+ ""
+#endif
+ : ""
+ );
+ }
+
+ return 1;
+}
+
+/*--------------------------------------------------
+ * parse_ctl - process changes on flags/time values
+ */
+static void
+parse_ctl(
+ struct parseunit *parse,
+ const struct refclockstat *in
+ )
+{
+ if (in)
+ {
+ if (in->haveflags & (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4))
+ {
+ parse->flags = (parse->flags & ~(CLK_FLAG1|CLK_FLAG2|CLK_FLAG3|CLK_FLAG4)) |
+ (in->flags & (CLK_FLAG1|CLK_FLAG2|CLK_FLAG3|CLK_FLAG4));
+#if defined(HAVE_PPSAPI)
+ if (CLK_PPS(parse->peer))
+ {
+ parse_ppsapi(parse);
+ }
+#endif
+ }
+
+ if (in->haveflags & CLK_HAVETIME1)
+ {
+ parse->generic->fudgetime1 = in->fudgetime1;
+ msyslog(LOG_INFO, "PARSE receiver #%d: new phase adjustment %.6f s",
+ CLK_UNIT(parse->peer),
+ parse->generic->fudgetime1);
+ }
+
+ if (in->haveflags & CLK_HAVETIME2)
+ {
+ parse->generic->fudgetime2 = in->fudgetime2;
+ if (parse->flags & PARSE_TRUSTTIME)
+ {
+ parse->maxunsync = (u_long)ABS(in->fudgetime2);
+ msyslog(LOG_INFO, "PARSE receiver #%d: new trust time %s",
+ CLK_UNIT(parse->peer),
+ l_mktime(parse->maxunsync));
+ }
+ else
+ {
+ parse->ppsphaseadjust = in->fudgetime2;
+ msyslog(LOG_INFO, "PARSE receiver #%d: new PPS phase adjustment %.6f s",
+ CLK_UNIT(parse->peer),
+ parse->ppsphaseadjust);
+#if defined(HAVE_PPSAPI)
+ if (CLK_PPS(parse->peer))
+ {
+ parse_ppsapi(parse);
+ }
+#endif
+ }
+ }
+ }
+}
+
+/*--------------------------------------------------
+ * parse_poll - called by the transmit procedure
+ */
+static void
+parse_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = peer->procptr->unitptr;
+
+ if (peer != parse->peer)
+ {
+ msyslog(LOG_ERR,
+ "PARSE receiver #%d: poll: INTERNAL: peer incorrect",
+ unit);
+ return;
+ }
+
+ /*
+ * Update clock stat counters
+ */
+ parse->generic->polls++;
+
+ if (parse->pollneeddata &&
+ ((current_time - parse->pollneeddata) > (1<<(max(min(parse->peer->hpoll, parse->peer->ppoll), parse->peer->minpoll)))))
+ {
+ /*
+ * start worrying when exceeding a poll inteval
+ * bad news - didn't get a response last time
+ */
+ parse->lastmissed = current_time;
+ parse_event(parse, CEVNT_TIMEOUT);
+
+ ERR(ERR_NODATA)
+ msyslog(LOG_WARNING, "PARSE receiver #%d: no data from device within poll interval (check receiver / wiring)", CLK_UNIT(parse->peer));
+ }
+
+ /*
+ * we just mark that we want the next sample for the clock filter
+ */
+ parse->pollneeddata = current_time;
+
+ if (parse->parse_type->cl_poll)
+ {
+ parse->parse_type->cl_poll(parse);
+ }
+
+ cparse_statistics(parse);
+
+ return;
+}
+
+#define LEN_STATES 300 /* length of state string */
+
+/*--------------------------------------------------
+ * parse_control - set fudge factors, return statistics
+ */
+static void
+parse_control(
+ int unit,
+ const struct refclockstat *in,
+ struct refclockstat *out,
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = peer->procptr->unitptr;
+ parsectl_t tmpctl;
+
+ static char outstatus[400]; /* status output buffer */
+
+ if (out)
+ {
+ out->lencode = 0;
+ out->p_lastcode = 0;
+ out->kv_list = (struct ctl_var *)0;
+ }
+
+ if (!parse || !parse->peer)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_control: unit invalid (UNIT INACTIVE)",
+ unit);
+ return;
+ }
+
+ unit = CLK_UNIT(parse->peer);
+
+ /*
+ * handle changes
+ */
+ parse_ctl(parse, in);
+
+ /*
+ * supply data
+ */
+ if (out)
+ {
+ u_long sum = 0;
+ char *tt, *start;
+ int i;
+
+ outstatus[0] = '\0';
+
+ out->type = REFCLK_PARSE;
+
+ /*
+ * keep fudgetime2 in sync with TRUSTTIME/MAXUNSYNC flag1
+ */
+ parse->generic->fudgetime2 = (parse->flags & PARSE_TRUSTTIME) ? (double)parse->maxunsync : parse->ppsphaseadjust;
+
+ /*
+ * figure out skew between PPS and RS232 - just for informational
+ * purposes
+ */
+ if (PARSE_SYNC(parse->timedata.parse_state))
+ {
+ if (PARSE_PPS(parse->timedata.parse_state) && PARSE_TIMECODE(parse->timedata.parse_state))
+ {
+ l_fp off;
+
+ /*
+ * we have a PPS and RS232 signal - calculate the skew
+ * WARNING: assumes on TIMECODE == PULSE (timecode after pulse)
+ */
+ off = parse->timedata.parse_stime.fp;
+ L_SUB(&off, &parse->timedata.parse_ptime.fp); /* true offset */
+ tt = add_var(&out->kv_list, 80, RO);
+ snprintf(tt, 80, "refclock_ppsskew=%s", lfptoms(&off, 6));
+ }
+ }
+
+ if (PARSE_PPS(parse->timedata.parse_state))
+ {
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ snprintf(tt, 80, "refclock_ppstime=\"%s\"", gmprettydate(&parse->timedata.parse_ptime.fp));
+ }
+
+ start = tt = add_var(&out->kv_list, 128, RO|DEF);
+ snprintf(tt, 128, "refclock_time=\"");
+ tt += strlen(tt);
+
+ if (parse->timedata.parse_time.fp.l_ui == 0)
+ {
+ strlcpy(tt, "<UNDEFINED>\"", BUFFER_SIZES(start, tt, 128));
+ }
+ else
+ {
+ snprintf(tt, 128, "%s\"", gmprettydate(&parse->timedata.parse_time.fp));
+ }
+
+ if (!PARSE_GETTIMECODE(parse, &tmpctl))
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_timecode() FAILED", unit);
+ }
+ else
+ {
+ start = tt = add_var(&out->kv_list, 512, RO|DEF);
+ snprintf(tt, 512, "refclock_status=\"");
+ tt += strlen(tt);
+
+ /*
+ * copy PPS flags from last read transaction (informational only)
+ */
+ tmpctl.parsegettc.parse_state |= parse->timedata.parse_state &
+ (PARSEB_PPS|PARSEB_S_PPS);
+
+ (void) parsestate(tmpctl.parsegettc.parse_state, tt, BUFFER_SIZES(start, tt, 512));
+
+ strlcat(tt, "\"", BUFFER_SIZES(start, tt, 512));
+
+ if (tmpctl.parsegettc.parse_count)
+ mkascii(outstatus+strlen(outstatus), (int)(sizeof(outstatus)- strlen(outstatus) - 1),
+ tmpctl.parsegettc.parse_buffer, (unsigned)(tmpctl.parsegettc.parse_count));
+
+ }
+
+ tmpctl.parseformat.parse_format = tmpctl.parsegettc.parse_format;
+
+ if (!PARSE_GETFMT(parse, &tmpctl))
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_getfmt() FAILED", unit);
+ }
+ else
+ {
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ snprintf(tt, 80, "refclock_format=\"");
+
+ strlcat(tt, tmpctl.parseformat.parse_buffer, 80);
+ strlcat(tt,"\"", 80);
+ }
+
+ /*
+ * gather state statistics
+ */
+
+ start = tt = add_var(&out->kv_list, LEN_STATES, RO|DEF);
+ strlcpy(tt, "refclock_states=\"", LEN_STATES);
+ tt += strlen(tt);
+
+ for (i = 0; i <= CEVNT_MAX; i++)
+ {
+ u_long s_time;
+ u_long d = current_time - parse->generic->timestarted;
+ u_long percent;
+
+ percent = s_time = PARSE_STATETIME(parse, i);
+
+ while (((u_long)(~0) / 10000) < percent)
+ {
+ percent /= 10;
+ d /= 10;
+ }
+
+ if (d)
+ percent = (percent * 10000) / d;
+ else
+ percent = 10000;
+
+ if (s_time)
+ {
+ char item[80];
+ int count;
+
+ snprintf(item, 80, "%s%s%s: %s (%d.%02d%%)",
+ sum ? "; " : "",
+ (parse->generic->currentstatus == i) ? "*" : "",
+ clockstatus((unsigned int)i),
+ l_mktime(s_time),
+ (int)(percent / 100), (int)(percent % 100));
+ if ((count = strlen(item)) < (LEN_STATES - 40 - (tt - start)))
+ {
+ strlcpy(tt, item, BUFFER_SIZES(start, tt, LEN_STATES));
+ tt += count;
+ }
+ sum += s_time;
+ }
+ }
+
+ snprintf(tt, BUFFER_SIZES(start, tt, LEN_STATES), "; running time: %s\"", l_mktime(sum));
+
+ tt = add_var(&out->kv_list, 32, RO);
+ snprintf(tt, 32, "refclock_id=\"%s\"", parse->parse_type->cl_id);
+
+ tt = add_var(&out->kv_list, 80, RO);
+ snprintf(tt, 80, "refclock_iomode=\"%s\"", parse->binding->bd_description);
+
+ tt = add_var(&out->kv_list, 128, RO);
+ snprintf(tt, 128, "refclock_driver_version=\"%s\"", rcsid);
+
+ {
+ struct ctl_var *k;
+
+ k = parse->kv;
+ while (k && !(k->flags & EOV))
+ {
+ set_var(&out->kv_list, k->text, strlen(k->text)+1, k->flags);
+ k++;
+ }
+ }
+
+ out->lencode = strlen(outstatus);
+ out->p_lastcode = outstatus;
+ }
+}
+
+/**===========================================================================
+ ** processing routines
+ **/
+
+/*--------------------------------------------------
+ * event handling - note that nominal events will also be posted
+ * keep track of state dwelling times
+ */
+static void
+parse_event(
+ struct parseunit *parse,
+ int event
+ )
+{
+ if (parse->generic->currentstatus != (u_char) event)
+ {
+ parse->statetime[parse->generic->currentstatus] += current_time - parse->lastchange;
+ parse->lastchange = current_time;
+
+ if (parse->parse_type->cl_event)
+ parse->parse_type->cl_event(parse, event);
+
+ if (event == CEVNT_NOMINAL)
+ {
+ NLOG(NLOG_CLOCKSTATUS)
+ msyslog(LOG_INFO, "PARSE receiver #%d: SYNCHRONIZED",
+ CLK_UNIT(parse->peer));
+ }
+
+ refclock_report(parse->peer, event);
+ }
+}
+
+/*--------------------------------------------------
+ * process a PARSE time sample
+ */
+static void
+parse_process(
+ struct parseunit *parse,
+ parsetime_t *parsetime
+ )
+{
+ l_fp off, rectime, reftime;
+ double fudge;
+
+ /* silence warning: 'off.Ul_i.Xl_i' may be used uninitialized in this function */
+ ZERO(off);
+
+ /*
+ * check for changes in conversion status
+ * (only one for each new status !)
+ */
+ if (((parsetime->parse_status & CVT_MASK) != CVT_OK) &&
+ ((parsetime->parse_status & CVT_MASK) != CVT_NONE) &&
+ (parse->timedata.parse_status != parsetime->parse_status))
+ {
+ char buffer[400];
+
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_WARNING, "PARSE receiver #%d: conversion status \"%s\"",
+ CLK_UNIT(parse->peer), parsestatus(parsetime->parse_status, buffer, sizeof(buffer)));
+
+ if ((parsetime->parse_status & CVT_MASK) == CVT_FAIL)
+ {
+ /*
+ * tell more about the story - list time code
+ * there is a slight change for a race condition and
+ * the time code might be overwritten by the next packet
+ */
+ parsectl_t tmpctl;
+
+ if (!PARSE_GETTIMECODE(parse, &tmpctl))
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_process: parse_timecode() FAILED", CLK_UNIT(parse->peer));
+ }
+ else
+ {
+ ERR(ERR_BADDATA)
+ msyslog(LOG_WARNING, "PARSE receiver #%d: FAILED TIMECODE: \"%s\" (check receiver configuration / wiring)",
+ CLK_UNIT(parse->peer), mkascii(buffer, sizeof buffer, tmpctl.parsegettc.parse_buffer, (unsigned)(tmpctl.parsegettc.parse_count - 1)));
+ }
+ }
+ }
+
+ /*
+ * examine status and post appropriate events
+ */
+ if ((parsetime->parse_status & CVT_MASK) != CVT_OK)
+ {
+ /*
+ * got bad data - tell the rest of the system
+ */
+ switch (parsetime->parse_status & CVT_MASK)
+ {
+ case CVT_NONE:
+ if ((parsetime->parse_status & CVT_ADDITIONAL) &&
+ parse->parse_type->cl_message)
+ parse->parse_type->cl_message(parse, parsetime);
+ /*
+ * save PPS information that comes piggyback
+ */
+ if (PARSE_PPS(parsetime->parse_state))
+ {
+ parse->timedata.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+ parse->timedata.parse_ptime = parsetime->parse_ptime;
+ }
+ break; /* well, still waiting - timeout is handled at higher levels */
+
+ case CVT_FAIL:
+ if (parsetime->parse_status & CVT_BADFMT)
+ {
+ parse_event(parse, CEVNT_BADREPLY);
+ }
+ else
+ if (parsetime->parse_status & CVT_BADDATE)
+ {
+ parse_event(parse, CEVNT_BADDATE);
+ }
+ else
+ if (parsetime->parse_status & CVT_BADTIME)
+ {
+ parse_event(parse, CEVNT_BADTIME);
+ }
+ else
+ {
+ parse_event(parse, CEVNT_BADREPLY); /* for the lack of something better */
+ }
+ }
+ return; /* skip the rest - useless */
+ }
+
+ /*
+ * check for format changes
+ * (in case somebody has swapped clocks 8-)
+ */
+ if (parse->lastformat != parsetime->parse_format)
+ {
+ parsectl_t tmpctl;
+
+ tmpctl.parseformat.parse_format = parsetime->parse_format;
+
+ if (!PARSE_GETFMT(parse, &tmpctl))
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_getfmt() FAILED", CLK_UNIT(parse->peer));
+ }
+ else
+ {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "PARSE receiver #%d: packet format \"%s\"",
+ CLK_UNIT(parse->peer), tmpctl.parseformat.parse_buffer);
+ }
+ parse->lastformat = parsetime->parse_format;
+ }
+
+ /*
+ * now, any changes ?
+ */
+ if ((parse->timedata.parse_state ^ parsetime->parse_state) &
+ ~(unsigned)(PARSEB_PPS|PARSEB_S_PPS))
+ {
+ char tmp1[200];
+ char tmp2[200];
+ /*
+ * something happend - except for PPS events
+ */
+
+ (void) parsestate(parsetime->parse_state, tmp1, sizeof(tmp1));
+ (void) parsestate(parse->timedata.parse_state, tmp2, sizeof(tmp2));
+
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO,"PARSE receiver #%d: STATE CHANGE: %s -> %s",
+ CLK_UNIT(parse->peer), tmp2, tmp1);
+ }
+
+ /*
+ * carry on PPS information if still usable
+ */
+ if (PARSE_PPS(parse->timedata.parse_state) && !PARSE_PPS(parsetime->parse_state))
+ {
+ parsetime->parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+ parsetime->parse_ptime = parse->timedata.parse_ptime;
+ }
+
+ /*
+ * remember for future
+ */
+ parse->timedata = *parsetime;
+
+ /*
+ * check to see, whether the clock did a complete powerup or lost PZF signal
+ * and post correct events for current condition
+ */
+ if (PARSE_POWERUP(parsetime->parse_state))
+ {
+ /*
+ * this is bad, as we have completely lost synchronisation
+ * well this is a problem with the receiver here
+ * for PARSE Meinberg DCF77 receivers the lost synchronisation
+ * is true as it is the powerup state and the time is taken
+ * from a crude real time clock chip
+ * for the PZF/GPS series this is only partly true, as
+ * PARSE_POWERUP only means that the pseudo random
+ * phase shift sequence cannot be found. this is only
+ * bad, if we have never seen the clock in the SYNC
+ * state, where the PHASE and EPOCH are correct.
+ * for reporting events the above business does not
+ * really matter, but we can use the time code
+ * even in the POWERUP state after having seen
+ * the clock in the synchronized state (PZF class
+ * receivers) unless we have had a telegram disruption
+ * after having seen the clock in the SYNC state. we
+ * thus require having seen the clock in SYNC state
+ * *after* having missed telegrams (noresponse) from
+ * the clock. one problem remains: we might use erroneously
+ * POWERUP data if the disruption is shorter than 1 polling
+ * interval. fortunately powerdowns last usually longer than 64
+ * seconds and the receiver is at least 2 minutes in the
+ * POWERUP or NOSYNC state before switching to SYNC
+ * for GPS receivers this can mean antenna problems and other causes.
+ * the additional grace period can be enables by a clock
+ * mode having the PARSE_F_POWERUPTRUST flag in cl_flag set.
+ */
+ parse_event(parse, CEVNT_FAULT);
+ NLOG(NLOG_CLOCKSTATUS)
+ ERR(ERR_BADSTATUS)
+ msyslog(LOG_ERR,"PARSE receiver #%d: NOT SYNCHRONIZED/RECEIVER PROBLEMS",
+ CLK_UNIT(parse->peer));
+ }
+ else
+ {
+ /*
+ * we have two states left
+ *
+ * SYNC:
+ * this state means that the EPOCH (timecode) and PHASE
+ * information has be read correctly (at least two
+ * successive PARSE timecodes were received correctly)
+ * this is the best possible state - full trust
+ *
+ * NOSYNC:
+ * The clock should be on phase with respect to the second
+ * signal, but the timecode has not been received correctly within
+ * at least the last two minutes. this is a sort of half baked state
+ * for PARSE Meinberg DCF77 clocks this is bad news (clock running
+ * without timecode confirmation)
+ * PZF 535 has also no time confirmation, but the phase should be
+ * very precise as the PZF signal can be decoded
+ */
+
+ if (PARSE_SYNC(parsetime->parse_state))
+ {
+ /*
+ * currently completely synchronized - best possible state
+ */
+ parse->lastsync = current_time;
+ clear_err(parse, ERR_BADSTATUS);
+ }
+ else
+ {
+ /*
+ * we have had some problems receiving the time code
+ */
+ parse_event(parse, CEVNT_PROP);
+ NLOG(NLOG_CLOCKSTATUS)
+ ERR(ERR_BADSTATUS)
+ msyslog(LOG_ERR,"PARSE receiver #%d: TIMECODE NOT CONFIRMED",
+ CLK_UNIT(parse->peer));
+ }
+ }
+
+ fudge = parse->generic->fudgetime1; /* standard RS232 Fudgefactor */
+
+ if (PARSE_TIMECODE(parsetime->parse_state))
+ {
+ rectime = parsetime->parse_stime.fp;
+ off = reftime = parsetime->parse_time.fp;
+
+ L_SUB(&off, &rectime); /* prepare for PPS adjustments logic */
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("PARSE receiver #%d: Reftime %s, Recvtime %s - initial offset %s\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&reftime),
+ prettydate(&rectime),
+ lfptoa(&off,6));
+#endif
+ }
+
+ if (PARSE_PPS(parsetime->parse_state) && CLK_PPS(parse->peer))
+ {
+ l_fp offset;
+ double ppsphaseadjust = parse->ppsphaseadjust;
+
+#ifdef HAVE_PPSAPI
+ /*
+ * set fudge = 0.0 if already included in PPS time stamps
+ */
+ if (parse->atom.pps_params.mode & (PPS_OFFSETCLEAR|PPS_OFFSETASSERT))
+ {
+ ppsphaseadjust = 0.0;
+ }
+#endif
+
+ /*
+ * we have a PPS signal - much better than the RS232 stuff (we hope)
+ */
+ offset = parsetime->parse_ptime.fp;
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("PARSE receiver #%d: PPStime %s\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&offset));
+#endif
+ if (PARSE_TIMECODE(parsetime->parse_state))
+ {
+ if (M_ISGEQ(off.l_i, off.l_uf, -1, 0x80000000) &&
+ M_ISGEQ(0, 0x7fffffff, off.l_i, off.l_uf))
+ {
+ fudge = ppsphaseadjust; /* pick PPS fudge factor */
+
+ /*
+ * RS232 offsets within [-0.5..0.5[ - take PPS offsets
+ */
+
+ if (parse->parse_type->cl_flags & PARSE_F_PPSONSECOND)
+ {
+ reftime = off = offset;
+ if (reftime.l_uf & 0x80000000)
+ reftime.l_ui++;
+ reftime.l_uf = 0;
+
+
+ /*
+ * implied on second offset
+ */
+ off.l_uf = ~off.l_uf; /* map [0.5..1[ -> [-0.5..0[ */
+ off.l_i = (off.l_uf & 0x8000000) ? -1 : 0; /* sign extend */
+ }
+ else
+ {
+ /*
+ * time code describes pulse
+ */
+ reftime = off = parsetime->parse_time.fp;
+
+ L_SUB(&off, &offset); /* true offset */
+ }
+ }
+ /*
+ * take RS232 offset when PPS when out of bounds
+ */
+ }
+ else
+ {
+ fudge = ppsphaseadjust; /* pick PPS fudge factor */
+ /*
+ * Well, no time code to guide us - assume on second pulse
+ * and pray, that we are within [-0.5..0.5[
+ */
+ off = offset;
+ reftime = offset;
+ if (reftime.l_uf & 0x80000000)
+ reftime.l_ui++;
+ reftime.l_uf = 0;
+ /*
+ * implied on second offset
+ */
+ off.l_uf = ~off.l_uf; /* map [0.5..1[ -> [-0.5..0[ */
+ off.l_i = (off.l_uf & 0x80000000) ? -1 : 0; /* sign extend */
+ }
+ }
+ else
+ {
+ if (!PARSE_TIMECODE(parsetime->parse_state))
+ {
+ /*
+ * Well, no PPS, no TIMECODE, no more work ...
+ */
+ if ((parsetime->parse_status & CVT_ADDITIONAL) &&
+ parse->parse_type->cl_message)
+ parse->parse_type->cl_message(parse, parsetime);
+ return;
+ }
+ }
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("PARSE receiver #%d: Reftime %s, Recvtime %s - final offset %s\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&reftime),
+ prettydate(&rectime),
+ lfptoa(&off,6));
+#endif
+
+
+ rectime = reftime;
+ L_SUB(&rectime, &off); /* just to keep the ntp interface happy */
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("PARSE receiver #%d: calculated Reftime %s, Recvtime %s\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&reftime),
+ prettydate(&rectime));
+#endif
+
+ if ((parsetime->parse_status & CVT_ADDITIONAL) &&
+ parse->parse_type->cl_message)
+ parse->parse_type->cl_message(parse, parsetime);
+
+ if (PARSE_SYNC(parsetime->parse_state))
+ {
+ /*
+ * log OK status
+ */
+ parse_event(parse, CEVNT_NOMINAL);
+ }
+
+ clear_err(parse, ERR_BADIO);
+ clear_err(parse, ERR_BADDATA);
+ clear_err(parse, ERR_NODATA);
+ clear_err(parse, ERR_INTERNAL);
+
+ /*
+ * and now stick it into the clock machine
+ * samples are only valid iff lastsync is not too old and
+ * we have seen the clock in sync at least once
+ * after the last time we didn't see an expected data telegram
+ * at startup being not in sync is also bad just like
+ * POWERUP state unless PARSE_F_POWERUPTRUST is set
+ * see the clock states section above for more reasoning
+ */
+ if (((current_time - parse->lastsync) > parse->maxunsync) ||
+ (parse->lastsync < parse->lastmissed) ||
+ ((parse->lastsync == 0) && !PARSE_SYNC(parsetime->parse_state)) ||
+ (((parse->parse_type->cl_flags & PARSE_F_POWERUPTRUST) == 0) &&
+ PARSE_POWERUP(parsetime->parse_state)))
+ {
+ parse->generic->leap = LEAP_NOTINSYNC;
+ parse->lastsync = 0; /* wait for full sync again */
+ }
+ else
+ {
+ 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).
+ */
+ parse->generic->leap = (parse->flags & PARSE_LEAP_DELETE) ? LEAP_DELSECOND : LEAP_ADDSECOND;
+ }
+ else
+ if (PARSE_LEAPDEL(parsetime->parse_state))
+ {
+ parse->generic->leap = LEAP_DELSECOND;
+ }
+ else
+ {
+ parse->generic->leap = LEAP_NOWARNING;
+ }
+ }
+
+ if (parse->generic->leap != LEAP_NOTINSYNC)
+ {
+ /*
+ * only good/trusted samples are interesting
+ */
+#ifdef DEBUG
+ if (debug > 2)
+ {
+ printf("PARSE receiver #%d: refclock_process_offset(reftime=%s, rectime=%s, Fudge=%f)\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&reftime),
+ prettydate(&rectime),
+ fudge);
+ }
+#endif
+ parse->generic->lastref = reftime;
+
+ refclock_process_offset(parse->generic, reftime, rectime, fudge);
+
+#ifdef HAVE_PPSAPI
+ /*
+ * pass PPS information on to PPS clock
+ */
+ if (PARSE_PPS(parsetime->parse_state) && CLK_PPS(parse->peer))
+ {
+ /* refclock_pps includes fudgetime1 - we keep the RS232 offset in there :-( */
+ double savedtime1 = parse->generic->fudgetime1;
+
+ parse->generic->fudgetime1 = fudge;
+
+ if (refclock_pps(parse->peer, &parse->atom,
+ parse->flags & (CLK_FLAG1|CLK_FLAG2|CLK_FLAG3|CLK_FLAG4))) {
+ parse->peer->flags |= FLAG_PPS;
+ } else {
+ parse->peer->flags &= ~FLAG_PPS;
+ }
+
+ parse->generic->fudgetime1 = savedtime1;
+
+ parse_hardpps(parse, PARSE_HARDPPS_ENABLE);
+ }
+#endif
+ } else {
+ parse_hardpps(parse, PARSE_HARDPPS_DISABLE);
+ parse->peer->flags &= ~FLAG_PPS;
+ }
+
+ /*
+ * ready, unless the machine wants a sample or
+ * we are in fast startup mode (peer->dist > MAXDISTANCE)
+ */
+ if (!parse->pollneeddata && parse->peer->disp <= MAXDISTANCE)
+ return;
+
+ parse->pollneeddata = 0;
+
+ parse->timedata.parse_state &= ~(unsigned)(PARSEB_PPS|PARSEB_S_PPS);
+
+ refclock_receive(parse->peer);
+}
+
+/**===========================================================================
+ ** special code for special clocks
+ **/
+
+static void
+mk_utcinfo(
+ char *t,
+ int wnt,
+ int wnlsf,
+ int dn,
+ int dtls,
+ int dtlsf,
+ int size
+ )
+{
+ l_fp leapdate;
+ char *start = t;
+
+ snprintf(t, size, "current correction %d sec", dtls);
+ t += strlen(t);
+
+ if (wnlsf < 990)
+ wnlsf += 1024;
+
+ if (wnt < 990)
+ wnt += 1024;
+
+ gpstolfp((unsigned short)wnlsf, (unsigned short)dn, 0, &leapdate);
+
+ if ((dtlsf != dtls) &&
+ ((wnlsf - wnt) < 52))
+ {
+ snprintf(t, BUFFER_SIZES(start, t, size), ", next correction %d sec on %s, new GPS-UTC offset %d",
+ dtlsf - dtls, gmprettydate(&leapdate), dtlsf);
+ }
+ else
+ {
+ snprintf(t, BUFFER_SIZES(start, t, size), ", last correction on %s",
+ gmprettydate(&leapdate));
+ }
+}
+
+#ifdef CLOCK_MEINBERG
+/**===========================================================================
+ ** Meinberg GPS166/GPS167 support
+ **/
+
+/*------------------------------------------------------------
+ * gps16x_message - process GPS16x messages
+ */
+static void
+gps16x_message(
+ struct parseunit *parse,
+ parsetime_t *parsetime
+ )
+{
+ if (parse->timedata.parse_msglen && parsetime->parse_msg[0] == SOH)
+ {
+ GPS_MSG_HDR header;
+ unsigned char *bufp = (unsigned char *)parsetime->parse_msg + 1;
+
+#ifdef DEBUG
+ if (debug > 2)
+ {
+ char msgbuffer[600];
+
+ mkreadable(msgbuffer, sizeof(msgbuffer), (char *)parsetime->parse_msg, parsetime->parse_msglen, 1);
+ printf("PARSE receiver #%d: received message (%d bytes) >%s<\n",
+ CLK_UNIT(parse->peer),
+ parsetime->parse_msglen,
+ msgbuffer);
+ }
+#endif
+ get_mbg_header(&bufp, &header);
+ if (header.gps_hdr_csum == mbg_csum(parsetime->parse_msg + 1, 6) &&
+ (header.gps_len == 0 ||
+ (header.gps_len < sizeof(parsetime->parse_msg) &&
+ header.gps_data_csum == mbg_csum(bufp, header.gps_len))))
+ {
+ /*
+ * clean message
+ */
+ switch (header.gps_cmd)
+ {
+ case GPS_SW_REV:
+ {
+ char buffer[64];
+ SW_REV gps_sw_rev;
+
+ get_mbg_sw_rev(&bufp, &gps_sw_rev);
+ snprintf(buffer, sizeof(buffer), "meinberg_gps_version=\"%x.%02x%s%s\"",
+ (gps_sw_rev.code >> 8) & 0xFF,
+ gps_sw_rev.code & 0xFF,
+ gps_sw_rev.name[0] ? " " : "",
+ gps_sw_rev.name);
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO|DEF);
+ }
+ break;
+
+ case GPS_STAT:
+ {
+ static struct state
+ {
+ unsigned short flag; /* status flag */
+ unsigned const char *string; /* bit name */
+ } states[] =
+ {
+ { TM_ANT_DISCONN, (const unsigned char *)"ANTENNA FAULTY" },
+ { TM_SYN_FLAG, (const unsigned char *)"NO SYNC SIGNAL" },
+ { TM_NO_SYNC, (const unsigned char *)"NO SYNC POWERUP" },
+ { TM_NO_POS, (const unsigned char *)"NO POSITION" },
+ { 0, (const unsigned char *)"" }
+ };
+ unsigned short status;
+ struct state *s = states;
+ char buffer[512];
+ char *p, *b;
+
+ status = get_lsb_short(&bufp);
+ snprintf(buffer, sizeof(buffer), "meinberg_gps_status=\"[0x%04x] ", status);
+
+ if (status)
+ {
+ p = b = buffer + strlen(buffer);
+ while (s->flag)
+ {
+ if (status & s->flag)
+ {
+ if (p != b)
+ {
+ *p++ = ',';
+ *p++ = ' ';
+ }
+
+ strlcat(p, (const char *)s->string, sizeof(buffer));
+ }
+ s++;
+ }
+
+ *p++ = '"';
+ *p = '\0';
+ }
+ else
+ {
+ strlcat(buffer, "<OK>\"", sizeof(buffer));
+ }
+
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO|DEF);
+ }
+ break;
+
+ case GPS_POS_XYZ:
+ {
+ XYZ xyz;
+ char buffer[256];
+
+ get_mbg_xyz(&bufp, xyz);
+ snprintf(buffer, sizeof(buffer), "gps_position(XYZ)=\"%s m, %s m, %s m\"",
+ mfptoa(xyz[XP].l_ui, xyz[XP].l_uf, 1),
+ mfptoa(xyz[YP].l_ui, xyz[YP].l_uf, 1),
+ mfptoa(xyz[ZP].l_ui, xyz[ZP].l_uf, 1));
+
+ set_var(&parse->kv, buffer, sizeof(buffer), RO|DEF);
+ }
+ break;
+
+ case GPS_POS_LLA:
+ {
+ LLA lla;
+ char buffer[256];
+
+ get_mbg_lla(&bufp, lla);
+
+ snprintf(buffer, sizeof(buffer), "gps_position(LLA)=\"%s deg, %s deg, %s m\"",
+ mfptoa(lla[LAT].l_ui, lla[LAT].l_uf, 4),
+ mfptoa(lla[LON].l_ui, lla[LON].l_uf, 4),
+ mfptoa(lla[ALT].l_ui, lla[ALT].l_uf, 1));
+
+ set_var(&parse->kv, buffer, sizeof(buffer), RO|DEF);
+ }
+ break;
+
+ case GPS_TZDL:
+ break;
+
+ case GPS_PORT_PARM:
+ break;
+
+ case GPS_SYNTH:
+ break;
+
+ case GPS_ANT_INFO:
+ {
+ ANT_INFO antinfo;
+ char buffer[512];
+ char *p;
+
+ get_mbg_antinfo(&bufp, &antinfo);
+ snprintf(buffer, sizeof(buffer), "meinberg_antenna_status=\"");
+ p = buffer + strlen(buffer);
+
+ switch (antinfo.status)
+ {
+ case ANT_INVALID:
+ strlcat(p, "<OK>", BUFFER_SIZE(buffer, p));
+ p += strlen(p);
+ break;
+
+ case ANT_DISCONN:
+ strlcat(p, "DISCONNECTED since ", BUFFER_SIZE(buffer, p));
+ NLOG(NLOG_CLOCKSTATUS)
+ ERR(ERR_BADSTATUS)
+ msyslog(LOG_ERR,"PARSE receiver #%d: ANTENNA FAILURE: %s",
+ CLK_UNIT(parse->peer), p);
+
+ p += strlen(p);
+ mbg_tm_str(&p, &antinfo.tm_disconn, BUFFER_SIZE(buffer, p));
+ *p = '\0';
+ break;
+
+ case ANT_RECONN:
+ strlcat(p, "RECONNECTED on ", BUFFER_SIZE(buffer, p));
+ p += strlen(p);
+ mbg_tm_str(&p, &antinfo.tm_reconn, BUFFER_SIZE(buffer, p));
+ snprintf(p, BUFFER_SIZE(buffer, p), ", reconnect clockoffset %c%ld.%07ld s, disconnect time ",
+ (antinfo.delta_t < 0) ? '-' : '+',
+ ABS(antinfo.delta_t) / 10000,
+ ABS(antinfo.delta_t) % 10000);
+ p += strlen(p);
+ mbg_tm_str(&p, &antinfo.tm_disconn, BUFFER_SIZE(buffer, p));
+ *p = '\0';
+ break;
+
+ default:
+ snprintf(p, BUFFER_SIZE(buffer, p), "bad status 0x%04x", antinfo.status);
+ p += strlen(p);
+ break;
+ }
+
+ strlcat(p, "\"", BUFFER_SIZE(buffer, p));
+
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO|DEF);
+ }
+ break;
+
+ case GPS_UCAP:
+ break;
+
+ case GPS_CFGH:
+ {
+ CFGH cfgh;
+ char buffer[512];
+ char *p;
+
+ get_mbg_cfgh(&bufp, &cfgh);
+ if (cfgh.valid)
+ {
+ int i;
+
+ p = buffer;
+ strlcpy(buffer, "gps_tot_51=\"", BUFFER_SIZE(buffer, p));
+ p += strlen(p);
+ mbg_tgps_str(&p, &cfgh.tot_51, BUFFER_SIZE(buffer, p));
+ strlcpy(p, "\"", BUFFER_SIZE(buffer, p));
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO);
+
+ p = buffer;
+ strlcpy(buffer, "gps_tot_63=\"", BUFFER_SIZE(buffer, p));
+ p += strlen(p);
+ mbg_tgps_str(&p, &cfgh.tot_63, BUFFER_SIZE(buffer, p));
+ strlcpy(p, "\"", BUFFER_SIZE(buffer, p));
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO);
+
+ p = buffer;
+ strlcpy(buffer, "gps_t0a=\"", BUFFER_SIZE(buffer, p));
+ p += strlen(p);
+ mbg_tgps_str(&p, &cfgh.t0a, BUFFER_SIZE(buffer, p));
+ strlcpy(p, "\"", BUFFER_SIZE(buffer, p));
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO);
+
+ for (i = MIN_SVNO; i < MAX_SVNO; i++)
+ {
+ p = buffer;
+ snprintf(p, BUFFER_SIZE(buffer, p), "gps_cfg[%d]=\"[0x%x] ", i, cfgh.cfg[i]);
+ p += strlen(p);
+ switch (cfgh.cfg[i] & 0x7)
+ {
+ case 0:
+ strlcpy(p, "BLOCK I", BUFFER_SIZE(buffer, p));
+ break;
+ case 1:
+ strlcpy(p, "BLOCK II", BUFFER_SIZE(buffer, p));
+ break;
+ default:
+ strlcpy(p, "bad CFG", BUFFER_SIZE(buffer, p));
+ break;
+ }
+ strlcat(p, "\"", BUFFER_SIZE(buffer, p));
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO);
+
+ p = buffer;
+ snprintf(p, BUFFER_SIZE(buffer, p), "gps_health[%d]=\"[0x%x] ", i, cfgh.health[i]);
+ p += strlen(p);
+ switch ((cfgh.health[i] >> 5) & 0x7 )
+ {
+ case 0:
+ strlcpy(p, "OK;", BUFFER_SIZE(buffer, p));
+ break;
+ case 1:
+ strlcpy(p, "PARITY;", BUFFER_SIZE(buffer, p));
+ break;
+ case 2:
+ strlcpy(p, "TLM/HOW;", BUFFER_SIZE(buffer, p));
+ break;
+ case 3:
+ strlcpy(p, "Z-COUNT;", BUFFER_SIZE(buffer, p));
+ break;
+ case 4:
+ strlcpy(p, "SUBFRAME 1,2,3;", BUFFER_SIZE(buffer, p));
+ break;
+ case 5:
+ strlcpy(p, "SUBFRAME 4,5;", BUFFER_SIZE(buffer, p));
+ break;
+ case 6:
+ strlcpy(p, "UPLOAD BAD;", BUFFER_SIZE(buffer, p));
+ break;
+ case 7:
+ strlcpy(p, "DATA BAD;", BUFFER_SIZE(buffer, p));
+ break;
+ }
+
+ p += strlen(p);
+
+ switch (cfgh.health[i] & 0x1F)
+ {
+ case 0:
+ strlcpy(p, "SIGNAL OK", BUFFER_SIZE(buffer, p));
+ break;
+ case 0x1C:
+ strlcpy(p, "SV TEMP OUT", BUFFER_SIZE(buffer, p));
+ break;
+ case 0x1D:
+ strlcpy(p, "SV WILL BE TEMP OUT", BUFFER_SIZE(buffer, p));
+ break;
+ case 0x1E:
+ break;
+ case 0x1F:
+ strlcpy(p, "MULTIPLE ERRS", BUFFER_SIZE(buffer, p));
+ break;
+ default:
+ strlcpy(p, "TRANSMISSION PROBLEMS", BUFFER_SIZE(buffer, p));
+ break;
+ }
+
+ strlcat(p, "\"", sizeof(buffer));
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO);
+ }
+ }
+ }
+ break;
+
+ case GPS_ALM:
+ break;
+
+ case GPS_EPH:
+ break;
+
+ case GPS_UTC:
+ {
+ UTC utc;
+ char buffer[512];
+ char *p;
+
+ p = buffer;
+
+ get_mbg_utc(&bufp, &utc);
+
+ if (utc.valid)
+ {
+ strlcpy(p, "gps_utc_correction=\"", sizeof(buffer));
+ p += strlen(p);
+ mk_utcinfo(p, utc.t0t.wn, utc.WNlsf, utc.DNt, utc.delta_tls, utc.delta_tlsf, BUFFER_SIZE(buffer, p));
+ strlcat(p, "\"", BUFFER_SIZE(buffer, p));
+ }
+ else
+ {
+ strlcpy(p, "gps_utc_correction=\"<NO UTC DATA>\"", BUFFER_SIZE(buffer, p));
+ }
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO|DEF);
+ }
+ break;
+
+ case GPS_IONO:
+ break;
+
+ case GPS_ASCII_MSG:
+ {
+ ASCII_MSG gps_ascii_msg;
+ char buffer[128];
+
+ get_mbg_ascii_msg(&bufp, &gps_ascii_msg);
+
+ if (gps_ascii_msg.valid)
+ {
+ char buffer1[128];
+ mkreadable(buffer1, sizeof(buffer1), gps_ascii_msg.s, strlen(gps_ascii_msg.s), (int)0);
+
+ snprintf(buffer, sizeof(buffer), "gps_message=\"%s\"", buffer1);
+ }
+ else
+ strlcpy(buffer, "gps_message=<NONE>", sizeof(buffer));
+
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO|DEF);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ msyslog(LOG_DEBUG, "PARSE receiver #%d: gps16x_message: message checksum error: hdr_csum = 0x%x (expected 0x%lx), data_len = %d, data_csum = 0x%x (expected 0x%lx)",
+ CLK_UNIT(parse->peer),
+ header.gps_hdr_csum, mbg_csum(parsetime->parse_msg + 1, 6),
+ header.gps_len,
+ header.gps_data_csum, mbg_csum(bufp, (unsigned)((header.gps_len < sizeof(parsetime->parse_msg)) ? header.gps_len : 0)));
+ }
+ }
+
+ return;
+}
+
+/*------------------------------------------------------------
+ * gps16x_poll - query the reciver peridically
+ */
+static void
+gps16x_poll(
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = peer->procptr->unitptr;
+
+ static GPS_MSG_HDR sequence[] =
+ {
+ { GPS_SW_REV, 0, 0, 0 },
+ { GPS_STAT, 0, 0, 0 },
+ { GPS_UTC, 0, 0, 0 },
+ { GPS_ASCII_MSG, 0, 0, 0 },
+ { GPS_ANT_INFO, 0, 0, 0 },
+ { GPS_CFGH, 0, 0, 0 },
+ { GPS_POS_XYZ, 0, 0, 0 },
+ { GPS_POS_LLA, 0, 0, 0 },
+ { (unsigned short)~0, 0, 0, 0 }
+ };
+
+ int rtc;
+ unsigned char cmd_buffer[64];
+ unsigned char *outp = cmd_buffer;
+ GPS_MSG_HDR *header;
+
+ if (((poll_info_t *)parse->parse_type->cl_data)->rate)
+ {
+ parse->peer->procptr->nextaction = current_time + ((poll_info_t *)parse->parse_type->cl_data)->rate;
+ }
+
+ if (sequence[parse->localstate].gps_cmd == (unsigned short)~0)
+ parse->localstate = 0;
+
+ header = sequence + parse->localstate++;
+
+ *outp++ = SOH; /* start command */
+
+ put_mbg_header(&outp, header);
+ outp = cmd_buffer + 1;
+
+ header->gps_hdr_csum = (short)mbg_csum(outp, 6);
+ put_mbg_header(&outp, header);
+
+#ifdef DEBUG
+ if (debug > 2)
+ {
+ char buffer[128];
+
+ mkreadable(buffer, sizeof(buffer), (char *)cmd_buffer, (unsigned)(outp - cmd_buffer), 1);
+ printf("PARSE receiver #%d: transmitted message #%ld (%d bytes) >%s<\n",
+ CLK_UNIT(parse->peer),
+ parse->localstate - 1,
+ (int)(outp - cmd_buffer),
+ buffer);
+ }
+#endif
+
+ rtc = write(parse->generic->io.fd, cmd_buffer, (unsigned long)(outp - cmd_buffer));
+
+ if (rtc < 0)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: gps16x_poll: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
+ }
+ else
+ if (rtc != outp - cmd_buffer)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: gps16x_poll: failed to send cmd incomplete (%d of %d bytes sent)", CLK_UNIT(parse->peer), rtc, (int)(outp - cmd_buffer));
+ }
+
+ clear_err(parse, ERR_BADIO);
+ return;
+}
+
+/*--------------------------------------------------
+ * init routine - setup timer
+ */
+static int
+gps16x_poll_init(
+ struct parseunit *parse
+ )
+{
+ if (((poll_info_t *)parse->parse_type->cl_data)->rate)
+ {
+ parse->peer->procptr->action = gps16x_poll;
+ gps16x_poll(parse->peer);
+ }
+
+ return 0;
+}
+
+#else
+static void
+gps16x_message(
+ struct parseunit *parse,
+ parsetime_t *parsetime
+ )
+{}
+static int
+gps16x_poll_init(
+ struct parseunit *parse
+ )
+{
+ return 1;
+}
+#endif /* CLOCK_MEINBERG */
+
+/**===========================================================================
+ ** clock polling support
+ **/
+
+/*--------------------------------------------------
+ * direct poll routine
+ */
+static void
+poll_dpoll(
+ struct parseunit *parse
+ )
+{
+ int rtc;
+ const char *ps = ((poll_info_t *)parse->parse_type->cl_data)->string;
+ int ct = ((poll_info_t *)parse->parse_type->cl_data)->count;
+
+ rtc = write(parse->generic->io.fd, ps, (unsigned long)ct);
+ if (rtc < 0)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
+ }
+ else
+ if (rtc != ct)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd incomplete (%d of %d bytes sent)", CLK_UNIT(parse->peer), rtc, ct);
+ }
+ clear_err(parse, ERR_BADIO);
+}
+
+/*--------------------------------------------------
+ * periodic poll routine
+ */
+static void
+poll_poll(
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = peer->procptr->unitptr;
+
+ if (parse->parse_type->cl_poll)
+ parse->parse_type->cl_poll(parse);
+
+ if (((poll_info_t *)parse->parse_type->cl_data)->rate)
+ {
+ parse->peer->procptr->nextaction = current_time + ((poll_info_t *)parse->parse_type->cl_data)->rate;
+ }
+}
+
+/*--------------------------------------------------
+ * init routine - setup timer
+ */
+static int
+poll_init(
+ struct parseunit *parse
+ )
+{
+ if (((poll_info_t *)parse->parse_type->cl_data)->rate)
+ {
+ parse->peer->procptr->action = poll_poll;
+ poll_poll(parse->peer);
+ }
+
+ return 0;
+}
+
+/**===========================================================================
+ ** Trimble support
+ **/
+
+/*-------------------------------------------------------------
+ * trimble TAIP init routine - setup EOL and then do poll_init.
+ */
+static int
+trimbletaip_init(
+ struct parseunit *parse
+ )
+{
+#ifdef HAVE_TERMIOS
+ struct termios tio;
+#endif
+#ifdef HAVE_SYSV_TTYS
+ struct termio tio;
+#endif
+ /*
+ * configure terminal line for trimble receiver
+ */
+ if (TTY_GETATTR(parse->generic->io.fd, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_init: tcgetattr(fd, &tio): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ else
+ {
+ tio.c_cc[VEOL] = TRIMBLETAIP_EOL;
+
+ if (TTY_SETATTR(parse->generic->io.fd, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_init: tcsetattr(fd, &tio): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ }
+ return poll_init(parse);
+}
+
+/*--------------------------------------------------
+ * trimble TAIP event routine - reset receiver upon data format trouble
+ */
+static const char *taipinit[] = {
+ ">FPV00000000<",
+ ">SRM;ID_FLAG=F;CS_FLAG=T;EC_FLAG=F;FR_FLAG=T;CR_FLAG=F<",
+ ">FTM00020001<",
+ (char *)0
+};
+
+static void
+trimbletaip_event(
+ struct parseunit *parse,
+ int event
+ )
+{
+ switch (event)
+ {
+ case CEVNT_BADREPLY: /* reset on garbled input */
+ case CEVNT_TIMEOUT: /* reset on no input */
+ {
+ const char **iv;
+
+ iv = taipinit;
+ while (*iv)
+ {
+ int rtc = write(parse->generic->io.fd, *iv, strlen(*iv));
+ if (rtc < 0)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_event: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
+ return;
+ }
+ else
+ {
+ if (rtc != strlen(*iv))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_event: failed to send cmd incomplete (%d of %d bytes sent)",
+ CLK_UNIT(parse->peer), rtc, (int)strlen(*iv));
+ return;
+ }
+ }
+ iv++;
+ }
+
+ NLOG(NLOG_CLOCKINFO)
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_event: RECEIVER INITIALIZED",
+ CLK_UNIT(parse->peer));
+ }
+ break;
+
+ default: /* ignore */
+ break;
+ }
+}
+
+/*
+ * This driver supports the Trimble SVee Six Plus GPS receiver module.
+ * It should support other Trimble receivers which use the Trimble Standard
+ * Interface Protocol (see below).
+ *
+ * The module has a serial I/O port for command/data and a 1 pulse-per-second
+ * output, about 1 microsecond wide. The leading edge of the pulse is
+ * coincident with the change of the GPS second. This is the same as
+ * the change of the UTC second +/- ~1 microsecond. Some other clocks
+ * specifically use a feature in the data message as a timing reference, but
+ * the SVee Six Plus does not do this. In fact there is considerable jitter
+ * on the timing of the messages, so this driver only supports the use
+ * of the PPS pulse for accurate timing. Where it is determined that
+ * the offset is way off, when first starting up ntpd for example,
+ * the timing of the data stream is used until the offset becomes low enough
+ * (|offset| < CLOCK_MAX), at which point the pps offset is used.
+ *
+ * It can use either option for receiving PPS information - the 'ppsclock'
+ * stream pushed onto the serial data interface to timestamp the Carrier
+ * Detect interrupts, where the 1PPS connects to the CD line. This only
+ * works on SunOS 4.1.x currently. To select this, define PPSPPS in
+ * Config.local. The other option is to use a pulse-stretcher/level-converter
+ * to convert the PPS pulse into a RS232 start pulse & feed this into another
+ * tty port. To use this option, define PPSCLK in Config.local. The pps input,
+ * by whichever method, is handled in ntp_loopfilter.c
+ *
+ * The receiver uses a serial message protocol called Trimble Standard
+ * Interface Protocol (it can support others but this driver only supports
+ * TSIP). Messages in this protocol have the following form:
+ *
+ * <DLE><id> ... <data> ... <DLE><ETX>
+ *
+ * Any bytes within the <data> portion of value 10 hex (<DLE>) are doubled
+ * on transmission and compressed back to one on reception. Otherwise
+ * the values of data bytes can be anything. The serial interface is RS-422
+ * asynchronous using 9600 baud, 8 data bits with odd party (**note** 9 bits
+ * in total!), and 1 stop bit. The protocol supports byte, integer, single,
+ * and double datatypes. Integers are two bytes, sent most significant first.
+ * Singles are IEEE754 single precision floating point numbers (4 byte) sent
+ * sign & exponent first. Doubles are IEEE754 double precision floating point
+ * numbers (8 byte) sent sign & exponent first.
+ * The receiver supports a large set of messages, only a small subset of
+ * which are used here. From driver to receiver the following are used:
+ *
+ * ID Description
+ *
+ * 21 Request current time
+ * 22 Mode Select
+ * 2C Set/Request operating parameters
+ * 2F Request UTC info
+ * 35 Set/Request I/O options
+
+ * From receiver to driver the following are recognised:
+ *
+ * ID Description
+ *
+ * 41 GPS Time
+ * 44 Satellite selection, PDOP, mode
+ * 46 Receiver health
+ * 4B Machine code/status
+ * 4C Report operating parameters (debug only)
+ * 4F UTC correction data (used to get leap second warnings)
+ * 55 I/O options (debug only)
+ *
+ * All others are accepted but ignored.
+ *
+ */
+
+#define PI 3.1415926535898 /* lots of sig figs */
+#define D2R PI/180.0
+
+/*-------------------------------------------------------------------
+ * sendcmd, sendbyte, sendetx, sendflt, sendint implement the command
+ * interface to the receiver.
+ *
+ * CAVEAT: the sendflt, sendint routines are byte order dependend and
+ * float implementation dependend - these must be converted to portable
+ * versions !
+ *
+ * CURRENT LIMITATION: float implementation. This runs only on systems
+ * with IEEE754 floats as native floats
+ */
+
+typedef struct trimble
+{
+ u_long last_msg; /* last message received */
+ u_long last_reset; /* last time a reset was issued */
+ u_char qtracking; /* query tracking status */
+ u_long ctrack; /* current tracking set */
+ u_long ltrack; /* last tracking set */
+} trimble_t;
+
+union uval {
+ u_char bd[8];
+ int iv;
+ float fv;
+ double dv;
+};
+
+struct txbuf
+{
+ short idx; /* index to first unused byte */
+ u_char *txt; /* pointer to actual data buffer */
+};
+
+void sendcmd (struct txbuf *buf, int c);
+void sendbyte (struct txbuf *buf, int b);
+void sendetx (struct txbuf *buf, struct parseunit *parse);
+void sendint (struct txbuf *buf, int a);
+void sendflt (struct txbuf *buf, double a);
+
+void
+sendcmd(
+ struct txbuf *buf,
+ int c
+ )
+{
+ buf->txt[0] = DLE;
+ buf->txt[1] = (u_char)c;
+ buf->idx = 2;
+}
+
+void sendcmd (struct txbuf *buf, int c);
+void sendbyte (struct txbuf *buf, int b);
+void sendetx (struct txbuf *buf, struct parseunit *parse);
+void sendint (struct txbuf *buf, int a);
+void sendflt (struct txbuf *buf, double a);
+
+void
+sendbyte(
+ struct txbuf *buf,
+ int b
+ )
+{
+ if (b == DLE)
+ buf->txt[buf->idx++] = DLE;
+ buf->txt[buf->idx++] = (u_char)b;
+}
+
+void
+sendetx(
+ struct txbuf *buf,
+ struct parseunit *parse
+ )
+{
+ buf->txt[buf->idx++] = DLE;
+ buf->txt[buf->idx++] = ETX;
+
+ if (write(parse->generic->io.fd, buf->txt, (unsigned long)buf->idx) != buf->idx)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: sendetx: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
+ }
+ else
+ {
+#ifdef DEBUG
+ if (debug > 2)
+ {
+ char buffer[256];
+
+ mkreadable(buffer, sizeof(buffer), (char *)buf->txt, (unsigned)buf->idx, 1);
+ printf("PARSE receiver #%d: transmitted message (%d bytes) >%s<\n",
+ CLK_UNIT(parse->peer),
+ buf->idx, buffer);
+ }
+#endif
+ clear_err(parse, ERR_BADIO);
+ }
+}
+
+void
+sendint(
+ struct txbuf *buf,
+ int a
+ )
+{
+ /* send 16bit int, msbyte first */
+ sendbyte(buf, (u_char)((a>>8) & 0xff));
+ sendbyte(buf, (u_char)(a & 0xff));
+}
+
+void
+sendflt(
+ struct txbuf *buf,
+ double a
+ )
+{
+ int i;
+ union uval uval;
+
+ uval.fv = a;
+#ifdef WORDS_BIGENDIAN
+ for (i=0; i<=3; i++)
+#else
+ for (i=3; i>=0; i--)
+#endif
+ sendbyte(buf, uval.bd[i]);
+}
+
+#define TRIM_POS_OPT 0x13 /* output position with high precision */
+#define TRIM_TIME_OPT 0x03 /* use UTC time stamps, on second */
+
+/*--------------------------------------------------
+ * trimble TSIP setup routine
+ */
+static int
+trimbletsip_setup(
+ struct parseunit *parse,
+ const char *reason
+ )
+{
+ u_char buffer[256];
+ struct txbuf buf;
+ trimble_t *t = parse->localdata;
+
+ if (t && t->last_reset &&
+ ((t->last_reset + TRIMBLE_RESET_HOLDOFF) > current_time)) {
+ return 1; /* not yet */
+ }
+
+ if (t)
+ t->last_reset = current_time;
+
+ buf.txt = buffer;
+
+ sendcmd(&buf, CMD_CVERSION); /* request software versions */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_COPERPARAM); /* set operating parameters */
+ sendbyte(&buf, 4); /* static */
+ sendflt(&buf, 5.0*D2R); /* elevation angle mask = 10 deg XXX */
+ sendflt(&buf, 4.0); /* s/n ratio mask = 6 XXX */
+ sendflt(&buf, 12.0); /* PDOP mask = 12 */
+ sendflt(&buf, 8.0); /* PDOP switch level = 8 */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CMODESEL); /* fix mode select */
+ sendbyte(&buf, 1); /* time transfer mode */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CMESSAGE); /* request system message */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CSUPER); /* superpacket fix */
+ sendbyte(&buf, 0x2); /* binary mode */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CIOOPTIONS); /* set I/O options */
+ sendbyte(&buf, TRIM_POS_OPT); /* position output */
+ sendbyte(&buf, 0x00); /* no velocity output */
+ sendbyte(&buf, TRIM_TIME_OPT); /* UTC, compute on seconds */
+ sendbyte(&buf, 0x00); /* no raw measurements */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CUTCPARAM); /* request UTC correction data */
+ sendetx(&buf, parse);
+
+ NLOG(NLOG_CLOCKINFO)
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletsip_setup: RECEIVER RE-INITIALIZED (%s)", CLK_UNIT(parse->peer), reason);
+
+ return 0;
+}
+
+/*--------------------------------------------------
+ * TRIMBLE TSIP check routine
+ */
+static void
+trimble_check(
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = peer->procptr->unitptr;
+ trimble_t *t = parse->localdata;
+ u_char buffer[256];
+ struct txbuf buf;
+ buf.txt = buffer;
+
+ if (t)
+ {
+ if (current_time > t->last_msg + TRIMBLETSIP_IDLE_TIME)
+ (void)trimbletsip_setup(parse, "message timeout");
+ }
+
+ poll_poll(parse->peer); /* emit query string and re-arm timer */
+
+ if (t && t->qtracking)
+ {
+ u_long oldsats = t->ltrack & ~t->ctrack;
+
+ t->qtracking = 0;
+ t->ltrack = t->ctrack;
+
+ if (oldsats)
+ {
+ int i;
+
+ for (i = 0; oldsats; i++) {
+ if (oldsats & (1 << i))
+ {
+ sendcmd(&buf, CMD_CSTATTRACK);
+ sendbyte(&buf, i+1); /* old sat */
+ sendetx(&buf, parse);
+ }
+ oldsats &= ~(1 << i);
+ }
+ }
+
+ sendcmd(&buf, CMD_CSTATTRACK);
+ sendbyte(&buf, 0x00); /* current tracking set */
+ sendetx(&buf, parse);
+ }
+}
+
+/*--------------------------------------------------
+ * TRIMBLE TSIP end routine
+ */
+static void
+trimbletsip_end(
+ struct parseunit *parse
+ )
+{ trimble_t *t = parse->localdata;
+
+ if (t)
+ {
+ free(t);
+ parse->localdata = NULL;
+ }
+ parse->peer->procptr->nextaction = 0;
+ parse->peer->procptr->action = NULL;
+}
+
+/*--------------------------------------------------
+ * TRIMBLE TSIP init routine
+ */
+static int
+trimbletsip_init(
+ struct parseunit *parse
+ )
+{
+#if defined(VEOL) || defined(VEOL2)
+#ifdef HAVE_TERMIOS
+ struct termios tio; /* NEEDED FOR A LONG TIME ! */
+#endif
+#ifdef HAVE_SYSV_TTYS
+ struct termio tio; /* NEEDED FOR A LONG TIME ! */
+#endif
+ /*
+ * allocate local data area
+ */
+ if (!parse->localdata)
+ {
+ trimble_t *t;
+
+ t = (trimble_t *)(parse->localdata = emalloc(sizeof(trimble_t)));
+
+ if (t)
+ {
+ memset((char *)t, 0, sizeof(trimble_t));
+ t->last_msg = current_time;
+ }
+ }
+
+ parse->peer->procptr->action = trimble_check;
+ parse->peer->procptr->nextaction = current_time;
+
+ /*
+ * configure terminal line for ICANON mode with VEOL characters
+ */
+ if (TTY_GETATTR(parse->generic->io.fd, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletsip_init: tcgetattr(%d, &tio): %m", CLK_UNIT(parse->peer), parse->generic->io.fd);
+ return 0;
+ }
+ else
+ {
+ if ((parse_clockinfo[CLK_TYPE(parse->peer)].cl_lflag & ICANON))
+ {
+#ifdef VEOL
+ tio.c_cc[VEOL] = ETX;
+#endif
+#ifdef VEOL2
+ tio.c_cc[VEOL2] = DLE;
+#endif
+ }
+
+ if (TTY_SETATTR(parse->generic->io.fd, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletsip_init: tcsetattr(%d, &tio): %m", CLK_UNIT(parse->peer), parse->generic->io.fd);
+ return 0;
+ }
+ }
+#endif
+ return trimbletsip_setup(parse, "initial startup");
+}
+
+/*------------------------------------------------------------
+ * trimbletsip_event - handle Trimble events
+ * simple evente handler - attempt to re-initialize receiver
+ */
+static void
+trimbletsip_event(
+ struct parseunit *parse,
+ int event
+ )
+{
+ switch (event)
+ {
+ case CEVNT_BADREPLY: /* reset on garbled input */
+ case CEVNT_TIMEOUT: /* reset on no input */
+ (void)trimbletsip_setup(parse, "event BAD_REPLY/TIMEOUT");
+ break;
+
+ default: /* ignore */
+ break;
+ }
+}
+
+/*
+ * getflt, getint convert fields in the incoming data into the
+ * appropriate type of item
+ *
+ * CAVEAT: these routines are currently definitely byte order dependent
+ * and assume Representation(float) == IEEE754
+ * These functions MUST be converted to portable versions (especially
+ * converting the float representation into ntp_fp formats in order
+ * to avoid floating point operations at all!
+ */
+
+static float
+getflt(
+ u_char *bp
+ )
+{
+ union uval uval;
+
+#ifdef WORDS_BIGENDIAN
+ uval.bd[0] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp;
+#else /* ! WORDS_BIGENDIAN */
+ uval.bd[3] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[0] = *bp;
+#endif /* ! WORDS_BIGENDIAN */
+ return uval.fv;
+}
+
+static double
+getdbl(
+ u_char *bp
+ )
+{
+ union uval uval;
+
+#ifdef WORDS_BIGENDIAN
+ uval.bd[0] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp++;
+ uval.bd[4] = *bp++;
+ uval.bd[5] = *bp++;
+ uval.bd[6] = *bp++;
+ uval.bd[7] = *bp;
+#else /* ! WORDS_BIGENDIAN */
+ uval.bd[7] = *bp++;
+ uval.bd[6] = *bp++;
+ uval.bd[5] = *bp++;
+ uval.bd[4] = *bp++;
+ uval.bd[3] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[0] = *bp;
+#endif /* ! WORDS_BIGENDIAN */
+ return uval.dv;
+}
+
+static int
+getshort(
+ unsigned char *p
+ )
+{
+ return get_msb_short(&p);
+}
+
+/*--------------------------------------------------
+ * trimbletsip_message - process trimble messages
+ */
+#define RTOD (180.0 / 3.1415926535898)
+#define mb(_X_) (buffer[2+(_X_)]) /* shortcut for buffer access */
+
+static void
+trimbletsip_message(
+ struct parseunit *parse,
+ parsetime_t *parsetime
+ )
+{
+ unsigned char *buffer = parsetime->parse_msg;
+ unsigned int size = parsetime->parse_msglen;
+
+ if ((size < 4) ||
+ (buffer[0] != DLE) ||
+ (buffer[size-1] != ETX) ||
+ (buffer[size-2] != DLE))
+ {
+#ifdef DEBUG
+ if (debug > 2) {
+ int i;
+
+ printf("TRIMBLE BAD packet, size %d:\n ", size);
+ for (i = 0; i < size; i++) {
+ printf ("%2.2x, ", buffer[i]&0xff);
+ if (i%16 == 15) printf("\n\t");
+ }
+ printf("\n");
+ }
+#endif
+ return;
+ }
+ else
+ {
+ int var_flag;
+ trimble_t *tr = parse->localdata;
+ unsigned int cmd = buffer[1];
+ char pbuffer[200];
+ char *t = pbuffer;
+ cmd_info_t *s;
+
+#ifdef DEBUG
+ if (debug > 3) {
+ int i;
+
+ printf("TRIMBLE packet 0x%02x, size %d:\n ", cmd, size);
+ for (i = 0; i < size; i++) {
+ printf ("%2.2x, ", buffer[i]&0xff);
+ if (i%16 == 15) printf("\n\t");
+ }
+ printf("\n");
+ }
+#endif
+
+ if (tr)
+ tr->last_msg = current_time;
+
+ s = trimble_convert(cmd, trimble_rcmds);
+
+ if (s)
+ {
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "%s=\"", s->varname);
+ }
+ else
+ {
+ DPRINTF(1, ("TRIMBLE UNKNOWN COMMAND 0x%02x\n", cmd));
+ return;
+ }
+
+ var_flag = s->varmode;
+
+ t += strlen(t);
+
+ switch(cmd)
+ {
+ case CMD_RCURTIME:
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "%f, %d, %f",
+ getflt((unsigned char *)&mb(0)), getshort((unsigned char *)&mb(4)),
+ getflt((unsigned char *)&mb(6)));
+ break;
+
+ case CMD_RBEST4:
+ strlcpy(t, "mode: ", BUFFER_SIZE(pbuffer, t));
+ t += strlen(t);
+ switch (mb(0) & 0xF)
+ {
+ default:
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "0x%x", mb(0) & 0x7);
+ break;
+
+ case 1:
+ strlcpy(t, "0D", BUFFER_SIZE(pbuffer, t));
+ break;
+
+ case 3:
+ strlcpy(t, "2D", BUFFER_SIZE(pbuffer, t));
+ break;
+
+ case 4:
+ strlcpy(t, "3D", BUFFER_SIZE(pbuffer, t));
+ break;
+ }
+ t += strlen(t);
+ if (mb(0) & 0x10)
+ strlcpy(t, "-MANUAL, ", BUFFER_SIZE(pbuffer, t));
+ else
+ strlcpy(t, "-AUTO, ", BUFFER_SIZE(pbuffer, t));
+ t += strlen(t);
+
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "satellites %02d %02d %02d %02d, PDOP %.2f, HDOP %.2f, VDOP %.2f, TDOP %.2f",
+ mb(1), mb(2), mb(3), mb(4),
+ getflt((unsigned char *)&mb(5)),
+ getflt((unsigned char *)&mb(9)),
+ getflt((unsigned char *)&mb(13)),
+ getflt((unsigned char *)&mb(17)));
+
+ break;
+
+ case CMD_RVERSION:
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "%d.%d (%d/%d/%d)",
+ mb(0)&0xff, mb(1)&0xff, 1900+(mb(4)&0xff), mb(2)&0xff, mb(3)&0xff);
+ break;
+
+ case CMD_RRECVHEALTH:
+ {
+ static const char *msgs[] =
+ {
+ "Battery backup failed",
+ "Signal processor error",
+ "Alignment error, channel or chip 1",
+ "Alignment error, channel or chip 2",
+ "Antenna feed line fault",
+ "Excessive ref freq. error",
+ "<BIT 6>",
+ "<BIT 7>"
+ };
+
+ int i, bits;
+
+ switch (mb(0) & 0xFF)
+ {
+ default:
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "illegal value 0x%02x", mb(0) & 0xFF);
+ break;
+ case 0x00:
+ strlcpy(t, "doing position fixes", BUFFER_SIZE(pbuffer, t));
+ break;
+ case 0x01:
+ strlcpy(t, "no GPS time yet", BUFFER_SIZE(pbuffer, t));
+ break;
+ case 0x03:
+ strlcpy(t, "PDOP too high", BUFFER_SIZE(pbuffer, t));
+ break;
+ case 0x08:
+ strlcpy(t, "no usable satellites", BUFFER_SIZE(pbuffer, t));
+ break;
+ case 0x09:
+ strlcpy(t, "only ONE usable satellite", BUFFER_SIZE(pbuffer, t));
+ break;
+ case 0x0A:
+ strlcpy(t, "only TWO usable satellites", BUFFER_SIZE(pbuffer, t));
+ break;
+ case 0x0B:
+ strlcpy(t, "only THREE usable satellites", BUFFER_SIZE(pbuffer, t));
+ break;
+ case 0x0C:
+ strlcpy(t, "the chosen satellite is unusable", BUFFER_SIZE(pbuffer, t));
+ break;
+ }
+
+ t += strlen(t);
+
+ bits = mb(1) & 0xFF;
+
+ for (i = 0; i < 8; i++)
+ if (bits & (0x1<<i))
+ {
+ snprintf(t, BUFFER_SIZE(pbuffer, t), ", %s", msgs[i]);
+ t += strlen(t);
+ }
+ }
+ break;
+
+ case CMD_RMESSAGE:
+ mkreadable(t, (int)BUFFER_SIZE(pbuffer, t), (char *)&mb(0), (unsigned)(size - 2 - (&mb(0) - buffer)), 0);
+ break;
+
+ case CMD_RMACHSTAT:
+ {
+ static const char *msgs[] =
+ {
+ "Synthesizer Fault",
+ "Battery Powered Time Clock Fault",
+ "A-to-D Converter Fault",
+ "The almanac stored in the receiver is not complete and current",
+ "<BIT 4>",
+ "<BIT 5",
+ "<BIT 6>",
+ "<BIT 7>"
+ };
+
+ int i, bits;
+
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "machine id 0x%02x", mb(0) & 0xFF);
+ t += strlen(t);
+
+ bits = mb(1) & 0xFF;
+
+ for (i = 0; i < 8; i++)
+ if (bits & (0x1<<i))
+ {
+ snprintf(t, BUFFER_SIZE(pbuffer, t), ", %s", msgs[i]);
+ t += strlen(t);
+ }
+
+ snprintf(t, BUFFER_SIZE(pbuffer, t), ", Superpackets %ssupported", (mb(2) & 0xFF) ? "" :"un" );
+ }
+ break;
+
+ case CMD_ROPERPARAM:
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "%2x %.1f %.1f %.1f %.1f",
+ mb(0), getflt((unsigned char *)&mb(1)), getflt((unsigned char *)&mb(5)),
+ getflt((unsigned char *)&mb(9)), getflt((unsigned char *)&mb(13)));
+ break;
+
+ case CMD_RUTCPARAM:
+ {
+ float t0t = getflt((unsigned char *)&mb(14));
+ short wnt = getshort((unsigned char *)&mb(18));
+ short dtls = getshort((unsigned char *)&mb(12));
+ short wnlsf = getshort((unsigned char *)&mb(20));
+ short dn = getshort((unsigned char *)&mb(22));
+ short dtlsf = getshort((unsigned char *)&mb(24));
+
+ if ((int)t0t != 0)
+ {
+ mk_utcinfo(t, wnt, wnlsf, dn, dtls, dtlsf, BUFFER_SIZE(pbuffer, t));
+ }
+ else
+ {
+ strlcpy(t, "<NO UTC DATA>", BUFFER_SIZE(pbuffer, t));
+ }
+ }
+ break;
+
+ case CMD_RSAT1BIAS:
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "%.1fm %.2fm/s at %.1fs",
+ getflt(&mb(0)), getflt(&mb(4)), getflt(&mb(8)));
+ break;
+
+ case CMD_RIOOPTIONS:
+ {
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "%02x %02x %02x %02x",
+ mb(0), mb(1), mb(2), mb(3));
+ if (mb(0) != TRIM_POS_OPT ||
+ mb(2) != TRIM_TIME_OPT)
+ {
+ (void)trimbletsip_setup(parse, "bad io options");
+ }
+ }
+ break;
+
+ case CMD_RSPOSXYZ:
+ {
+ double x = getflt((unsigned char *)&mb(0));
+ double y = getflt((unsigned char *)&mb(4));
+ double z = getflt((unsigned char *)&mb(8));
+ double f = getflt((unsigned char *)&mb(12));
+
+ if (f > 0.0)
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "x= %.1fm, y= %.1fm, z= %.1fm, time_of_fix= %f sec",
+ x, y, z,
+ f);
+ else
+ return;
+ }
+ break;
+
+ case CMD_RSLLAPOS:
+ {
+ double lat = getflt((unsigned char *)&mb(0));
+ double lng = getflt((unsigned char *)&mb(4));
+ double f = getflt((unsigned char *)&mb(12));
+
+ if (f > 0.0)
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "lat %f %c, long %f %c, alt %.2fm",
+ ((lat < 0.0) ? (-lat) : (lat))*RTOD, (lat < 0.0 ? 'S' : 'N'),
+ ((lng < 0.0) ? (-lng) : (lng))*RTOD, (lng < 0.0 ? 'W' : 'E'),
+ getflt((unsigned char *)&mb(8)));
+ else
+ return;
+ }
+ break;
+
+ case CMD_RDOUBLEXYZ:
+ {
+ double x = getdbl((unsigned char *)&mb(0));
+ double y = getdbl((unsigned char *)&mb(8));
+ double z = getdbl((unsigned char *)&mb(16));
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "x= %.1fm, y= %.1fm, z= %.1fm",
+ x, y, z);
+ }
+ break;
+
+ case CMD_RDOUBLELLA:
+ {
+ double lat = getdbl((unsigned char *)&mb(0));
+ double lng = getdbl((unsigned char *)&mb(8));
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "lat %f %c, lon %f %c, alt %.2fm",
+ ((lat < 0.0) ? (-lat) : (lat))*RTOD, (lat < 0.0 ? 'S' : 'N'),
+ ((lng < 0.0) ? (-lng) : (lng))*RTOD, (lng < 0.0 ? 'W' : 'E'),
+ getdbl((unsigned char *)&mb(16)));
+ }
+ break;
+
+ case CMD_RALLINVIEW:
+ {
+ int i, sats;
+
+ strlcpy(t, "mode: ", BUFFER_SIZE(pbuffer, t));
+ t += strlen(t);
+ switch (mb(0) & 0x7)
+ {
+ default:
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "0x%x", mb(0) & 0x7);
+ break;
+
+ case 3:
+ strlcpy(t, "2D", BUFFER_SIZE(pbuffer, t));
+ break;
+
+ case 4:
+ strlcpy(t, "3D", BUFFER_SIZE(pbuffer, t));
+ break;
+ }
+ t += strlen(t);
+ if (mb(0) & 0x8)
+ strlcpy(t, "-MANUAL, ", BUFFER_SIZE(pbuffer, t));
+ else
+ strlcpy(t, "-AUTO, ", BUFFER_SIZE(pbuffer, t));
+ t += strlen(t);
+
+ sats = (mb(0)>>4) & 0xF;
+
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "PDOP %.2f, HDOP %.2f, VDOP %.2f, TDOP %.2f, %d satellite%s in view: ",
+ getflt((unsigned char *)&mb(1)),
+ getflt((unsigned char *)&mb(5)),
+ getflt((unsigned char *)&mb(9)),
+ getflt((unsigned char *)&mb(13)),
+ sats, (sats == 1) ? "" : "s");
+ t += strlen(t);
+
+ for (i=0; i < sats; i++)
+ {
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "%s%02d", i ? ", " : "", mb(17+i));
+ t += strlen(t);
+ if (tr)
+ tr->ctrack |= (1 << (mb(17+i)-1));
+ }
+
+ if (tr)
+ { /* mark for tracking status query */
+ tr->qtracking = 1;
+ }
+ }
+ break;
+
+ case CMD_RSTATTRACK:
+ {
+ snprintf(t-2, BUFFER_SIZE(pbuffer, t-2), "[%02d]=\"", mb(0)); /* add index to var name */
+ t += strlen(t);
+
+ if (getflt((unsigned char *)&mb(4)) < 0.0)
+ {
+ strlcpy(t, "<NO MEASUREMENTS>", BUFFER_SIZE(pbuffer, t));
+ var_flag &= ~DEF;
+ }
+ else
+ {
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "ch=%d, acq=%s, eph=%d, signal_level= %5.2f, elevation= %5.2f, azimuth= %6.2f",
+ (mb(1) & 0xFF)>>3,
+ mb(2) ? ((mb(2) == 1) ? "ACQ" : "SRCH") : "NEVER",
+ mb(3),
+ getflt((unsigned char *)&mb(4)),
+ getflt((unsigned char *)&mb(12)) * RTOD,
+ getflt((unsigned char *)&mb(16)) * RTOD);
+ t += strlen(t);
+ if (mb(20))
+ {
+ var_flag &= ~DEF;
+ strlcpy(t, ", OLD", BUFFER_SIZE(pbuffer, t));
+ }
+ t += strlen(t);
+ if (mb(22))
+ {
+ if (mb(22) == 1)
+ strlcpy(t, ", BAD PARITY", BUFFER_SIZE(pbuffer, t));
+ else
+ if (mb(22) == 2)
+ strlcpy(t, ", BAD EPH HEALTH", BUFFER_SIZE(pbuffer, t));
+ }
+ t += strlen(t);
+ if (mb(23))
+ strlcpy(t, ", collecting data", BUFFER_SIZE(pbuffer, t));
+ }
+ }
+ break;
+
+ default:
+ strlcpy(t, "<UNDECODED>", BUFFER_SIZE(pbuffer, t));
+ break;
+ }
+ t += strlen(t);
+
+ strlcpy(t,"\"", BUFFER_SIZE(pbuffer, t));
+ set_var(&parse->kv, pbuffer, sizeof(pbuffer), var_flag);
+ }
+}
+
+
+/**============================================================
+ ** RAWDCF support
+ **/
+
+/*--------------------------------------------------
+ * rawdcf_init_1 - set up modem lines for RAWDCF receivers
+ * SET DTR line
+ */
+#if defined(TIOCMSET) && (defined(TIOCM_DTR) || defined(CIOCM_DTR))
+static int
+rawdcf_init_1(
+ struct parseunit *parse
+ )
+{
+ /* fixed 2000 for using with Linux by Wolfram Pienkoss <wp@bszh.de> */
+ /*
+ * You can use the RS232 to supply the power for a DCF77 receiver.
+ * Here a voltage between the DTR and the RTS line is used. Unfortunately
+ * the name has changed from CIOCM_DTR to TIOCM_DTR recently.
+ */
+ int sl232;
+
+ if (ioctl(parse->generic->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_1: WARNING: ioctl(fd, TIOCMGET, [C|T]IOCM_DTR): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+
+#ifdef TIOCM_DTR
+ sl232 = (sl232 & ~TIOCM_RTS) | TIOCM_DTR; /* turn on DTR, clear RTS for power supply */
+#else
+ sl232 = (sl232 & ~CIOCM_RTS) | CIOCM_DTR; /* turn on DTR, clear RTS for power supply */
+#endif
+
+ if (ioctl(parse->generic->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_1: WARNING: ioctl(fd, TIOCMSET, [C|T]IOCM_DTR): %m", CLK_UNIT(parse->peer));
+ }
+ return 0;
+}
+#else
+static int
+rawdcfdtr_init_1(
+ struct parseunit *parse
+ )
+{
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_1: WARNING: OS interface incapable of setting DTR to power DCF modules", CLK_UNIT(parse->peer));
+ return 0;
+}
+#endif /* DTR initialisation type */
+
+/*--------------------------------------------------
+ * rawdcf_init_2 - set up modem lines for RAWDCF receivers
+ * CLR DTR line, SET RTS line
+ */
+#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
+static int
+rawdcf_init_2(
+ struct parseunit *parse
+ )
+{
+ /* fixed 2000 for using with Linux by Wolfram Pienkoss <wp@bszh.de> */
+ /*
+ * You can use the RS232 to supply the power for a DCF77 receiver.
+ * Here a voltage between the DTR and the RTS line is used. Unfortunately
+ * the name has changed from CIOCM_DTR to TIOCM_DTR recently.
+ */
+ int sl232;
+
+ if (ioctl(parse->generic->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_2: WARNING: ioctl(fd, TIOCMGET, [C|T]IOCM_RTS): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+
+#ifdef TIOCM_RTS
+ sl232 = (sl232 & ~TIOCM_DTR) | TIOCM_RTS; /* turn on RTS, clear DTR for power supply */
+#else
+ sl232 = (sl232 & ~CIOCM_DTR) | CIOCM_RTS; /* turn on RTS, clear DTR for power supply */
+#endif
+
+ if (ioctl(parse->generic->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_2: WARNING: ioctl(fd, TIOCMSET, [C|T]IOCM_RTS): %m", CLK_UNIT(parse->peer));
+ }
+ return 0;
+}
+#else
+static int
+rawdcf_init_2(
+ struct parseunit *parse
+ )
+{
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_2: WARNING: OS interface incapable of setting RTS to power DCF modules", CLK_UNIT(parse->peer));
+ return 0;
+}
+#endif /* DTR initialisation type */
+
+#else /* defined(REFCLOCK) && defined(PARSE) */
+NONEMPTY_TRANSLATION_UNIT
+#endif /* defined(REFCLOCK) && defined(PARSE) */
+
+/*
+ * History:
+ *
+ * refclock_parse.c,v
+ * Revision 4.81 2009/05/01 10:15:29 kardel
+ * use new refclock_ppsapi interface
+ *
+ * Revision 4.80 2007/08/11 12:06:29 kardel
+ * update comments wrt/ to PPS
+ *
+ * Revision 4.79 2007/08/11 11:52:23 kardel
+ * - terminate io bindings before io_closeclock() will close our file descriptor
+ *
+ * Revision 4.78 2006/12/22 20:08:27 kardel
+ * Bug 746 (RFE): add configuration for Expert mouseCLOCK USB v2.0 as mode 19
+ *
+ * Revision 4.77 2006/08/05 07:44:49 kardel
+ * support optionally separate PPS devices via /dev/refclockpps-{0..3}
+ *
+ * Revision 4.76 2006/06/22 18:40:47 kardel
+ * clean up signedness (gcc 4)
+ *
+ * Revision 4.75 2006/06/22 16:58:10 kardel
+ * Bug #632: call parse_ppsapi() in parse_ctl() when updating
+ * the PPS offset. Fix sign of offset passed to kernel.
+ *
+ * Revision 4.74 2006/06/18 21:18:37 kardel
+ * NetBSD Coverity CID 3796: possible NULL deref
+ *
+ * Revision 4.73 2006/05/26 14:23:46 kardel
+ * cleanup of copyright info
+ *
+ * Revision 4.72 2006/05/26 14:19:43 kardel
+ * cleanup of ioctl cruft
+ *
+ * Revision 4.71 2006/05/26 14:15:57 kardel
+ * delay adding refclock to async refclock io after all initializations
+ *
+ * Revision 4.70 2006/05/25 18:20:50 kardel
+ * bug #619
+ * terminate parse io engine after de-registering
+ * from refclock io engine
+ *
+ * Revision 4.69 2006/05/25 17:28:02 kardel
+ * complete refclock io structure initialization *before* inserting it into the
+ * refclock input machine (avoids null pointer deref) (bug #619)
+ *
+ * Revision 4.68 2006/05/01 17:02:51 kardel
+ * copy receiver method also for newlwy created receive buffers
+ *
+ * Revision 4.67 2006/05/01 14:37:29 kardel
+ * If an input buffer parses into more than one message do insert the
+ * parsed message in a new input buffer instead of processing it
+ * directly. This avoids deed complicated processing in signal
+ * handling.
+ *
+ * Revision 4.66 2006/03/18 00:45:30 kardel
+ * coverity fixes found in NetBSD coverity scan
+ *
+ * Revision 4.65 2006/01/26 06:08:33 kardel
+ * output errno on PPS setup failure
+ *
+ * Revision 4.64 2005/11/09 20:44:47 kardel
+ * utilize full PPS timestamp resolution from PPS API
+ *
+ * Revision 4.63 2005/10/07 22:10:25 kardel
+ * bounded buffer implementation
+ *
+ * Revision 4.62.2.2 2005/09/25 10:20:16 kardel
+ * avoid unexpected buffer overflows due to sprintf("%f") on strange floats:
+ * replace almost all str* and *printf functions be their buffer bounded
+ * counterparts
+ *
+ * Revision 4.62.2.1 2005/08/27 16:19:27 kardel
+ * limit re-set rate of trimble clocks
+ *
+ * Revision 4.62 2005/08/06 17:40:00 kardel
+ * cleanup size handling wrt/ to buffer boundaries
+ *
+ * Revision 4.61 2005/07/27 21:16:19 kardel
+ * fix a long (> 11 years) misconfiguration wrt/ Meinberg cflag factory
+ * default setup. CSTOPB was missing for the 7E2 default data format of
+ * the DCF77 clocks.
+ *
+ * Revision 4.60 2005/07/17 21:14:44 kardel
+ * change contents of version string to include the RCS/CVS Id
+ *
+ * Revision 4.59 2005/07/06 06:56:38 kardel
+ * syntax error
+ *
+ * Revision 4.58 2005/07/04 13:10:40 kardel
+ * fix bug 455: tripping over NULL pointer on cleanup
+ * fix shadow storage logic for ppsphaseadjust and trustime wrt/ time2
+ * fix compiler warnings for some platforms wrt/ printf formatstrings and
+ * varying structure element sizes
+ * reorder assignment in binding to avoid tripping over NULL pointers
+ *
+ * Revision 4.57 2005/06/25 09:25:19 kardel
+ * sort out log output sequence
+ *
+ * Revision 4.56 2005/06/14 21:47:27 kardel
+ * collect samples only if samples are ok (sync or trusted flywheel)
+ * propagate pps phase adjustment value to kernel via PPSAPI to help HARDPPS
+ * en- and dis-able HARDPPS in correlation to receiver sync state
+ *
+ * Revision 4.55 2005/06/02 21:28:31 kardel
+ * clarify trust logic
+ *
+ * Revision 4.54 2005/06/02 17:06:49 kardel
+ * change status reporting to use fixed refclock_report()
+ *
+ * Revision 4.53 2005/06/02 16:33:31 kardel
+ * fix acceptance of clocks unsync clocks right at start
+ *
+ * Revision 4.52 2005/05/26 21:55:06 kardel
+ * cleanup status reporting
+ *
+ * Revision 4.51 2005/05/26 19:19:14 kardel
+ * implement fast refclock startup
+ *
+ * Revision 4.50 2005/04/16 20:51:35 kardel
+ * set hardpps_enable = 1 when binding a kernel PPS source
+ *
+ * Revision 4.49 2005/04/16 17:29:26 kardel
+ * add non polling clock type 18 for just listenning to Meinberg clocks
+ *
+ * Revision 4.48 2005/04/16 16:22:27 kardel
+ * bk sync 20050415 ntp-dev
+ *
+ * Revision 4.47 2004/11/29 10:42:48 kardel
+ * bk sync ntp-dev 20041129
+ *
+ * Revision 4.46 2004/11/29 10:26:29 kardel
+ * keep fudgetime2 in sync with trusttime/ppsphaseadjust depending in flag1
+ *
+ * Revision 4.45 2004/11/14 20:53:20 kardel
+ * clear PPS flags after using them
+ *
+ * Revision 4.44 2004/11/14 15:29:41 kardel
+ * support PPSAPI, upgrade Copyright to Berkeley style
+ *
+ * Revision 4.43 2001/05/26 22:53:16 kardel
+ * 20010526 reconcilation
+ *
+ * Revision 4.42 2000/05/14 15:31:51 kardel
+ * PPSAPI && RAWDCF modemline support
+ *
+ * Revision 4.41 2000/04/09 19:50:45 kardel
+ * fixed rawdcfdtr_init() -> rawdcf_init_1
+ *
+ * Revision 4.40 2000/04/09 15:27:55 kardel
+ * modem line fiddle in rawdcf_init_2
+ *
+ * Revision 4.39 2000/03/18 09:16:55 kardel
+ * PPSAPI integration
+ *
+ * Revision 4.38 2000/03/05 20:25:06 kardel
+ * support PPSAPI
+ *
+ * Revision 4.37 2000/03/05 20:11:14 kardel
+ * 4.0.99g reconcilation
+ *
+ * Revision 4.36 1999/11/28 17:18:20 kardel
+ * disabled burst mode
+ *
+ * Revision 4.35 1999/11/28 09:14:14 kardel
+ * RECON_4_0_98F
+ *
+ * Revision 4.34 1999/05/14 06:08:05 kardel
+ * store current_time in a suitable container (u_long)
+ *
+ * Revision 4.33 1999/05/13 21:48:38 kardel
+ * double the no response timeout interval
+ *
+ * Revision 4.32 1999/05/13 20:09:13 kardel
+ * complain only about missing polls after a full poll interval
+ *
+ * Revision 4.31 1999/05/13 19:59:32 kardel
+ * add clock type 16 for RTS set DTR clr in RAWDCF
+ *
+ * Revision 4.30 1999/02/28 20:36:43 kardel
+ * fixed printf fmt
+ *
+ * Revision 4.29 1999/02/28 19:58:23 kardel
+ * updated copyright information
+ *
+ * Revision 4.28 1999/02/28 19:01:50 kardel
+ * improved debug out on sent Meinberg messages
+ *
+ * Revision 4.27 1999/02/28 18:05:55 kardel
+ * no linux/ppsclock.h stuff
+ *
+ * Revision 4.26 1999/02/28 15:27:27 kardel
+ * wharton clock integration
+ *
+ * Revision 4.25 1999/02/28 14:04:46 kardel
+ * added missing double quotes to UTC information string
+ *
+ * Revision 4.24 1999/02/28 12:06:50 kardel
+ * (parse_control): using gmprettydate instead of prettydate()
+ * (mk_utcinfo): new function for formatting GPS derived UTC information
+ * (gps16x_message): changed to use mk_utcinfo()
+ * (trimbletsip_message): changed to use mk_utcinfo()
+ * ignoring position information in unsynchronized mode
+ * (parse_start): augument linux support for optional ASYNC_LOW_LATENCY
+ *
+ * Revision 4.23 1999/02/23 19:47:53 kardel
+ * fixed #endifs
+ * (stream_receive): fixed formats
+ *
+ * Revision 4.22 1999/02/22 06:21:02 kardel
+ * use new autoconfig symbols
+ *
+ * Revision 4.21 1999/02/21 12:18:13 kardel
+ * 4.91f reconcilation
+ *
+ * Revision 4.20 1999/02/21 10:53:36 kardel
+ * initial Linux PPSkit version
+ *
+ * Revision 4.19 1999/02/07 09:10:45 kardel
+ * clarify STREAMS mitigation rules in comment
+ *
+ * Revision 4.18 1998/12/20 23:45:34 kardel
+ * fix types and warnings
+ *
+ * Revision 4.17 1998/11/15 21:24:51 kardel
+ * cannot access mbg_ routines when CLOCK_MEINBERG
+ * is not defined
+ *
+ * Revision 4.16 1998/11/15 20:28:17 kardel
+ * Release 4.0.73e13 reconcilation
+ *
+ * Revision 4.15 1998/08/22 21:56:08 kardel
+ * fixed IO handling for non-STREAM IO
+ *
+ * Revision 4.14 1998/08/16 19:00:48 kardel
+ * (gps16x_message): reduced UTC parameter information (dropped A0,A1)
+ * made uval a local variable (killed one of the last globals)
+ * (sendetx): added logging of messages when in debug mode
+ * (trimble_check): added periodic checks to facilitate re-initialization
+ * (trimbletsip_init): made use of EOL character if in non-kernel operation
+ * (trimbletsip_message): extended message interpretation
+ * (getdbl): fixed data conversion
+ *
+ * Revision 4.13 1998/08/09 22:29:13 kardel
+ * Trimble TSIP support
+ *
+ * Revision 4.12 1998/07/11 10:05:34 kardel
+ * Release 4.0.73d reconcilation
+ *
+ * Revision 4.11 1998/06/14 21:09:42 kardel
+ * Sun acc cleanup
+ *
+ * Revision 4.10 1998/06/13 12:36:45 kardel
+ * signed/unsigned, name clashes
+ *
+ * Revision 4.9 1998/06/12 15:30:00 kardel
+ * prototype fixes
+ *
+ * Revision 4.8 1998/06/12 11:19:42 kardel
+ * added direct input processing routine for refclocks in
+ * order to avaiod that single character io gobbles up all
+ * receive buffers and drops input data. (Problem started
+ * with fast machines so a character a buffer was possible
+ * one of the few cases where faster machines break existing
+ * allocation algorithms)
+ *
+ * Revision 4.7 1998/06/06 18:35:20 kardel
+ * (parse_start): added BURST mode initialisation
+ *
+ * Revision 4.6 1998/05/27 06:12:46 kardel
+ * RAWDCF_BASEDELAY default added
+ * old comment removed
+ * casts for ioctl()
+ *
+ * Revision 4.5 1998/05/25 22:05:09 kardel
+ * RAWDCF_SETDTR option removed
+ * clock type 14 attempts to set DTR for
+ * power supply of RAWDCF receivers
+ *
+ * Revision 4.4 1998/05/24 16:20:47 kardel
+ * updated comments referencing Meinberg clocks
+ * added RAWDCF clock with DTR set option as type 14
+ *
+ * Revision 4.3 1998/05/24 10:48:33 kardel
+ * calibrated CONRAD RAWDCF default fudge factor
+ *
+ * Revision 4.2 1998/05/24 09:59:35 kardel
+ * corrected version information (ntpq support)
+ *
+ * Revision 4.1 1998/05/24 09:52:31 kardel
+ * use fixed format only (new IO model)
+ * output debug to stdout instead of msyslog()
+ * don't include >"< in ASCII output in order not to confuse
+ * ntpq parsing
+ *
+ * Revision 4.0 1998/04/10 19:52:11 kardel
+ * Start 4.0 release version numbering
+ *
+ * Revision 1.2 1998/04/10 19:28:04 kardel
+ * initial NTP VERSION 4 integration of PARSE with GPS166 binary support
+ * derived from 3.105.1.2 from V3 tree
+ *
+ * Revision information 3.1 - 3.105 from log deleted 1998/04/10 kardel
+ *
+ */
diff --git a/ntpd/refclock_pcf.c b/ntpd/refclock_pcf.c
new file mode 100644
index 0000000..4379832
--- /dev/null
+++ b/ntpd/refclock_pcf.c
@@ -0,0 +1,227 @@
+/*
+ * refclock_pcf - clock driver for the Conrad parallel port radio clock
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_PCF)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the parallel port radio clock sold by Conrad
+ * Electronic under order numbers 967602 and 642002.
+ *
+ * It requires that the local timezone be CET/CEST and that the pcfclock
+ * device driver be installed. A device driver for Linux is available at
+ * http://home.pages.de/~voegele/pcf.html. Information about a FreeBSD
+ * driver is available at http://schumann.cx/pcfclock/.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/pcfclocks/%d"
+#define OLDDEVICE "/dev/pcfclock%d"
+#define PRECISION (-1) /* precision assumed (about 0.5 s) */
+#define REFID "PCF"
+#define DESCRIPTION "Conrad parallel port radio clock"
+
+#define LENPCF 18 /* timecode length */
+
+/*
+ * Function prototypes
+ */
+static int pcf_start (int, struct peer *);
+static void pcf_shutdown (int, struct peer *);
+static void pcf_poll (int, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_pcf = {
+ pcf_start, /* start up driver */
+ pcf_shutdown, /* shut down driver */
+ pcf_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * pcf_start - open the device and initialize data for processing
+ */
+static int
+pcf_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ int fd;
+ char device[128];
+
+ /*
+ * Open device file for reading.
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+ fd = open(device, O_RDONLY);
+ if (fd == -1) {
+ snprintf(device, sizeof(device), OLDDEVICE, unit);
+ fd = open(device, O_RDONLY);
+ }
+#ifdef DEBUG
+ if (debug)
+ printf ("starting PCF with device %s\n",device);
+#endif
+ if (fd == -1) {
+ return (0);
+ }
+
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ /* one transmission takes 172.5 milliseconds since the radio clock
+ transmits 69 bits with a period of 2.5 milliseconds per bit */
+ pp->fudgetime1 = 0.1725;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+ return (1);
+}
+
+
+/*
+ * pcf_shutdown - shut down the clock
+ */
+static void
+pcf_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ if (NULL != pp)
+ close(pp->io.fd);
+}
+
+
+/*
+ * pcf_poll - called by the transmit procedure
+ */
+static void
+pcf_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ char buf[LENPCF];
+ struct tm tm, *tp;
+ time_t t;
+
+ pp = peer->procptr;
+
+ buf[0] = 0;
+ if (read(pp->io.fd, buf, sizeof(buf)) < sizeof(buf) || buf[0] != 9) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+
+ ZERO(tm);
+
+ tm.tm_mday = buf[11] * 10 + buf[10];
+ tm.tm_mon = buf[13] * 10 + buf[12] - 1;
+ tm.tm_year = buf[15] * 10 + buf[14];
+ tm.tm_hour = buf[7] * 10 + buf[6];
+ tm.tm_min = buf[5] * 10 + buf[4];
+ tm.tm_sec = buf[3] * 10 + buf[2];
+ tm.tm_isdst = (buf[8] & 1) ? 1 : (buf[8] & 2) ? 0 : -1;
+
+ /*
+ * Y2K convert the 2-digit year
+ */
+ if (tm.tm_year < 99)
+ tm.tm_year += 100;
+
+ t = mktime(&tm);
+ if (t == (time_t) -1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+#if defined(__GLIBC__) && defined(_BSD_SOURCE)
+ if ((tm.tm_isdst > 0 && tm.tm_gmtoff != 7200)
+ || (tm.tm_isdst == 0 && tm.tm_gmtoff != 3600)
+ || tm.tm_isdst < 0) {
+#ifdef DEBUG
+ if (debug)
+ printf ("local time zone not set to CET/CEST\n");
+#endif
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+#endif
+
+ pp->lencode = strftime(pp->a_lastcode, BMAX, "%Y %m %d %H %M %S", &tm);
+
+#if defined(_REENTRANT) || defined(_THREAD_SAFE)
+ tp = gmtime_r(&t, &tm);
+#else
+ tp = gmtime(&t);
+#endif
+ if (!tp) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+
+ get_systime(&pp->lastrec);
+ pp->polls++;
+ pp->year = tp->tm_year + 1900;
+ pp->day = tp->tm_yday + 1;
+ pp->hour = tp->tm_hour;
+ pp->minute = tp->tm_min;
+ pp->second = tp->tm_sec;
+ pp->nsec = buf[16] * 31250000;
+ if (buf[17] & 1)
+ pp->nsec += 500000000;
+
+#ifdef DEBUG
+ if (debug)
+ printf ("pcf%d: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
+ unit, pp->year, tp->tm_mon + 1, tp->tm_mday, pp->hour,
+ pp->minute, pp->second);
+#endif
+
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ if ((buf[1] & 1) && !(pp->sloppyclockflag & CLK_FLAG2))
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+}
+#else
+int refclock_pcf_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_pst.c b/ntpd/refclock_pst.c
new file mode 100644
index 0000000..39be051
--- /dev/null
+++ b/ntpd/refclock_pst.c
@@ -0,0 +1,318 @@
+/*
+ * refclock_pst - clock driver for PSTI/Traconex WWV/WWVH receivers
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_PST)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the PSTI 1010 and Traconex 1020 WWV/WWVH
+ * Receivers. No specific claim of accuracy is made for these receiver,
+ * but actual experience suggests that 10 ms would be a conservative
+ * assumption.
+ *
+ * The DIPswitches should be set for 9600 bps line speed, 24-hour day-
+ * of-year format and UTC time zone. Automatic correction for DST should
+ * be disabled. It is very important that the year be set correctly in
+ * the DIPswitches; otherwise, the day of year will be incorrect after
+ * 28 April of a normal or leap year. The propagation delay DIPswitches
+ * should be set according to the distance from the transmitter for both
+ * WWV and WWVH, as described in the instructions. While the delay can
+ * be set only to within 11 ms, the fudge time1 parameter can be used
+ * for vernier corrections.
+ *
+ * Using the poll sequence QTQDQM, the response timecode is in three
+ * sections totalling 50 ASCII printing characters, as concatenated by
+ * the driver, in the following format:
+ *
+ * ahh:mm:ss.fffs<cr> yy/dd/mm/ddd<cr> frdzycchhSSFTttttuuxx<cr>
+ *
+ * on-time = first <cr>
+ * hh:mm:ss.fff = hours, minutes, seconds, milliseconds
+ * a = AM/PM indicator (' ' for 24-hour mode)
+ * yy = year (from internal switches)
+ * dd/mm/ddd = day of month, month, day of year
+ * s = daylight-saving indicator (' ' for 24-hour mode)
+ * f = frequency enable (O = all frequencies enabled)
+ * r = baud rate (3 = 1200, 6 = 9600)
+ * d = features indicator (@ = month/day display enabled)
+ * z = time zone (0 = UTC)
+ * y = year (5 = 91)
+ * cc = WWV propagation delay (52 = 22 ms)
+ * hh = WWVH propagation delay (81 = 33 ms)
+ * SS = status (80 or 82 = operating correctly)
+ * F = current receive frequency (4 = 15 MHz)
+ * T = transmitter (C = WWV, H = WWVH)
+ * tttt = time since last update (0000 = minutes)
+ * uu = flush character (03 = ^c)
+ * xx = 94 (unknown)
+ *
+ * The alarm condition is indicated by other than '8' at A, which occurs
+ * during initial synchronization and when received signal is lost for
+ * an extended period; unlock condition is indicated by other than
+ * "0000" in the tttt subfield at Q.
+ *
+ * Fudge Factors
+ *
+ * There are no special fudge factors other than the generic.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/wwv%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define WWVREFID "WWV\0" /* WWV reference ID */
+#define WWVHREFID "WWVH" /* WWVH reference ID */
+#define DESCRIPTION "PSTI/Traconex WWV/WWVH Receiver" /* WRU */
+#define PST_PHI (10e-6) /* max clock oscillator offset */
+#define LENPST 46 /* min timecode length */
+
+/*
+ * Unit control structure
+ */
+struct pstunit {
+ int tcswitch; /* timecode switch */
+ char *lastptr; /* pointer to timecode data */
+};
+
+/*
+ * Function prototypes
+ */
+static int pst_start (int, struct peer *);
+static void pst_shutdown (int, struct peer *);
+static void pst_receive (struct recvbuf *);
+static void pst_poll (int, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_pst = {
+ pst_start, /* start up driver */
+ pst_shutdown, /* shut down driver */
+ pst_poll, /* transmit poll message */
+ noentry, /* not used (old pst_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old pst_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * pst_start - open the devices and initialize data for processing
+ */
+static int
+pst_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+ fd = refclock_open(device, SPEED232, LDISC_CLK);
+ if (fd <= 0)
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->io.clock_recv = pst_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ return (0);
+ }
+ pp->unitptr = up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, WWVREFID, 4);
+ return (1);
+}
+
+
+/*
+ * pst_shutdown - shut down the clock
+ */
+static void
+pst_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ if (NULL != up)
+ free(up);
+}
+
+
+/*
+ * pst_receive - receive data from the serial interface
+ */
+static void
+pst_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ u_long ltemp;
+ char ampmchar; /* AM/PM indicator */
+ char daychar; /* standard/daylight indicator */
+ char junque[10]; /* "yy/dd/mm/" discard */
+ char info[14]; /* "frdzycchhSSFT" clock info */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+ up->lastptr += refclock_gtlin(rbufp, up->lastptr, pp->a_lastcode
+ + BMAX - 2 - up->lastptr, &trtmp);
+ *up->lastptr++ = ' ';
+ *up->lastptr = '\0';
+
+ /*
+ * Note we get a buffer and timestamp for each <cr>, but only
+ * the first timestamp is retained.
+ */
+ if (up->tcswitch == 0)
+ pp->lastrec = trtmp;
+ up->tcswitch++;
+ pp->lencode = up->lastptr - pp->a_lastcode;
+ if (up->tcswitch < 3)
+ return;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENPST) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format:
+ * "ahh:mm:ss.fffs yy/dd/mm/ddd frdzycchhSSFTttttuuxx"
+ */
+ if (sscanf(pp->a_lastcode,
+ "%c%2d:%2d:%2d.%3ld%c %9s%3d%13s%4ld",
+ &ampmchar, &pp->hour, &pp->minute, &pp->second, &pp->nsec,
+ &daychar, junque, &pp->day, info, &ltemp) != 10) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ pp->nsec *= 1000000;
+
+ /*
+ * Decode synchronization, quality and last update. If
+ * unsynchronized, set the leap bits accordingly and exit. Once
+ * synchronized, the dispersion depends only on when the clock
+ * was last heard, which depends on the time since last update,
+ * as reported by the clock.
+ */
+ if (info[9] != '8')
+ pp->leap = LEAP_NOTINSYNC;
+ if (info[12] == 'H')
+ memcpy((char *)&pp->refid, WWVHREFID, 4);
+ else
+ memcpy((char *)&pp->refid, WWVREFID, 4);
+ if (peer->stratum <= 1)
+ peer->refid = pp->refid;
+ if (ltemp == 0)
+ pp->lastref = pp->lastrec;
+ pp->disp = PST_PHI * ltemp * 60;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp))
+ refclock_report(peer, CEVNT_BADTIME);
+ else if (peer->disp > MAXDISTANCE)
+ refclock_receive(peer);
+}
+
+
+/*
+ * pst_poll - called by the transmit procedure
+ */
+static void
+pst_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Time to poll the clock. The PSTI/Traconex clock responds to a
+ * "QTQDQMT" by returning a timecode in the format specified
+ * above. Note there is no checking on state, since this may not
+ * be the only customer reading the clock. Only one customer
+ * need poll the clock; all others just listen in. If the clock
+ * becomes unreachable, declare a timeout and keep going.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+ up->tcswitch = 0;
+ up->lastptr = pp->a_lastcode;
+ if (write(pp->io.fd, "QTQDQMT", 6) != 6)
+ refclock_report(peer, CEVNT_FAULT);
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("pst: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+ pp->polls++;
+}
+
+#else
+int refclock_pst_int;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_ripencc.c b/ntpd/refclock_ripencc.c
new file mode 100644
index 0000000..75d8f38
--- /dev/null
+++ b/ntpd/refclock_ripencc.c
@@ -0,0 +1,5255 @@
+/*
+ * $Id: refclock_ripencc.c,v 1.13 2002/06/18 14:20:55 marks Exp marks $
+ *
+ * Copyright (c) 2002 RIPE NCC
+ *
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of the author not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+ * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ *
+ *
+ * This driver was developed for use with the RIPE NCC TTM project.
+ *
+ *
+ * The initial driver was developed by Daniel Karrenberg <dfk@ripe.net>
+ * using the code made available by Trimble. This was for xntpd-3.x.x
+ *
+ * Rewrite of the driver for ntpd-4.x.x by Mark Santcroos <marks@ripe.net>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#if defined(REFCLOCK) && defined(CLOCK_RIPENCC)
+
+#include "ntp_stdlib.h"
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_io.h"
+
+#ifdef HAVE_PPSAPI
+# include "ppsapi_timepps.h"
+#endif
+
+/*
+ * Definitions
+ */
+
+/* we are on little endian */
+#define BYTESWAP
+
+/*
+ * DEBUG statements: uncomment if necessary
+ */
+/* #define DEBUG_NCC */ /* general debug statements */
+/* #define DEBUG_PPS */ /* debug pps */
+/* #define DEBUG_RAW */ /* print raw packets */
+
+#define TRIMBLE_OUTPUT_FUNC
+#define TSIP_VERNUM "7.12a"
+
+#ifndef FALSE
+#define FALSE (0)
+#define TRUE (!FALSE)
+#endif /* FALSE */
+
+#define GPS_PI (3.1415926535898)
+#define GPS_C (299792458.)
+#define D2R (GPS_PI/180.0)
+#define R2D (180.0/GPS_PI)
+#define WEEK (604800.)
+#define MAXCHAN (8)
+
+/* control characters for TSIP packets */
+#define DLE (0x10)
+#define ETX (0x03)
+
+#define MAX_RPTBUF (256)
+
+/* values of TSIPPKT.status */
+#define TSIP_PARSED_EMPTY 0
+#define TSIP_PARSED_FULL 1
+#define TSIP_PARSED_DLE_1 2
+#define TSIP_PARSED_DATA 3
+#define TSIP_PARSED_DLE_2 4
+
+#define UTCF_UTC_AVAIL (unsigned char) (1) /* UTC available */
+#define UTCF_LEAP_SCHD (unsigned char) (1<<4) /* Leap scheduled */
+#define UTCF_LEAP_PNDG (unsigned char) (1<<5) /* Leap pending, will occur at end of day */
+
+#define DEVICE "/dev/gps%d" /* name of radio device */
+#define PRECISION (-9) /* precision assumed (about 2 ms) */
+#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference id */
+#define REFID_LEN 4
+#define DESCRIPTION "RIPE NCC GPS (Palisade)" /* Description */
+#define SPEED232 B9600 /* 9600 baud */
+
+#define NSAMPLES 3 /* stages of median filter */
+
+/* Structures */
+
+/* TSIP packets have the following structure, whether report or command. */
+typedef struct {
+ short
+ counter, /* counter */
+ len; /* size of buf; < MAX_RPTBUF unsigned chars */
+ unsigned char
+ status, /* TSIP packet format/parse status */
+ code, /* TSIP code */
+ buf[MAX_RPTBUF]; /* report or command string */
+} TSIPPKT;
+
+/* TSIP binary data structures */
+typedef struct {
+ unsigned char
+ t_oa_raw, SV_health;
+ float
+ e, t_oa, i_0, OMEGADOT, sqrt_A,
+ OMEGA_0, omega, M_0, a_f0, a_f1,
+ Axis, n, OMEGA_n, ODOT_n, t_zc;
+ short
+ weeknum, wn_oa;
+} ALM_INFO;
+
+typedef struct { /* Almanac health page (25) parameters */
+ unsigned char
+ WN_a, SV_health[32], t_oa;
+} ALH_PARMS;
+
+typedef struct { /* Universal Coordinated Time (UTC) parms */
+ double
+ A_0;
+ float
+ A_1;
+ short
+ delta_t_LS;
+ float
+ t_ot;
+ short
+ WN_t, WN_LSF, DN, delta_t_LSF;
+} UTC_INFO;
+
+typedef struct { /* Ionospheric info (float) */
+ float
+ alpha_0, alpha_1, alpha_2, alpha_3,
+ beta_0, beta_1, beta_2, beta_3;
+} ION_INFO;
+
+typedef struct { /* Subframe 1 info (float) */
+ short
+ weeknum;
+ unsigned char
+ codeL2, L2Pdata, SVacc_raw, SV_health;
+ short
+ IODC;
+ float
+ T_GD, t_oc, a_f2, a_f1, a_f0, SVacc;
+} EPHEM_CLOCK;
+
+typedef struct { /* Ephemeris info (float) */
+ unsigned char
+ IODE, fit_interval;
+ float
+ C_rs, delta_n;
+ double
+ M_0;
+ float
+ C_uc;
+ double
+ e;
+ float
+ C_us;
+ double
+ sqrt_A;
+ float
+ t_oe, C_ic;
+ double
+ OMEGA_0;
+ float
+ C_is;
+ double
+ i_0;
+ float
+ C_rc;
+ double
+ omega;
+ float
+ OMEGADOT, IDOT;
+ double
+ Axis, n, r1me2, OMEGA_n, ODOT_n;
+} EPHEM_ORBIT;
+
+typedef struct { /* Navigation data structure */
+ short
+ sv_number; /* SV number (0 = no entry) */
+ float
+ t_ephem; /* time of ephemeris collection */
+ EPHEM_CLOCK
+ ephclk; /* subframe 1 data */
+ EPHEM_ORBIT
+ ephorb; /* ephemeris data */
+} NAV_INFO;
+
+typedef struct {
+ unsigned char
+ bSubcode,
+ operating_mode,
+ dgps_mode,
+ dyn_code,
+ trackmode;
+ float
+ elev_mask,
+ cno_mask,
+ dop_mask,
+ dop_switch;
+ unsigned char
+ dgps_age_limit;
+} TSIP_RCVR_CFG;
+
+
+#ifdef TRIMBLE_OUTPUT_FUNC
+static char
+ *dayname[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"},
+ old_baudnum[] = {0, 1, 4, 5, 6, 8, 9, 11, 28, 12},
+ *st_baud_text_app [] = {"", "", " 300", " 600", " 1200", " 2400",
+ " 4800", " 9600", "19200", "38400"},
+ *old_parity_text[] = {"EVEN", "ODD", "", "", "NONE"},
+ *parity_text [] = {"NONE", "ODD", "EVEN"},
+ *old_input_ch[] = { "TSIP", "RTCM (6 of 8 bits)"},
+ *old_output_ch[] = { "TSIP", "No output", "", "", "", "NMEA 0183"},
+ *protocols_in_text[] = { "", "TSIP", "", ""},
+ *protocols_out_text[] = { "", "TSIP", "NMEA"},
+ *rcvr_port_text [] = { "Port A ", "Port B ", "Current Port"},
+ *dyn_text [] = {"Unchanged", "Land", "Sea", "Air", "Static"},
+ *NavModeText0xBB[] = {"automatic", "time only (0-D)", "", "2-D",
+ "3-D", "", "", "OverDetermined Time"},
+ *PPSTimeBaseText[] = {"GPS", "UTC", "USER"},
+ *PPSPolarityText[] = {"Positive", "Negative"},
+ *MaskText[] = { "Almanac ", "Ephemeris", "UTC ", "Iono ",
+ "GPS Msg ", "Alm Hlth ", "Time Fix ", "SV Select",
+ "Ext Event", "Pos Fix ", "Raw Meas "};
+
+#endif /* TRIMBLE_OUTPUT_FUNC */
+
+/*
+ * Unit control structure
+ */
+struct ripencc_unit {
+ int unit; /* unit number */
+ int pollcnt; /* poll message counter */
+ int polled; /* Hand in a sample? */
+ char leapdelta; /* delta of next leap event */
+ unsigned char utcflags; /* delta of next leap event */
+ l_fp tstamp; /* timestamp of last poll */
+
+ struct timespec ts; /* last timestamp */
+ pps_params_t pps_params; /* pps parameters */
+ pps_info_t pps_info; /* last pps data */
+ pps_handle_t handle; /* pps handlebars */
+
+};
+
+
+/******************* PROTOYPES *****************/
+
+/* prototypes for report parsing primitives */
+short rpt_0x3D (TSIPPKT *rpt, unsigned char *tx_baud_index,
+ unsigned char *rx_baud_index, unsigned char *char_format_index,
+ unsigned char *stop_bits, unsigned char *tx_mode_index,
+ unsigned char *rx_mode_index);
+short rpt_0x40 (TSIPPKT *rpt, unsigned char *sv_prn, short *week_num,
+ float *t_zc, float *eccentricity, float *t_oa, float *i_0,
+ float *OMEGA_dot, float *sqrt_A, float *OMEGA_0, float *omega,
+ float *M_0);
+short rpt_0x41 (TSIPPKT *rpt, float *time_of_week, float *UTC_offset,
+ short *week_num);
+short rpt_0x42 (TSIPPKT *rpt, float ECEF_pos[3], float *time_of_fix);
+short rpt_0x43 (TSIPPKT *rpt, float ECEF_vel[3], float *freq_offset,
+ float *time_of_fix);
+short rpt_0x45 (TSIPPKT *rpt, unsigned char *major_nav_version,
+ unsigned char *minor_nav_version, unsigned char *nav_day,
+ unsigned char *nav_month, unsigned char *nav_year,
+ unsigned char *major_dsp_version, unsigned char *minor_dsp_version,
+ unsigned char *dsp_day, unsigned char *dsp_month,
+ unsigned char *dsp_year);
+short rpt_0x46 (TSIPPKT *rpt, unsigned char *status1, unsigned char *status2);
+short rpt_0x47 (TSIPPKT *rpt, unsigned char *nsvs, unsigned char *sv_prn,
+ float *snr);
+short rpt_0x48 (TSIPPKT *rpt, unsigned char *message);
+short rpt_0x49 (TSIPPKT *rpt, unsigned char *sv_health);
+short rpt_0x4A (TSIPPKT *rpt, float *lat, float *lon, float *alt,
+ float *clock_bias, float *time_of_fix);
+short rpt_0x4A_2 (TSIPPKT *rpt, float *alt, float *dummy,
+ unsigned char *alt_flag);
+short rpt_0x4B (TSIPPKT *rpt, unsigned char *machine_id,
+ unsigned char *status3, unsigned char *status4);
+short rpt_0x4C (TSIPPKT *rpt, unsigned char *dyn_code, float *el_mask,
+ float *snr_mask, float *dop_mask, float *dop_switch);
+short rpt_0x4D (TSIPPKT *rpt, float *osc_offset);
+short rpt_0x4E (TSIPPKT *rpt, unsigned char *response);
+short rpt_0x4F (TSIPPKT *rpt, double *a0, float *a1, float *time_of_data,
+ short *dt_ls, short *wn_t, short *wn_lsf, short *dn, short *dt_lsf);
+short rpt_0x54 (TSIPPKT *rpt, float *clock_bias, float *freq_offset,
+ float *time_of_fix);
+short rpt_0x55 (TSIPPKT *rpt, unsigned char *pos_code, unsigned char *vel_code,
+ unsigned char *time_code, unsigned char *aux_code);
+short rpt_0x56 (TSIPPKT *rpt, float vel_ENU[3], float *freq_offset,
+ float *time_of_fix);
+short rpt_0x57 (TSIPPKT *rpt, unsigned char *source_code,
+ unsigned char *diag_code, short *week_num, float *time_of_fix);
+short rpt_0x58 (TSIPPKT *rpt, unsigned char *op_code, unsigned char *data_type,
+ unsigned char *sv_prn, unsigned char *data_length,
+ unsigned char *data_packet);
+short rpt_0x59 (TSIPPKT *rpt, unsigned char *code_type,
+ unsigned char status_code[32]);
+short rpt_0x5A (TSIPPKT *rpt, unsigned char *sv_prn, float *sample_length,
+ float *signal_level, float *code_phase, float *Doppler,
+ double *time_of_fix);
+short rpt_0x5B (TSIPPKT *rpt, unsigned char *sv_prn, unsigned char *sv_health,
+ unsigned char *sv_iode, unsigned char *fit_interval_flag,
+ float *time_of_collection, float *time_of_eph, float *sv_accy);
+short rpt_0x5C (TSIPPKT *rpt, unsigned char *sv_prn, unsigned char *slot,
+ unsigned char *chan, unsigned char *acq_flag, unsigned char *eph_flag,
+ float *signal_level, float *time_of_last_msmt, float *elev,
+ float *azim, unsigned char *old_msmt_flag,
+ unsigned char *integer_msec_flag, unsigned char *bad_data_flag,
+ unsigned char *data_collect_flag);
+short rpt_0x6D (TSIPPKT *rpt, unsigned char *manual_mode, unsigned char *nsvs,
+ unsigned char *ndim, unsigned char sv_prn[], float *pdop,
+ float *hdop, float *vdop, float *tdop);
+short rpt_0x82 (TSIPPKT *rpt, unsigned char *diff_mode);
+short rpt_0x83 (TSIPPKT *rpt, double ECEF_pos[3], double *clock_bias,
+ float *time_of_fix);
+short rpt_0x84 (TSIPPKT *rpt, double *lat, double *lon, double *alt,
+ double *clock_bias, float *time_of_fix);
+short rpt_Paly0xBB(TSIPPKT *rpt, TSIP_RCVR_CFG *TsipxBB);
+short rpt_0xBC (TSIPPKT *rpt, unsigned char *port_num,
+ unsigned char *in_baud, unsigned char *out_baud,
+ unsigned char *data_bits, unsigned char *parity,
+ unsigned char *stop_bits, unsigned char *flow_control,
+ unsigned char *protocols_in, unsigned char *protocols_out,
+ unsigned char *reserved);
+
+/* prototypes for superpacket parsers */
+
+short rpt_0x8F0B (TSIPPKT *rpt, unsigned short *event, double *tow,
+ unsigned char *date, unsigned char *month, short *year,
+ unsigned char *dim_mode, short *utc_offset, double *bias, double *drift,
+ float *bias_unc, float *dr_unc, double *lat, double *lon, double *alt,
+ char sv_id[8]);
+short rpt_0x8F14 (TSIPPKT *rpt, short *datum_idx, double datum_coeffs[5]);
+short rpt_0x8F15 (TSIPPKT *rpt, short *datum_idx, double datum_coeffs[5]);
+short rpt_0x8F20 (TSIPPKT *rpt, unsigned char *info, double *lat,
+ double *lon, double *alt, double vel_enu[], double *time_of_fix,
+ short *week_num, unsigned char *nsvs, unsigned char sv_prn[],
+ short sv_IODC[], short *datum_index);
+short rpt_0x8F41 (TSIPPKT *rpt, unsigned char *bSearchRange,
+ unsigned char *bBoardOptions, unsigned long *iiSerialNumber,
+ unsigned char *bBuildYear, unsigned char *bBuildMonth,
+ unsigned char *bBuildDay, unsigned char *bBuildHour,
+ float *fOscOffset, unsigned short *iTestCodeId);
+short rpt_0x8F42 (TSIPPKT *rpt, unsigned char *bProdOptionsPre,
+ unsigned char *bProdNumberExt, unsigned short *iCaseSerialNumberPre,
+ unsigned long *iiCaseSerialNumber, unsigned long *iiProdNumber,
+ unsigned short *iPremiumOptions, unsigned short *iMachineID,
+ unsigned short *iKey);
+short rpt_0x8F45 (TSIPPKT *rpt, unsigned char *bSegMask);
+short rpt_0x8F4A_16 (TSIPPKT *rpt, unsigned char *pps_enabled,
+ unsigned char *pps_timebase, unsigned char *pos_polarity,
+ double *pps_offset, float *bias_unc_threshold);
+short rpt_0x8F4B (TSIPPKT *rpt, unsigned long *decorr_max);
+short rpt_0x8F4D (TSIPPKT *rpt, unsigned long *event_mask);
+short rpt_0x8FA5 (TSIPPKT *rpt, unsigned char *spktmask);
+short rpt_0x8FAD (TSIPPKT *rpt, unsigned short *COUNT, double *FracSec,
+ unsigned char *Hour, unsigned char *Minute, unsigned char *Second,
+ unsigned char *Day, unsigned char *Month, unsigned short *Year,
+ unsigned char *Status, unsigned char *Flags);
+
+/**/
+/* prototypes for command-encode primitives with suffix convention: */
+/* c = clear, s = set, q = query, e = enable, d = disable */
+void cmd_0x1F (TSIPPKT *cmd);
+void cmd_0x26 (TSIPPKT *cmd);
+void cmd_0x2F (TSIPPKT *cmd);
+void cmd_0x35s (TSIPPKT *cmd, unsigned char pos_code, unsigned char vel_code,
+ unsigned char time_code, unsigned char opts_code);
+void cmd_0x3C (TSIPPKT *cmd, unsigned char sv_prn);
+void cmd_0x3Ds (TSIPPKT *cmd, unsigned char baud_out, unsigned char baud_inp,
+ unsigned char char_code, unsigned char stopbitcode,
+ unsigned char output_mode, unsigned char input_mode);
+void cmd_0xBBq (TSIPPKT *cmd, unsigned char subcode) ;
+
+/* prototypes 8E commands */
+void cmd_0x8E0Bq (TSIPPKT *cmd);
+void cmd_0x8E41q (TSIPPKT *cmd);
+void cmd_0x8E42q (TSIPPKT *cmd);
+void cmd_0x8E4Aq (TSIPPKT *cmd);
+void cmd_0x8E4As (TSIPPKT *cmd, unsigned char PPSOnOff, unsigned char TimeBase,
+ unsigned char Polarity, double PPSOffset, float Uncertainty);
+void cmd_0x8E4Bq (TSIPPKT *cmd);
+void cmd_0x8E4Ds (TSIPPKT *cmd, unsigned long AutoOutputMask);
+void cmd_0x8EADq (TSIPPKT *cmd);
+
+/* header/source border XXXXXXXXXXXXXXXXXXXXXXXXXX */
+
+/* Trimble parse functions */
+static int parse0x8FAD (TSIPPKT *, struct peer *);
+static int parse0x8F0B (TSIPPKT *, struct peer *);
+#ifdef TRIMBLE_OUTPUT_FUNC
+static int parseany (TSIPPKT *, struct peer *);
+static void TranslateTSIPReportToText (TSIPPKT *, char *);
+#endif /* TRIMBLE_OUTPUT_FUNC */
+static int parse0x5C (TSIPPKT *, struct peer *);
+static int parse0x4F (TSIPPKT *, struct peer *);
+static void tsip_input_proc (TSIPPKT *, int);
+
+/* Trimble helper functions */
+static void bPutFloat (float *, unsigned char *);
+static void bPutDouble (double *, unsigned char *);
+static void bPutULong (unsigned long *, unsigned char *);
+static int print_msg_table_header (int rptcode, char *HdrStr, int force);
+static char * show_time (float time_of_week);
+
+/* RIPE NCC functions */
+static void ripencc_control (int, const struct refclockstat *,
+ struct refclockstat *, struct peer *);
+static int ripencc_ppsapi (struct peer *, int, int);
+static int ripencc_get_pps_ts (struct ripencc_unit *, l_fp *);
+static int ripencc_start (int, struct peer *);
+static void ripencc_shutdown (int, struct peer *);
+static void ripencc_poll (int, struct peer *);
+static void ripencc_send (struct peer *, TSIPPKT spt);
+static void ripencc_receive (struct recvbuf *);
+
+/* fill in reflock structure for our clock */
+struct refclock refclock_ripencc = {
+ ripencc_start, /* start up driver */
+ ripencc_shutdown, /* shut down driver */
+ ripencc_poll, /* transmit poll message */
+ ripencc_control, /* control function */
+ noentry, /* initialize driver */
+ noentry, /* debug info */
+ NOFLAGS /* clock flags */
+};
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+
+/*
+ * ripencc_start - open the GPS devices and initialize data for processing
+ */
+static int
+ripencc_start(int unit, struct peer *peer)
+{
+ register struct ripencc_unit *up;
+ struct refclockproc *pp;
+ char device[40];
+ int fd;
+ struct termios tio;
+ TSIPPKT spt;
+
+ pp = peer->procptr;
+
+ /*
+ * Open serial port
+ */
+ (void)snprintf(device, sizeof(device), DEVICE, unit);
+ fd = refclock_open(device, SPEED232, LDISC_RAW);
+ if (fd <= 0) {
+ pp->io.fd = -1;
+ return (0);
+ }
+
+ pp->io.fd = fd;
+
+ /* from refclock_palisade.c */
+ if (tcgetattr(fd, &tio) < 0) {
+ msyslog(LOG_ERR, "Palisade(%d) tcgetattr(fd, &tio): %m",unit);
+ return (0);
+ }
+
+ /*
+ * set flags
+ */
+ tio.c_cflag |= (PARENB|PARODD);
+ tio.c_iflag &= ~ICRNL;
+ if (tcsetattr(fd, TCSANOW, &tio) == -1) {
+ msyslog(LOG_ERR, "Palisade(%d) tcsetattr(fd, &tio): %m",unit);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+
+ pp->io.clock_recv = ripencc_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ if (!io_addclock(&pp->io)) {
+ pp->io.fd = -1;
+ close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, REFID_LEN);
+ up->pollcnt = 2;
+ up->unit = unit;
+ up->leapdelta = 0;
+ up->utcflags = 0;
+
+ /*
+ * Initialize the Clock
+ */
+
+ /* query software versions */
+ cmd_0x1F(&spt);
+ ripencc_send(peer, spt);
+
+ /* query receiver health */
+ cmd_0x26(&spt);
+ ripencc_send(peer, spt);
+
+ /* query serial numbers */
+ cmd_0x8E42q(&spt);
+ ripencc_send(peer, spt);
+
+ /* query manuf params */
+ cmd_0x8E41q(&spt);
+ ripencc_send(peer, spt);
+
+ /* i/o opts */ /* trimble manual page A30 */
+ cmd_0x35s(&spt,
+ 0x1C, /* position */
+ 0x00, /* velocity */
+ 0x05, /* timing */
+ 0x0a); /* auxilary */
+ ripencc_send(peer, spt);
+
+ /* turn off port A */
+ cmd_0x3Ds (&spt,
+ 0x0B, /* baud_out */
+ 0x0B, /* baud_inp */
+ 0x07, /* char_code */
+ 0x07, /* stopbitcode */
+ 0x01, /* output_mode */
+ 0x00); /* input_mode */
+ ripencc_send(peer, spt);
+
+ /* set i/o options */
+ cmd_0x8E4As (&spt,
+ 0x01, /* PPS on */
+ 0x01, /* Timebase UTC */
+ 0x00, /* polarity positive */
+ 0., /* 100 ft. cable XXX make flag */
+ 1e-6 * GPS_C); /* turn of biasuncert. > (1us) */
+ ripencc_send(peer,spt);
+
+ /* all outomatic packet output off */
+ cmd_0x8E4Ds(&spt,
+ 0x00000000); /* AutoOutputMask */
+ ripencc_send(peer, spt);
+
+ cmd_0xBBq (&spt,
+ 0x00); /* query primary configuration */
+ ripencc_send(peer,spt);
+
+
+ /* query PPS parameters */
+ cmd_0x8E4Aq (&spt); /* query PPS params */
+ ripencc_send(peer,spt);
+
+ /* query survey limit */
+ cmd_0x8E4Bq (&spt); /* query survey limit */
+ ripencc_send(peer,spt);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ printf("ripencc_start: success\n");
+#endif /* DEBUG_NCC */
+
+ /*
+ * Start the PPSAPI interface if it is there. Default to use
+ * the assert edge and do not enable the kernel hardpps.
+ */
+ if (time_pps_create(fd, &up->handle) < 0) {
+ up->handle = 0;
+ msyslog(LOG_ERR, "refclock_ripencc: time_pps_create failed: %m");
+ return (1);
+ }
+
+ return(ripencc_ppsapi(peer, 0, 0));
+}
+
+/*
+ * ripencc_control - fudge control
+ */
+static void
+ripencc_control(
+ int unit, /* unit (not used) */
+ const struct refclockstat *in, /* input parameters (not used) */
+ struct refclockstat *out, /* output parameters (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+
+#ifdef DEBUG_NCC
+ msyslog(LOG_INFO,"%s()",__FUNCTION__);
+#endif /* DEBUG_NCC */
+
+ pp = peer->procptr;
+ ripencc_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
+ pp->sloppyclockflag & CLK_FLAG3);
+}
+
+
+/*
+ * Initialize PPSAPI
+ */
+int
+ripencc_ppsapi(
+ struct peer *peer, /* peer structure pointer */
+ int enb_clear, /* clear enable */
+ int enb_hardpps /* hardpps enable */
+ )
+{
+ struct refclockproc *pp;
+ struct ripencc_unit *up;
+ int capability;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (time_pps_getcap(up->handle, &capability) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ripencc: time_pps_getcap failed: %m");
+ return (0);
+ }
+ memset(&up->pps_params, 0, sizeof(pps_params_t));
+ if (enb_clear)
+ up->pps_params.mode = capability & PPS_CAPTURECLEAR;
+ else
+ up->pps_params.mode = capability & PPS_CAPTUREASSERT;
+ if (!up->pps_params.mode) {
+ msyslog(LOG_ERR,
+ "refclock_ripencc: invalid capture edge %d",
+ !enb_clear);
+ return (0);
+ }
+ up->pps_params.mode |= PPS_TSFMT_TSPEC;
+ if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ripencc: time_pps_setparams failed: %m");
+ return (0);
+ }
+ if (enb_hardpps) {
+ if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
+ up->pps_params.mode & ~PPS_TSFMT_TSPEC,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ripencc: time_pps_kcbind failed: %m");
+ return (0);
+ }
+ pps_enable = 1;
+ }
+ peer->precision = PPS_PRECISION;
+
+#if DEBUG_NCC
+ if (debug) {
+ time_pps_getparams(up->handle, &up->pps_params);
+ printf(
+ "refclock_ripencc: capability 0x%x version %d mode 0x%x kern %d\n",
+ capability, up->pps_params.api_version,
+ up->pps_params.mode, enb_hardpps);
+ }
+#endif /* DEBUG_NCC */
+
+ return (1);
+}
+
+/*
+ * This function is called every 64 seconds from ripencc_receive
+ * It will fetch the pps time
+ *
+ * Return 0 on failure and 1 on success.
+ */
+static int
+ripencc_get_pps_ts(
+ struct ripencc_unit *up,
+ l_fp *tsptr
+ )
+{
+ pps_info_t pps_info;
+ struct timespec timeout, ts;
+ double dtemp;
+ l_fp tstmp;
+
+#ifdef DEBUG_PPS
+ msyslog(LOG_INFO,"ripencc_get_pps_ts");
+#endif /* DEBUG_PPS */
+
+
+ /*
+ * Convert the timespec nanoseconds field to ntp l_fp units.
+ */
+ if (up->handle == 0)
+ return (0);
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
+ if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
+ &timeout) < 0)
+ return (0);
+ if (up->pps_params.mode & PPS_CAPTUREASSERT) {
+ if (pps_info.assert_sequence ==
+ up->pps_info.assert_sequence)
+ return (0);
+ ts = up->pps_info.assert_timestamp;
+ } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
+ if (pps_info.clear_sequence ==
+ up->pps_info.clear_sequence)
+ return (0);
+ ts = up->pps_info.clear_timestamp;
+ } else {
+ return (0);
+ }
+ if ((up->ts.tv_sec == ts.tv_sec) && (up->ts.tv_nsec == ts.tv_nsec))
+ return (0);
+ up->ts = ts;
+
+ tstmp.l_ui = ts.tv_sec + JAN_1970;
+ dtemp = ts.tv_nsec * FRAC / 1e9;
+ tstmp.l_uf = (u_int32)dtemp;
+
+#ifdef DEBUG_PPS
+ msyslog(LOG_INFO,"ts.tv_sec: %d",(int)ts.tv_sec);
+ msyslog(LOG_INFO,"ts.tv_nsec: %ld",ts.tv_nsec);
+#endif /* DEBUG_PPS */
+
+ *tsptr = tstmp;
+ return (1);
+}
+
+/*
+ * ripencc_shutdown - shut down a GPS clock
+ */
+static void
+ripencc_shutdown(int unit, struct peer *peer)
+{
+ register struct ripencc_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ if (up != NULL) {
+ if (up->handle != 0)
+ time_pps_destroy(up->handle);
+ free(up);
+ }
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+
+ return;
+}
+
+/*
+ * ripencc_poll - called by the transmit procedure
+ */
+static void
+ripencc_poll(int unit, struct peer *peer)
+{
+ register struct ripencc_unit *up;
+ struct refclockproc *pp;
+ TSIPPKT spt;
+
+#ifdef DEBUG_NCC
+ if (debug)
+ fprintf(stderr, "ripencc_poll(%d)\n", unit);
+#endif /* DEBUG_NCC */
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+
+ pp->polls++;
+ up->polled = 1;
+
+ /* poll for UTC superpacket */
+ cmd_0x8EADq (&spt);
+ ripencc_send(peer,spt);
+}
+
+/*
+ * ripencc_send - send message to clock
+ * use the structures being created by the trimble functions!
+ * makes the code more readable/clean
+ */
+static void
+ripencc_send(struct peer *peer, TSIPPKT spt)
+{
+ unsigned char *ip, *op;
+ unsigned char obuf[512];
+
+#ifdef DEBUG_RAW
+ {
+ register struct ripencc_unit *up;
+ register struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (debug)
+ printf("ripencc_send(%d, %02X)\n", up->unit, cmd);
+ }
+#endif /* DEBUG_RAW */
+
+ ip = spt.buf;
+ op = obuf;
+
+ *op++ = 0x10;
+ *op++ = spt.code;
+
+ while (spt.len--) {
+ if (op-obuf > sizeof(obuf)-5) {
+ msyslog(LOG_ERR, "ripencc_send obuf overflow!");
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+
+ if (*ip == 0x10) /* byte stuffing */
+ *op++ = 0x10;
+ *op++ = *ip++;
+ }
+
+ *op++ = 0x10;
+ *op++ = 0x03;
+
+#ifdef DEBUG_RAW
+ if (debug) { /* print raw packet */
+ unsigned char *cp;
+ int i;
+
+ printf("ripencc_send: len %d\n", op-obuf);
+ for (i=1, cp=obuf; cp<op; i++, cp++) {
+ printf(" %02X", *cp);
+ if (i%10 == 0)
+ printf("\n");
+ }
+ printf("\n");
+ }
+#endif /* DEBUG_RAW */
+
+ if (write(peer->procptr->io.fd, obuf, op-obuf) == -1) {
+ refclock_report(peer, CEVNT_FAULT);
+ }
+}
+
+/*
+ * ripencc_receive()
+ *
+ * called when a packet is received on the serial port
+ * takes care of further processing
+ *
+ */
+static void
+ripencc_receive(struct recvbuf *rbufp)
+{
+ register struct ripencc_unit *up;
+ register struct refclockproc *pp;
+ struct peer *peer;
+ static TSIPPKT rpt; /* for current incoming TSIP report */
+ TSIPPKT spt; /* send packet */
+ int ns_since_pps;
+ int i;
+ char *cp;
+ /* these variables hold data until we decide it's worth keeping */
+ char rd_lastcode[BMAX];
+ l_fp rd_tmp;
+ u_short rd_lencode;
+
+ /* msyslog(LOG_INFO, "%s",__FUNCTION__); */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+ rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
+
+#ifdef DEBUG_RAW
+ if (debug)
+ fprintf(stderr, "ripencc_receive(%d)\n", up->unit);
+#endif /* DEBUG_RAW */
+
+#ifdef DEBUG_RAW
+ if (debug) { /* print raw packet */
+ int i;
+ unsigned char *cp;
+
+ printf("ripencc_receive: len %d\n", rbufp->recv_length);
+ for (i=1, cp=(char*)&rbufp->recv_space;
+ i <= rbufp->recv_length;
+ i++, cp++) {
+ printf(" %02X", *cp);
+ if (i%10 == 0)
+ printf("\n");
+ }
+ printf("\n");
+ }
+#endif /* DEBUG_RAW */
+
+ cp = (char*) &rbufp->recv_space;
+ i=rbufp->recv_length;
+
+ while (i--) { /* loop over received chars */
+
+ tsip_input_proc(&rpt, (unsigned char) *cp++);
+
+ if (rpt.status != TSIP_PARSED_FULL)
+ continue;
+
+ switch (rpt.code) {
+
+ case 0x8F: /* superpacket */
+
+ switch (rpt.buf[0]) {
+
+ case 0xAD: /* UTC Time */
+ /*
+ ** When polling on port B the timecode is
+ ** the time of the previous PPS. If we
+ ** completed receiving the packet less than
+ ** 150ms after the turn of the second, it
+ ** may have the code of the previous second.
+ ** We do not trust that and simply poll
+ ** again without even parsing it.
+ **
+ ** More elegant would be to re-schedule the
+ ** poll, but I do not know (yet) how to do
+ ** that cleanly.
+ **
+ */
+ /* BLA ns_since_pps = ncc_tstmp(rbufp, &trtmp); */
+/* if (up->polled && ns_since_pps > -1 && ns_since_pps < 150) { */
+
+ ns_since_pps = 200;
+ if (up->polled && ns_since_pps < 150) {
+ msyslog(LOG_INFO, "%s(): up->polled",
+ __FUNCTION__);
+ ripencc_poll(up->unit, peer);
+ break;
+ }
+
+ /*
+ * Parse primary utc time packet
+ * and fill refclock structure
+ * from results.
+ */
+ if (parse0x8FAD(&rpt, peer) < 0) {
+ msyslog(LOG_INFO, "%s(): parse0x8FAD < 0",__FUNCTION__);
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+ /*
+ * If the PPSAPI is working, rather use its
+ * timestamps.
+ * assume that the PPS occurs on the second
+ * so blow any msec
+ */
+ if (ripencc_get_pps_ts(up, &rd_tmp) == 1) {
+ pp->lastrec = up->tstamp = rd_tmp;
+ pp->nsec = 0;
+ }
+ else
+ msyslog(LOG_INFO, "%s(): ripencc_get_pps_ts returns failure",__FUNCTION__);
+
+
+ if (!up->polled) {
+ msyslog(LOG_INFO, "%s(): unrequested packet",__FUNCTION__);
+ /* unrequested packet */
+ break;
+ }
+
+ /* we have been polled ! */
+ up->polled = 0;
+ up->pollcnt = 2;
+
+ /* poll for next packet */
+ cmd_0x8E0Bq(&spt);
+ ripencc_send(peer,spt);
+
+ if (ns_since_pps < 0) { /* no PPS */
+ msyslog(LOG_INFO, "%s(): ns_since_pps < 0",__FUNCTION__);
+ refclock_report(peer, CEVNT_BADTIME);
+ break;
+ }
+
+ /*
+ ** Process the new sample in the median
+ ** filter and determine the reference clock
+ ** offset and dispersion.
+ */
+ if (!refclock_process(pp)) {
+ msyslog(LOG_INFO, "%s(): !refclock_process",__FUNCTION__);
+ refclock_report(peer, CEVNT_BADTIME);
+ break;
+ }
+
+ refclock_receive(peer);
+ break;
+
+ case 0x0B: /* comprehensive time packet */
+ parse0x8F0B(&rpt, peer);
+ break;
+
+ default: /* other superpackets */
+#ifdef DEBUG_NCC
+ msyslog(LOG_INFO, "%s(): calling parseany",
+ __FUNCTION__);
+#endif /* DEBUG_NCC */
+#ifdef TRIMBLE_OUTPUT_FUNC
+ parseany(&rpt, peer);
+#endif /* TRIMBLE_OUTPUT_FUNC */
+ break;
+ }
+ break;
+
+ case 0x4F: /* UTC parameters, for leap info */
+ parse0x4F(&rpt, peer);
+ break;
+
+ case 0x5C: /* sat tracking data */
+ parse0x5C(&rpt, peer);
+ break;
+
+ default: /* other packets */
+#ifdef TRIMBLE_OUTPUT_FUNC
+ parseany(&rpt, peer);
+#endif /* TRIMBLE_OUTPUT_FUNC */
+ break;
+ }
+ rpt.status = TSIP_PARSED_EMPTY;
+ }
+}
+
+/*
+ * All trimble functions that are directly referenced from driver code
+ * (so not from parseany)
+ */
+
+/* request software versions */
+void
+cmd_0x1F(
+ TSIPPKT *cmd
+ )
+{
+ cmd->len = 0;
+ cmd->code = 0x1F;
+}
+
+/* request receiver health */
+void
+cmd_0x26(
+ TSIPPKT *cmd
+ )
+{
+ cmd->len = 0;
+ cmd->code = 0x26;
+}
+
+/* request UTC params */
+void
+cmd_0x2F(
+ TSIPPKT *cmd
+ )
+{
+ cmd->len = 0;
+ cmd->code = 0x2F;
+}
+
+/* set serial I/O options */
+void
+cmd_0x35s(
+ TSIPPKT *cmd,
+ unsigned char pos_code,
+ unsigned char vel_code,
+ unsigned char time_code,
+ unsigned char opts_code
+ )
+{
+ cmd->buf[0] = pos_code;
+ cmd->buf[1] = vel_code;
+ cmd->buf[2] = time_code;
+ cmd->buf[3] = opts_code;
+ cmd->len = 4;
+ cmd->code = 0x35;
+}
+
+/* request tracking status */
+void
+cmd_0x3C(
+ TSIPPKT *cmd,
+ unsigned char sv_prn
+ )
+{
+ cmd->buf[0] = sv_prn;
+ cmd->len = 1;
+ cmd->code = 0x3C;
+}
+
+/* set Channel A configuration for dual-port operation */
+void
+cmd_0x3Ds(
+ TSIPPKT *cmd,
+ unsigned char baud_out,
+ unsigned char baud_inp,
+ unsigned char char_code,
+ unsigned char stopbitcode,
+ unsigned char output_mode,
+ unsigned char input_mode
+ )
+{
+ cmd->buf[0] = baud_out; /* XMT baud rate */
+ cmd->buf[1] = baud_inp; /* RCV baud rate */
+ cmd->buf[2] = char_code; /* parity and #bits per byte */
+ cmd->buf[3] = stopbitcode; /* number of stop bits code */
+ cmd->buf[4] = output_mode; /* Ch. A transmission mode */
+ cmd->buf[5] = input_mode; /* Ch. A reception mode */
+ cmd->len = 6;
+ cmd->code = 0x3D;
+}
+
+
+/* query primary configuration */
+void
+cmd_0xBBq(
+ TSIPPKT *cmd,
+ unsigned char subcode
+ )
+{
+ cmd->len = 1;
+ cmd->code = 0xBB;
+ cmd->buf[0] = subcode;
+}
+
+
+/**** Superpackets ****/
+/* 8E-0B to query 8F-0B controls */
+void
+cmd_0x8E0Bq(
+ TSIPPKT *cmd
+ )
+{
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x0B;
+}
+
+
+/* 8F-41 to query board serial number */
+void
+cmd_0x8E41q(
+ TSIPPKT *cmd
+ )
+{
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x41;
+}
+
+
+/* 8F-42 to query product serial number */
+void
+cmd_0x8E42q(
+ TSIPPKT *cmd
+ )
+{
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x42;
+}
+
+
+/* 8F-4A to query PPS parameters */
+void
+cmd_0x8E4Aq(
+ TSIPPKT *cmd
+ )
+{
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x4A;
+}
+
+
+/* set i/o options */
+void
+cmd_0x8E4As(
+ TSIPPKT *cmd,
+ unsigned char PPSOnOff,
+ unsigned char TimeBase,
+ unsigned char Polarity,
+ double PPSOffset,
+ float Uncertainty
+ )
+{
+ cmd->len = 16;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x4A;
+ cmd->buf[1] = PPSOnOff;
+ cmd->buf[2] = TimeBase;
+ cmd->buf[3] = Polarity;
+ bPutDouble (&PPSOffset, &cmd->buf[4]);
+ bPutFloat (&Uncertainty, &cmd->buf[12]);
+}
+
+/* 8F-4B query survey limit */
+void
+cmd_0x8E4Bq(
+ TSIPPKT *cmd
+ )
+{
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x4B;
+}
+
+/* poll for UTC superpacket */
+/* 8E-AD to query 8F-AD controls */
+void
+cmd_0x8EADq(
+ TSIPPKT *cmd
+ )
+{
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0xAD;
+}
+
+/* all outomatic packet output off */
+void
+cmd_0x8E4Ds(
+ TSIPPKT *cmd,
+ unsigned long AutoOutputMask
+ )
+{
+ cmd->len = 5;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x4D;
+ bPutULong (&AutoOutputMask, &cmd->buf[1]);
+}
+
+
+/*
+ * for DOS machines, reverse order of bytes as they come through the
+ * serial port.
+ */
+#ifdef BYTESWAP
+static short
+bGetShort(
+ unsigned char *bp
+ )
+{
+ short outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 1;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+#ifdef TRIMBLE_OUTPUT_FUNC
+static unsigned short
+bGetUShort(
+ unsigned char *bp
+ )
+{
+ unsigned short outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 1;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+static long
+bGetLong(
+ unsigned char *bp
+ )
+{
+ long outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 3;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+static unsigned long
+bGetULong(
+ unsigned char *bp
+ )
+{
+ unsigned long outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 3;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+#endif /* TRIMBLE_OUTPUT_FUNC */
+
+static float
+bGetSingle(
+ unsigned char *bp
+ )
+{
+ float outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 3;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+static double
+bGetDouble(
+ unsigned char *bp
+ )
+{
+ double outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 7;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+#else /* not BYTESWAP */
+
+#define bGetShort(bp) (*(short*)(bp))
+#define bGetLong(bp) (*(long*)(bp))
+#define bGetULong(bp) (*(unsigned long*)(bp))
+#define bGetSingle(bp) (*(float*)(bp))
+#define bGetDouble(bp) (*(double*)(bp))
+
+#endif /* BYTESWAP */
+/*
+ * Byte-reversal is necessary for little-endian (Intel-based) machines.
+ * TSIP streams are Big-endian (Motorola-based).
+ */
+#ifdef BYTESWAP
+
+void
+bPutFloat(
+ float *in,
+ unsigned char *out
+ )
+{
+ unsigned char *inptr;
+
+ inptr = (unsigned char*)in + 3;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out = *inptr;
+}
+
+static void
+bPutULong(
+ unsigned long *in,
+ unsigned char *out
+ )
+{
+ unsigned char *inptr;
+
+ inptr = (unsigned char*)in + 3;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out = *inptr;
+}
+
+static void
+bPutDouble(
+ double *in,
+ unsigned char *out
+ )
+{
+ unsigned char *inptr;
+
+ inptr = (unsigned char*)in + 7;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out = *inptr;
+}
+
+#else /* not BYTESWAP */
+
+void bPutShort (short a, unsigned char *cmdbuf) {*(short*) cmdbuf = a;}
+void bPutULong (long a, unsigned char *cmdbuf) {*(long*) cmdbuf = a;}
+void bPutFloat (float a, unsigned char *cmdbuf) {*(float*) cmdbuf = a;}
+void bPutDouble (double a, unsigned char *cmdbuf){*(double*) cmdbuf = a;}
+
+#endif /* BYTESWAP */
+
+/*
+ * Parse primary utc time packet
+ * and fill refclock structure
+ * from results.
+ *
+ * 0 = success
+ * -1 = errors
+ */
+
+static int
+parse0x8FAD(
+ TSIPPKT *rpt,
+ struct peer *peer
+ )
+{
+ register struct refclockproc *pp;
+ register struct ripencc_unit *up;
+
+ unsigned day, month, year; /* data derived from received timecode */
+ unsigned hour, minute, second;
+ unsigned char trackstat, utcflags;
+
+ static char logbuf[1024]; /* logging string buffer */
+ int i;
+ unsigned char *buf;
+
+ buf = rpt->buf;
+ pp = peer->procptr;
+
+ if (rpt->len != 22)
+ return (-1);
+
+ if (bGetShort(&buf[1]) != 0) {
+#ifdef DEBUG_NCC
+ if (debug)
+ printf("parse0x8FAD: event count != 0\n");
+#endif /* DEBUG_NCC */
+ return(-1);
+ }
+
+ if (bGetDouble(&buf[3]) != 0.0) {
+#ifdef DEBUG_NCC
+ if (debug)
+ printf("parse0x8FAD: fracsecs != 0\n");
+#endif /* DEBUG_NCC */
+ return(-1);
+ }
+
+ hour = (unsigned int) buf[11];
+ minute = (unsigned int) buf[12];
+ second = (unsigned int) buf[13];
+ day = (unsigned int) buf[14];
+ month = (unsigned int) buf[15];
+ year = bGetShort(&buf[16]);
+ trackstat = buf[18];
+ utcflags = buf[19];
+
+
+ sprintf(logbuf, "U1 %d.%d.%d %02d:%02d:%02d %d %02x",
+ day, month, year, hour, minute, second, trackstat, utcflags);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(logbuf);
+#endif /* DEBUG_NCC */
+
+ record_clock_stats(&peer->srcadr, logbuf);
+
+ if (!utcflags & UTCF_UTC_AVAIL)
+ return(-1);
+
+ /* poll for UTC parameters once and then if UTC flag changed */
+ up = (struct ripencc_unit *) pp->unitptr;
+ if (utcflags != up->utcflags) {
+ TSIPPKT spt; /* local structure for send packet */
+ cmd_0x2F (&spt); /* request UTC params */
+ ripencc_send(peer,spt);
+ up->utcflags = utcflags;
+ }
+
+ /*
+ * If we hit the leap second, we choose to skip this sample
+ * rather than rely on other code to be perfectly correct.
+ * No offense, just defense ;-).
+ */
+ if (second == 60)
+ return(-1);
+
+ /* now check and convert the time we received */
+
+ pp->year = year;
+ if (month < 1 || month > 12 || day < 1 || day > 31)
+ return(-1);
+
+ if (pp->year % 4) { /* XXX: use is_leapyear() ? */
+ if (day > day1tab[month - 1])
+ return(-1);
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1])
+ return(-1);
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+ pp->hour = hour;
+ pp->minute = minute;
+ pp-> second = second;
+ pp->nsec = 0;
+
+ if ((utcflags&UTCF_LEAP_PNDG) && up->leapdelta != 0)
+ pp-> leap = (up->leapdelta > 0)
+ ? LEAP_ADDSECOND
+ : LEAP_DELSECOND;
+ else
+ pp-> leap = LEAP_NOWARNING;
+
+ return (0);
+}
+
+/*
+ * Parse comprehensive time packet
+ *
+ * 0 = success
+ * -1 = errors
+ */
+
+int
+parse0x8F0B(
+ TSIPPKT *rpt,
+ struct peer *peer
+ )
+{
+ register struct refclockproc *pp;
+
+ unsigned day, month, year; /* data derived from received timecode */
+ unsigned hour, minute, second;
+ unsigned utcoff;
+ unsigned char mode;
+ double bias, rate;
+ float biasunc, rateunc;
+ double lat, lon, alt;
+ short lat_deg, lon_deg;
+ float lat_min, lon_min;
+ unsigned char north_south, east_west;
+ char sv[9];
+
+ static char logbuf[1024]; /* logging string buffer */
+ unsigned char b;
+ int i;
+ unsigned char *buf;
+ double tow;
+
+ buf = rpt->buf;
+ pp = peer->procptr;
+
+ if (rpt->len != 74)
+ return (-1);
+
+ if (bGetShort(&buf[1]) != 0)
+ return(-1);;
+
+ tow = bGetDouble(&buf[3]);
+
+ if (tow == -1.0) {
+ return(-1);
+ }
+ else if ((tow >= 604800.0) || (tow < 0.0)) {
+ return(-1);
+ }
+ else
+ {
+ if (tow < 604799.9) tow = tow + .00000001;
+ second = (unsigned int) fmod(tow, 60.);
+ minute = (unsigned int) fmod(tow/60., 60.);
+ hour = (unsigned int )fmod(tow / 3600., 24.);
+ }
+
+ day = (unsigned int) buf[11];
+ month = (unsigned int) buf[12];
+ year = bGetShort(&buf[13]);
+ mode = buf[15];
+ utcoff = bGetShort(&buf[16]);
+ bias = bGetDouble(&buf[18]) / GPS_C * 1e9; /* ns */
+ rate = bGetDouble(&buf[26]) / GPS_C * 1e9; /* ppb */
+ biasunc = bGetSingle(&buf[34]) / GPS_C * 1e9; /* ns */
+ rateunc = bGetSingle(&buf[38]) / GPS_C * 1e9; /* ppb */
+ lat = bGetDouble(&buf[42]) * R2D;
+ lon = bGetDouble(&buf[50]) * R2D;
+ alt = bGetDouble(&buf[58]);
+
+ if (lat < 0.0) {
+ north_south = 'S';
+ lat = -lat;
+ }
+ else {
+ north_south = 'N';
+ }
+ lat_deg = (short)lat;
+ lat_min = (lat - lat_deg) * 60.0;
+
+ if (lon < 0.0) {
+ east_west = 'W';
+ lon = -lon;
+ }
+ else {
+ east_west = 'E';
+ }
+
+ lon_deg = (short)lon;
+ lon_min = (lon - lon_deg) * 60.0;
+
+ for (i=0; i<8; i++) {
+ sv[i] = buf[i + 66];
+ if (sv[i]) {
+ TSIPPKT spt; /* local structure for sendpacket */
+ b = (unsigned char) (sv[i]<0 ? -sv[i] : sv[i]);
+ /* request tracking status */
+ cmd_0x3C (&spt, b);
+ ripencc_send(peer,spt);
+ }
+ }
+
+
+ sprintf(logbuf, "C1 %02d%02d%04d %02d%02d%02d %d %7.0f %.1f %.0f %.1f %d %02d%09.6f %c %02d%09.6f %c %.0f %d %d %d %d %d %d %d %d",
+ day, month, year, hour, minute, second, mode, bias, biasunc,
+ rate, rateunc, utcoff, lat_deg, lat_min, north_south, lon_deg,
+ lon_min, east_west, alt, sv[0], sv[1], sv[2], sv[3], sv[4],
+ sv[5], sv[6], sv[7]);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(logbuf);
+#endif /* DEBUG_NCC */
+
+ record_clock_stats(&peer->srcadr, logbuf);
+
+ return (0);
+}
+
+#ifdef TRIMBLE_OUTPUT_FUNC
+/*
+ * Parse any packet using Trimble machinery
+ */
+int
+parseany(
+ TSIPPKT *rpt,
+ struct peer *peer
+ )
+{
+ static char logbuf[1024]; /* logging string buffer */
+
+ TranslateTSIPReportToText (rpt, logbuf); /* anything else */
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(&logbuf[1]);
+#endif /* DEBUG_NCC */
+ record_clock_stats(&peer->srcadr, &logbuf[1]);
+ return(0);
+}
+#endif /* TRIMBLE_OUTPUT_FUNC */
+
+
+/*
+ * Parse UTC Parameter Packet
+ *
+ * See the IDE for documentation!
+ *
+ * 0 = success
+ * -1 = errors
+ */
+
+int
+parse0x4F(
+ TSIPPKT *rpt,
+ struct peer *peer
+ )
+{
+ register struct ripencc_unit *up;
+
+ double a0;
+ float a1, tot;
+ int dt_ls, wn_t, wn_lsf, dn, dt_lsf;
+
+ static char logbuf[1024]; /* logging string buffer */
+ unsigned char *buf;
+
+ buf = rpt->buf;
+
+ if (rpt->len != 26)
+ return (-1);
+ a0 = bGetDouble (buf);
+ a1 = bGetSingle (&buf[8]);
+ dt_ls = bGetShort (&buf[12]);
+ tot = bGetSingle (&buf[14]);
+ wn_t = bGetShort (&buf[18]);
+ wn_lsf = bGetShort (&buf[20]);
+ dn = bGetShort (&buf[22]);
+ dt_lsf = bGetShort (&buf[24]);
+
+ sprintf(logbuf, "L1 %d %d %d %g %g %g %d %d %d",
+ dt_lsf - dt_ls, dt_ls, dt_lsf, a0, a1, tot, wn_t, wn_lsf, dn);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(logbuf);
+#endif /* DEBUG_NCC */
+
+ record_clock_stats(&peer->srcadr, logbuf);
+
+ up = (struct ripencc_unit *) peer->procptr->unitptr;
+ up->leapdelta = dt_lsf - dt_ls;
+
+ return (0);
+}
+
+/*
+ * Parse Tracking Status packet
+ *
+ * 0 = success
+ * -1 = errors
+ */
+
+int
+parse0x5C(
+ TSIPPKT *rpt,
+ struct peer *peer
+ )
+{
+ unsigned char prn, channel, aqflag, ephstat;
+ float snr, azinuth, elevation;
+
+ static char logbuf[1024]; /* logging string buffer */
+ unsigned char *buf;
+
+ buf = rpt->buf;
+
+ if (rpt->len != 24)
+ return(-1);
+
+ prn = buf[0];
+ channel = (unsigned char)(buf[1] >> 3);
+ if (channel == 0x10)
+ channel = 2;
+ else
+ channel++;
+ aqflag = buf[2];
+ ephstat = buf[3];
+ snr = bGetSingle(&buf[4]);
+ elevation = bGetSingle(&buf[12]) * R2D;
+ azinuth = bGetSingle(&buf[16]) * R2D;
+
+ sprintf(logbuf, "S1 %02d %d %d %02x %4.1f %5.1f %4.1f",
+ prn, channel, aqflag, ephstat, snr, azinuth, elevation);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(logbuf);
+#endif /* DEBUG_NCC */
+
+ record_clock_stats(&peer->srcadr, logbuf);
+
+ return (0);
+}
+
+/******* Code below is from Trimble Tsipchat *************/
+
+/*
+ * *************************************************************************
+ *
+ * Trimble Navigation, Ltd.
+ * OEM Products Development Group
+ * P.O. Box 3642
+ * 645 North Mary Avenue
+ * Sunnyvale, California 94088-3642
+ *
+ * Corporate Headquarter:
+ * Telephone: (408) 481-8000
+ * Fax: (408) 481-6005
+ *
+ * Technical Support Center:
+ * Telephone: (800) 767-4822 (U.S. and Canada)
+ * (408) 481-6940 (outside U.S. and Canada)
+ * Fax: (408) 481-6020
+ * BBS: (408) 481-7800
+ * e-mail: trimble_support@trimble.com
+ * ftp://ftp.trimble.com/pub/sct/embedded/bin
+ *
+ * *************************************************************************
+ *
+ * ------- BYTE-SWAPPING -------
+ * TSIP is big-endian (Motorola) protocol. To use on little-endian (Intel)
+ * systems, the bytes of all multi-byte types (shorts, floats, doubles, etc.)
+ * must be reversed. This is controlled by the MACRO BYTESWAP; if defined, it
+ * assumes little-endian protocol.
+ * --------------------------------
+ *
+ * T_PARSER.C and T_PARSER.H contains primitive functions that interpret
+ * reports received from the receiver. A second source file pair,
+ * T_FORMAT.C and T_FORMAT.H, contin the matching TSIP command formatters.
+ *
+ * The module is in very portable, basic C language. It can be used as is, or
+ * with minimal changes if a TSIP communications application is needed separate
+ * from TSIPCHAT. The construction of most argument lists avoid the use of
+ * structures, but the developer is encouraged to reconstruct them using such
+ * definitions to meet project requirements. Declarations of T_PARSER.C
+ * functions are included in T_PARSER.H to provide prototyping definitions.
+ *
+ * There are two types of functions: a serial input processing routine,
+ * tsip_input_proc()
+ * which assembles incoming bytes into a TSIPPKT structure, and the
+ * report parsers, rpt_0x??().
+ *
+ * 1) The function tsip_input_proc() accumulates bytes from the receiver,
+ * strips control bytes (DLE), and checks if the report end sequence (DLE ETX)
+ * has been received. rpt.status is defined as TSIP_PARSED_FULL (== 1)
+ * if a complete packet is available.
+ *
+ * 2) The functions rpt_0x??() are report string interpreters patterned after
+ * the document called "Trimble Standard Interface Protocol". It should be
+ * noted that if the report buffer is sent into the receiver with the wrong
+ * length (byte count), the rpt_0x??() returns the Boolean equivalence for
+ * TRUE.
+ *
+ * *************************************************************************
+ *
+ */
+
+
+/*
+ * reads bytes until serial buffer is empty or a complete report
+ * has been received; end of report is signified by DLE ETX.
+ */
+static void
+tsip_input_proc(
+ TSIPPKT *rpt,
+ int inbyte
+ )
+{
+ unsigned char newbyte;
+
+ if (inbyte < 0 || inbyte > 0xFF) return;
+
+ newbyte = (unsigned char)(inbyte);
+ switch (rpt->status)
+ {
+ case TSIP_PARSED_DLE_1:
+ switch (newbyte)
+ {
+ case 0:
+ case ETX:
+ /* illegal TSIP IDs */
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_EMPTY;
+ break;
+ case DLE:
+ /* try normal message start again */
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_DLE_1;
+ break;
+ default:
+ /* legal TSIP ID; start message */
+ rpt->code = newbyte;
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_DATA;
+ break;
+ }
+ break;
+ case TSIP_PARSED_DATA:
+ switch (newbyte) {
+ case DLE:
+ /* expect DLE or ETX next */
+ rpt->status = TSIP_PARSED_DLE_2;
+ break;
+ default:
+ /* normal data byte */
+ rpt->buf[rpt->len] = newbyte;
+ rpt->len++;
+ /* no change in rpt->status */
+ break;
+ }
+ break;
+ case TSIP_PARSED_DLE_2:
+ switch (newbyte) {
+ case DLE:
+ /* normal data byte */
+ rpt->buf[rpt->len] = newbyte;
+ rpt->len++;
+ rpt->status = TSIP_PARSED_DATA;
+ break;
+ case ETX:
+ /* end of message; return TRUE here. */
+ rpt->status = TSIP_PARSED_FULL;
+ break;
+ default:
+ /* error: treat as TSIP_PARSED_DLE_1; start new report packet */
+ rpt->code = newbyte;
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_DATA;
+ }
+ break;
+ case TSIP_PARSED_FULL:
+ case TSIP_PARSED_EMPTY:
+ default:
+ switch (newbyte) {
+ case DLE:
+ /* normal message start */
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_DLE_1;
+ break;
+ default:
+ /* error: ignore newbyte */
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_EMPTY;
+ }
+ break;
+ }
+ if (rpt->len > MAX_RPTBUF) {
+ /* error: start new report packet */
+ rpt->status = TSIP_PARSED_EMPTY;
+ rpt->len = 0;
+ }
+}
+
+#ifdef TRIMBLE_OUTPUT_FUNC
+
+/**/
+/* Channel A configuration for dual port operation */
+short
+rpt_0x3D(
+ TSIPPKT *rpt,
+ unsigned char *tx_baud_index,
+ unsigned char *rx_baud_index,
+ unsigned char *char_format_index,
+ unsigned char *stop_bits,
+ unsigned char *tx_mode_index,
+ unsigned char *rx_mode_index
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 6) return TRUE;
+ *tx_baud_index = buf[0];
+ *rx_baud_index = buf[1];
+ *char_format_index = buf[2];
+ *stop_bits = (unsigned char)((buf[3] == 0x07) ? 1 : 2);
+ *tx_mode_index = buf[4];
+ *rx_mode_index = buf[5];
+ return FALSE;
+}
+
+/**/
+/* almanac data for specified satellite */
+short
+rpt_0x40(
+ TSIPPKT *rpt,
+ unsigned char *sv_prn,
+ short *week_num,
+ float *t_zc,
+ float *eccentricity,
+ float *t_oa,
+ float *i_0,
+ float *OMEGA_dot,
+ float *sqrt_A,
+ float *OMEGA_0,
+ float *omega,
+ float *M_0
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 39) return TRUE;
+ *sv_prn = buf[0];
+ *t_zc = bGetSingle (&buf[1]);
+ *week_num = bGetShort (&buf[5]);
+ *eccentricity = bGetSingle (&buf[7]);
+ *t_oa = bGetSingle (&buf[11]);
+ *i_0 = bGetSingle (&buf[15]);
+ *OMEGA_dot = bGetSingle (&buf[19]);
+ *sqrt_A = bGetSingle (&buf[23]);
+ *OMEGA_0 = bGetSingle (&buf[27]);
+ *omega = bGetSingle (&buf[31]);
+ *M_0 = bGetSingle (&buf[35]);
+ return FALSE;
+}
+
+/* GPS time */
+short
+rpt_0x41(
+ TSIPPKT *rpt,
+ float *time_of_week,
+ float *UTC_offset,
+ short *week_num
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 10) return TRUE;
+ *time_of_week = bGetSingle (buf);
+ *week_num = bGetShort (&buf[4]);
+ *UTC_offset = bGetSingle (&buf[6]);
+ return FALSE;
+}
+
+/* position in ECEF, single precision */
+short
+rpt_0x42(
+ TSIPPKT *rpt,
+ float pos_ECEF[3],
+ float *time_of_fix
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 16) return TRUE;
+ pos_ECEF[0] = bGetSingle (buf);
+ pos_ECEF[1]= bGetSingle (&buf[4]);
+ pos_ECEF[2]= bGetSingle (&buf[8]);
+ *time_of_fix = bGetSingle (&buf[12]);
+ return FALSE;
+}
+
+/* velocity in ECEF, single precision */
+short
+rpt_0x43(
+ TSIPPKT *rpt,
+ float ECEF_vel[3],
+ float *freq_offset,
+ float *time_of_fix
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 20) return TRUE;
+ ECEF_vel[0] = bGetSingle (buf);
+ ECEF_vel[1] = bGetSingle (&buf[4]);
+ ECEF_vel[2] = bGetSingle (&buf[8]);
+ *freq_offset = bGetSingle (&buf[12]);
+ *time_of_fix = bGetSingle (&buf[16]);
+ return FALSE;
+}
+
+/* software versions */
+short
+rpt_0x45(
+ TSIPPKT *rpt,
+ unsigned char *major_nav_version,
+ unsigned char *minor_nav_version,
+ unsigned char *nav_day,
+ unsigned char *nav_month,
+ unsigned char *nav_year,
+ unsigned char *major_dsp_version,
+ unsigned char *minor_dsp_version,
+ unsigned char *dsp_day,
+ unsigned char *dsp_month,
+ unsigned char *dsp_year
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 10) return TRUE;
+ *major_nav_version = buf[0];
+ *minor_nav_version = buf[1];
+ *nav_day = buf[2];
+ *nav_month = buf[3];
+ *nav_year = buf[4];
+ *major_dsp_version = buf[5];
+ *minor_dsp_version = buf[6];
+ *dsp_day = buf[7];
+ *dsp_month = buf[8];
+ *dsp_year = buf[9];
+ return FALSE;
+}
+
+/* receiver health and status */
+short
+rpt_0x46(
+ TSIPPKT *rpt,
+ unsigned char *status1,
+ unsigned char *status2
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 2) return TRUE;
+ *status1 = buf[0];
+ *status2 = buf[1];
+ return FALSE;
+}
+
+/* signal levels for all satellites tracked */
+short
+rpt_0x47(
+ TSIPPKT *rpt,
+ unsigned char *nsvs,
+ unsigned char *sv_prn,
+ float *snr
+ )
+{
+ short isv;
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 1 + 5*buf[0]) return TRUE;
+ *nsvs = buf[0];
+ for (isv = 0; isv < (*nsvs); isv++) {
+ sv_prn[isv] = buf[5*isv + 1];
+ snr[isv] = bGetSingle (&buf[5*isv + 2]);
+ }
+ return FALSE;
+}
+
+/* GPS system message */
+short
+rpt_0x48(
+ TSIPPKT *rpt,
+ unsigned char *message
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 22) return TRUE;
+ memcpy (message, buf, 22);
+ message[22] = 0;
+ return FALSE;
+}
+
+/* health for all satellites from almanac health page */
+short
+rpt_0x49(
+ TSIPPKT *rpt,
+ unsigned char *sv_health
+ )
+{
+ short i;
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 32) return TRUE;
+ for (i = 0; i < 32; i++) sv_health [i]= buf[i];
+ return FALSE;
+}
+
+/* position in lat-lon-alt, single precision */
+short
+rpt_0x4A(
+ TSIPPKT *rpt,
+ float *lat,
+ float *lon,
+ float *alt,
+ float *clock_bias,
+ float *time_of_fix
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 20) return TRUE;
+ *lat = bGetSingle (buf);
+ *lon = bGetSingle (&buf[4]);
+ *alt = bGetSingle (&buf[8]);
+ *clock_bias = bGetSingle (&buf[12]);
+ *time_of_fix = bGetSingle (&buf[16]);
+ return FALSE;
+}
+
+/* reference altitude parameters */
+short
+rpt_0x4A_2(
+ TSIPPKT *rpt,
+ float *alt,
+ float *dummy,
+ unsigned char *alt_flag
+ )
+{
+ unsigned char *buf;
+
+ buf = rpt->buf;
+
+ if (rpt->len != 9) return TRUE;
+ *alt = bGetSingle (buf);
+ *dummy = bGetSingle (&buf[4]);
+ *alt_flag = buf[8];
+ return FALSE;
+}
+
+/* machine ID code, status */
+short
+rpt_0x4B(
+ TSIPPKT *rpt,
+ unsigned char *machine_id,
+ unsigned char *status3,
+ unsigned char *status4
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 3) return TRUE;
+ *machine_id = buf[0];
+ *status3 = buf[1];
+ *status4 = buf[2];
+ return FALSE;
+}
+
+/* operating parameters and masks */
+short
+rpt_0x4C(
+ TSIPPKT *rpt,
+ unsigned char *dyn_code,
+ float *el_mask,
+ float *snr_mask,
+ float *dop_mask,
+ float *dop_switch
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 17) return TRUE;
+ *dyn_code = buf[0];
+ *el_mask = bGetSingle (&buf[1]);
+ *snr_mask = bGetSingle (&buf[5]);
+ *dop_mask = bGetSingle (&buf[9]);
+ *dop_switch = bGetSingle (&buf[13]);
+ return FALSE;
+}
+
+/* oscillator offset */
+short
+rpt_0x4D(
+ TSIPPKT *rpt,
+ float *osc_offset
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 4) return TRUE;
+ *osc_offset = bGetSingle (buf);
+ return FALSE;
+}
+
+/* yes/no response to command to set GPS time */
+short
+rpt_0x4E(
+ TSIPPKT *rpt,
+ unsigned char *response
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 1) return TRUE;
+ *response = buf[0];
+ return FALSE;
+}
+
+/* UTC data */
+short
+rpt_0x4F(
+ TSIPPKT *rpt,
+ double *a0,
+ float *a1,
+ float *time_of_data,
+ short *dt_ls,
+ short *wn_t,
+ short *wn_lsf,
+ short *dn,
+ short *dt_lsf
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 26) return TRUE;
+ *a0 = bGetDouble (buf);
+ *a1 = bGetSingle (&buf[8]);
+ *dt_ls = bGetShort (&buf[12]);
+ *time_of_data = bGetSingle (&buf[14]);
+ *wn_t = bGetShort (&buf[18]);
+ *wn_lsf = bGetShort (&buf[20]);
+ *dn = bGetShort (&buf[22]);
+ *dt_lsf = bGetShort (&buf[24]);
+ return FALSE;
+}
+
+/**/
+/* clock offset and frequency offset in 1-SV (0-D) mode */
+short
+rpt_0x54(
+ TSIPPKT *rpt,
+ float *clock_bias,
+ float *freq_offset,
+ float *time_of_fix
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 12) return TRUE;
+ *clock_bias = bGetSingle (buf);
+ *freq_offset = bGetSingle (&buf[4]);
+ *time_of_fix = bGetSingle (&buf[8]);
+ return FALSE;
+}
+
+/* I/O serial options */
+short
+rpt_0x55(
+ TSIPPKT *rpt,
+ unsigned char *pos_code,
+ unsigned char *vel_code,
+ unsigned char *time_code,
+ unsigned char *aux_code
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 4) return TRUE;
+ *pos_code = buf[0];
+ *vel_code = buf[1];
+ *time_code = buf[2];
+ *aux_code = buf[3];
+ return FALSE;
+}
+
+/* velocity in east-north-up coordinates */
+short
+rpt_0x56(
+ TSIPPKT *rpt,
+ float vel_ENU[3],
+ float *freq_offset,
+ float *time_of_fix
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 20) return TRUE;
+ /* east */
+ vel_ENU[0] = bGetSingle (buf);
+ /* north */
+ vel_ENU[1] = bGetSingle (&buf[4]);
+ /* up */
+ vel_ENU[2] = bGetSingle (&buf[8]);
+ *freq_offset = bGetSingle (&buf[12]);
+ *time_of_fix = bGetSingle (&buf[16]);
+ return FALSE;
+}
+
+/* info about last computed fix */
+short
+rpt_0x57(
+ TSIPPKT *rpt,
+ unsigned char *source_code,
+ unsigned char *diag_code,
+ short *week_num,
+ float *time_of_fix
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 8) return TRUE;
+ *source_code = buf[0];
+ *diag_code = buf[1];
+ *time_of_fix = bGetSingle (&buf[2]);
+ *week_num = bGetShort (&buf[6]);
+ return FALSE;
+}
+
+/* GPS system data or acknowledgment of GPS system data load */
+short
+rpt_0x58(
+ TSIPPKT *rpt,
+ unsigned char *op_code,
+ unsigned char *data_type,
+ unsigned char *sv_prn,
+ unsigned char *data_length,
+ unsigned char *data_packet
+ )
+{
+ unsigned char *buf, *buf4;
+ short dl;
+ ALM_INFO* alminfo;
+ ION_INFO* ioninfo;
+ UTC_INFO* utcinfo;
+ NAV_INFO* navinfo;
+
+ buf = rpt->buf;
+
+ if (buf[0] == 2) {
+ if (rpt->len < 4) return TRUE;
+ if (rpt->len != 4+buf[3]) return TRUE;
+ }
+ else if (rpt->len != 3) {
+ return TRUE;
+ }
+ *op_code = buf[0];
+ *data_type = buf[1];
+ *sv_prn = buf[2];
+ if (*op_code == 2) {
+ dl = buf[3];
+ *data_length = (unsigned char)dl;
+ buf4 = &buf[4];
+ switch (*data_type) {
+ case 2:
+ /* Almanac */
+ if (*data_length != sizeof (ALM_INFO)) return TRUE;
+ alminfo = (ALM_INFO*)data_packet;
+ alminfo->t_oa_raw = buf4[0];
+ alminfo->SV_health = buf4[1];
+ alminfo->e = bGetSingle(&buf4[2]);
+ alminfo->t_oa = bGetSingle(&buf4[6]);
+ alminfo->i_0 = bGetSingle(&buf4[10]);
+ alminfo->OMEGADOT = bGetSingle(&buf4[14]);
+ alminfo->sqrt_A = bGetSingle(&buf4[18]);
+ alminfo->OMEGA_0 = bGetSingle(&buf4[22]);
+ alminfo->omega = bGetSingle(&buf4[26]);
+ alminfo->M_0 = bGetSingle(&buf4[30]);
+ alminfo->a_f0 = bGetSingle(&buf4[34]);
+ alminfo->a_f1 = bGetSingle(&buf4[38]);
+ alminfo->Axis = bGetSingle(&buf4[42]);
+ alminfo->n = bGetSingle(&buf4[46]);
+ alminfo->OMEGA_n = bGetSingle(&buf4[50]);
+ alminfo->ODOT_n = bGetSingle(&buf4[54]);
+ alminfo->t_zc = bGetSingle(&buf4[58]);
+ alminfo->weeknum = bGetShort(&buf4[62]);
+ alminfo->wn_oa = bGetShort(&buf4[64]);
+ break;
+
+ case 3:
+ /* Almanac health page */
+ if (*data_length != sizeof (ALH_PARMS) + 3) return TRUE;
+
+ /* this record is returned raw */
+ memcpy (data_packet, buf4, dl);
+ break;
+
+ case 4:
+ /* Ionosphere */
+ if (*data_length != sizeof (ION_INFO) + 8) return TRUE;
+ ioninfo = (ION_INFO*)data_packet;
+ ioninfo->alpha_0 = bGetSingle (&buf4[8]);
+ ioninfo->alpha_1 = bGetSingle (&buf4[12]);
+ ioninfo->alpha_2 = bGetSingle (&buf4[16]);
+ ioninfo->alpha_3 = bGetSingle (&buf4[20]);
+ ioninfo->beta_0 = bGetSingle (&buf4[24]);
+ ioninfo->beta_1 = bGetSingle (&buf4[28]);
+ ioninfo->beta_2 = bGetSingle (&buf4[32]);
+ ioninfo->beta_3 = bGetSingle (&buf4[36]);
+ break;
+
+ case 5:
+ /* UTC */
+ if (*data_length != sizeof (UTC_INFO) + 13) return TRUE;
+ utcinfo = (UTC_INFO*)data_packet;
+ utcinfo->A_0 = bGetDouble (&buf4[13]);
+ utcinfo->A_1 = bGetSingle (&buf4[21]);
+ utcinfo->delta_t_LS = bGetShort (&buf4[25]);
+ utcinfo->t_ot = bGetSingle(&buf4[27]);
+ utcinfo->WN_t = bGetShort (&buf4[31]);
+ utcinfo->WN_LSF = bGetShort (&buf4[33]);
+ utcinfo->DN = bGetShort (&buf4[35]);
+ utcinfo->delta_t_LSF = bGetShort (&buf4[37]);
+ break;
+
+ case 6:
+ /* Ephemeris */
+ if (*data_length != sizeof (NAV_INFO) - 1) return TRUE;
+
+ navinfo = (NAV_INFO*)data_packet;
+
+ navinfo->sv_number = buf4[0];
+ navinfo->t_ephem = bGetSingle (&buf4[1]);
+ navinfo->ephclk.weeknum = bGetShort (&buf4[5]);
+
+ navinfo->ephclk.codeL2 = buf4[7];
+ navinfo->ephclk.L2Pdata = buf4[8];
+ navinfo->ephclk.SVacc_raw = buf4[9];
+ navinfo->ephclk.SV_health = buf4[10];
+ navinfo->ephclk.IODC = bGetShort (&buf4[11]);
+ navinfo->ephclk.T_GD = bGetSingle (&buf4[13]);
+ navinfo->ephclk.t_oc = bGetSingle (&buf4[17]);
+ navinfo->ephclk.a_f2 = bGetSingle (&buf4[21]);
+ navinfo->ephclk.a_f1 = bGetSingle (&buf4[25]);
+ navinfo->ephclk.a_f0 = bGetSingle (&buf4[29]);
+ navinfo->ephclk.SVacc = bGetSingle (&buf4[33]);
+
+ navinfo->ephorb.IODE = buf4[37];
+ navinfo->ephorb.fit_interval = buf4[38];
+ navinfo->ephorb.C_rs = bGetSingle (&buf4[39]);
+ navinfo->ephorb.delta_n = bGetSingle (&buf4[43]);
+ navinfo->ephorb.M_0 = bGetDouble (&buf4[47]);
+ navinfo->ephorb.C_uc = bGetSingle (&buf4[55]);
+ navinfo->ephorb.e = bGetDouble (&buf4[59]);
+ navinfo->ephorb.C_us = bGetSingle (&buf4[67]);
+ navinfo->ephorb.sqrt_A = bGetDouble (&buf4[71]);
+ navinfo->ephorb.t_oe = bGetSingle (&buf4[79]);
+ navinfo->ephorb.C_ic = bGetSingle (&buf4[83]);
+ navinfo->ephorb.OMEGA_0 = bGetDouble (&buf4[87]);
+ navinfo->ephorb.C_is = bGetSingle (&buf4[95]);
+ navinfo->ephorb.i_0 = bGetDouble (&buf4[99]);
+ navinfo->ephorb.C_rc = bGetSingle (&buf4[107]);
+ navinfo->ephorb.omega = bGetDouble (&buf4[111]);
+ navinfo->ephorb.OMEGADOT=bGetSingle (&buf4[119]);
+ navinfo->ephorb.IDOT = bGetSingle (&buf4[123]);
+ navinfo->ephorb.Axis = bGetDouble (&buf4[127]);
+ navinfo->ephorb.n = bGetDouble (&buf4[135]);
+ navinfo->ephorb.r1me2 = bGetDouble (&buf4[143]);
+ navinfo->ephorb.OMEGA_n=bGetDouble (&buf4[151]);
+ navinfo->ephorb.ODOT_n = bGetDouble (&buf4[159]);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+/* satellite enable/disable or health heed/ignore list */
+short
+rpt_0x59(
+ TSIPPKT *rpt,
+ unsigned char *code_type,
+ unsigned char status_code[32]
+ )
+{
+ short iprn;
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 33) return TRUE;
+ *code_type = buf[0];
+ for (iprn = 0; iprn < 32; iprn++)
+ status_code[iprn] = buf[iprn + 1];
+ return FALSE;
+}
+
+/* raw measurement data - code phase/Doppler */
+short
+rpt_0x5A(
+ TSIPPKT *rpt,
+ unsigned char *sv_prn,
+ float *sample_length,
+ float *signal_level,
+ float *code_phase,
+ float *Doppler,
+ double *time_of_fix
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 25) return TRUE;
+ *sv_prn = buf[0];
+ *sample_length = bGetSingle (&buf[1]);
+ *signal_level = bGetSingle (&buf[5]);
+ *code_phase = bGetSingle (&buf[9]);
+ *Doppler = bGetSingle (&buf[13]);
+ *time_of_fix = bGetDouble (&buf[17]);
+ return FALSE;
+}
+
+/* satellite ephorb status */
+short
+rpt_0x5B(
+ TSIPPKT *rpt,
+ unsigned char *sv_prn,
+ unsigned char *sv_health,
+ unsigned char *sv_iode,
+ unsigned char *fit_interval_flag,
+ float *time_of_collection,
+ float *time_of_eph,
+ float *sv_accy
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 16) return TRUE;
+ *sv_prn = buf[0];
+ *time_of_collection = bGetSingle (&buf[1]);
+ *sv_health = buf[5];
+ *sv_iode = buf[6];
+ *time_of_eph = bGetSingle (&buf[7]);
+ *fit_interval_flag = buf[11];
+ *sv_accy = bGetSingle (&buf[12]);
+ return FALSE;
+}
+
+/* satellite tracking status */
+short
+rpt_0x5C(
+ TSIPPKT *rpt,
+ unsigned char *sv_prn,
+ unsigned char *slot,
+ unsigned char *chan,
+ unsigned char *acq_flag,
+ unsigned char *eph_flag,
+ float *signal_level,
+ float *time_of_last_msmt,
+ float *elev,
+ float *azim,
+ unsigned char *old_msmt_flag,
+ unsigned char *integer_msec_flag,
+ unsigned char *bad_data_flag,
+ unsigned char *data_collect_flag
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 24) return TRUE;
+ *sv_prn = buf[0];
+ *slot = (unsigned char)((buf[1] & 0x07) + 1);
+ *chan = (unsigned char)(buf[1] >> 3);
+ if (*chan == 0x10) *chan = 2;
+ else (*chan)++;
+ *acq_flag = buf[2];
+ *eph_flag = buf[3];
+ *signal_level = bGetSingle (&buf[4]);
+ *time_of_last_msmt = bGetSingle (&buf[8]);
+ *elev = bGetSingle (&buf[12]);
+ *azim = bGetSingle (&buf[16]);
+ *old_msmt_flag = buf[20];
+ *integer_msec_flag = buf[21];
+ *bad_data_flag = buf[22];
+ *data_collect_flag = buf[23];
+ return FALSE;
+}
+
+/**/
+/* over-determined satellite selection for position fixes, PDOP, fix mode */
+short
+rpt_0x6D(
+ TSIPPKT *rpt,
+ unsigned char *manual_mode,
+ unsigned char *nsvs,
+ unsigned char *ndim,
+ unsigned char sv_prn[],
+ float *pdop,
+ float *hdop,
+ float *vdop,
+ float *tdop
+ )
+{
+ short islot;
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ *nsvs = (unsigned char)((buf[0] & 0xF0) >> 4);
+ if ((*nsvs)>8) return TRUE;
+ if (rpt->len != 17 + (*nsvs) ) return TRUE;
+
+ *manual_mode = (unsigned char)(buf[0] & 0x08);
+ *ndim = (unsigned char)((buf[0] & 0x07));
+ *pdop = bGetSingle (&buf[1]);
+ *hdop = bGetSingle (&buf[5]);
+ *vdop = bGetSingle (&buf[9]);
+ *tdop = bGetSingle (&buf[13]);
+ for (islot = 0; islot < (*nsvs); islot++)
+ sv_prn[islot] = buf[islot + 17];
+ return FALSE;
+}
+
+/**/
+/* differential fix mode */
+short
+rpt_0x82(
+ TSIPPKT *rpt,
+ unsigned char *diff_mode
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 1) return TRUE;
+ *diff_mode = buf[0];
+ return FALSE;
+}
+
+/* position, ECEF double precision */
+short
+rpt_0x83(
+ TSIPPKT *rpt,
+ double ECEF_pos[3],
+ double *clock_bias,
+ float *time_of_fix
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 36) return TRUE;
+ ECEF_pos[0] = bGetDouble (buf);
+ ECEF_pos[1] = bGetDouble (&buf[8]);
+ ECEF_pos[2] = bGetDouble (&buf[16]);
+ *clock_bias = bGetDouble (&buf[24]);
+ *time_of_fix = bGetSingle (&buf[32]);
+ return FALSE;
+}
+
+/* position, lat-lon-alt double precision */
+short
+rpt_0x84(
+ TSIPPKT *rpt,
+ double *lat,
+ double *lon,
+ double *alt,
+ double *clock_bias,
+ float *time_of_fix
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 36) return TRUE;
+ *lat = bGetDouble (buf);
+ *lon = bGetDouble (&buf[8]);
+ *alt = bGetDouble (&buf[16]);
+ *clock_bias = bGetDouble (&buf[24]);
+ *time_of_fix = bGetSingle (&buf[32]);
+ return FALSE;
+}
+
+short
+rpt_Paly0xBB(
+ TSIPPKT *rpt,
+ TSIP_RCVR_CFG *TsipxBB
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ /* Palisade is inconsistent with other TSIP, which has a length of 40 */
+ /* if (rpt->len != 40) return TRUE; */
+ if (rpt->len != 43) return TRUE;
+
+ TsipxBB->bSubcode = buf[0];
+ TsipxBB->operating_mode = buf[1];
+ TsipxBB->dyn_code = buf[3];
+ TsipxBB->elev_mask = bGetSingle (&buf[5]);
+ TsipxBB->cno_mask = bGetSingle (&buf[9]);
+ TsipxBB->dop_mask = bGetSingle (&buf[13]);
+ TsipxBB->dop_switch = bGetSingle (&buf[17]);
+ return FALSE;
+}
+
+/* Receiver serial port configuration */
+short
+rpt_0xBC(
+ TSIPPKT *rpt,
+ unsigned char *port_num,
+ unsigned char *in_baud,
+ unsigned char *out_baud,
+ unsigned char *data_bits,
+ unsigned char *parity,
+ unsigned char *stop_bits,
+ unsigned char *flow_control,
+ unsigned char *protocols_in,
+ unsigned char *protocols_out,
+ unsigned char *reserved
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 10) return TRUE;
+ *port_num = buf[0];
+ *in_baud = buf[1];
+ *out_baud = buf[2];
+ *data_bits = buf[3];
+ *parity = buf[4];
+ *stop_bits = buf[5];
+ *flow_control = buf[6];
+ *protocols_in = buf[7];
+ *protocols_out = buf[8];
+ *reserved = buf[9];
+
+ return FALSE;
+}
+
+/**** Superpackets ****/
+
+short
+rpt_0x8F0B(
+ TSIPPKT *rpt,
+ unsigned short *event,
+ double *tow,
+ unsigned char *date,
+ unsigned char *month,
+ short *year,
+ unsigned char *dim_mode,
+ short *utc_offset,
+ double *bias,
+ double *drift,
+ float *bias_unc,
+ float *dr_unc,
+ double *lat,
+ double *lon,
+ double *alt,
+ char sv_id[8]
+ )
+{
+ short local_index;
+ unsigned char *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 74) return TRUE;
+ *event = bGetShort(&buf[1]);
+ *tow = bGetDouble(&buf[3]);
+ *date = buf[11];
+ *month = buf[12];
+ *year = bGetShort(&buf[13]);
+ *dim_mode = buf[15];
+ *utc_offset = bGetShort(&buf[16]);
+ *bias = bGetDouble(&buf[18]);
+ *drift = bGetDouble(&buf[26]);
+ *bias_unc = bGetSingle(&buf[34]);
+ *dr_unc = bGetSingle(&buf[38]);
+ *lat = bGetDouble(&buf[42]);
+ *lon = bGetDouble(&buf[50]);
+ *alt = bGetDouble(&buf[58]);
+
+ for (local_index=0; local_index<8; local_index++) sv_id[local_index] = buf[local_index + 66];
+ return FALSE;
+}
+
+/* datum index and coefficients */
+short
+rpt_0x8F14(
+ TSIPPKT *rpt,
+ short *datum_idx,
+ double datum_coeffs[5]
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 43) return TRUE;
+ *datum_idx = bGetShort(&buf[1]);
+ datum_coeffs[0] = bGetDouble (&buf[3]);
+ datum_coeffs[1] = bGetDouble (&buf[11]);
+ datum_coeffs[2] = bGetDouble (&buf[19]);
+ datum_coeffs[3] = bGetDouble (&buf[27]);
+ datum_coeffs[4] = bGetDouble (&buf[35]);
+ return FALSE;
+}
+
+
+/* datum index and coefficients */
+short
+rpt_0x8F15(
+ TSIPPKT *rpt,
+ short *datum_idx,
+ double datum_coeffs[5]
+ )
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 43) return TRUE;
+ *datum_idx = bGetShort(&buf[1]);
+ datum_coeffs[0] = bGetDouble (&buf[3]);
+ datum_coeffs[1] = bGetDouble (&buf[11]);
+ datum_coeffs[2] = bGetDouble (&buf[19]);
+ datum_coeffs[3] = bGetDouble (&buf[27]);
+ datum_coeffs[4] = bGetDouble (&buf[35]);
+ return FALSE;
+}
+
+
+#define MAX_LONG (2147483648.) /* 2**31 */
+
+short
+rpt_0x8F20(
+ TSIPPKT *rpt,
+ unsigned char *info,
+ double *lat,
+ double *lon,
+ double *alt,
+ double vel_enu[],
+ double *time_of_fix,
+ short *week_num,
+ unsigned char *nsvs,
+ unsigned char sv_prn[],
+ short sv_IODC[],
+ short *datum_index
+ )
+{
+ short
+ isv;
+ unsigned char
+ *buf, prnx, iode;
+ unsigned long
+ ulongtemp;
+ long
+ longtemp;
+ double
+ vel_scale;
+
+ buf = rpt->buf;
+
+ if (rpt->len != 56) return TRUE;
+
+ vel_scale = (buf[24]&1)? 0.020 : 0.005;
+ vel_enu[0] = bGetShort (buf+2)*vel_scale;
+ vel_enu[1] = bGetShort (buf+4)*vel_scale;
+ vel_enu[2] = bGetShort (buf+6)*vel_scale;
+
+ *time_of_fix = bGetULong (buf+8)*.001;
+
+ longtemp = bGetLong (buf+12);
+ *lat = longtemp*(GPS_PI/MAX_LONG);
+
+ ulongtemp = bGetULong (buf+16);
+ *lon = ulongtemp*(GPS_PI/MAX_LONG);
+ if (*lon > GPS_PI) *lon -= 2.0*GPS_PI;
+
+ *alt = bGetLong (buf+20)*.001;
+ /* 25 blank; 29 = UTC */
+ (*datum_index) = (short)((short)buf[26]-1);
+ *info = buf[27];
+ *nsvs = buf[28];
+ *week_num = bGetShort (&buf[30]);
+ for (isv = 0; isv < 8; isv++) {
+ prnx = buf[32+2*isv];
+ sv_prn[isv] = (unsigned char)(prnx&0x3F);
+ iode = buf[33+2*isv];
+ sv_IODC[isv] = (short)(iode | ((prnx>>6)<<8));
+ }
+ return FALSE;
+}
+
+short
+rpt_0x8F41(
+ TSIPPKT *rpt,
+ unsigned char *bSearchRange,
+ unsigned char *bBoardOptions,
+ unsigned long *iiSerialNumber,
+ unsigned char *bBuildYear,
+ unsigned char *bBuildMonth,
+ unsigned char *bBuildDay,
+ unsigned char *bBuildHour,
+ float *fOscOffset,
+ unsigned short *iTestCodeId
+ )
+{
+ if (rpt->len != 17) return FALSE;
+ *bSearchRange = rpt->buf[1];
+ *bBoardOptions = rpt->buf[2];
+ *iiSerialNumber = bGetLong(&rpt->buf[3]);
+ *bBuildYear = rpt->buf[7];
+ *bBuildMonth = rpt->buf[8];
+ *bBuildDay = rpt->buf[9];
+ *bBuildHour = rpt->buf[10];
+ *fOscOffset = bGetSingle(&rpt->buf[11]);
+ *iTestCodeId = bGetShort(&rpt->buf[15]);
+/* Tsipx8E41Data = *Tsipx8E41; */
+ return TRUE;
+}
+
+short
+rpt_0x8F42(
+ TSIPPKT *rpt,
+ unsigned char *bProdOptionsPre,
+ unsigned char *bProdNumberExt,
+ unsigned short *iCaseSerialNumberPre,
+ unsigned long *iiCaseSerialNumber,
+ unsigned long *iiProdNumber,
+ unsigned short *iPremiumOptions,
+ unsigned short *iMachineID,
+ unsigned short *iKey
+ )
+{
+ if (rpt->len != 19) return FALSE;
+ *bProdOptionsPre = rpt->buf[1];
+ *bProdNumberExt = rpt->buf[2];
+ *iCaseSerialNumberPre = bGetShort(&rpt->buf[3]);
+ *iiCaseSerialNumber = bGetLong(&rpt->buf[5]);
+ *iiProdNumber = bGetLong(&rpt->buf[9]);
+ *iPremiumOptions = bGetShort(&rpt->buf[13]);
+ *iMachineID = bGetShort(&rpt->buf[15]);
+ *iKey = bGetShort(&rpt->buf[17]);
+ return TRUE;
+}
+
+short
+rpt_0x8F45(
+ TSIPPKT *rpt,
+ unsigned char *bSegMask
+ )
+{
+ if (rpt->len != 2) return FALSE;
+ *bSegMask = rpt->buf[1];
+ return TRUE;
+}
+
+/* Stinger PPS definition */
+short
+rpt_0x8F4A_16(
+ TSIPPKT *rpt,
+ unsigned char *pps_enabled,
+ unsigned char *pps_timebase,
+ unsigned char *pos_polarity,
+ double *pps_offset,
+ float *bias_unc_threshold
+ )
+{
+ unsigned char
+ *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 16) return TRUE;
+ *pps_enabled = buf[1];
+ *pps_timebase = buf[2];
+ *pos_polarity = buf[3];
+ *pps_offset = bGetDouble(&buf[4]);
+ *bias_unc_threshold = bGetSingle(&buf[12]);
+ return FALSE;
+}
+
+short
+rpt_0x8F4B(
+ TSIPPKT *rpt,
+ unsigned long *decorr_max
+ )
+{
+ unsigned char
+ *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 5) return TRUE;
+ *decorr_max = bGetLong(&buf[1]);
+ return FALSE;
+}
+
+short
+rpt_0x8F4D(
+ TSIPPKT *rpt,
+ unsigned long *event_mask
+ )
+{
+ unsigned char
+ *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 5) return TRUE;
+ *event_mask = bGetULong (&buf[1]);
+ return FALSE;
+}
+
+short
+rpt_0x8FA5(
+ TSIPPKT *rpt,
+ unsigned char *spktmask
+ )
+{
+ unsigned char
+ *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 5) return TRUE;
+ spktmask[0] = buf[1];
+ spktmask[1] = buf[2];
+ spktmask[2] = buf[3];
+ spktmask[3] = buf[4];
+ return FALSE;
+}
+
+short
+rpt_0x8FAD(
+ TSIPPKT *rpt,
+ unsigned short *COUNT,
+ double *FracSec,
+ unsigned char *Hour,
+ unsigned char *Minute,
+ unsigned char *Second,
+ unsigned char *Day,
+ unsigned char *Month,
+ unsigned short *Year,
+ unsigned char *Status,
+ unsigned char *Flags
+ )
+{
+ if (rpt->len != 22) return TRUE;
+
+ *COUNT = bGetUShort(&rpt->buf[1]);
+ *FracSec = bGetDouble(&rpt->buf[3]);
+ *Hour = rpt->buf[11];
+ *Minute = rpt->buf[12];
+ *Second = rpt->buf[13];
+ *Day = rpt->buf[14];
+ *Month = rpt->buf[15];
+ *Year = bGetUShort(&rpt->buf[16]);
+ *Status = rpt->buf[18];
+ *Flags = rpt->buf[19];
+ return FALSE;
+}
+
+
+/*
+ * *************************************************************************
+ *
+ * Trimble Navigation, Ltd.
+ * OEM Products Development Group
+ * P.O. Box 3642
+ * 645 North Mary Avenue
+ * Sunnyvale, California 94088-3642
+ *
+ * Corporate Headquarter:
+ * Telephone: (408) 481-8000
+ * Fax: (408) 481-6005
+ *
+ * Technical Support Center:
+ * Telephone: (800) 767-4822 (U.S. and Canada)
+ * (408) 481-6940 (outside U.S. and Canada)
+ * Fax: (408) 481-6020
+ * BBS: (408) 481-7800
+ * e-mail: trimble_support@trimble.com
+ * ftp://ftp.trimble.com/pub/sct/embedded/bin
+ *
+ * *************************************************************************
+ *
+ * T_REPORT.C consists of a primary function TranslateTSIPReportToText()
+ * called by main().
+ *
+ * This function takes a character buffer that has been received as a report
+ * from a TSIP device and interprets it. The character buffer has been
+ * assembled using tsip_input_proc() in T_PARSER.C.
+ *
+ * A large case statement directs processing to one of many mid-level
+ * functions. The mid-level functions specific to the current report
+ * code passes the report buffer to the appropriate report decoder
+ * rpt_0x?? () in T_PARSER.C, which converts the byte stream in rpt.buf
+ * to data values approporaite for use.
+ *
+ * *************************************************************************
+ *
+ */
+
+
+#define GOOD_PARSE 0
+#define BADID_PARSE 1
+#define BADLEN_PARSE 2
+#define BADDATA_PARSE 3
+
+#define B_TSIP 0x02
+#define B_NMEA 0x04
+
+
+/* pbuf is the pointer to the current location of the text output */
+static char
+*pbuf;
+
+/* keep track of whether the message has been successfully parsed */
+static short
+parsed;
+
+
+/* convert time of week into day-hour-minute-second and print */
+char *
+show_time(
+ float time_of_week
+ )
+{
+ short days, hours, minutes;
+ float seconds;
+ double tow = 0;
+ static char timestring [80];
+
+ if (time_of_week == -1.0)
+ {
+ sprintf(timestring, " <No time yet> ");
+ }
+ else if ((time_of_week >= 604800.0) || (time_of_week < 0.0))
+ {
+ sprintf(timestring, " <Bad time> ");
+ }
+ else
+ {
+ if (time_of_week < 604799.9)
+ tow = time_of_week + .00000001;
+ seconds = (float)fmod(tow, 60.);
+ minutes = (short) fmod(tow/60., 60.);
+ hours = (short)fmod(tow / 3600., 24.);
+ days = (short)(tow / 86400.0);
+ sprintf(timestring, " %s %02d:%02d:%05.2f ",
+ dayname[days], hours, minutes, seconds);
+ }
+ return timestring;
+}
+
+/**/
+/* 0x3D */
+static void
+rpt_chan_A_config(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ tx_baud_index, rx_baud_index,
+ char_format_index, stop_bits,
+ tx_mode_index, rx_mode_index,
+ databits, parity;
+ int
+ i, nbaud;
+
+ /* unload rptbuf */
+ if (rpt_0x3D (rpt,
+ &tx_baud_index, &rx_baud_index, &char_format_index,
+ &stop_bits, &tx_mode_index, &rx_mode_index)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nChannel A Configuration");
+
+ nbaud = sizeof(old_baudnum);
+
+ for (i = 0; i < nbaud; ++i) if (tx_baud_index == old_baudnum[i]) break;
+ pbuf += sprintf(pbuf, "\n Transmit speed: %s at %s",
+ old_output_ch[tx_mode_index], st_baud_text_app[i]);
+
+ for (i = 0; i < nbaud; ++i) if (rx_baud_index == old_baudnum[i]) break;
+ pbuf += sprintf(pbuf, "\n Receive speed: %s at %s",
+ old_input_ch[rx_mode_index], st_baud_text_app[i]);
+
+ databits = (unsigned char)((char_format_index & 0x03) + 5);
+
+ parity = (unsigned char)(char_format_index >> 2);
+ if (parity > 4) parity = 2;
+
+ pbuf += sprintf(pbuf, "\n Character format (bits/char, parity, stop bits): %d-%s-%d",
+ databits, old_parity_text[parity], stop_bits);
+}
+
+/**/
+/* 0x40 */
+static void
+rpt_almanac_data_page(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ sv_prn;
+ short
+ week_num;
+ float
+ t_zc,
+ eccentricity,
+ t_oa,
+ i_0,
+ OMEGA_dot,
+ sqrt_A,
+ OMEGA_0,
+ omega,
+ M_0;
+
+ /* unload rptbuf */
+ if (rpt_0x40 (rpt,
+ &sv_prn, &week_num, &t_zc, &eccentricity, &t_oa,
+ &i_0, &OMEGA_dot, &sqrt_A, &OMEGA_0, &omega, &M_0)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nAlmanac for SV %02d", sv_prn);
+ pbuf += sprintf(pbuf, "\n Captured:%15.0f %s",
+ t_zc, show_time (t_zc));
+ pbuf += sprintf(pbuf, "\n week:%15d", week_num);
+ pbuf += sprintf(pbuf, "\n Eccentricity:%15g", eccentricity);
+ pbuf += sprintf(pbuf, "\n T_oa:%15.0f %s",
+ t_oa, show_time (t_oa));
+ pbuf += sprintf(pbuf, "\n i 0:%15g", i_0);
+ pbuf += sprintf(pbuf, "\n OMEGA dot:%15g", OMEGA_dot);
+ pbuf += sprintf(pbuf, "\n sqrt A:%15g", sqrt_A);
+ pbuf += sprintf(pbuf, "\n OMEGA 0:%15g", OMEGA_0);
+ pbuf += sprintf(pbuf, "\n omega:%15g", omega);
+ pbuf += sprintf(pbuf, "\n M 0:%15g", M_0);
+}
+
+/* 0x41 */
+static void
+rpt_GPS_time(
+ TSIPPKT *rpt
+ )
+{
+ float
+ time_of_week, UTC_offset;
+ short
+ week_num;
+
+ /* unload rptbuf */
+ if (rpt_0x41 (rpt, &time_of_week, &UTC_offset, &week_num)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nGPS time:%s GPS week: %d UTC offset %.1f",
+ show_time(time_of_week), week_num, UTC_offset);
+
+}
+
+/* 0x42 */
+static void
+rpt_single_ECEF_position(
+ TSIPPKT *rpt
+ )
+{
+ float
+ ECEF_pos[3], time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x42 (rpt, ECEF_pos, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nSXYZ: %15.0f %15.0f %15.0f %s",
+ ECEF_pos[0], ECEF_pos[1], ECEF_pos[2],
+ show_time(time_of_fix));
+}
+
+/* 0x43 */
+static void
+rpt_single_ECEF_velocity(
+ TSIPPKT *rpt
+ )
+{
+
+ float
+ ECEF_vel[3], freq_offset, time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x43 (rpt, ECEF_vel, &freq_offset, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nVelECEF: %11.3f %11.3f %11.3f %12.3f%s",
+ ECEF_vel[0], ECEF_vel[1], ECEF_vel[2], freq_offset,
+ show_time(time_of_fix));
+}
+
+/* 0x45 */
+static void
+rpt_SW_version(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ major_nav_version, minor_nav_version,
+ nav_day, nav_month, nav_year,
+ major_dsp_version, minor_dsp_version,
+ dsp_day, dsp_month, dsp_year;
+
+ /* unload rptbuf */
+ if (rpt_0x45 (rpt,
+ &major_nav_version, &minor_nav_version,
+ &nav_day, &nav_month, &nav_year,
+ &major_dsp_version, &minor_dsp_version,
+ &dsp_day, &dsp_month, &dsp_year)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf,
+ "\nFW Versions: Nav Proc %2d.%02d %2d/%2d/%2d Sig Proc %2d.%02d %2d/%2d/%2d",
+ major_nav_version, minor_nav_version, nav_day, nav_month, nav_year,
+ major_dsp_version, minor_dsp_version, dsp_day, dsp_month, dsp_year);
+}
+
+/* 0x46 */
+static void
+rpt_rcvr_health(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ status1, status2;
+ const char
+ *text;
+ static const char const
+ *sc_text[] = {
+ "Doing position fixes",
+ "Don't have GPS time yet",
+ "Waiting for almanac collection",
+ "DOP too high ",
+ "No satellites available",
+ "Only 1 satellite available",
+ "Only 2 satellites available",
+ "Only 3 satellites available",
+ "No satellites usable ",
+ "Only 1 satellite usable",
+ "Only 2 satellites usable",
+ "Only 3 satellites usable",
+ "Chosen satellite unusable"};
+
+
+ /* unload rptbuf */
+ if (rpt_0x46 (rpt, &status1, &status2))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ text = (status1 < COUNTOF(sc_text))
+ ? sc_text[status1]
+ : "(out of range)";
+ pbuf += sprintf(pbuf, "\nRcvr status1: %s (%02Xh); ",
+ text, status1);
+
+ pbuf += sprintf(pbuf, "status2: %s, %s (%02Xh)",
+ (status2 & 0x01)?"No BBRAM":"BBRAM OK",
+ (status2 & 0x10)?"No Ant":"Ant OK",
+ status2);
+}
+
+/* 0x47 */
+static void
+rpt_SNR_all_SVs(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ nsvs, sv_prn[12];
+ short
+ isv;
+ float
+ snr[12];
+
+ /* unload rptbuf */
+ if (rpt_0x47 (rpt, &nsvs, sv_prn, snr))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nSNR for satellites: %d", nsvs);
+ for (isv = 0; isv < nsvs; isv++)
+ {
+ pbuf += sprintf(pbuf, "\n SV %02d %6.2f",
+ sv_prn[isv], snr[isv]);
+ }
+}
+
+/* 0x48 */
+static void
+rpt_GPS_system_message(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ message[23];
+
+ /* unload rptbuf */
+ if (rpt_0x48 (rpt, message))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nGPS message: %s", message);
+}
+
+/* 0x49 */
+static void
+rpt_almanac_health_page(
+ TSIPPKT *rpt
+ )
+{
+ short
+ iprn;
+ unsigned char
+ sv_health [32];
+
+ /* unload rptbuf */
+ if (rpt_0x49 (rpt, sv_health))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nAlmanac health page:");
+ for (iprn = 0; iprn < 32; iprn++)
+ {
+ if (!(iprn%5)) *pbuf++ = '\n';
+ pbuf += sprintf(pbuf, " SV%02d %2X",
+ (iprn+1) , sv_health[iprn]);
+ }
+}
+
+/* 0x4A */
+static void
+rpt_single_lla_position(
+ TSIPPKT *rpt
+ )
+{
+ short
+ lat_deg, lon_deg;
+ float
+ lat, lon,
+ alt, clock_bias, time_of_fix;
+ double lat_min, lon_min;
+ unsigned char
+ north_south, east_west;
+
+ if (rpt_0x4A (rpt,
+ &lat, &lon, &alt, &clock_bias, &time_of_fix))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ /* convert from radians to degrees */
+ lat *= (float)R2D;
+ north_south = 'N';
+ if (lat < 0.0)
+ {
+ north_south = 'S';
+ lat = -lat;
+ }
+ lat_deg = (short)lat;
+ lat_min = (lat - lat_deg) * 60.0;
+
+ lon *= (float)R2D;
+ east_west = 'E';
+ if (lon < 0.0)
+ {
+ east_west = 'W';
+ lon = -lon;
+ }
+ lon_deg = (short)lon;
+ lon_min = (lon - lon_deg) * 60.0;
+
+ pbuf += sprintf(pbuf, "\nSLLA: %4d: %06.3f %c%5d:%06.3f %c%10.2f %12.2f%s",
+ lat_deg, lat_min, north_south,
+ lon_deg, lon_min, east_west,
+ alt, clock_bias,
+ show_time(time_of_fix));
+}
+
+/* 0x4A */
+static void
+rpt_ref_alt(
+ TSIPPKT *rpt
+ )
+{
+ float
+ alt, dummy;
+ unsigned char
+ alt_flag;
+
+ if (rpt_0x4A_2 (rpt, &alt, &dummy, &alt_flag))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nReference Alt: %.1f m; %s",
+ alt, alt_flag?"ON":"OFF");
+}
+
+/* 0x4B */
+static void
+rpt_rcvr_id_and_status(
+ TSIPPKT *rpt
+ )
+{
+
+ unsigned char
+ machine_id, status3, status4;
+
+ /* unload rptbuf */
+ if (rpt_0x4B (rpt, &machine_id, &status3, &status4))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nRcvr Machine ID: %d; Status3 = %s, %s (%02Xh)",
+ machine_id,
+ (status3 & 0x02)?"No RTC":"RTC OK",
+ (status3 & 0x08)?"No Alm":"Alm OK",
+ status3);
+}
+
+/* 0x4C */
+static void
+rpt_operating_parameters(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ dyn_code;
+ float
+ el_mask, snr_mask, dop_mask, dop_switch;
+
+ /* unload rptbuf */
+ if (rpt_0x4C (rpt, &dyn_code, &el_mask,
+ &snr_mask, &dop_mask, &dop_switch))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nOperating Parameters:");
+ pbuf += sprintf(pbuf, "\n Dynamics code = %d %s",
+ dyn_code, dyn_text[dyn_code]);
+ pbuf += sprintf(pbuf, "\n Elevation mask = %.2f", el_mask * R2D);
+ pbuf += sprintf(pbuf, "\n SNR mask = %.2f", snr_mask);
+ pbuf += sprintf(pbuf, "\n DOP mask = %.2f", dop_mask);
+ pbuf += sprintf(pbuf, "\n DOP switch = %.2f", dop_switch);
+}
+
+/* 0x4D */
+static void
+rpt_oscillator_offset(
+ TSIPPKT *rpt
+ )
+{
+ float
+ osc_offset;
+
+ /* unload rptbuf */
+ if (rpt_0x4D (rpt, &osc_offset))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nOscillator offset: %.2f Hz = %.3f PPM",
+ osc_offset, osc_offset/1575.42);
+}
+
+/* 0x4E */
+static void
+rpt_GPS_time_set_response(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ response;
+
+ /* unload rptbuf */
+ if (rpt_0x4E (rpt, &response))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ switch (response)
+ {
+ case 'Y':
+ pbuf += sprintf(pbuf, "\nTime set accepted");
+ break;
+
+ case 'N':
+ pbuf += sprintf(pbuf, "\nTime set rejected or not required");
+ break;
+
+ default:
+ parsed = BADDATA_PARSE;
+ }
+}
+
+/* 0x4F */
+static void
+rpt_UTC_offset(
+ TSIPPKT *rpt
+ )
+{
+ double
+ a0;
+ float
+ a1, time_of_data;
+ short
+ dt_ls, wn_t, wn_lsf, dn, dt_lsf;
+
+ /* unload rptbuf */
+ if (rpt_0x4F (rpt, &a0, &a1, &time_of_data,
+ &dt_ls, &wn_t, &wn_lsf, &dn, &dt_lsf)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nUTC Correction Data");
+ pbuf += sprintf(pbuf, "\n A_0 = %g ", a0);
+ pbuf += sprintf(pbuf, "\n A_1 = %g ", a1);
+ pbuf += sprintf(pbuf, "\n delta_t_LS = %d ", dt_ls);
+ pbuf += sprintf(pbuf, "\n t_ot = %.0f ", time_of_data);
+ pbuf += sprintf(pbuf, "\n WN_t = %d ", wn_t );
+ pbuf += sprintf(pbuf, "\n WN_LSF = %d ", wn_lsf );
+ pbuf += sprintf(pbuf, "\n DN = %d ", dn );
+ pbuf += sprintf(pbuf, "\n delta_t_LSF = %d ", dt_lsf );
+}
+
+/**/
+/* 0x54 */
+static void
+rpt_1SV_bias(
+ TSIPPKT *rpt
+ )
+{
+ float
+ clock_bias, freq_offset, time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x54 (rpt, &clock_bias, &freq_offset, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf (pbuf, "\nTime Fix Clock Bias: %6.2f m Freq Bias: %6.2f m/s%s",
+ clock_bias, freq_offset, show_time (time_of_fix));
+}
+
+/* 0x55 */
+static void
+rpt_io_opt(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ pos_code, vel_code, time_code, aux_code;
+
+ /* unload rptbuf */
+ if (rpt_0x55 (rpt,
+ &pos_code, &vel_code, &time_code, &aux_code)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ /* rptbuf unloaded */
+
+ pbuf += sprintf(pbuf, "\nI/O Options: %2X %2X %2X %2X",
+ pos_code, vel_code, time_code, aux_code);
+
+ if (pos_code & 0x01) {
+ pbuf += sprintf(pbuf, "\n ECEF XYZ position output");
+ }
+
+ if (pos_code & 0x02) {
+ pbuf += sprintf(pbuf, "\n LLA position output");
+ }
+
+ pbuf += sprintf(pbuf, (pos_code & 0x04)?
+ "\n MSL altitude output (Geoid height) ":
+ "\n WGS-84 altitude output");
+
+ pbuf += sprintf(pbuf, (pos_code & 0x08)?
+ "\n MSL altitude input":
+ "\n WGS-84 altitude input");
+
+ pbuf += sprintf(pbuf, (pos_code & 0x10)?
+ "\n Double precision":
+ "\n Single precision");
+
+ if (pos_code & 0x20) {
+ pbuf += sprintf(pbuf, "\n All Enabled Superpackets");
+ }
+
+ if (vel_code & 0x01) {
+ pbuf += sprintf(pbuf, "\n ECEF XYZ velocity output");
+ }
+
+ if (vel_code & 0x02) {
+ pbuf += sprintf(pbuf, "\n ENU velocity output");
+ }
+
+ pbuf += sprintf(pbuf, (time_code & 0x01)?
+ "\n Time tags in UTC":
+ "\n Time tags in GPS time");
+
+ if (time_code & 0x02) {
+ pbuf += sprintf(pbuf, "\n Fixes delayed to integer seconds");
+ }
+
+ if (time_code & 0x04) {
+ pbuf += sprintf(pbuf, "\n Fixes sent only on request");
+ }
+
+ if (time_code & 0x08) {
+ pbuf += sprintf(pbuf, "\n Synchronized measurements");
+ }
+
+ if (time_code & 0x10) {
+ pbuf += sprintf(pbuf, "\n Minimize measurement propagation");
+ }
+
+ pbuf += sprintf(pbuf, (time_code & 0x20) ?
+ "\n PPS output at all times" :
+ "\n PPS output during fixes");
+
+ if (aux_code & 0x01) {
+ pbuf += sprintf(pbuf, "\n Raw measurement output");
+ }
+
+ if (aux_code & 0x02) {
+ pbuf += sprintf(pbuf, "\n Code-phase smoothed before output");
+ }
+
+ if (aux_code & 0x04) {
+ pbuf += sprintf(pbuf, "\n Additional fix status");
+ }
+
+ pbuf += sprintf(pbuf, (aux_code & 0x08)?
+ "\n Signal Strength Output as dBHz" :
+ "\n Signal Strength Output as AMU");
+}
+
+/* 0x56 */
+static void
+rpt_ENU_velocity(
+ TSIPPKT *rpt
+ )
+{
+ float
+ vel_ENU[3], freq_offset, time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x56 (rpt, vel_ENU, &freq_offset, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nVel ENU: %11.3f %11.3f %11.3f %12.3f%s",
+ vel_ENU[0], vel_ENU[1], vel_ENU[2], freq_offset,
+ show_time (time_of_fix));
+}
+
+/* 0x57 */
+static void
+rpt_last_fix_info(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ source_code, diag_code;
+ short
+ week_num;
+ float
+ time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x57 (rpt, &source_code, &diag_code, &week_num, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n source code %d; diag code: %2Xh",
+ source_code, diag_code);
+ pbuf += sprintf(pbuf, "\n Time of last fix:%s", show_time(time_of_fix));
+ pbuf += sprintf(pbuf, "\n Week of last fix: %d", week_num);
+}
+
+/* 0x58 */
+static void
+rpt_GPS_system_data(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ iprn,
+ op_code, data_type, sv_prn,
+ data_length, data_packet[250];
+ ALM_INFO
+ *almanac;
+ ALH_PARMS
+ *almh;
+ UTC_INFO
+ *utc;
+ ION_INFO
+ *ionosphere;
+ EPHEM_CLOCK
+ *cdata;
+ EPHEM_ORBIT
+ *edata;
+ NAV_INFO
+ *nav_data;
+ unsigned char
+ curr_t_oa;
+ unsigned short
+ curr_wn_oa;
+ static char
+ *datname[] =
+ {"", "", "Almanac Orbit",
+ "Health Page & Ref Time", "Ionosphere", "UTC ",
+ "Ephemeris"};
+
+ /* unload rptbuf */
+ if (rpt_0x58 (rpt, &op_code, &data_type, &sv_prn,
+ &data_length, data_packet))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nSystem data [%d]: %s SV%02d",
+ data_type, datname[data_type], sv_prn);
+ switch (op_code)
+ {
+ case 1:
+ pbuf += sprintf(pbuf, " Acknowledgment");
+ break;
+ case 2:
+ pbuf += sprintf(pbuf, " length = %d bytes", data_length);
+ switch (data_type) {
+ case 2:
+ /* Almanac */
+ if (sv_prn == 0 || sv_prn > 32) {
+ pbuf += sprintf(pbuf, " Binary PRN invalid");
+ return;
+ }
+ almanac = (ALM_INFO*)data_packet;
+ pbuf += sprintf(pbuf, "\n t_oa_raw = % -12d SV_hlth = % -12d ",
+ almanac->t_oa_raw , almanac->SV_health );
+ pbuf += sprintf(pbuf, "\n e = % -12g t_oa = % -12g ",
+ almanac->e , almanac->t_oa );
+ pbuf += sprintf(pbuf, "\n i_0 = % -12g OMEGADOT = % -12g ",
+ almanac->i_0 , almanac->OMEGADOT );
+ pbuf += sprintf(pbuf, "\n sqrt_A = % -12g OMEGA_0 = % -12g ",
+ almanac->sqrt_A , almanac->OMEGA_0 );
+ pbuf += sprintf(pbuf, "\n omega = % -12g M_0 = % -12g ",
+ almanac->omega , almanac->M_0 );
+ pbuf += sprintf(pbuf, "\n a_f0 = % -12g a_f1 = % -12g ",
+ almanac->a_f0 , almanac->a_f1 );
+ pbuf += sprintf(pbuf, "\n Axis = % -12g n = % -12g ",
+ almanac->Axis , almanac->n );
+ pbuf += sprintf(pbuf, "\n OMEGA_n = % -12g ODOT_n = % -12g ",
+ almanac->OMEGA_n , almanac->ODOT_n );
+ pbuf += sprintf(pbuf, "\n t_zc = % -12g weeknum = % -12d ",
+ almanac->t_zc , almanac->weeknum );
+ pbuf += sprintf(pbuf, "\n wn_oa = % -12d", almanac->wn_oa );
+ break;
+
+ case 3:
+ /* Almanac health page */
+ almh = (ALH_PARMS*)data_packet;
+ pbuf += sprintf(pbuf, "\n t_oa = %d, wn_oa&0xFF = %d ",
+ almh->t_oa, almh->WN_a);
+ pbuf += sprintf(pbuf, "\nAlmanac health page:");
+ for (iprn = 0; iprn < 32; iprn++) {
+ if (!(iprn%5)) *pbuf++ = '\n';
+ pbuf += sprintf(pbuf, " SV%02d %2X",
+ (iprn+1) , almh->SV_health[iprn]);
+ }
+ curr_t_oa = data_packet[34];
+ curr_wn_oa = (unsigned short)((data_packet[35]<<8) + data_packet[36]);
+ pbuf += sprintf(pbuf, "\n current t_oa = %d, wn_oa = %d ",
+ curr_t_oa, curr_wn_oa);
+ break;
+
+ case 4:
+ /* Ionosphere */
+ ionosphere = (ION_INFO*)data_packet;
+ pbuf += sprintf(pbuf, "\n alpha_0 = % -12g alpha_1 = % -12g ",
+ ionosphere->alpha_0, ionosphere->alpha_1);
+ pbuf += sprintf(pbuf, "\n alpha_2 = % -12g alpha_3 = % -12g ",
+ ionosphere->alpha_2, ionosphere->alpha_3);
+ pbuf += sprintf(pbuf, "\n beta_0 = % -12g beta_1 = % -12g ",
+ ionosphere->beta_0, ionosphere->beta_1);
+ pbuf += sprintf(pbuf, "\n beta_2 = % -12g beta_3 = % -12g ",
+ ionosphere->beta_2, ionosphere->beta_3);
+ break;
+
+ case 5:
+ /* UTC */
+ utc = (UTC_INFO*)data_packet;
+ pbuf += sprintf(pbuf, "\n A_0 = %g ", utc->A_0);
+ pbuf += sprintf(pbuf, "\n A_1 = %g ", utc->A_1);
+ pbuf += sprintf(pbuf, "\n delta_t_LS = %d ", utc->delta_t_LS);
+ pbuf += sprintf(pbuf, "\n t_ot = %.0f ", utc->t_ot );
+ pbuf += sprintf(pbuf, "\n WN_t = %d ", utc->WN_t );
+ pbuf += sprintf(pbuf, "\n WN_LSF = %d ", utc->WN_LSF );
+ pbuf += sprintf(pbuf, "\n DN = %d ", utc->DN );
+ pbuf += sprintf(pbuf, "\n delta_t_LSF = %d ", utc->delta_t_LSF );
+ break;
+
+ case 6: /* Ephemeris */
+ if (sv_prn == 0 || sv_prn > 32) {
+ pbuf += sprintf(pbuf, " Binary PRN invalid");
+ return;
+ }
+ nav_data = (NAV_INFO*)data_packet;
+
+ pbuf += sprintf(pbuf, "\n SV_PRN = % -12d . t_ephem = % -12g . ",
+ nav_data->sv_number , nav_data->t_ephem );
+ cdata = &(nav_data->ephclk);
+ pbuf += sprintf(pbuf,
+ "\n weeknum = % -12d . codeL2 = % -12d . L2Pdata = % -12d",
+ cdata->weeknum , cdata->codeL2 , cdata->L2Pdata );
+ pbuf += sprintf(pbuf,
+ "\n SVacc_raw = % -12d .SV_health = % -12d . IODC = % -12d",
+ cdata->SVacc_raw, cdata->SV_health, cdata->IODC );
+ pbuf += sprintf(pbuf,
+ "\n T_GD = % -12g . t_oc = % -12g . a_f2 = % -12g",
+ cdata->T_GD, cdata->t_oc, cdata->a_f2 );
+ pbuf += sprintf(pbuf,
+ "\n a_f1 = % -12g . a_f0 = % -12g . SVacc = % -12g",
+ cdata->a_f1, cdata->a_f0, cdata->SVacc );
+ edata = &(nav_data->ephorb);
+ pbuf += sprintf(pbuf,
+ "\n IODE = % -12d .fit_intvl = % -12d . C_rs = % -12g",
+ edata->IODE, edata->fit_interval, edata->C_rs );
+ pbuf += sprintf(pbuf,
+ "\n delta_n = % -12g . M_0 = % -12g . C_uc = % -12g",
+ edata->delta_n, edata->M_0, edata->C_uc );
+ pbuf += sprintf(pbuf,
+ "\n ecc = % -12g . C_us = % -12g . sqrt_A = % -12g",
+ edata->e, edata->C_us, edata->sqrt_A );
+ pbuf += sprintf(pbuf,
+ "\n t_oe = % -12g . C_ic = % -12g . OMEGA_0 = % -12g",
+ edata->t_oe, edata->C_ic, edata->OMEGA_0 );
+ pbuf += sprintf(pbuf,
+ "\n C_is = % -12g . i_0 = % -12g . C_rc = % -12g",
+ edata->C_is, edata->i_0, edata->C_rc );
+ pbuf += sprintf(pbuf,
+ "\n omega = % -12g . OMEGADOT = % -12g . IDOT = % -12g",
+ edata->omega, edata->OMEGADOT, edata->IDOT );
+ pbuf += sprintf(pbuf,
+ "\n Axis = % -12g . n = % -12g . r1me2 = % -12g",
+ edata->Axis, edata->n, edata->r1me2 );
+ pbuf += sprintf(pbuf,
+ "\n OMEGA_n = % -12g . ODOT_n = % -12g",
+ edata->OMEGA_n, edata->ODOT_n );
+ break;
+ }
+ }
+}
+
+
+/* 0x59: */
+static void
+rpt_SVs_enabled(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ numsvs,
+ code_type,
+ status_code[32];
+ short
+ iprn;
+
+ /* unload rptbuf */
+ if (rpt_0x59 (rpt, &code_type, status_code))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ switch (code_type)
+ {
+ case 3: pbuf += sprintf(pbuf, "\nSVs Disabled:\n"); break;
+ case 6: pbuf += sprintf(pbuf, "\nSVs with Health Ignored:\n"); break;
+ default: return;
+ }
+ numsvs = 0;
+ for (iprn = 0; iprn < 32; iprn++)
+ {
+ if (status_code[iprn])
+ {
+ pbuf += sprintf(pbuf, " %02d", iprn+1);
+ numsvs++;
+ }
+ }
+ if (numsvs == 0) pbuf += sprintf(pbuf, "None");
+}
+
+
+/* 0x5A */
+static void
+rpt_raw_msmt(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ sv_prn;
+ float
+ sample_length, signal_level, code_phase, Doppler;
+ double
+ time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x5A (rpt, &sv_prn, &sample_length, &signal_level,
+ &code_phase, &Doppler, &time_of_fix))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n %02d %5.0f %7.1f %10.2f %10.2f %12.3f %s",
+ sv_prn, sample_length, signal_level, code_phase, Doppler, time_of_fix,
+ show_time ((float)time_of_fix));
+}
+
+/* 0x5B */
+static void
+rpt_SV_ephemeris_status(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ sv_prn, sv_health, sv_iode, fit_interval_flag;
+ float
+ time_of_collection, time_of_eph, sv_accy;
+
+ /* unload rptbuf */
+ if (rpt_0x5B (rpt, &sv_prn, &sv_health, &sv_iode, &fit_interval_flag,
+ &time_of_collection, &time_of_eph, &sv_accy))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n SV%02d %s %2Xh %2Xh ",
+ sv_prn, show_time (time_of_collection), sv_health, sv_iode);
+ /* note: cannot use show_time twice in same call */
+ pbuf += sprintf(pbuf, "%s %1d %4.1f",
+ show_time (time_of_eph), fit_interval_flag, sv_accy);
+}
+
+/* 0x5C */
+static void
+rpt_SV_tracking_status(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ sv_prn, chan, slot, acq_flag, eph_flag,
+ old_msmt_flag, integer_msec_flag, bad_data_flag,
+ data_collect_flag;
+ float
+ signal_level, time_of_last_msmt,
+ elev, azim;
+
+ /* unload rptbuf */
+ if (rpt_0x5C (rpt,
+ &sv_prn, &slot, &chan, &acq_flag, &eph_flag,
+ &signal_level, &time_of_last_msmt, &elev, &azim,
+ &old_msmt_flag, &integer_msec_flag, &bad_data_flag,
+ &data_collect_flag))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf,
+ "\n SV%2d %1d %1d %1d %4.1f %s %5.1f %5.1f",
+ sv_prn, chan,
+ acq_flag, eph_flag, signal_level,
+ show_time(time_of_last_msmt),
+ elev*R2D, azim*R2D);
+}
+
+/**/
+/* 0x6D */
+static void
+rpt_allSV_selection(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ manual_mode, nsvs, sv_prn[8], ndim;
+ short
+ islot;
+ float
+ pdop, hdop, vdop, tdop;
+
+ /* unload rptbuf */
+ if (rpt_0x6D (rpt,
+ &manual_mode, &nsvs, &ndim, sv_prn,
+ &pdop, &hdop, &vdop, &tdop))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ switch (ndim)
+ {
+ case 0:
+ pbuf += sprintf(pbuf, "\nMode: Searching, %d-SV:", nsvs);
+ break;
+ case 1:
+ pbuf += sprintf(pbuf, "\nMode: One-SV Timing:");
+ break;
+ case 3: case 4:
+ pbuf += sprintf(pbuf, "\nMode: %c-%dD, %d-SV:",
+ manual_mode ? 'M' : 'A', ndim - 1, nsvs);
+ break;
+ case 5:
+ pbuf += sprintf(pbuf, "\nMode: Timing, %d-SV:", nsvs);
+ break;
+ default:
+ pbuf += sprintf(pbuf, "\nMode: Unknown = %d:", ndim);
+ break;
+ }
+
+ for (islot = 0; islot < nsvs; islot++)
+ {
+ if (sv_prn[islot]) pbuf += sprintf(pbuf, " %02d", sv_prn[islot]);
+ }
+ if (ndim == 3 || ndim == 4)
+ {
+ pbuf += sprintf(pbuf, "; DOPs: P %.1f H %.1f V %.1f T %.1f",
+ pdop, hdop, vdop, tdop);
+ }
+}
+
+/**/
+/* 0x82 */
+static void
+rpt_DGPS_position_mode(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ diff_mode;
+
+ /* unload rptbuf */
+ if (rpt_0x82 (rpt, &diff_mode)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nFix is%s DGPS-corrected (%s mode) (%d)",
+ (diff_mode&1) ? "" : " not",
+ (diff_mode&2) ? "auto" : "manual",
+ diff_mode);
+}
+
+/* 0x83 */
+static void
+rpt_double_ECEF_position(
+ TSIPPKT *rpt
+ )
+{
+ double
+ ECEF_pos[3], clock_bias;
+ float
+ time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x83 (rpt, ECEF_pos, &clock_bias, &time_of_fix))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nDXYZ:%12.2f %13.2f %13.2f %12.2f%s",
+ ECEF_pos[0], ECEF_pos[1], ECEF_pos[2], clock_bias,
+ show_time(time_of_fix));
+}
+
+/* 0x84 */
+static void
+rpt_double_lla_position(
+ TSIPPKT *rpt
+ )
+{
+ short
+ lat_deg, lon_deg;
+ double
+ lat, lon, lat_min, lon_min,
+ alt, clock_bias;
+ float
+ time_of_fix;
+ unsigned char
+ north_south, east_west;
+
+ /* unload rptbuf */
+ if (rpt_0x84 (rpt,
+ &lat, &lon, &alt, &clock_bias, &time_of_fix))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ lat *= R2D;
+ lon *= R2D;
+ if (lat < 0.0) {
+ north_south = 'S';
+ lat = -lat;
+ } else {
+ north_south = 'N';
+ }
+ lat_deg = (short)lat;
+ lat_min = (lat - lat_deg) * 60.0;
+
+ if (lon < 0.0) {
+ east_west = 'W';
+ lon = -lon;
+ } else {
+ east_west = 'E';
+ }
+ lon_deg = (short)lon;
+ lon_min = (lon - lon_deg) * 60.0;
+ pbuf += sprintf(pbuf, "\nDLLA: %2d:%08.5f %c; %3d:%08.5f %c; %10.2f %12.2f%s",
+ lat_deg, lat_min, north_south,
+ lon_deg, lon_min, east_west,
+ alt, clock_bias,
+ show_time(time_of_fix));
+}
+
+/* 0xBB */
+static void
+rpt_complete_rcvr_config(
+ TSIPPKT *rpt
+ )
+{
+ TSIP_RCVR_CFG TsipxBB ;
+ /* unload rptbuf */
+ if (rpt_Paly0xBB (rpt, &TsipxBB))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n operating mode: %s",
+ NavModeText0xBB[TsipxBB.operating_mode]);
+ pbuf += sprintf(pbuf, "\n dynamics: %s",
+ dyn_text[TsipxBB.dyn_code]);
+ pbuf += sprintf(pbuf, "\n elev angle mask: %g deg",
+ TsipxBB.elev_mask * R2D);
+ pbuf += sprintf(pbuf, "\n SNR mask: %g AMU",
+ TsipxBB.cno_mask);
+ pbuf += sprintf(pbuf, "\n DOP mask: %g",
+ TsipxBB.dop_mask);
+ pbuf += sprintf(pbuf, "\n DOP switch: %g",
+ TsipxBB.dop_switch);
+ return ;
+}
+
+/* 0xBC */
+static void
+rpt_rcvr_serial_port_config(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ port_num, in_baud, out_baud, data_bits, parity, stop_bits, flow_control,
+ protocols_in, protocols_out, reserved;
+ unsigned char known;
+
+ /* unload rptbuf */
+ if (rpt_0xBC (rpt, &port_num, &in_baud, &out_baud, &data_bits, &parity,
+ &stop_bits, &flow_control, &protocols_in, &protocols_out, &reserved)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ /* rptbuf unloaded */
+
+ pbuf += sprintf(pbuf, "\n RECEIVER serial port %s config:",
+ rcvr_port_text[port_num]);
+
+ pbuf += sprintf(pbuf, "\n I/O Baud %s/%s, %d - %s - %d",
+ st_baud_text_app[in_baud],
+ st_baud_text_app[out_baud],
+ data_bits+5,
+ parity_text[parity],
+ stop_bits=1);
+ pbuf += sprintf(pbuf, "\n Input protocols: ");
+ known = FALSE;
+ if (protocols_in&B_TSIP)
+ {
+ pbuf += sprintf(pbuf, "%s ", protocols_in_text[1]);
+ known = TRUE;
+ }
+ if (known == FALSE) pbuf += sprintf(pbuf, "No known");
+
+ pbuf += sprintf(pbuf, "\n Output protocols: ");
+ known = FALSE;
+ if (protocols_out&B_TSIP)
+ {
+ pbuf += sprintf(pbuf, "%s ", protocols_out_text[1]);
+ known = TRUE;
+ }
+ if (protocols_out&B_NMEA)
+ {
+ pbuf += sprintf(pbuf, "%s ", protocols_out_text[2]);
+ known = TRUE;
+ }
+ if (known == FALSE) pbuf += sprintf(pbuf, "No known");
+ reserved = reserved;
+
+}
+
+/* 0x8F */
+/* 8F0B */
+static void
+rpt_8F0B(
+ TSIPPKT *rpt
+ )
+{
+ const char
+ *oprtng_dim[7] = {
+ "horizontal (2-D)",
+ "full position (3-D)",
+ "single satellite (0-D)",
+ "automatic",
+ "N/A",
+ "N/A",
+ "overdetermined clock"};
+ char
+ sv_id[8];
+ unsigned char
+ month,
+ date,
+ dim_mode,
+ north_south,
+ east_west;
+ unsigned short
+ event;
+ short
+ utc_offset,
+ year,
+ local_index;
+ short
+ lat_deg,
+ lon_deg;
+ float
+ bias_unc,
+ dr_unc;
+ double
+ tow,
+ bias,
+ drift,
+ lat,
+ lon,
+ alt,
+ lat_min,
+ lon_min;
+ int
+ numfix,
+ numnotfix;
+
+ if (rpt_0x8F0B(rpt,
+ &event,
+ &tow,
+ &date,
+ &month,
+ &year,
+ &dim_mode,
+ &utc_offset,
+ &bias,
+ &drift,
+ &bias_unc,
+ &dr_unc,
+ &lat,
+ &lon,
+ &alt,
+ sv_id))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ if (event == 0)
+ {
+ pbuf += sprintf(pbuf, "\nNew partial+full meas");
+ }
+ else
+ {
+ pbuf += sprintf(pbuf, "\nEvent count: %5d", event);
+ }
+
+ pbuf += sprintf(pbuf, "\nGPS time : %s %2d/%2d/%2d (DMY)",
+ show_time(tow), date, month, year);
+ pbuf += sprintf(pbuf, "\nMode : %s", oprtng_dim[dim_mode]);
+ pbuf += sprintf(pbuf, "\nUTC offset: %2d", utc_offset);
+ pbuf += sprintf(pbuf, "\nClock Bias: %6.2f m", bias);
+ pbuf += sprintf(pbuf, "\nFreq bias : %6.2f m/s", drift);
+ pbuf += sprintf(pbuf, "\nBias unc : %6.2f m", bias_unc);
+ pbuf += sprintf(pbuf, "\nFreq unc : %6.2f m/s", dr_unc);
+
+ lat *= R2D; /* convert from radians to degrees */
+ lon *= R2D;
+ if (lat < 0.0)
+ {
+ north_south = 'S';
+ lat = -lat;
+ }
+ else
+ {
+ north_south = 'N';
+ }
+
+ lat_deg = (short)lat;
+ lat_min = (lat - lat_deg) * 60.0;
+ if (lon < 0.0)
+ {
+ east_west = 'W';
+ lon = -lon;
+ }
+ else
+ {
+ east_west = 'E';
+ }
+
+ lon_deg = (short)lon;
+ lon_min = (lon - lon_deg) * 60.0;
+ pbuf += sprintf(pbuf, "\nPosition :");
+ pbuf += sprintf(pbuf, " %4d %6.3f %c", lat_deg, lat_min, north_south);
+ pbuf += sprintf(pbuf, " %5d %6.3f %c", lon_deg, lon_min, east_west);
+ pbuf += sprintf(pbuf, " %10.2f", alt);
+
+ numfix = numnotfix = 0;
+ for (local_index=0; local_index<8; local_index++)
+ {
+ if (sv_id[local_index] < 0) numnotfix++;
+ if (sv_id[local_index] > 0) numfix++;
+ }
+ if (numfix > 0)
+ {
+ pbuf += sprintf(pbuf, "\nSVs used in fix : ");
+ for (local_index=0; local_index<8; local_index++)
+ {
+ if (sv_id[local_index] > 0)
+ {
+ pbuf += sprintf(pbuf, "%2d ", sv_id[local_index]);
+ }
+ }
+ }
+ if (numnotfix > 0)
+ {
+ pbuf += sprintf(pbuf, "\nOther SVs tracked: ");
+ for (local_index=0; local_index<8; local_index++)
+ {
+ if (sv_id[local_index] < 0)
+ {
+ pbuf += sprintf(pbuf, "%2d ", sv_id[local_index]);
+ }
+ }
+ }
+}
+
+/* 0x8F14 */
+/* Datum parameters */
+static void
+rpt_8F14(
+ TSIPPKT *rpt
+ )
+{
+ double
+ datum_coeffs[5];
+ short
+ datum_idx;
+
+ /* unload rptbuf */
+ if (rpt_0x8F14 (rpt, &datum_idx, datum_coeffs))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ if (datum_idx == -1)
+ {
+ pbuf += sprintf(pbuf, "\nUser-Entered Datum:");
+ pbuf += sprintf(pbuf, "\n dx = %6.1f", datum_coeffs[0]);
+ pbuf += sprintf(pbuf, "\n dy = %6.1f", datum_coeffs[1]);
+ pbuf += sprintf(pbuf, "\n dz = %6.1f", datum_coeffs[2]);
+ pbuf += sprintf(pbuf, "\n a-axis = %10.3f", datum_coeffs[3]);
+ pbuf += sprintf(pbuf, "\n e-squared = %16.14f", datum_coeffs[4]);
+ }
+ else if (datum_idx == 0)
+ {
+ pbuf += sprintf(pbuf, "\nWGS-84 datum, Index 0 ");
+ }
+ else
+ {
+ pbuf += sprintf(pbuf, "\nStandard Datum, Index %3d ", datum_idx);
+ }
+}
+
+/* 0x8F15 */
+/* Datum parameters */
+static void
+rpt_8F15(
+ TSIPPKT *rpt
+ )
+{
+ double
+ datum_coeffs[5];
+ short
+ datum_idx;
+
+ /* unload rptbuf */
+ if (rpt_0x8F15 (rpt, &datum_idx, datum_coeffs)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ if (datum_idx == -1)
+ {
+ pbuf += sprintf(pbuf, "\nUser-Entered Datum:");
+ pbuf += sprintf(pbuf, "\n dx = %6.1f", datum_coeffs[0]);
+ pbuf += sprintf(pbuf, "\n dy = %6.1f", datum_coeffs[1]);
+ pbuf += sprintf(pbuf, "\n dz = %6.1f", datum_coeffs[2]);
+ pbuf += sprintf(pbuf, "\n a-axis = %10.3f", datum_coeffs[3]);
+ pbuf += sprintf(pbuf, "\n e-squared = %16.14f", datum_coeffs[4]);
+ }
+ else if (datum_idx == 0)
+ {
+ pbuf += sprintf(pbuf, "\nWGS-84 datum, Index 0 ");
+ }
+ else
+ {
+ pbuf += sprintf(pbuf, "\nStandard Datum, Index %3d ", datum_idx);
+ }
+}
+
+/* 0x8F20 */
+#define INFO_DGPS 0x02
+#define INFO_2D 0x04
+#define INFO_ALTSET 0x08
+#define INFO_FILTERED 0x10
+static void
+rpt_8F20(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ info, nsvs, sv_prn[32];
+ short
+ week_num, datum_index, sv_IODC[32];
+ double
+ lat, lon, alt, time_of_fix;
+ double
+ londeg, latdeg, vel[3];
+ short
+ isv;
+ char
+ datum_string[20];
+
+ /* unload rptbuf */
+ if (rpt_0x8F20 (rpt,
+ &info, &lat, &lon, &alt, vel,
+ &time_of_fix,
+ &week_num, &nsvs, sv_prn, sv_IODC, &datum_index))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ pbuf += sprintf(pbuf,
+ "\nFix at: %04d:%3s:%02d:%02d:%06.3f GPS (=UTC+%2ds) FixType: %s%s%s",
+ week_num,
+ dayname[(short)(time_of_fix/86400.0)],
+ (short)fmod(time_of_fix/3600., 24.),
+ (short)fmod(time_of_fix/60., 60.),
+ fmod(time_of_fix, 60.),
+ (char)rpt->buf[29], /* UTC offset */
+ (info & INFO_DGPS)?"Diff":"",
+ (info & INFO_2D)?"2D":"3D",
+ (info & INFO_FILTERED)?"-Filtrd":"");
+
+ if (datum_index > 0)
+ {
+ sprintf(datum_string, "Datum%3d", datum_index);
+ }
+ else if (datum_index)
+ {
+ sprintf(datum_string, "Unknown ");
+ }
+ else
+ {
+ sprintf(datum_string, "WGS-84");
+ }
+
+ /* convert from radians to degrees */
+ latdeg = R2D * fabs(lat);
+ londeg = R2D * fabs(lon);
+ pbuf += sprintf(pbuf,
+ "\n Pos: %4d:%09.6f %c %5d:%09.6f %c %10.2f m HAE (%s)",
+ (short)latdeg, fmod (latdeg, 1.)*60.0,
+ (lat<0.0)?'S':'N',
+ (short)londeg, fmod (londeg, 1.)*60.0,
+ (lon<0.0)?'W':'E',
+ alt,
+ datum_string);
+ pbuf += sprintf(pbuf,
+ "\n Vel: %9.3f E %9.3f N %9.3f U (m/sec)",
+ vel[0], vel[1], vel[2]);
+
+ pbuf += sprintf(pbuf,
+ "\n SVs: ");
+ for (isv = 0; isv < nsvs; isv++) {
+ pbuf += sprintf(pbuf, " %02d", sv_prn[isv]);
+ }
+ pbuf += sprintf(pbuf, " (IODEs:");
+ for (isv = 0; isv < nsvs; isv++) {
+ pbuf += sprintf(pbuf, " %02X", sv_IODC[isv]&0xFF);
+ }
+ pbuf += sprintf(pbuf, ")");
+}
+
+/* 0x8F41 */
+static void
+rpt_8F41(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ bSearchRange,
+ bBoardOptions,
+ bBuildYear,
+ bBuildMonth,
+ bBuildDay,
+ bBuildHour;
+ float
+ fOscOffset;
+ unsigned short
+ iTestCodeId;
+ unsigned long
+ iiSerialNumber;
+
+ if (!rpt_0x8F41(rpt,
+ &bSearchRange,
+ &bBoardOptions,
+ &iiSerialNumber,
+ &bBuildYear,
+ &bBuildMonth,
+ &bBuildDay,
+ &bBuildHour,
+ &fOscOffset,
+ &iTestCodeId))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n search range: %d",
+ bSearchRange);
+ pbuf += sprintf(pbuf, "\n board options: %d",
+ bBoardOptions);
+ pbuf += sprintf(pbuf, "\n board serial #: %ld",
+ iiSerialNumber);
+ pbuf += sprintf(pbuf, "\n build date/hour: %02d/%02d/%02d %02d:00",
+ bBuildDay, bBuildMonth, bBuildYear, bBuildHour);
+ pbuf += sprintf(pbuf, "\n osc offset: %.3f PPM (%.0f Hz)",
+ fOscOffset/1575.42, fOscOffset);
+ pbuf += sprintf(pbuf, "\n test code: %d",
+ iTestCodeId);
+}
+
+/* 0x8F42 */
+static void
+rpt_8F42(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ bProdOptionsPre,
+ bProdNumberExt;
+ unsigned short
+ iCaseSerialNumberPre,
+ iPremiumOptions,
+ iMachineID,
+ iKey;
+ unsigned long
+ iiCaseSerialNumber,
+ iiProdNumber;
+
+ if (!rpt_0x8F42(rpt,
+ &bProdOptionsPre,
+ &bProdNumberExt,
+ &iCaseSerialNumberPre,
+ &iiCaseSerialNumber,
+ &iiProdNumber,
+ &iPremiumOptions,
+ &iMachineID,
+ &iKey))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nProduct ID 8F42");
+ pbuf += sprintf(pbuf, "\n extension: %d", bProdNumberExt);
+ pbuf += sprintf(pbuf, "\n case serial # prefix: %d", iCaseSerialNumberPre);
+ pbuf += sprintf(pbuf, "\n case serial #: %ld", iiCaseSerialNumber);
+ pbuf += sprintf(pbuf, "\n prod. #: %ld", iiProdNumber);
+ pbuf += sprintf(pbuf, "\n premium options: %Xh", iPremiumOptions);
+ pbuf += sprintf(pbuf, "\n machine ID: %d", iMachineID);
+ pbuf += sprintf(pbuf, "\n key: %Xh", iKey);
+}
+
+/* 0x8F45 */
+static void
+rpt_8F45(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char bSegMask;
+
+ if (!rpt_0x8F45(rpt,
+ &bSegMask))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ pbuf += sprintf(pbuf, "\nCleared Segment Mask: %Xh", bSegMask);
+}
+
+/* Stinger PPS def */
+static void
+rpt_8F4A(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ pps_enabled,
+ pps_timebase,
+ pps_polarity;
+ float
+ bias_unc_threshold;
+ double
+ pps_offset;
+
+ if (rpt_0x8F4A_16 (rpt,
+ &pps_enabled,
+ &pps_timebase,
+ &pps_polarity,
+ &pps_offset,
+ &bias_unc_threshold))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nPPS is %s", pps_enabled?"enabled":"disabled");
+ pbuf += sprintf(pbuf, "\n timebase: %s", PPSTimeBaseText[pps_timebase]);
+ pbuf += sprintf(pbuf, "\n polarity: %s", PPSPolarityText[pps_polarity]);
+ pbuf += sprintf(pbuf, "\n offset: %.1f ns, ", pps_offset*1.e9);
+ pbuf += sprintf(pbuf, "\n biasunc: %.1f ns", bias_unc_threshold/GPS_C*1.e9);
+}
+
+/* fast-SA decorrolation time for self-survey */
+static void
+rpt_8F4B(
+ TSIPPKT *rpt
+ )
+{
+ unsigned long
+ decorr_max;
+
+ if (rpt_0x8F4B(rpt, &decorr_max))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf,
+ "\nMax # of position fixes for self-survey : %ld",
+ decorr_max);
+}
+
+static void
+rpt_8F4D(
+ TSIPPKT *rpt
+ )
+{
+ static char
+ *linestart;
+ unsigned long
+ OutputMask;
+ static unsigned long
+ MaskBit[] = {
+ 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010,
+ 0x00000020,
+ 0x00000100L, 0x00000800L, 0x00001000L,
+ 0x40000000L, 0x80000000L};
+ int
+ ichoice,
+ numchoices;
+
+ if (rpt_0x8F4D(rpt, &OutputMask))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nAuto-Report Mask: %02X %02X %02X %02X",
+ (unsigned char)(OutputMask>>24),
+ (unsigned char)(OutputMask>>16),
+ (unsigned char)(OutputMask>>8),
+ (unsigned char)OutputMask);
+
+ numchoices = sizeof(MaskText)/sizeof(char*);
+ pbuf += sprintf(pbuf, "\nAuto-Reports scheduled for Output:");
+ linestart = pbuf;
+ for (ichoice = 0; ichoice < numchoices; ichoice++)
+ {
+ if (OutputMask&MaskBit[ichoice])
+ {
+ pbuf += sprintf(pbuf, "%s %s",
+ (pbuf==linestart)?"\n ":",",
+ MaskText[ichoice]);
+ if (pbuf-linestart > 60) linestart = pbuf;
+ }
+ }
+
+ pbuf += sprintf(pbuf, "\nAuto-Reports NOT scheduled for Output:");
+ linestart = pbuf;
+ for (ichoice = 0; ichoice < numchoices; ichoice++)
+ {
+ if (OutputMask&MaskBit[ichoice]) continue;
+ pbuf += sprintf(pbuf, "%s %s",
+ (pbuf==linestart)?"\n ":",",
+ MaskText[ichoice]);
+ if (pbuf-linestart > 60) linestart = pbuf;
+ }
+}
+
+static void
+rpt_8FA5(
+ TSIPPKT *rpt
+ )
+{
+ unsigned char
+ spktmask[4];
+
+ if (rpt_0x8FA5(rpt, spktmask))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nSuperpacket auto-output mask: %02X %02X %02X %02X",
+ spktmask[0], spktmask[1], spktmask[2], spktmask[3]);
+
+ if (spktmask[0]&0x01) pbuf+= sprintf (pbuf, "\n PPS 8F-0B");
+ if (spktmask[0]&0x02) pbuf+= sprintf (pbuf, "\n Event 8F-0B");
+ if (spktmask[0]&0x10) pbuf+= sprintf (pbuf, "\n PPS 8F-AD");
+ if (spktmask[0]&0x20) pbuf+= sprintf (pbuf, "\n Event 8F-AD");
+ if (spktmask[2]&0x01) pbuf+= sprintf (pbuf, "\n ppos Fix 8F-20");
+}
+
+static void
+rpt_8FAD(
+ TSIPPKT *rpt
+ )
+{
+ unsigned short
+ Count,
+ Year;
+ double
+ FracSec;
+ unsigned char
+ Hour,
+ Minute,
+ Second,
+ Day,
+ Month,
+ Status,
+ Flags;
+ static char* Status8FADText[] = {
+ "CODE_DOING_FIXES",
+ "CODE_GOOD_1_SV",
+ "CODE_APPX_1SV",
+ "CODE_NEED_TIME",
+ "CODE_NEED_INITIALIZATION",
+ "CODE_PDOP_HIGH",
+ "CODE_BAD_1SV",
+ "CODE_0SVS",
+ "CODE_1SV",
+ "CODE_2SVS",
+ "CODE_3SVS",
+ "CODE_NO_INTEGRITY",
+ "CODE_DCORR_GEN",
+ "CODE_OVERDET_CLK",
+ "Invalid Status"},
+ *LeapStatusText[] = {
+ " UTC Avail", " ", " ", " ",
+ " Scheduled", " Pending", " Warning", " In Progress"};
+ int i;
+
+ if (rpt_0x8FAD (rpt,
+ &Count,
+ &FracSec,
+ &Hour,
+ &Minute,
+ &Second,
+ &Day,
+ &Month,
+ &Year,
+ &Status,
+ &Flags))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n8FAD Count: %d Status: %s",
+ Count, Status8FADText[Status]);
+
+ pbuf += sprintf(pbuf, "\n Leap Flags:");
+ if (Flags)
+ {
+ for (i=0; i<8; i++)
+ {
+ if (Flags&(1<<i)) pbuf += sprintf(pbuf, LeapStatusText[i]);
+ }
+ }
+ else
+ {
+ pbuf += sprintf(pbuf, " UTC info not available");
+ }
+
+ pbuf += sprintf(pbuf, "\n %02d/%02d/%04d (DMY) %02d:%02d:%02d.%09ld UTC",
+ Day, Month, Year, Hour, Minute, Second, (long)(FracSec*1.e9));
+}
+
+
+int
+print_msg_table_header(
+ int rptcode,
+ char *HdrStr,
+ int force
+ )
+{
+ /* force header is to help auto-output function */
+ /* last_rptcode is to determine whether to print a header */
+ /* for the first occurrence of a series of reports */
+ static int
+ last_rptcode = 0;
+ int
+ numchars;
+
+ numchars = 0;
+ if (force || rptcode!=last_rptcode)
+ {
+ /* supply a header in console output */
+ switch (rptcode)
+ {
+ case 0x5A:
+ numchars = sprintf(HdrStr, "\nRaw Measurement Data");
+ numchars += sprintf(HdrStr+numchars,
+ "\n SV Sample SNR Code Phase Doppler Seconds Time of Meas");
+ break;
+
+ case 0x5B:
+ numchars = sprintf(HdrStr, "\nEphemeris Status");
+ numchars += sprintf(HdrStr+numchars,
+ "\n SV Time collected Health IODE t oe Fit URA");
+ break;
+
+ case 0x5C:
+ numchars = sprintf(HdrStr, "\nTracking Info");
+ numchars += sprintf(HdrStr+numchars,
+ "\n SV C Acq Eph SNR Time of Meas Elev Azim ");
+ break;
+
+ }
+ }
+ last_rptcode = rptcode;
+ return (short)numchars;
+}
+
+static void
+unknown_rpt(
+ TSIPPKT *rpt
+ )
+{
+ int i;
+
+ /* app-specific rpt packets */
+ if (parsed == BADLEN_PARSE)
+ {
+ pbuf += sprintf(pbuf, "\nTSIP report packet ID %2Xh, length %d: Bad length",
+ rpt->code, rpt->len);
+ }
+ if (parsed == BADID_PARSE)
+ {
+ pbuf += sprintf(pbuf,
+ "\nTSIP report packet ID %2Xh, length %d: translation not supported",
+ rpt->code, rpt->len);
+ }
+
+ if (parsed == BADDATA_PARSE)
+ {
+ pbuf += sprintf(pbuf,
+ "\nTSIP report packet ID %2Xh, length %d: data content incorrect",
+ rpt->code, rpt->len);
+ }
+
+ for (i = 0; i < rpt->len; i++) {
+ if ((i % 20) == 0) *pbuf++ = '\n';
+ pbuf += sprintf(pbuf, " %02X", rpt->buf[i]);
+ }
+}
+/**/
+
+/*
+** main subroutine, called from ProcessInputBytesWhileWaitingForKBHit()
+*/
+void
+TranslateTSIPReportToText(
+ TSIPPKT *rpt,
+ char *TextOutputBuffer
+ )
+{
+
+ /* pbuf is the pointer to the current location of the text output */
+ pbuf = TextOutputBuffer;
+
+ /* keep track of whether the message has been successfully parsed */
+ parsed = GOOD_PARSE;
+
+ /* print a header if this is the first of a series of messages */
+ pbuf += print_msg_table_header (rpt->code, pbuf, FALSE);
+
+ /* process incoming TSIP report according to code */
+ switch (rpt->code)
+ {
+ case 0x3D: rpt_chan_A_config (rpt); break;
+ case 0x40: rpt_almanac_data_page (rpt); break;
+ case 0x41: rpt_GPS_time (rpt); break;
+ case 0x42: rpt_single_ECEF_position (rpt); break;
+ case 0x43: rpt_single_ECEF_velocity (rpt); break;
+ case 0x45: rpt_SW_version (rpt); break;
+ case 0x46: rpt_rcvr_health (rpt); break;
+ case 0x47: rpt_SNR_all_SVs (rpt); break;
+ case 0x48: rpt_GPS_system_message (rpt); break;
+ case 0x49: rpt_almanac_health_page (rpt); break;
+ case 0x4A: switch (rpt->len) {
+ /*
+ ** special case (=slip-up) in the TSIP protocol;
+ ** parsing method depends on length
+ */
+ case 20: rpt_single_lla_position (rpt); break;
+ case 9: rpt_ref_alt (rpt); break;
+ } break;
+ case 0x4B: rpt_rcvr_id_and_status (rpt);break;
+ case 0x4C: rpt_operating_parameters (rpt); break;
+ case 0x4D: rpt_oscillator_offset (rpt); break;
+ case 0x4E: rpt_GPS_time_set_response (rpt); break;
+ case 0x4F: rpt_UTC_offset (rpt); break;
+ case 0x54: rpt_1SV_bias (rpt); break;
+ case 0x55: rpt_io_opt (rpt); break;
+ case 0x56: rpt_ENU_velocity (rpt); break;
+ case 0x57: rpt_last_fix_info (rpt); break;
+ case 0x58: rpt_GPS_system_data (rpt); break;
+ case 0x59: rpt_SVs_enabled (rpt); break;
+ case 0x5A: rpt_raw_msmt (rpt); break;
+ case 0x5B: rpt_SV_ephemeris_status (rpt); break;
+ case 0x5C: rpt_SV_tracking_status (rpt); break;
+ case 0x6D: rpt_allSV_selection (rpt); break;
+ case 0x82: rpt_DGPS_position_mode (rpt); break;
+ case 0x83: rpt_double_ECEF_position (rpt); break;
+ case 0x84: rpt_double_lla_position (rpt); break;
+ case 0xBB: rpt_complete_rcvr_config (rpt); break;
+ case 0xBC: rpt_rcvr_serial_port_config (rpt); break;
+
+ case 0x8F: switch (rpt->buf[0])
+ {
+ /* superpackets; parsed according to subcodes */
+ case 0x0B: rpt_8F0B(rpt); break;
+ case 0x14: rpt_8F14(rpt); break;
+ case 0x15: rpt_8F15(rpt); break;
+ case 0x20: rpt_8F20(rpt); break;
+ case 0x41: rpt_8F41(rpt); break;
+ case 0x42: rpt_8F42(rpt); break;
+ case 0x45: rpt_8F45(rpt); break;
+ case 0x4A: rpt_8F4A(rpt); break;
+ case 0x4B: rpt_8F4B(rpt); break;
+ case 0x4D: rpt_8F4D(rpt); break;
+ case 0xA5: rpt_8FA5(rpt); break;
+ case 0xAD: rpt_8FAD(rpt); break;
+ default: parsed = BADID_PARSE; break;
+ }
+ break;
+
+ default: parsed = BADID_PARSE; break;
+ }
+
+ if (parsed != GOOD_PARSE)
+ {
+ /*
+ **The message has TSIP structure (DLEs, etc.)
+ ** but could not be parsed by above routines
+ */
+ unknown_rpt (rpt);
+ }
+
+ /* close TextOutputBuffer */
+ pbuf = '\0';
+}
+
+#endif /* TRIMBLE_OUTPUT_FUNC */
+
+#else /* defined(REFCLOCK) && defined(CLOCK_RIPENCC) */
+int refclock_ripencc_bs;
+#endif /* defined(REFCLOCK) && defined(CLOCK_RIPENCC) */
+
diff --git a/ntpd/refclock_shm.c b/ntpd/refclock_shm.c
new file mode 100644
index 0000000..6540e6f
--- /dev/null
+++ b/ntpd/refclock_shm.c
@@ -0,0 +1,571 @@
+/*
+ * refclock_shm - clock driver for utc via shared memory
+ * - under construction -
+ * To add new modes: Extend or union the shmTime-struct. Do not
+ * extend/shrink size, because otherwise existing implementations
+ * will specify wrong size of shared memory-segment
+ * PB 18.3.97
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntp_types.h"
+
+#if defined(REFCLOCK) && defined(CLOCK_SHM)
+
+#include "ntpd.h"
+#undef fileno
+#include "ntp_io.h"
+#undef fileno
+#include "ntp_refclock.h"
+#undef fileno
+#include "timespecops.h"
+#undef fileno
+#include "ntp_stdlib.h"
+
+#undef fileno
+#include <ctype.h>
+#undef fileno
+
+#ifndef SYS_WINNT
+# include <sys/ipc.h>
+# include <sys/shm.h>
+# include <assert.h>
+# include <unistd.h>
+# include <stdio.h>
+#endif
+
+/*
+ * This driver supports a reference clock attached thru shared memory
+ */
+
+/*
+ * SHM interface definitions
+ */
+#define PRECISION (-1) /* precision assumed (0.5 s) */
+#define REFID "SHM" /* reference ID */
+#define DESCRIPTION "SHM/Shared memory interface"
+
+#define NSAMPLES 3 /* stages of median filter */
+
+/*
+ * Function prototypes
+ */
+static int shm_start (int unit, struct peer *peer);
+static void shm_shutdown (int unit, struct peer *peer);
+static void shm_poll (int unit, struct peer *peer);
+static void shm_timer (int unit, struct peer *peer);
+static void shm_peek (int unit, struct peer *peer);
+static void shm_clockstats (int unit, struct peer *peer);
+static void shm_control (int unit, const struct refclockstat * in_st,
+ struct refclockstat * out_st, struct peer *peer);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_shm = {
+ shm_start, /* start up driver */
+ shm_shutdown, /* shut down driver */
+ shm_poll, /* transmit poll message */
+ shm_control, /* control settings */
+ noentry, /* not used: init */
+ noentry, /* not used: buginfo */
+ shm_timer, /* once per second */
+};
+
+struct shmTime {
+ int mode; /* 0 - if valid is set:
+ * use values,
+ * clear valid
+ * 1 - if valid is set:
+ * if count before and after read of values is equal,
+ * use values
+ * clear valid
+ */
+ volatile int count;
+ time_t clockTimeStampSec;
+ int clockTimeStampUSec;
+ time_t receiveTimeStampSec;
+ int receiveTimeStampUSec;
+ int leap;
+ int precision;
+ int nsamples;
+ volatile int valid;
+ unsigned clockTimeStampNSec; /* Unsigned ns timestamps */
+ unsigned receiveTimeStampNSec; /* Unsigned ns timestamps */
+ int dummy[8];
+};
+
+struct shmunit {
+ struct shmTime *shm; /* pointer to shared memory segment */
+
+ /* debugging/monitoring counters - reset when printed */
+ int ticks; /* number of attempts to read data*/
+ int good; /* number of valid samples */
+ int notready; /* number of peeks without data ready */
+ int bad; /* number of invalid samples */
+ int clash; /* number of access clashes while reading */
+
+ time_t max_delta; /* difference limit */
+ time_t max_delay; /* age/stale limit */
+};
+
+
+struct shmTime *getShmTime(int);
+
+struct shmTime *getShmTime (int unit) {
+#ifndef SYS_WINNT
+ int shmid=0;
+
+ /* 0x4e545030 is NTP0.
+ * Big units will give non-ascii but that's OK
+ * as long as everybody does it the same way.
+ */
+ shmid=shmget (0x4e545030 + unit, sizeof (struct shmTime),
+ IPC_CREAT | ((unit < 2) ? 0600 : 0666));
+ if (shmid == -1) { /* error */
+ msyslog(LOG_ERR, "SHM shmget (unit %d): %m", unit);
+ return 0;
+ }
+ else { /* no error */
+ struct shmTime *p = (struct shmTime *)shmat (shmid, 0, 0);
+ if (p == (struct shmTime *)-1) { /* error */
+ msyslog(LOG_ERR, "SHM shmat (unit %d): %m", unit);
+ return 0;
+ }
+ return p;
+ }
+#else
+ char buf[10];
+ LPSECURITY_ATTRIBUTES psec = 0;
+ HANDLE shmid = 0;
+ SECURITY_DESCRIPTOR sd;
+ SECURITY_ATTRIBUTES sa;
+
+ snprintf(buf, sizeof(buf), "NTP%d", unit);
+ if (unit >= 2) { /* world access */
+ if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
+ msyslog(LOG_ERR,"SHM InitializeSecurityDescriptor (unit %d): %m", unit);
+ return 0;
+ }
+ if (!SetSecurityDescriptorDacl(&sd, 1, 0, 0)) {
+ msyslog(LOG_ERR, "SHM SetSecurityDescriptorDacl (unit %d): %m", unit);
+ return 0;
+ }
+ sa.nLength=sizeof (SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = &sd;
+ sa.bInheritHandle = 0;
+ psec = &sa;
+ }
+ shmid = CreateFileMapping ((HANDLE)0xffffffff, psec, PAGE_READWRITE,
+ 0, sizeof (struct shmTime), buf);
+ if (!shmid) { /*error*/
+ char buf[1000];
+
+ FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, GetLastError (), 0, buf, sizeof (buf), 0);
+ msyslog(LOG_ERR, "SHM CreateFileMapping (unit %d): %s", unit, buf);
+ return 0;
+ } else {
+ struct shmTime *p = (struct shmTime *) MapViewOfFile (shmid,
+ FILE_MAP_WRITE, 0, 0, sizeof (struct shmTime));
+ if (p == 0) { /*error*/
+ char buf[1000];
+
+ FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, GetLastError (), 0, buf, sizeof (buf), 0);
+ msyslog(LOG_ERR,"SHM MapViewOfFile (unit %d): %s", unit, buf)
+ return 0;
+ }
+ return p;
+ }
+#endif
+}
+/*
+ * shm_start - attach to shared memory
+ */
+static int
+shm_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct shmunit *up;
+
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = -1;
+
+ up = emalloc_zero(sizeof(*up));
+
+ up->shm = getShmTime(unit);
+
+ /*
+ * Initialize miscellaneous peer variables
+ */
+ memcpy((char *)&pp->refid, REFID, 4);
+ if (up->shm != 0) {
+ pp->unitptr = up;
+ up->shm->precision = PRECISION;
+ peer->precision = up->shm->precision;
+ up->shm->valid = 0;
+ up->shm->nsamples = NSAMPLES;
+ pp->clockdesc = DESCRIPTION;
+ /* items to be changed later in 'shm_control()': */
+ up->max_delay = 5;
+ up->max_delta = 4*3600;
+ return 1;
+ } else {
+ free(up);
+ pp->unitptr = NULL;
+ return 0;
+ }
+}
+
+
+/*
+ * shm_control - configure flag1/time2 params
+ *
+ * These are not yet available during 'shm_start', so we have to do any
+ * pre-computations we want to avoid during regular poll/timer callbacks
+ * in this callback.
+ */
+static void
+shm_control(
+ int unit,
+ const struct refclockstat * in_st,
+ struct refclockstat * out_st,
+ struct peer * peer
+ )
+{
+ struct refclockproc *pp;
+ struct shmunit *up;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ if (NULL == up)
+ return;
+ if (pp->sloppyclockflag & CLK_FLAG1)
+ up->max_delta = 0;
+ else if (pp->fudgetime2 < 1. || pp->fudgetime2 > 86400.)
+ up->max_delta = 4*3600;
+ else
+ up->max_delta = (time_t)floor(pp->fudgetime2 + 0.5);
+}
+
+
+/*
+ * shm_shutdown - shut down the clock
+ */
+static void
+shm_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct shmunit *up;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ if (NULL == up)
+ return;
+#ifndef SYS_WINNT
+ /* HMS: shmdt() wants char* or const void * */
+ (void) shmdt ((char *)up->shm);
+#else
+ UnmapViewOfFile (up->shm);
+#endif
+ free(up);
+}
+
+
+/*
+ * shm_timer - called every second
+ */
+static void
+shm_timer(int unit, struct peer *peer)
+{
+ shm_peek(unit, peer);
+}
+
+
+/*
+ * shm_poll - called by the transmit procedure
+ */
+static void
+shm_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct shmunit *up;
+ int major_error;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ pp->polls++;
+
+ /* get dominant reason if we have no samples at all */
+ major_error = max(up->notready, up->bad);
+ major_error = max(major_error, up->clash);
+
+ /*
+ * Process median filter samples. If none received, see what
+ * happened, tell the core and keep going.
+ */
+ if (pp->coderecv != pp->codeproc) {
+ /* have some samples, everything OK */
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ } else if (NULL == up->shm) { /* is this possible at all? */
+ /* we're out of business without SHM access */
+ refclock_report(peer, CEVNT_FAULT);
+ } else if (major_error == up->clash) {
+ /* too many collisions is like a bad signal */
+ refclock_report(peer, CEVNT_PROP);
+ } else if (major_error == up->bad) {
+ /* too much stale/bad/garbled data */
+ refclock_report(peer, CEVNT_BADREPLY);
+ } else {
+ /* in any other case assume it's just a timeout */
+ refclock_report(peer, CEVNT_TIMEOUT);
+ }
+ /* shm_clockstats() clears the tallies, so it must be last... */
+ shm_clockstats(unit, peer);
+}
+
+/*
+ * shm_peek - try to grab a sample
+ */
+static void
+shm_peek(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct shmunit *up;
+
+ /* access order is important for lock-free SHM access; we
+ ** enforce order by treating the whole structure volatile.
+ **
+ ** IMPORTANT NOTE: This does not protect from reordering on CPU
+ ** level, and it does nothing for cache consistency and
+ ** visibility of changes by other cores. We need atomic and/or
+ ** fence instructions for that.
+ */
+ volatile struct shmTime *shm;
+
+ struct timespec tvr;
+ struct timespec tvt;
+ l_fp tsrcv;
+ l_fp tsref;
+ unsigned int c;
+ unsigned int cns_new, rns_new;
+ int cnt;
+
+ /* for formatting 'a_lastcode': */
+ struct calendar cd;
+ time_t tt, now;
+ vint64 ts;
+
+ /*
+ * This is the main routine. It snatches the time from the shm
+ * board and tacks on a local timestamp.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+ up->ticks++;
+ if (up->shm == 0) {
+ /* try to map again - this may succeed if meanwhile some-
+ body has ipcrm'ed the old (unaccessible) shared mem segment */
+ up->shm = getShmTime(unit);
+ }
+ shm = up->shm;
+ if (shm == 0) {
+ DPRINTF(1, ("%s: no SHM segment\n",
+ refnumtoa(&peer->srcadr)));
+ return;
+ }
+ if ( ! shm->valid) {
+ DPRINTF(1, ("%s: SHM not ready\n",
+ refnumtoa(&peer->srcadr)));
+ up->notready++;
+ return;
+ }
+
+ switch (shm->mode) {
+ case 0:
+ tvr.tv_sec = shm->receiveTimeStampSec;
+ tvr.tv_nsec = shm->receiveTimeStampUSec * 1000;
+ rns_new = shm->receiveTimeStampNSec;
+ tvt.tv_sec = shm->clockTimeStampSec;
+ tvt.tv_nsec = shm->clockTimeStampUSec * 1000;
+ cns_new = shm->clockTimeStampNSec;
+
+ /* Since the following comparisons are between unsigned
+ ** variables they are always well defined, and any
+ ** (signed) underflow will turn into very large unsigned
+ ** values, well above the 1000 cutoff.
+ **
+ ** Note: The usecs *must* be a *truncated*
+ ** representation of the nsecs. This code will fail for
+ ** *rounded* usecs, and the logic to deal with
+ ** wrap-arounds in the presence of rounded values is
+ ** much more convoluted.
+ */
+ if ( ((cns_new - (unsigned)tvt.tv_nsec) < 1000)
+ && ((rns_new - (unsigned)tvr.tv_nsec) < 1000)) {
+ tvt.tv_nsec = cns_new;
+ tvr.tv_nsec = rns_new;
+ }
+ /* At this point tvr and tvt contains valid ns-level
+ ** timestamps, possibly generated by extending the old
+ ** us-level timestamps
+ */
+ DPRINTF(2, ("%s: SHM type 0 sample\n",
+ refnumtoa(&peer->srcadr)));
+ break;
+
+ case 1:
+ cnt = shm->count;
+
+ tvr.tv_sec = shm->receiveTimeStampSec;
+ tvr.tv_nsec = shm->receiveTimeStampUSec * 1000;
+ rns_new = shm->receiveTimeStampNSec;
+ tvt.tv_sec = shm->clockTimeStampSec;
+ tvt.tv_nsec = shm->clockTimeStampUSec * 1000;
+ cns_new = shm->clockTimeStampNSec;
+ if (cnt != shm->count) {
+ DPRINTF(1, ("%s: type 1 access clash\n",
+ refnumtoa(&peer->srcadr)));
+ msyslog (LOG_NOTICE, "SHM: access clash in shared memory");
+ up->clash++;
+ return;
+ }
+
+ /* See the case above for an explanation of the
+ ** following test.
+ */
+ if ( ((cns_new - (unsigned)tvt.tv_nsec) < 1000)
+ && ((rns_new - (unsigned)tvr.tv_nsec) < 1000)) {
+ tvt.tv_nsec = cns_new;
+ tvr.tv_nsec = rns_new;
+ }
+ /* At this point tvr and tvt contains valid ns-level
+ ** timestamps, possibly generated by extending the old
+ ** us-level timestamps
+ */
+ DPRINTF(2, ("%s: SHM type 1 sample\n",
+ refnumtoa(&peer->srcadr)));
+ break;
+
+ default:
+ DPRINTF(1, ("%s: SHM type blooper, mode=%d\n",
+ refnumtoa(&peer->srcadr), shm->mode));
+ up->bad++;
+ msyslog (LOG_ERR, "SHM: bad mode found in shared memory: %d",
+ shm->mode);
+ return;
+ }
+ shm->valid = 0;
+
+ /* format the last time code in human-readable form into
+ * 'pp->a_lastcode'. Someone claimed: "NetBSD has incompatible
+ * tv_sec". I can't find a base for this claim, but we can work
+ * around that potential problem. BTW, simply casting a pointer
+ * is a receipe for disaster on some architectures.
+ */
+ tt = (time_t)tvt.tv_sec;
+ ts = time_to_vint64(&tt);
+ ntpcal_time_to_date(&cd, &ts);
+
+ /* add ntpq -c cv timecode in ISO 8601 format */
+ c = snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
+ "%04u-%02u-%02uT%02u:%02u:%02u.%09ldZ",
+ cd.year, cd.month, cd.monthday,
+ cd.hour, cd.minute, cd.second,
+ (long)tvt.tv_nsec);
+ pp->lencode = (c < sizeof(pp->a_lastcode)) ? c : 0;
+
+ /* check 1: age control of local time stamp */
+ time(&now);
+ tt = now - tvr.tv_sec;
+ if (tt < 0 || tt > up->max_delay) {
+ DPRINTF(1, ("%s:SHM stale/bad receive time, delay=%llds\n",
+ refnumtoa(&peer->srcadr), (long long)tt));
+ up->bad++;
+ msyslog (LOG_ERR, "SHM: stale/bad receive time, delay=%llds",
+ (long long)tt);
+ return;
+ }
+
+ /* check 2: delta check */
+ tt = tvr.tv_sec - tvt.tv_sec - (tvr.tv_nsec < tvt.tv_nsec);
+ if (tt < 0)
+ tt = -tt;
+ if (up->max_delta > 0 && tt > up->max_delta) {
+ DPRINTF(1, ("%s: SHM diff limit exceeded, delta=%llds\n",
+ refnumtoa(&peer->srcadr), (long long)tt));
+ up->bad++;
+ msyslog (LOG_ERR, "SHM: difference limit exceeded, delta=%llds\n",
+ (long long)tt);
+ return;
+ }
+
+ /* if we really made it to this point... we're winners! */
+ DPRINTF(2, ("%s: SHM feeding data\n",
+ refnumtoa(&peer->srcadr)));
+ tsrcv = tspec_stamp_to_lfp(tvr);
+ tsref = tspec_stamp_to_lfp(tvt);
+ pp->leap = shm->leap;
+ peer->precision = shm->precision;
+ refclock_process_offset(pp, tsref, tsrcv, pp->fudgetime1);
+ up->good++;
+}
+
+/*
+ * shm_clockstats - dump and reset counters
+ */
+static void shm_clockstats(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct shmunit *up;
+ char logbuf[64];
+ unsigned int llen;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ /* if snprintf() returns a negative values on errors
+ ** (some older ones do) make sure we are NUL
+ ** terminated. Using an unsigned result does the trick.
+ */
+ llen = snprintf(logbuf, sizeof(logbuf),
+ "%3d %3d %3d %3d %3d",
+ up->ticks, up->good, up->notready,
+ up->bad, up->clash);
+ logbuf[min(llen, sizeof(logbuf)-1)] = '\0';
+ record_clock_stats(&peer->srcadr, logbuf);
+ }
+ up->ticks = up->good = up->notready = up->bad = up->clash = 0;
+
+}
+
+#else
+NONEMPTY_TRANSLATION_UNIT
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_tpro.c b/ntpd/refclock_tpro.c
new file mode 100644
index 0000000..ac511bb
--- /dev/null
+++ b/ntpd/refclock_tpro.c
@@ -0,0 +1,208 @@
+/*
+ * refclock_tpro - clock driver for the KSI/Odetics TPRO-S IRIG-B reader
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_TPRO)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "sys/tpro.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the KSI/Odetecs TPRO-S IRIG-B reader and TPRO-
+ * SAT GPS receiver for the Sun Microsystems SBus. It requires that the
+ * tpro.o device driver be installed and loaded.
+ */
+
+/*
+ * TPRO interface definitions
+ */
+#define DEVICE "/dev/tpro%d" /* device name and unit */
+#define PRECISION (-20) /* precision assumed (1 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "KSI/Odetics TPRO/S IRIG Interface" /* WRU */
+
+/*
+ * Unit control structure
+ */
+struct tprounit {
+ struct tproval tprodata; /* data returned from tpro read */
+};
+
+/*
+ * Function prototypes
+ */
+static int tpro_start (int, struct peer *);
+static void tpro_shutdown (int, struct peer *);
+static void tpro_poll (int unit, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_tpro = {
+ tpro_start, /* start up driver */
+ tpro_shutdown, /* shut down driver */
+ tpro_poll, /* transmit poll message */
+ noentry, /* not used (old tpro_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old tpro_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * tpro_start - open the TPRO device and initialize data for processing
+ */
+static int
+tpro_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+ char device[20];
+ int fd;
+
+ /*
+ * Open TPRO device
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+ fd = open(device, O_RDONLY | O_NDELAY, 0777);
+ if (fd == -1) {
+ msyslog(LOG_ERR, "tpro_start: open of %s: %m", device);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ pp->unitptr = up;
+
+ /*
+ * Initialize miscellaneous peer variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * tpro_shutdown - shut down the clock
+ */
+static void
+tpro_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ io_closeclock(&pp->io);
+ if (NULL != up)
+ free(up);
+}
+
+
+/*
+ * tpro_poll - called by the transmit procedure
+ */
+static void
+tpro_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+ struct tproval *tp;
+
+ /*
+ * This is the main routine. It snatches the time from the TPRO
+ * board and tacks on a local timestamp.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ tp = &up->tprodata;
+ if (read(pp->io.fd, (char *)tp, sizeof(struct tproval)) < 0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ get_systime(&pp->lastrec);
+ pp->polls++;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit. Note: we
+ * can't use the sec/usec conversion produced by the driver,
+ * since the year may be suspect. All format error checking is
+ * done by the snprintf() and sscanf() routines.
+ *
+ * Note that the refclockproc usec member has now become nsec.
+ * We could either multiply the read-in usec value by 1000 or
+ * we could pad the written string appropriately and read the
+ * resulting value in already scaled.
+ */
+ snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
+ "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x",
+ tp->day100, tp->day10, tp->day1, tp->hour10, tp->hour1,
+ tp->min10, tp->min1, tp->sec10, tp->sec1, tp->ms100,
+ tp->ms10, tp->ms1, tp->usec100, tp->usec10, tp->usec1,
+ tp->status);
+ pp->lencode = strlen(pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("tpro: time %s timecode %d %s\n",
+ ulfptoa(&pp->lastrec, 6), pp->lencode,
+ pp->a_lastcode);
+#endif
+ if (sscanf(pp->a_lastcode, "%3d %2d:%2d:%2d.%6ld", &pp->day,
+ &pp->hour, &pp->minute, &pp->second, &pp->nsec)
+ != 5) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->nsec *= 1000; /* Convert usec to nsec */
+ if (!tp->status & 0x3)
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+}
+
+#else
+int refclock_tpro_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_true.c b/ntpd/refclock_true.c
new file mode 100644
index 0000000..36ade81
--- /dev/null
+++ b/ntpd/refclock_true.c
@@ -0,0 +1,986 @@
+/*
+ * refclock_true - clock driver for the Kinemetrics/TrueTime receivers
+ * Receiver Version 3.0C - tested plain, with CLKLDISC
+ * Development work being done:
+ * - Support TL-3 WWV TOD receiver
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_TRUETIME)
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#ifdef SYS_WINNT
+extern int async_write(int, const void *, unsigned int);
+#undef write
+#define write(fd, data, octets) async_write(fd, data, octets)
+#endif
+
+/* This should be an atom clock but those are very hard to build.
+ *
+ * The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a bunch
+ * of TTL input and output pins, all brought out to the back panel. If you
+ * wire a PPS signal (such as the TTL PPS coming out of a GOES or other
+ * Kinemetrics/Truetime clock) to the 8253's GATE0, and then also wire the
+ * 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read CTR0 to get the
+ * number of uSecs since the last PPS upward swing, mediated by reading OUT0
+ * to find out if the counter has wrapped around (this happens if more than
+ * 65535us (65ms) elapses between the PPS event and our being called.)
+ */
+#ifdef CLOCK_PPS720
+# undef min /* XXX */
+# undef max /* XXX */
+# include <machine/inline.h>
+# include <sys/pcl720.h>
+# include <sys/i8253.h>
+# define PCL720_IOB 0x2a0 /* XXX */
+# define PCL720_CTR 0 /* XXX */
+#endif
+
+/*
+ * Support for Kinemetrics Truetime Receivers
+ * GOES: (468-DC, usable with GPS->GOES converting antenna)
+ * GPS/TM-TMD:
+ * XL-DC: (a 151-602-210, reported by the driver as a GPS/TM-TMD)
+ * GPS-800 TCU: (an 805-957 with the RS232 Talker/Listener module)
+ * TL-3: 3 channel WWV/H receiver w/ IRIG and RS-232 outputs
+ * OM-DC: getting stale ("OMEGA")
+ *
+ * Most of this code is originally from refclock_wwvb.c with thanks.
+ * It has been so mangled that wwvb is not a recognizable ancestor.
+ *
+ * Timcode format: ADDD:HH:MM:SSQCL
+ * A - control A (this is stripped before we see it)
+ * Q - Quality indication (see below)
+ * C - Carriage return
+ * L - Line feed
+ *
+ * Quality codes indicate possible error of
+ * 468-DC GOES Receiver:
+ * GPS-TM/TMD Receiver: (default quality codes for XL-DC)
+ * ? +/- 1 milliseconds # +/- 100 microseconds
+ * * +/- 10 microseconds . +/- 1 microsecond
+ * space less than 1 microsecond
+ * TL-3 Receiver: (default quality codes for TL-3)
+ * ? unknown quality (receiver is unlocked)
+ * space +/- 5 milliseconds
+ * OM-DC OMEGA Receiver: (default quality codes for OMEGA)
+ * WARNING OMEGA navigation system is no longer existent
+ * > >+- 5 seconds
+ * ? >+/- 500 milliseconds # >+/- 50 milliseconds
+ * * >+/- 5 milliseconds . >+/- 1 millisecond
+ * A-H less than 1 millisecond. Character indicates which station
+ * is being received as follows:
+ * A = Norway, B = Liberia, C = Hawaii, D = North Dakota,
+ * E = La Reunion, F = Argentina, G = Australia, H = Japan.
+ *
+ * The carriage return start bit begins on 0 seconds and extends to 1 bit time.
+ *
+ * Notes on 468-DC and OMEGA receiver:
+ *
+ * Send the clock a 'R' or 'C' and once per second a timestamp will
+ * appear. Send a 'P' to get the satellite position once (GOES only.)
+ *
+ * Notes on the 468-DC receiver:
+ *
+ * Since the old east/west satellite locations are only historical, you can't
+ * set your clock propagation delay settings correctly and still use
+ * automatic mode. The manual says to use a compromise when setting the
+ * switches. This results in significant errors. The solution; use fudge
+ * time1 and time2 to incorporate corrections. If your clock is set for
+ * 50 and it should be 58 for using the west and 46 for using the east,
+ * use the line
+ *
+ * fudge 127.127.5.0 time1 +0.008 time2 -0.004
+ *
+ * This corrects the 4 milliseconds advance and 8 milliseconds retard
+ * needed. The software will ask the clock which satellite it sees.
+ *
+ * Notes on the TrueTime TimeLink TL-3 WWV TOD receiver:
+ *
+ * This clock may be polled, or send one timecode per second.
+ * That mode may be toggled via the front panel ("C" mode), or controlled
+ * from the RS-232 port. Send the receiver "ST1" to turn it on, and
+ * "ST0" to turn it off. Send "QV" to get the firmware revision (useful
+ * for identifying this model.)
+ *
+ * Note that it can take several polling cycles, especially if the receiver
+ * was in the continuous timecode mode. (It can be slow to leave that mode.)
+ *
+ * ntp.conf parameters:
+ * time1 - offset applied to samples when reading WEST satellite (default = 0)
+ * time2 - offset applied to samples when reading EAST satellite (default = 0)
+ * stratum - stratum to assign to this clock (default = 0)
+ * refid - refid assigned to this clock (default = "TRUE", see below)
+ * flag1 - will silence the clock side of ntpd, just reading the clock
+ * without trying to write to it. (default = 0)
+ * flag2 - generate a debug file /tmp/true%d.
+ * flag3 - enable ppsclock streams module
+ * flag4 - use the PCL-720 (BSD/OS only)
+ */
+
+
+/*
+ * Definitions
+ */
+#define DEVICE "/dev/true%d"
+#define SPEED232 B9600 /* 9600 baud */
+
+/*
+ * Radio interface parameters
+ */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "TRUE" /* reference id */
+#define DESCRIPTION "Kinemetrics/TrueTime Receiver"
+
+/*
+ * Tags which station (satellite) we see
+ */
+#define GOES_WEST 0 /* Default to WEST satellite and apply time1 */
+#define GOES_EAST 1 /* until you discover otherwise */
+
+/*
+ * used by the state machine
+ */
+enum true_event {e_Init, e_Huh, e_F18, e_F50, e_F51, e_Satellite,
+ e_TL3, e_Poll, e_Location, e_TS, e_Max};
+const char *events[] = {"Init", "Huh", "F18", "F50", "F51", "Satellite",
+ "TL3", "Poll", "Location", "TS"};
+#define eventStr(x) (((int)x<(int)e_Max) ? events[(int)x] : "?")
+
+enum true_state {s_Base, s_InqTM, s_InqTCU, s_InqOmega, s_InqGOES,
+ s_InqTL3, s_Init, s_F18, s_F50, s_Start, s_Auto, s_Max};
+const char *states[] = {"Base", "InqTM", "InqTCU", "InqOmega", "InqGOES",
+ "InqTL3", "Init", "F18", "F50", "Start", "Auto"};
+#define stateStr(x) (((int)x<(int)s_Max) ? states[(int)x] : "?")
+
+enum true_type {t_unknown, t_goes, t_tm, t_tcu, t_omega, t_tl3, t_Max};
+const char *types[] = {"unknown", "goes", "tm", "tcu", "omega", "tl3"};
+#define typeStr(x) (((int)x<(int)t_Max) ? types[(int)x] : "?")
+
+/*
+ * unit control structure
+ */
+struct true_unit {
+ unsigned int pollcnt; /* poll message counter */
+ unsigned int station; /* which station we are on */
+ unsigned int polled; /* Hand in a time sample? */
+ enum true_state state; /* state machine */
+ enum true_type type; /* what kind of clock is it? */
+ int unit; /* save an extra copy of this */
+ FILE *debug; /* debug logging file */
+#ifdef CLOCK_PPS720
+ int pcl720init; /* init flag for PCL 720 */
+#endif
+};
+
+/*
+ * Function prototypes
+ */
+static int true_start (int, struct peer *);
+static void true_shutdown (int, struct peer *);
+static void true_receive (struct recvbuf *);
+static void true_poll (int, struct peer *);
+static void true_send (struct peer *, const char *);
+static void true_doevent (struct peer *, enum true_event);
+
+#ifdef CLOCK_PPS720
+static u_long true_sample720 (void);
+#endif
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_true = {
+ true_start, /* start up driver */
+ true_shutdown, /* shut down driver */
+ true_poll, /* transmit poll message */
+ noentry, /* not used (old true_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old true_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+#if !defined(__STDC__)
+# define true_debug (void)
+#else
+static void
+true_debug(struct peer *peer, const char *fmt, ...)
+{
+ va_list ap;
+ int want_debugging, now_debugging;
+ struct refclockproc *pp;
+ struct true_unit *up;
+
+ va_start(ap, fmt);
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ want_debugging = (pp->sloppyclockflag & CLK_FLAG2) != 0;
+ now_debugging = (up->debug != NULL);
+ if (want_debugging != now_debugging)
+ {
+ if (want_debugging) {
+ char filename[40];
+ int fd;
+
+ snprintf(filename, sizeof(filename),
+ "/tmp/true%d.debug", up->unit);
+ fd = open(filename, O_CREAT | O_WRONLY | O_EXCL,
+ 0600);
+ if (fd >= 0 && (up->debug = fdopen(fd, "r+"))) {
+#ifdef HAVE_SETVBUF
+ static char buf[BUFSIZ];
+
+ setvbuf(up->debug, buf, _IOLBF, BUFSIZ);
+#else
+ setlinebuf(up->debug);
+#endif
+ }
+ } else {
+ fclose(up->debug);
+ up->debug = NULL;
+ }
+ }
+
+ if (up->debug) {
+ fprintf(up->debug, "true%d: ", up->unit);
+ vfprintf(up->debug, fmt, ap);
+ }
+ va_end(ap);
+}
+#endif /*STDC*/
+
+/*
+ * true_start - open the devices and initialize data for processing
+ */
+static int
+true_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct true_unit *up;
+ struct refclockproc *pp;
+ char device[40];
+ int fd;
+
+ /*
+ * Open serial port
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+ fd = refclock_open(device, SPEED232, LDISC_CLK);
+ if (fd <= 0)
+ return 0;
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->io.clock_recv = true_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ return (0);
+ }
+ pp->unitptr = up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy(&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ up->type = t_unknown;
+ up->state = s_Base;
+
+ /*
+ * Send a CTRL-C character at the start,
+ * just in case the clock is already
+ * sending timecodes
+ */
+ true_send(peer, "\03\r");
+
+ true_doevent(peer, e_Init);
+
+ return (1);
+}
+
+
+/*
+ * true_shutdown - shut down the clock
+ */
+static void
+true_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct true_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (pp->io.fd != -1)
+ io_closeclock(&pp->io);
+ if (up != NULL)
+ free(up);
+}
+
+
+/*
+ * true_receive - receive data from the serial interface on a clock
+ */
+static void
+true_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct true_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ u_short new_station;
+ char synced;
+ int i;
+ int lat, lon, off; /* GOES Satellite position */
+ /* These variables hold data until we decide to keep it */
+ char rd_lastcode[BMAX];
+ l_fp rd_tmp;
+ u_short rd_lencode;
+
+ /*
+ * Get the clock this applies to and pointers to the data.
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Read clock output. Automatically handles STREAMS, CLKLDISC.
+ */
+ rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
+ rd_lastcode[rd_lencode] = '\0';
+
+ /*
+ * There is a case where <cr><lf> generates 2 timestamps.
+ */
+ if (rd_lencode == 0)
+ return;
+ pp->lencode = rd_lencode;
+ strlcpy(pp->a_lastcode, rd_lastcode, sizeof(pp->a_lastcode));
+ pp->lastrec = rd_tmp;
+ true_debug(peer, "receive(%s) [%d]\n", pp->a_lastcode,
+ pp->lencode);
+
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. This code decodes a multitude of different
+ * clock messages. Timecodes are processed if needed. All replies
+ * will be run through the state machine to tweak driver options
+ * and program the clock.
+ */
+
+ /*
+ * Clock misunderstood our last command?
+ */
+ if (pp->a_lastcode[0] == '?' ||
+ strcmp(pp->a_lastcode, "ERROR 05 NO SUCH FUNCTION") == 0) {
+ true_doevent(peer, e_Huh);
+ return;
+ }
+
+ /*
+ * Timecode: "nnnnn+nnn-nnn"
+ * (from GOES clock when asked about satellite position)
+ */
+ if ((pp->a_lastcode[5] == '+' || pp->a_lastcode[5] == '-') &&
+ (pp->a_lastcode[9] == '+' || pp->a_lastcode[9] == '-') &&
+ sscanf(pp->a_lastcode, "%5d%*c%3d%*c%3d", &lon, &lat, &off) == 3
+ ) {
+ const char *label = "Botch!";
+
+ /*
+ * This is less than perfect. Call the (satellite)
+ * either EAST or WEST and adjust slop accodingly
+ * Perfectionists would recalculate the exact delay
+ * and adjust accordingly...
+ */
+ if (lon > 7000 && lon < 14000) {
+ if (lon < 10000) {
+ new_station = GOES_EAST;
+ label = "EAST";
+ } else {
+ new_station = GOES_WEST;
+ label = "WEST";
+ }
+
+ if (new_station != up->station) {
+ double dtemp;
+
+ dtemp = pp->fudgetime1;
+ pp->fudgetime1 = pp->fudgetime2;
+ pp->fudgetime2 = dtemp;
+ up->station = new_station;
+ }
+ }
+ else {
+ /*refclock_report(peer, CEVNT_BADREPLY);*/
+ label = "UNKNOWN";
+ }
+ true_debug(peer, "GOES: station %s\n", label);
+ true_doevent(peer, e_Satellite);
+ return;
+ }
+
+ /*
+ * Timecode: "Fnn"
+ * (from TM/TMD clock when it wants to tell us what it's up to.)
+ */
+ if (sscanf(pp->a_lastcode, "F%2d", &i) == 1 && i > 0 && i < 80) {
+ switch (i) {
+ case 50:
+ true_doevent(peer, e_F50);
+ break;
+ case 51:
+ true_doevent(peer, e_F51);
+ break;
+ default:
+ true_debug(peer, "got F%02d - ignoring\n", i);
+ break;
+ }
+ return;
+ }
+
+ /*
+ * Timecode: "VER xx.xx"
+ * (from a TL3 when sent "QV", so id's it during initialization.)
+ */
+ if (pp->a_lastcode[0] == 'V' && pp->a_lastcode[1] == 'E' &&
+ pp->a_lastcode[2] == 'R' && pp->a_lastcode[6] == '.') {
+ true_doevent(peer, e_TL3);
+ NLOG(NLOG_CLOCKSTATUS) {
+ msyslog(LOG_INFO, "TL3: %s", pp->a_lastcode);
+ }
+ return;
+ }
+
+ /*
+ * Timecode: " TRUETIME Mk III" or " TRUETIME XL"
+ * (from a TM/TMD/XL clock during initialization.)
+ */
+ if (strncmp(pp->a_lastcode, " TRUETIME Mk III ", 17) == 0 ||
+ strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) {
+ true_doevent(peer, e_F18);
+ NLOG(NLOG_CLOCKSTATUS) {
+ msyslog(LOG_INFO, "TM/TMD/XL: %s", pp->a_lastcode);
+ }
+ return;
+ }
+
+ /*
+ * Timecode: "N03726428W12209421+000033"
+ * 1 2
+ * index 0123456789012345678901234
+ * (from a TCU during initialization)
+ */
+ if ((pp->a_lastcode[0] == 'N' || pp->a_lastcode[0] == 'S') &&
+ (pp->a_lastcode[9] == 'W' || pp->a_lastcode[9] == 'E') &&
+ pp->a_lastcode[18] == '+') {
+ true_doevent(peer, e_Location);
+ NLOG(NLOG_CLOCKSTATUS) {
+ msyslog(LOG_INFO, "TCU-800: %s", pp->a_lastcode);
+ }
+ return;
+ }
+ /*
+ * Timecode: "ddd:hh:mm:ssQ"
+ * 1 2
+ * index 0123456789012345678901234
+ * (from all clocks supported by this driver.)
+ */
+ if (pp->a_lastcode[3] == ':' &&
+ pp->a_lastcode[6] == ':' &&
+ pp->a_lastcode[9] == ':' &&
+ sscanf(pp->a_lastcode, "%3d:%2d:%2d:%2d%c",
+ &pp->day, &pp->hour, &pp->minute,
+ &pp->second, &synced) == 5) {
+
+ /*
+ * Adjust the synchronize indicator according to timecode
+ * say were OK, and then say not if we really are not OK
+ */
+ if (synced == '>' || synced == '#' || synced == '?'
+ || synced == 'X')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ true_doevent(peer, e_TS);
+
+#ifdef CLOCK_PPS720
+ /* If it's taken more than 65ms to get here, we'll lose. */
+ if ((pp->sloppyclockflag & CLK_FLAG4) && up->pcl720init) {
+ l_fp off;
+
+#ifdef CLOCK_ATOM
+ /*
+ * find out what time it really is. Include
+ * the count from the PCL720
+ */
+ if (!clocktime(pp->day, pp->hour, pp->minute,
+ pp->second, GMT, pp->lastrec.l_ui,
+ &pp->yearstart, &off.l_ui)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ off.l_uf = 0;
+#endif
+
+ pp->usec = true_sample720();
+#ifdef CLOCK_ATOM
+ TVUTOTSF(pp->usec, off.l_uf);
+#endif
+
+ /*
+ * Stomp all over the timestamp that was pulled out
+ * of the input stream. It's irrelevant since we've
+ * adjusted the input time to reflect now (via pp->usec)
+ * rather than when the data was collected.
+ */
+ get_systime(&pp->lastrec);
+#ifdef CLOCK_ATOM
+ /*
+ * Create a true offset for feeding to pps_sample()
+ */
+ L_SUB(&off, &pp->lastrec);
+
+ pps_sample(peer, &off);
+#endif
+ true_debug(peer, "true_sample720: %luus\n", pp->usec);
+ }
+#endif
+
+ /*
+ * The clock will blurt a timecode every second but we only
+ * want one when polled. If we havn't been polled, bail out.
+ */
+ if (!up->polled)
+ return;
+
+ /* We only call doevent if additional things need be done
+ * at poll interval. Currently, its only for GOES. We also
+ * call it for clock unknown so that it gets logged.
+ */
+ if (up->type == t_goes || up->type == t_unknown)
+ true_doevent(peer, e_Poll);
+
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ /*
+ * If clock is good we send a NOMINAL message so that
+ * any previous BAD messages are nullified
+ */
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ refclock_report(peer, CEVNT_NOMINAL);
+
+ /*
+ * We have succedded in answering the poll.
+ * Turn off the flag and return
+ */
+ up->polled = 0;
+
+ return;
+ }
+
+ /*
+ * No match to known timecodes, report failure and return
+ */
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+}
+
+
+/*
+ * true_send - time to send the clock a signal to cough up a time sample
+ */
+static void
+true_send(
+ struct peer *peer,
+ const char *cmd
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ if (!(pp->sloppyclockflag & CLK_FLAG1)) {
+ int len = strlen(cmd);
+
+ true_debug(peer, "Send '%s'\n", cmd);
+ if (write(pp->io.fd, cmd, (unsigned)len) != len)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+ }
+}
+
+
+/*
+ * state machine for initializing and controlling a clock
+ */
+static void
+true_doevent(
+ struct peer *peer,
+ enum true_event event
+ )
+{
+ struct true_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (event != e_TS) {
+ NLOG(NLOG_CLOCKSTATUS) {
+ msyslog(LOG_INFO, "TRUE: clock %s, state %s, event %s",
+ typeStr(up->type),
+ stateStr(up->state),
+ eventStr(event));
+ }
+ }
+ true_debug(peer, "clock %s, state %s, event %s\n",
+ typeStr(up->type), stateStr(up->state), eventStr(event));
+ switch (up->type) {
+ case t_goes:
+ switch (event) {
+ case e_Init: /* FALLTHROUGH */
+ case e_Satellite:
+ /*
+ * Switch back to on-second time codes and return.
+ */
+ true_send(peer, "C");
+ up->state = s_Start;
+ break;
+ case e_Poll:
+ /*
+ * After each poll, check the station (satellite).
+ */
+ true_send(peer, "P");
+ /* No state change needed. */
+ break;
+ default:
+ break;
+ }
+ /* FALLTHROUGH */
+ case t_omega:
+ switch (event) {
+ case e_Init:
+ true_send(peer, "C");
+ up->state = s_Start;
+ break;
+ case e_TS:
+ if (up->state != s_Start && up->state != s_Auto) {
+ true_send(peer, "\03\r");
+ break;
+ }
+ up->state = s_Auto;
+ break;
+ default:
+ break;
+ }
+ break;
+ case t_tm:
+ switch (event) {
+ case e_Init:
+ true_send(peer, "F18\r");
+ up->state = s_Init;
+ break;
+ case e_F18:
+ true_send(peer, "F50\r");
+ /*
+ * Timecode: " TRUETIME Mk III" or " TRUETIME XL"
+ * (from a TM/TMD/XL clock during initialization.)
+ */
+ if ( strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0 ||
+ strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) {
+ true_doevent(peer, e_F18);
+ NLOG(NLOG_CLOCKSTATUS) {
+ msyslog(LOG_INFO, "TM/TMD/XL: %s",
+ pp->a_lastcode);
+ }
+ return;
+ }
+ up->state = s_F18;
+ break;
+ case e_F50:
+ true_send(peer, "F51\r");
+ up->state = s_F50;
+ break;
+ case e_F51:
+ true_send(peer, "F08\r");
+ up->state = s_Start;
+ break;
+ case e_TS:
+ if (up->state != s_Start && up->state != s_Auto) {
+ true_send(peer, "\03\r");
+ break;
+ }
+ up->state = s_Auto;
+ break;
+ default:
+ break;
+ }
+ break;
+ case t_tcu:
+ switch (event) {
+ case e_Init:
+ true_send(peer, "MD3\r"); /* GPS Synch'd Gen. */
+ true_send(peer, "TSU\r"); /* UTC, not GPS. */
+ true_send(peer, "AU\r"); /* Auto Timestamps. */
+ up->state = s_Start;
+ break;
+ case e_TS:
+ if (up->state != s_Start && up->state != s_Auto) {
+ true_send(peer, "\03\r");
+ break;
+ }
+ up->state = s_Auto;
+ break;
+ default:
+ break;
+ }
+ break;
+ case t_tl3:
+ switch (event) {
+ case e_Init:
+ true_send(peer, "ST1"); /* Turn on continuous stream */
+ break;
+ case e_TS:
+ up->state = s_Auto;
+ break;
+ default:
+ break;
+ }
+ break;
+ case t_unknown:
+ if (event == e_Poll)
+ break;
+ switch (up->state) {
+ case s_Base:
+ if (event != e_Init)
+ abort();
+ true_send(peer, "P\r");
+ up->state = s_InqGOES;
+ break;
+ case s_InqGOES:
+ switch (event) {
+ case e_Satellite:
+ up->type = t_goes;
+ true_doevent(peer, e_Init);
+ break;
+ case e_Init: /*FALLTHROUGH*/
+ case e_Huh:
+ case e_TS:
+ true_send(peer, "ST0"); /* turn off TL3 auto */
+ sleep(1); /* wait for it */
+ up->state = s_InqTL3;
+ true_send(peer, "QV"); /* see if its a TL3 */
+ break;
+ default:
+ abort();
+ }
+ break;
+ case s_InqTL3:
+ switch (event) {
+ case e_TL3:
+ up->type = t_tl3;
+ up->state = s_Auto; /* Inq side-effect. */
+ true_send(peer, "ST1"); /* Turn on 1/sec data */
+ break;
+ case e_Init: /*FALLTHROUGH*/
+ case e_Huh:
+ up->state = s_InqOmega;
+ true_send(peer, "C\r");
+ break;
+ case e_TS:
+ up->type = t_tl3; /* Already sending data */
+ up->state = s_Auto;
+ break;
+ default:
+ msyslog(LOG_INFO,
+ "TRUE: TL3 init fellthrough! (%d)", event);
+ break;
+ }
+ break;
+ case s_InqOmega:
+ switch (event) {
+ case e_TS:
+ up->type = t_omega;
+ up->state = s_Auto; /* Inq side-effect. */
+ break;
+ case e_Init: /*FALLTHROUGH*/
+ case e_Huh:
+ up->state = s_InqTM;
+ true_send(peer, "F18\r");
+ break;
+ default:
+ abort();
+ }
+ break;
+ case s_InqTM:
+ switch (event) {
+ case e_F18:
+ up->type = t_tm;
+ true_doevent(peer, e_Init);
+ break;
+ case e_Init: /*FALLTHROUGH*/
+ case e_Huh:
+ true_send(peer, "PO\r");
+ up->state = s_InqTCU;
+ break;
+ default:
+ msyslog(LOG_INFO,
+ "TRUE: TM/TMD init fellthrough!");
+ break;
+ }
+ break;
+ case s_InqTCU:
+ switch (event) {
+ case e_Location:
+ up->type = t_tcu;
+ true_doevent(peer, e_Init);
+ break;
+ case e_Init: /*FALLTHROUGH*/
+ case e_Huh:
+ up->state = s_Base;
+ sleep(1); /* XXX */
+ break;
+ default:
+ msyslog(LOG_INFO,
+ "TRUE: TCU init fellthrough!");
+ break;
+ }
+ break;
+ /*
+ * An expedient hack to prevent lint complaints,
+ * these don't actually need to be used here...
+ */
+ case s_Init:
+ case s_F18:
+ case s_F50:
+ case s_Start:
+ case s_Auto:
+ case s_Max:
+ msyslog(LOG_INFO, "TRUE: state %s is unexpected!",
+ stateStr(up->state));
+ }
+ break;
+ default:
+ msyslog(LOG_INFO, "TRUE: cannot identify refclock!");
+ abort();
+ /* NOTREACHED */
+ }
+
+#ifdef CLOCK_PPS720
+ if ((pp->sloppyclockflag & CLK_FLAG4) && !up->pcl720init) {
+ /* Make counter trigger on gate0, count down from 65535. */
+ pcl720_load(PCL720_IOB, PCL720_CTR, i8253_oneshot, 65535);
+ /*
+ * (These constants are OK since
+ * they represent hardware maximums.)
+ */
+ NLOG(NLOG_CLOCKINFO) {
+ msyslog(LOG_NOTICE, "PCL-720 initialized");
+ }
+ up->pcl720init++;
+ }
+#endif
+
+
+}
+
+/*
+ * true_poll - called by the transmit procedure
+ */
+static void
+true_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct true_unit *up;
+ struct refclockproc *pp;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (up->pollcnt > 0) {
+ up->pollcnt--;
+ } else {
+ true_doevent(peer, e_Init);
+ refclock_report(peer, CEVNT_TIMEOUT);
+ }
+
+ /*
+ * polled every 64 seconds. Ask true_receive to hand in a
+ * timestamp.
+ */
+ up->polled = 1;
+ pp->polls++;
+}
+
+#ifdef CLOCK_PPS720
+/*
+ * true_sample720 - sample the PCL-720
+ */
+static u_long
+true_sample720(void)
+{
+ unsigned long f;
+
+ /* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3.
+ * If it is not being held low now, we did not get called
+ * within 65535us.
+ */
+ if (inb(pcl720_data_16_23(PCL720_IOB)) & 0x01) {
+ NLOG(NLOG_CLOCKINFO) {
+ msyslog(LOG_NOTICE, "PCL-720 out of synch");
+ }
+ return (0);
+ }
+ f = (65536 - pcl720_read(PCL720_IOB, PCL720_CTR));
+#ifdef PPS720_DEBUG
+ msyslog(LOG_DEBUG, "PCL-720: %luus", f);
+#endif
+ return (f);
+}
+#endif
+
+#else
+int refclock_true_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_tsyncpci.c b/ntpd/refclock_tsyncpci.c
new file mode 100644
index 0000000..e70a7cf
--- /dev/null
+++ b/ntpd/refclock_tsyncpci.c
@@ -0,0 +1,912 @@
+/*******************************************************************************
+*
+* Module : refclock_tsyncpci.c
+* Date : 09/08/08
+* Purpose : Implements a reference clock driver for the NTP daemon. This
+* reference clock driver provides a means to communicate with
+* the Spectracom TSYNC PCI timing devices and use them as a time
+* source.
+*
+* (C) Copyright 2008 Spectracom Corporation
+*
+* This software is provided by Spectracom Corporation 'as is' and
+* any express or implied warranties, including, but not limited to, the
+* implied warranties of merchantability and fitness for a particular purpose
+* are disclaimed. In no event shall Spectracom Corporation be liable
+* for any direct, indirect, incidental, special, exemplary, or consequential
+* damages (including, but not limited to, procurement of substitute goods
+* or services; loss of use, data, or profits; or business interruption)
+* however caused and on any theory of liability, whether in contract, strict
+* liability, or tort (including negligence or otherwise) arising in any way
+* out of the use of this software, even if advised of the possibility of
+* such damage.
+*
+* This software is released for distribution according to the NTP copyright
+* and license contained in html/copyright.html of NTP source.
+*
+*******************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_TSYNCPCI)
+
+#include <asm/ioctl.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <netinet/in.h>
+
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+#include "ntp_calendar.h"
+
+
+/*******************************************************************************
+**
+** This driver supports the Spectracom TSYNC PCI GPS receiver. It requires
+** that the tsyncpci.o device driver be installed and loaded.
+**
+*******************************************************************************/
+
+#define TSYNC_PCI_REVISION "1.11"
+
+/*
+** TPRO interface definitions
+*/
+#define DEVICE "/dev/tsyncpci" /* device name */
+#define PRECISION (-20) /* precision assumed (1 us) */
+#define DESCRIPTION "Spectracom TSYNC-PCI" /* WRU */
+
+#define SECONDS_1900_TO_1970 (2208988800U)
+
+#define TSYNC_REF_IID (0x2500) // SS CAI, REF IID
+#define TSYNC_REF_DEST_ID (0x0001) // KTS Firmware
+#define TSYNC_REF_IN_PYLD_OFF (0)
+#define TSYNC_REF_IN_LEN (0)
+#define TSYNC_REF_OUT_PYLD_OFF (0)
+#define TSYNC_REF_OUT_LEN (8)
+#define TSYNC_REF_MAX_OUT_LEN (16)
+#define TSYNC_REF_PYLD_LEN (TSYNC_REF_IN_LEN + \
+ TSYNC_REF_MAX_OUT_LEN)
+#define TSYNC_REF_LEN (4)
+#define TSYNC_REF_LOCAL ("LOCL")
+
+#define TSYNC_TMSCL_IID (0x2301) // CS CAI, TIMESCALE IID
+#define TSYNC_TMSCL_DEST_ID (0x0001) // KTS Firmware
+#define TSYNC_TMSCL_IN_PYLD_OFF (0)
+#define TSYNC_TMSCL_IN_LEN (0)
+#define TSYNC_TMSCL_OUT_PYLD_OFF (0)
+#define TSYNC_TMSCL_OUT_LEN (4)
+#define TSYNC_TMSCL_MAX_OUT_LEN (12)
+#define TSYNC_TMSCL_PYLD_LEN (TSYNC_TMSCL_IN_LEN + \
+ TSYNC_TMSCL_MAX_OUT_LEN)
+
+#define TSYNC_LEAP_IID (0x2307) // CS CAI, LEAP SEC IID
+#define TSYNC_LEAP_DEST_ID (0x0001) // KTS Firmware
+#define TSYNC_LEAP_IN_PYLD_OFF (0)
+#define TSYNC_LEAP_IN_LEN (0)
+#define TSYNC_LEAP_OUT_PYLD_OFF (0)
+#define TSYNC_LEAP_OUT_LEN (28)
+#define TSYNC_LEAP_MAX_OUT_LEN (36)
+#define TSYNC_LEAP_PYLD_LEN (TSYNC_LEAP_IN_LEN + \
+ TSYNC_LEAP_MAX_OUT_LEN)
+
+// These define the base date/time of the system clock. The system time will
+// be tracked as the number of seconds from this date/time.
+#define TSYNC_TIME_BASE_YEAR (1970) // earliest acceptable year
+
+#define TSYNC_LCL_STRATUM (0)
+
+/*
+** TSYNC Time Scales type
+*/
+typedef enum
+{
+ TIME_SCALE_UTC = 0, // Universal Coordinated Time
+ TIME_SCALE_TAI = 1, // International Atomic Time
+ TIME_SCALE_GPS = 2, // Global Positioning System
+ TIME_SCALE_LOCAL = 3, // UTC w/local rules for time zone and DST
+ NUM_TIME_SCALES = 4, // Number of time scales
+
+ TIME_SCALE_MAX = 15 // Maximum number of timescales
+
+} TIME_SCALE;
+
+/*
+** TSYNC Board Object
+*/
+typedef struct BoardObj {
+
+ int file_descriptor;
+ unsigned short devid;
+ unsigned short options;
+ unsigned char firmware[5];
+ unsigned char FPGA[5];
+ unsigned char driver[7];
+
+} BoardObj;
+
+/*
+** TSYNC Time Object
+*/
+typedef struct TimeObj {
+
+ unsigned char syncOption; /* -M option */
+ unsigned int secsDouble; /* seconds floating pt */
+ unsigned char seconds; /* seconds whole num */
+ unsigned char minutes;
+ unsigned char hours;
+ unsigned short days;
+ unsigned short year;
+ unsigned short flags; /* bit 2 SYNC, bit 1 TCODE; all others 0 */
+
+} TimeObj;
+
+/*
+** NTP Time Object
+*/
+typedef struct NtpTimeObj {
+
+ TimeObj timeObj;
+ struct timeval tv;
+ unsigned int refId;
+
+} NtpTimeObj;
+/*
+** TSYNC Supervisor Reference Object
+*/
+typedef struct ReferenceObj {
+
+ char time[TSYNC_REF_LEN];
+ char pps[TSYNC_REF_LEN];
+
+} ReferenceObj;
+
+/*
+** TSYNC Seconds Time Object
+*/
+typedef struct SecTimeObj
+{
+ unsigned int seconds;
+ unsigned int ns;
+}
+SecTimeObj;
+
+/*
+** TSYNC DOY Time Object
+*/
+typedef struct DoyTimeObj
+{
+ unsigned int year;
+ unsigned int doy;
+ unsigned int hour;
+ unsigned int minute;
+ unsigned int second;
+ unsigned int ns;
+}
+DoyTimeObj;
+
+/*
+** TSYNC Leap Second Object
+*/
+typedef struct LeapSecondObj
+{
+ int offset;
+ DoyTimeObj utcDate;
+}
+LeapSecondObj;
+
+/*
+ * structures for ioctl interactions with driver
+ */
+#define DI_PAYLOADS_STARTER_LENGTH 4
+typedef struct ioctl_trans_di {
+
+ // input parameters
+ uint16_t dest;
+ uint16_t iid;
+
+ uint32_t inPayloadOffset;
+ uint32_t inLength;
+ uint32_t outPayloadOffset;
+ uint32_t maxOutLength;
+
+ // output parameters
+ uint32_t actualOutLength;
+ int32_t status;
+
+ // Input and output
+
+ // The payloads field MUST be last in ioctl_trans_di.
+ uint8_t payloads[DI_PAYLOADS_STARTER_LENGTH];
+
+}ioctl_trans_di;
+
+/*
+ * structure for looking up a reference ID from a reference name
+ */
+typedef struct
+{
+ const char* pRef; // KTS Reference Name
+ const char* pRefId; // NTP Reference ID
+
+} RefIdLookup;
+
+/*
+ * unit control structure
+ */
+typedef struct {
+ uint32_t refPrefer; // Reference prefer flag
+ uint32_t refId; // Host peer reference ID
+ uint8_t refStratum; // Host peer reference stratum
+
+} TsyncUnit;
+
+/*
+** Function prototypes
+*/
+static void tsync_poll (int unit, struct peer *);
+static void tsync_shutdown (int, struct peer *);
+static int tsync_start (int, struct peer *);
+
+/*
+** Helper functions
+*/
+static void ApplyTimeOffset (DoyTimeObj* pDt, int off);
+static void SecTimeFromDoyTime (SecTimeObj* pSt, DoyTimeObj* pDt);
+static void DoyTimeFromSecTime (DoyTimeObj* pDt, SecTimeObj* pSt);
+
+/*
+** Transfer vector
+*/
+struct refclock refclock_tsyncpci = {
+ tsync_start, /* start up driver */
+ tsync_shutdown, /* shut down driver */
+ tsync_poll, /* transmit poll message */
+ noentry, /* not used (old tsync_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old tsync_buginfo) */
+ NOFLAGS /* not used */
+};
+
+/*
+ * Reference ID lookup table
+ */
+static RefIdLookup RefIdLookupTbl[] =
+{
+ {"gps", "GPS"},
+ {"ir", "IRIG"},
+ {"hvq", "HVQ"},
+ {"frq", "FREQ"},
+ {"mdm", "ACTS"},
+ {"epp", "PPS"},
+ {"ptp", "PTP"},
+ {"asc", "ATC"},
+ {"hst0", "USER"},
+ {"hst", TSYNC_REF_LOCAL},
+ {"self", TSYNC_REF_LOCAL},
+ {NULL, NULL}
+};
+
+/*******************************************************************************
+** IOCTL DEFINITIONS
+*******************************************************************************/
+#define IOCTL_TPRO_ID 't'
+#define IOCTL_TPRO_OPEN _IOWR(IOCTL_TPRO_ID, 0, BoardObj)
+#define IOCTL_TPRO_GET_NTP_TIME _IOWR(IOCTL_TPRO_ID, 25, NtpTimeObj)
+#define IOCTL_TSYNC_GET _IOWR(IOCTL_TPRO_ID, 26, ioctl_trans_di)
+
+/******************************************************************************
+ *
+ * Function: tsync_start()
+ * Description: Used to intialize the Spectracom TSYNC reference driver.
+ *
+ * Parameters:
+ * IN: unit - not used.
+ * *peer - pointer to this reference clock's peer structure
+ * Returns: 0 - unsuccessful
+ * 1 - successful
+ *
+*******************************************************************************/
+static int tsync_start(int unit, struct peer *peer)
+{
+ struct refclockproc *pp;
+ TsyncUnit *up;
+
+
+ /*
+ ** initialize reference clock and peer parameters
+ */
+ pp = peer->procptr;
+ pp->clockdesc = DESCRIPTION;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ peer->precision = PRECISION;
+
+ // Allocate and initialize unit structure
+ if (!(up = (TsyncUnit*)emalloc(sizeof(TsyncUnit))))
+ {
+ return (0);
+ }
+
+ // Store reference preference
+ up->refPrefer = peer->flags & FLAG_PREFER;
+
+ // Initialize reference stratum level and ID
+ up->refStratum = STRATUM_UNSPEC;
+ strncpy((char *)&up->refId, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
+
+ // Attach unit structure
+ pp->unitptr = (caddr_t)up;
+
+ /* Declare our refId as local in the beginning because we do not know
+ * what our actual refid is yet.
+ */
+ strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
+
+ return (1);
+
+} /* End - tsync_start() */
+
+/*******************************************************************************
+**
+** Function: tsync_shutdown()
+** Description: Handles anything related to shutting down the reference clock
+** driver. Nothing at this point in time.
+**
+** Parameters:
+** IN: unit - not used.
+** *peer - pointer to this reference clock's peer structure
+** Returns: none.
+**
+*******************************************************************************/
+static void tsync_shutdown(int unit, struct peer *peer)
+{
+
+} /* End - tsync_shutdown() */
+
+/******************************************************************************
+ *
+ * Function: tsync_poll()
+ * Description: Retrieve time from the TSYNC device.
+ *
+ * Parameters:
+ * IN: unit - not used.
+ * *peer - pointer to this reference clock's peer structure
+ * Returns: none.
+ *
+*******************************************************************************/
+static void tsync_poll(int unit, struct peer *peer)
+{
+ char device[32];
+ struct refclockproc *pp;
+ struct calendar jt;
+ TsyncUnit *up;
+ unsigned char synch;
+ double seconds;
+ int err;
+ int err1;
+ int err2;
+ int err3;
+ int i;
+ int j;
+ unsigned int itAllocationLength;
+ unsigned int itAllocationLength1;
+ unsigned int itAllocationLength2;
+ NtpTimeObj TimeContext;
+ BoardObj hBoard;
+ char timeRef[TSYNC_REF_LEN + 1];
+ char ppsRef [TSYNC_REF_LEN + 1];
+ TIME_SCALE tmscl = TIME_SCALE_UTC;
+ LeapSecondObj leapSec;
+ ioctl_trans_di *it;
+ ioctl_trans_di *it1;
+ ioctl_trans_di *it2;
+ l_fp offset;
+ l_fp ltemp;
+ ReferenceObj * pRefObj;
+
+
+ /* Construct the device name */
+ sprintf(device, "%s%d", DEVICE, (int)peer->refclkunit);
+
+ printf("Polling device number %d...\n", (int)peer->refclkunit);
+
+ /* Open the TSYNC device */
+ hBoard.file_descriptor = open(device, O_RDONLY | O_NDELAY, 0777);
+
+ /* If error opening TSYNC device... */
+ if (hBoard.file_descriptor < 0)
+ {
+ msyslog(LOG_ERR, "Couldn't open device");
+ return;
+ }
+
+ /* If error while initializing the board... */
+ if (ioctl(hBoard.file_descriptor, IOCTL_TPRO_OPEN, &hBoard) < 0)
+ {
+ msyslog(LOG_ERR, "Couldn't initialize device");
+ close(hBoard.file_descriptor);
+ return;
+ }
+
+ /* Allocate memory for ioctl message */
+ itAllocationLength =
+ (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
+ TSYNC_REF_IN_LEN + TSYNC_REF_MAX_OUT_LEN;
+
+ it = (ioctl_trans_di*)alloca(itAllocationLength);
+ if (it == NULL) {
+ msyslog(LOG_ERR, "Couldn't allocate transaction memory - Reference");
+ return;
+ }
+
+ /* Build SS_GetRef ioctl message */
+ it->dest = TSYNC_REF_DEST_ID;
+ it->iid = TSYNC_REF_IID;
+ it->inPayloadOffset = TSYNC_REF_IN_PYLD_OFF;
+ it->inLength = TSYNC_REF_IN_LEN;
+ it->outPayloadOffset = TSYNC_REF_OUT_PYLD_OFF;
+ it->maxOutLength = TSYNC_REF_MAX_OUT_LEN;
+ it->actualOutLength = 0;
+ it->status = 0;
+ memset(it->payloads, 0, TSYNC_REF_MAX_OUT_LEN);
+
+ /* Read the reference from the TSYNC-PCI device */
+ err = ioctl(hBoard.file_descriptor,
+ IOCTL_TSYNC_GET,
+ (char *)it);
+
+ /* Allocate memory for ioctl message */
+ itAllocationLength1 =
+ (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
+ TSYNC_TMSCL_IN_LEN + TSYNC_TMSCL_MAX_OUT_LEN;
+
+ it1 = (ioctl_trans_di*)alloca(itAllocationLength1);
+ if (it1 == NULL) {
+ msyslog(LOG_ERR, "Couldn't allocate transaction memory - Time Scale");
+ return;
+ }
+
+ /* Build CS_GetTimeScale ioctl message */
+ it1->dest = TSYNC_TMSCL_DEST_ID;
+ it1->iid = TSYNC_TMSCL_IID;
+ it1->inPayloadOffset = TSYNC_TMSCL_IN_PYLD_OFF;
+ it1->inLength = TSYNC_TMSCL_IN_LEN;
+ it1->outPayloadOffset = TSYNC_TMSCL_OUT_PYLD_OFF;
+ it1->maxOutLength = TSYNC_TMSCL_MAX_OUT_LEN;
+ it1->actualOutLength = 0;
+ it1->status = 0;
+ memset(it1->payloads, 0, TSYNC_TMSCL_MAX_OUT_LEN);
+
+ /* Read the Time Scale info from the TSYNC-PCI device */
+ err1 = ioctl(hBoard.file_descriptor,
+ IOCTL_TSYNC_GET,
+ (char *)it1);
+
+ /* Allocate memory for ioctl message */
+ itAllocationLength2 =
+ (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
+ TSYNC_LEAP_IN_LEN + TSYNC_LEAP_MAX_OUT_LEN;
+
+ it2 = (ioctl_trans_di*)alloca(itAllocationLength2);
+ if (it2 == NULL) {
+ msyslog(LOG_ERR, "Couldn't allocate transaction memory - Leap Second");
+ return;
+ }
+
+ /* Build CS_GetLeapSec ioctl message */
+ it2->dest = TSYNC_LEAP_DEST_ID;
+ it2->iid = TSYNC_LEAP_IID;
+ it2->inPayloadOffset = TSYNC_LEAP_IN_PYLD_OFF;
+ it2->inLength = TSYNC_LEAP_IN_LEN;
+ it2->outPayloadOffset = TSYNC_LEAP_OUT_PYLD_OFF;
+ it2->maxOutLength = TSYNC_LEAP_MAX_OUT_LEN;
+ it2->actualOutLength = 0;
+ it2->status = 0;
+ memset(it2->payloads, 0, TSYNC_LEAP_MAX_OUT_LEN);
+
+ /* Read the leap seconds info from the TSYNC-PCI device */
+ err2 = ioctl(hBoard.file_descriptor,
+ IOCTL_TSYNC_GET,
+ (char *)it2);
+
+ pp = peer->procptr;
+ up = (TsyncUnit*)pp->unitptr;
+
+ /* Read the time from the TSYNC-PCI device */
+ err3 = ioctl(hBoard.file_descriptor,
+ IOCTL_TPRO_GET_NTP_TIME,
+ (char *)&TimeContext);
+
+ /* Close the TSYNC device */
+ close(hBoard.file_descriptor);
+
+ // Check for errors
+ if ((err < 0) ||(err1 < 0) || (err2 < 0) || (err3 < 0) ||
+ (it->status != 0) || (it1->status != 0) || (it2->status != 0) ||
+ (it->actualOutLength != TSYNC_REF_OUT_LEN) ||
+ (it1->actualOutLength != TSYNC_TMSCL_OUT_LEN) ||
+ (it2->actualOutLength != TSYNC_LEAP_OUT_LEN)) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+
+ // Extract reference identifiers from ioctl payload
+ memset(timeRef, '\0', sizeof(timeRef));
+ memset(ppsRef, '\0', sizeof(ppsRef));
+ pRefObj = (void *)it->payloads;
+ memcpy(timeRef, pRefObj->time, TSYNC_REF_LEN);
+ memcpy(ppsRef, pRefObj->pps, TSYNC_REF_LEN);
+
+ // Extract the Clock Service Time Scale and convert to correct byte order
+ memcpy(&tmscl, ((TIME_SCALE*)(it1->payloads)), sizeof(tmscl));
+ tmscl = ntohl(tmscl);
+
+ // Extract leap second info from ioctl payload and perform byte swapping
+ for (i = 0; i < (sizeof(leapSec) / 4); i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ ((unsigned char*)&leapSec)[(i * 4) + j] =
+ ((unsigned char*)(it2->payloads))[(i * 4) + (3 - j)];
+ }
+ }
+
+ // Determine time reference ID from reference name
+ for (i = 0; RefIdLookupTbl[i].pRef != NULL; i++)
+ {
+ // Search RefID table
+ if (strstr(timeRef, RefIdLookupTbl[i].pRef) != NULL)
+ {
+ // Found the matching string
+ break;
+ }
+ }
+
+ // Determine pps reference ID from reference name
+ for (j = 0; RefIdLookupTbl[j].pRef != NULL; j++)
+ {
+ // Search RefID table
+ if (strstr(ppsRef, RefIdLookupTbl[j].pRef) != NULL)
+ {
+ // Found the matching string
+ break;
+ }
+ }
+
+ // Determine synchronization state from flags
+ synch = (TimeContext.timeObj.flags == 0x4) ? 1 : 0;
+
+ // Pull seconds information from time object
+ seconds = (double) (TimeContext.timeObj.secsDouble);
+ seconds /= (double) 1000000.0;
+
+ /*
+ ** Convert the number of microseconds to double and then place in the
+ ** peer's last received long floating point format.
+ */
+ DTOLFP(((double)TimeContext.tv.tv_usec / 1000000.0), &pp->lastrec);
+
+ /*
+ ** The specTimeStamp is the number of seconds since 1/1/1970, while the
+ ** peer's lastrec time should be compatible with NTP which is seconds since
+ ** 1/1/1900. So Add the number of seconds between 1900 and 1970 to the
+ ** specTimeStamp and place in the peer's lastrec long floating point struct.
+ */
+ pp->lastrec.Ul_i.Xl_ui += (unsigned int)TimeContext.tv.tv_sec +
+ SECONDS_1900_TO_1970;
+
+ pp->polls++;
+
+ /*
+ ** set the reference clock object
+ */
+ sprintf(pp->a_lastcode, "%03d %02d:%02d:%02.6f",
+ TimeContext.timeObj.days, TimeContext.timeObj.hours,
+ TimeContext.timeObj.minutes, seconds);
+
+ pp->lencode = strlen (pp->a_lastcode);
+ pp->day = TimeContext.timeObj.days;
+ pp->hour = TimeContext.timeObj.hours;
+ pp->minute = TimeContext.timeObj.minutes;
+ pp->second = (int) seconds;
+ seconds = (seconds - (double) (pp->second / 1.0)) * 1000000000;
+ pp->nsec = (long) seconds;
+
+ /*
+ ** calculate year start
+ */
+ jt.year = TimeContext.timeObj.year;
+ jt.yearday = 1;
+ jt.monthday = 1;
+ jt.month = 1;
+ jt.hour = 0;
+ jt.minute = 0;
+ jt.second = 0;
+ pp->yearstart = caltontp(&jt);
+
+ // Calculate and report reference clock offset
+ offset.l_ui = (long)(((pp->day - 1) * 24) + pp->hour + GMT);
+ offset.l_ui = (offset.l_ui * 60) + (long)pp->minute;
+ offset.l_ui = (offset.l_ui * 60) + (long)pp->second;
+ offset.l_ui = offset.l_ui + (long)pp->yearstart;
+ offset.l_uf = 0;
+ DTOLFP(pp->nsec / 1e9, &ltemp);
+ L_ADD(&offset, &ltemp);
+ refclock_process_offset(pp, offset, pp->lastrec,
+ pp->fudgetime1);
+
+ // KTS in sync
+ if (synch) {
+ // Subtract leap second info by one second to determine effective day
+ ApplyTimeOffset(&(leapSec.utcDate), -1);
+
+ // If there is a leap second today and the KTS is using a time scale
+ // which handles leap seconds then
+ if ((tmscl != TIME_SCALE_GPS) && (tmscl != TIME_SCALE_TAI) &&
+ (leapSec.utcDate.year == (unsigned int)TimeContext.timeObj.year) &&
+ (leapSec.utcDate.doy == (unsigned int)TimeContext.timeObj.days))
+ {
+ // If adding a second
+ if (leapSec.offset == 1)
+ {
+ pp->leap = LEAP_ADDSECOND;
+ }
+ // Else if removing a second
+ else if (leapSec.offset == -1)
+ {
+ pp->leap = LEAP_DELSECOND;
+ }
+ // Else report no leap second pending (no handling of offsets
+ // other than +1 or -1)
+ else
+ {
+ pp->leap = LEAP_NOWARNING;
+ }
+ }
+ // Else report no leap second pending
+ else
+ {
+ pp->leap = LEAP_NOWARNING;
+ }
+
+ peer->leap = pp->leap;
+ refclock_report(peer, CEVNT_NOMINAL);
+
+ // If reference name reported, then not in holdover
+ if ((RefIdLookupTbl[i].pRef != NULL) &&
+ (RefIdLookupTbl[j].pRef != NULL))
+ {
+ // Determine if KTS being synchronized by host (identified as
+ // "LOCL")
+ if ((strcmp(RefIdLookupTbl[i].pRefId, TSYNC_REF_LOCAL) == 0) ||
+ (strcmp(RefIdLookupTbl[j].pRefId, TSYNC_REF_LOCAL) == 0))
+ {
+ // Clear prefer flag
+ peer->flags &= ~FLAG_PREFER;
+
+ // Set reference clock stratum level as unusable
+ pp->stratum = STRATUM_UNSPEC;
+ peer->stratum = pp->stratum;
+
+ // If a valid peer is available
+ if ((sys_peer != NULL) && (sys_peer != peer))
+ {
+ // Store reference peer stratum level and ID
+ up->refStratum = sys_peer->stratum;
+ up->refId = addr2refid(&sys_peer->srcadr);
+ }
+ }
+ else
+ {
+ // Restore prefer flag
+ peer->flags |= up->refPrefer;
+
+ // Store reference stratum as local clock
+ up->refStratum = TSYNC_LCL_STRATUM;
+ strncpy((char *)&up->refId, RefIdLookupTbl[j].pRefId,
+ TSYNC_REF_LEN);
+
+ // Set reference clock stratum level as local clock
+ pp->stratum = TSYNC_LCL_STRATUM;
+ peer->stratum = pp->stratum;
+ }
+
+ // Update reference name
+ strncpy((char *)&pp->refid, RefIdLookupTbl[j].pRefId,
+ TSYNC_REF_LEN);
+ peer->refid = pp->refid;
+ }
+ // Else in holdover
+ else
+ {
+ // Restore prefer flag
+ peer->flags |= up->refPrefer;
+
+ // Update reference ID to saved ID
+ pp->refid = up->refId;
+ peer->refid = pp->refid;
+
+ // Update stratum level to saved stratum level
+ pp->stratum = up->refStratum;
+ peer->stratum = pp->stratum;
+ }
+ }
+ // Else KTS not in sync
+ else {
+ // Place local identifier in peer RefID
+ strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
+ peer->refid = pp->refid;
+
+ // Report not in sync
+ pp->leap = LEAP_NOTINSYNC;
+ peer->leap = pp->leap;
+ }
+
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+
+ /* Increment the number of times the reference has been polled */
+ pp->polls++;
+
+} /* End - tsync_poll() */
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Function: ApplyTimeOffset
+// Description: The ApplyTimeOffset function adds an offset (in seconds) to a
+// specified date and time. The specified date and time is passed
+// back after being modified.
+//
+// Assumptions: 1. Every fourth year is a leap year. Therefore, this function
+// is only accurate through Feb 28, 2100.
+////////////////////////////////////////////////////////////////////////////////
+void ApplyTimeOffset(DoyTimeObj* pDt, int off)
+{
+ SecTimeObj st; // Time, in seconds
+
+
+ // Convert date and time to seconds
+ SecTimeFromDoyTime(&st, pDt);
+
+ // Apply offset
+ st.seconds = (int)((signed long long)st.seconds + (signed long long)off);
+
+ // Convert seconds to date and time
+ DoyTimeFromSecTime(pDt, &st);
+
+} // End ApplyTimeOffset
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Function: SecTimeFromDoyTime
+// Description: The SecTimeFromDoyTime function converts a specified date
+// and time into a count of seconds since the base time. This
+// function operates across the range Base Time to Max Time for
+// the system.
+//
+// Assumptions: 1. A leap year is any year evenly divisible by 4. Therefore,
+// this function is only accurate through Feb 28, 2100.
+// 2. Conversion does not account for leap seconds.
+////////////////////////////////////////////////////////////////////////////////
+void SecTimeFromDoyTime(SecTimeObj* pSt, DoyTimeObj* pDt)
+{
+ unsigned int yrs; // Years
+ unsigned int lyrs; // Leap years
+
+
+ // Start with accumulated time of 0
+ pSt->seconds = 0;
+
+ // Calculate the number of years and leap years
+ yrs = pDt->year - TSYNC_TIME_BASE_YEAR;
+ lyrs = (yrs + 1) / 4;
+
+ // Convert leap years and years
+ pSt->seconds += lyrs * SECSPERLEAPYEAR;
+ pSt->seconds += (yrs - lyrs) * SECSPERYEAR;
+
+ // Convert days, hours, minutes and seconds
+ pSt->seconds += (pDt->doy - 1) * SECSPERDAY;
+ pSt->seconds += pDt->hour * SECSPERHR;
+ pSt->seconds += pDt->minute * SECSPERMIN;
+ pSt->seconds += pDt->second;
+
+ // Copy the subseconds count
+ pSt->ns = pDt->ns;
+
+} // End SecTimeFromDoyTime
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Function: DoyTimeFromSecTime
+// Description: The DoyTimeFromSecTime function converts a specified count
+// of seconds since the start of our base time into a SecTimeObj
+// structure.
+//
+// Assumptions: 1. A leap year is any year evenly divisible by 4. Therefore,
+// this function is only accurate through Feb 28, 2100.
+// 2. Conversion does not account for leap seconds.
+////////////////////////////////////////////////////////////////////////////////
+void DoyTimeFromSecTime(DoyTimeObj* pDt, SecTimeObj* pSt)
+{
+ signed long long secs; // Seconds accumulator variable
+ unsigned int yrs; // Years accumulator variable
+ unsigned int doys; // Days accumulator variable
+ unsigned int hrs; // Hours accumulator variable
+ unsigned int mins; // Minutes accumulator variable
+
+
+ // Convert the seconds count into a signed 64-bit number for calculations
+ secs = (signed long long)(pSt->seconds);
+
+ // Calculate the number of 4 year chunks
+ yrs = (unsigned int)((secs /
+ ((SECSPERYEAR * 3) + SECSPERLEAPYEAR)) * 4);
+ secs %= ((SECSPERYEAR * 3) + SECSPERLEAPYEAR);
+
+ // If there is at least a normal year worth of time left
+ if (secs >= SECSPERYEAR)
+ {
+ // Increment the number of years and subtract a normal year of time
+ yrs++;
+ secs -= SECSPERYEAR;
+ }
+
+ // If there is still at least a normal year worth of time left
+ if (secs >= SECSPERYEAR)
+ {
+ // Increment the number of years and subtract a normal year of time
+ yrs++;
+ secs -= SECSPERYEAR;
+ }
+
+ // If there is still at least a leap year worth of time left
+ if (secs >= SECSPERLEAPYEAR)
+ {
+ // Increment the number of years and subtract a leap year of time
+ yrs++;
+ secs -= SECSPERLEAPYEAR;
+ }
+
+ // Calculate the day of year as the number of days left, then add 1
+ // because months start on the 1st.
+ doys = (unsigned int)((secs / SECSPERDAY) + 1);
+ secs %= SECSPERDAY;
+
+ // Calculate the hour
+ hrs = (unsigned int)(secs / SECSPERHR);
+ secs %= SECSPERHR;
+
+ // Calculate the minute
+ mins = (unsigned int)(secs / SECSPERMIN);
+ secs %= SECSPERMIN;
+
+ // Fill in the doytime structure
+ pDt->year = yrs + TSYNC_TIME_BASE_YEAR;
+ pDt->doy = doys;
+ pDt->hour = hrs;
+ pDt->minute = mins;
+ pDt->second = (unsigned int)secs;
+ pDt->ns = pSt->ns;
+
+} // End DoyTimeFromSecTime
+
+#else
+int refclock_tsyncpci_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_tt560.c b/ntpd/refclock_tt560.c
new file mode 100644
index 0000000..171ba5c
--- /dev/null
+++ b/ntpd/refclock_tt560.c
@@ -0,0 +1,270 @@
+/*
+ * refclock_tt560 - clock driver for the TrueTime 560 IRIG-B decoder
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_TT560)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "sys/tt560_api.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the TrueTime 560 IRIG-B decoder for the PCI bus.
+ */
+
+/*
+ * TT560 interface definitions
+ */
+#define DEVICE "/dev/tt560%d" /* device name and unit */
+#define PRECISION (-20) /* precision assumed (1 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "TrueTime 560 IRIG-B PCI Decoder"
+
+/*
+ * Unit control structure
+ */
+struct tt560unit {
+ tt_mem_space_t *tt_mem; /* mapped address of PCI board */
+ time_freeze_reg_t tt560rawt; /* data returned from PCI board */
+};
+
+typedef union byteswap_u
+{
+ unsigned int long_word;
+ unsigned char byte[4];
+} byteswap_t;
+
+/*
+ * Function prototypes
+ */
+static int tt560_start (int, struct peer *);
+static void tt560_shutdown (int, struct peer *);
+static void tt560_poll (int unit, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_tt560 = {
+ tt560_start, /* clock_start */
+ tt560_shutdown, /* clock_shutdown */
+ tt560_poll, /* clock_poll */
+ noentry, /* clock_control (not used) */
+ noentry, /* clock_init (not used) */
+ noentry, /* clock_buginfo (not used) */
+ NOFLAGS /* clock_flags (not used) */
+};
+
+
+/*
+ * tt560_start - open the TT560 device and initialize data for processing
+ */
+static int
+tt560_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tt560unit *up;
+ struct refclockproc *pp;
+ char device[20];
+ int fd;
+ caddr_t membase;
+
+ /*
+ * Open TT560 device
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+ fd = open(device, O_RDWR);
+ if (fd == -1) {
+ msyslog(LOG_ERR, "tt560_start: open of %s: %m", device);
+ return (0);
+ }
+
+ /*
+ * Map the device registers into user space.
+ */
+ membase = mmap ((caddr_t) 0, TTIME_MEMORY_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, (off_t)0);
+
+ if (membase == (caddr_t) -1) {
+ msyslog(LOG_ERR, "tt560_start: mapping of %s: %m", device);
+ (void) close(fd);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct tt560unit *) emalloc(sizeof(struct tt560unit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct tt560unit));
+ up->tt_mem = (tt_mem_space_t *)membase;
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous peer variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * tt560_shutdown - shut down the clock
+ */
+static void
+tt560_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tt560unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct tt560unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * tt560_poll - called by the transmit procedure
+ */
+static void
+tt560_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tt560unit *up;
+ struct refclockproc *pp;
+ time_freeze_reg_t *tp;
+ tt_mem_space_t *mp;
+
+ int i;
+ unsigned int *p_time_t, *tt_mem_t;
+
+ /*
+ * This is the main routine. It snatches the time from the TT560
+ * board and tacks on a local timestamp.
+ */
+ pp = peer->procptr;
+ up = (struct tt560unit *)pp->unitptr;
+ mp = up->tt_mem;
+ tp = &up->tt560rawt;
+
+ p_time_t = (unsigned int *)tp;
+ tt_mem_t = (unsigned int *)&mp->time_freeze_reg;
+
+ *tt_mem_t = 0; /* update the time freeze register */
+ /* and copy time stamp to memory */
+ for (i=0; i < TIME_FREEZE_REG_LEN; i++) {
+ *p_time_t = byte_swap(*tt_mem_t);
+ p_time_t++;
+ tt_mem_t++;
+ }
+
+ get_systime(&pp->lastrec);
+ pp->polls++;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit. Note: we
+ * can't use the sec/usec conversion produced by the driver,
+ * since the year may be suspect. All format error checking is
+ * done by the snprintf() and sscanf() routines.
+ */
+ snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
+ "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x",
+ tp->hun_day, tp->tens_day, tp->unit_day,
+ tp->tens_hour, tp->unit_hour,
+ tp->tens_min, tp->unit_min,
+ tp->tens_sec, tp->unit_sec,
+ tp->hun_ms, tp->tens_ms, tp->unit_ms,
+ tp->hun_us, tp->tens_us, tp->unit_us,
+ tp->status);
+ pp->lencode = strlen(pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("tt560: time %s timecode %d %s\n",
+ ulfptoa(&pp->lastrec, 6), pp->lencode,
+ pp->a_lastcode);
+#endif
+ if (sscanf(pp->a_lastcode, "%3d %2d:%2d:%2d.%6ld",
+ &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->usec)
+ != 5) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if ((tp->status & 0x6) != 0x6)
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+}
+
+/******************************************************************
+ *
+ * byte_swap
+ *
+ * Inputs: 32 bit integer
+ *
+ * Output: byte swapped 32 bit integer.
+ *
+ * This routine is used to compensate for the byte alignment
+ * differences between big-endian and little-endian integers.
+ *
+ ******************************************************************/
+static unsigned int
+byte_swap(unsigned int input_num)
+{
+ byteswap_t byte_swap;
+ unsigned char temp;
+
+ byte_swap.long_word = input_num;
+
+ temp = byte_swap.byte[3];
+ byte_swap.byte[3] = byte_swap.byte[0];
+ byte_swap.byte[0] = temp;
+
+ temp = byte_swap.byte[2];
+ byte_swap.byte[2] = byte_swap.byte[1];
+ byte_swap.byte[1] = temp;
+
+ return (byte_swap.long_word);
+}
+
+#else
+int refclock_tt560_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_ulink.c b/ntpd/refclock_ulink.c
new file mode 100644
index 0000000..d8f24a5
--- /dev/null
+++ b/ntpd/refclock_ulink.c
@@ -0,0 +1,568 @@
+/*
+ * refclock_ulink - clock driver for Ultralink WWVB receiver
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ULINK)
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/* This driver supports ultralink Model 320,325,330,331,332 WWVB radios
+ *
+ * this driver was based on the refclock_wwvb.c driver
+ * in the ntp distribution.
+ *
+ * Fudge Factors
+ *
+ * fudge flag1 0 don't poll clock
+ * 1 send poll character
+ *
+ * revision history:
+ * 99/9/09 j.c.lang original edit's
+ * 99/9/11 j.c.lang changed timecode parse to
+ * match what the radio actually
+ * sends.
+ * 99/10/11 j.c.lang added support for continous
+ * time code mode (dipsw2)
+ * 99/11/26 j.c.lang added support for 320 decoder
+ * (taken from Dave Strout's
+ * Model 320 driver)
+ * 99/11/29 j.c.lang added fudge flag 1 to control
+ * clock polling
+ * 99/12/15 j.c.lang fixed 320 quality flag
+ * 01/02/21 s.l.smith fixed 33x quality flag
+ * added more debugging stuff
+ * updated 33x time code explanation
+ * 04/01/23 frank migge added support for 325 decoder
+ * (tested with ULM325.F)
+ *
+ * Questions, bugs, ideas send to:
+ * Joseph C. Lang
+ * tcnojl1@earthlink.net
+ *
+ * Dave Strout
+ * dstrout@linuxfoundry.com
+ *
+ * Frank Migge
+ * frank.migge@oracle.com
+ *
+ *
+ * on the Ultralink model 33X decoder Dip switch 2 controls
+ * polled or continous timecode
+ * set fudge flag1 if using polled (needed for model 320 and 325)
+ * dont set fudge flag1 if dip switch 2 is set on model 33x decoder
+*/
+
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/wwvb%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 10 ms) */
+#define REFID "WWVB" /* reference ID */
+#define DESCRIPTION "Ultralink WWVB Receiver" /* WRU */
+
+#define LEN33X 32 /* timecode length Model 33X and 325 */
+#define LEN320 24 /* timecode length Model 320 */
+
+#define SIGLCHAR33x 'S' /* signal strength identifier char 325 */
+#define SIGLCHAR325 'R' /* signal strength identifier char 33x */
+
+/*
+ * unit control structure
+ */
+struct ulinkunit {
+ u_char tcswitch; /* timecode switch */
+ l_fp laststamp; /* last receive timestamp */
+};
+
+/*
+ * Function prototypes
+ */
+static int ulink_start (int, struct peer *);
+static void ulink_shutdown (int, struct peer *);
+static void ulink_receive (struct recvbuf *);
+static void ulink_poll (int, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_ulink = {
+ ulink_start, /* start up driver */
+ ulink_shutdown, /* shut down driver */
+ ulink_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS
+};
+
+
+/*
+ * ulink_start - open the devices and initialize data for processing
+ */
+static int
+ulink_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct ulinkunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+ fd = refclock_open(device, SPEED232, LDISC_CLK);
+ if (fd <= 0)
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc(sizeof(struct ulinkunit));
+ memset(up, 0, sizeof(struct ulinkunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = ulink_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ return (0);
+ }
+ pp->unitptr = up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * ulink_shutdown - shut down the clock
+ */
+static void
+ulink_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct ulinkunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (pp->io.fd != -1)
+ io_closeclock(&pp->io);
+ if (up != NULL)
+ free(up);
+}
+
+
+/*
+ * ulink_receive - receive data from the serial interface
+ */
+static void
+ulink_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct ulinkunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ l_fp trtmp; /* arrival timestamp */
+ int quality = INT_MAX; /* quality indicator */
+ int temp; /* int temp */
+ char syncchar; /* synchronization indicator */
+ char leapchar; /* leap indicator */
+ char modechar; /* model 320 mode flag */
+ char siglchar; /* model difference between 33x/325 */
+ char char_quality[2]; /* temp quality flag */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ /*
+ * Note we get a buffer and timestamp for both a <cr> and <lf>,
+ * but only the <cr> timestamp is retained.
+ */
+ if (temp == 0) {
+ if (up->tcswitch == 0) {
+ up->tcswitch = 1;
+ up->laststamp = trtmp;
+ } else
+ up->tcswitch = 0;
+ return;
+ }
+ pp->lencode = temp;
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ up->tcswitch = 1;
+#ifdef DEBUG
+ if (debug)
+ printf("ulink: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ syncchar = leapchar = modechar = siglchar = ' ';
+ switch (pp->lencode ) {
+ case LEN33X:
+
+ /*
+ * First we check if the format is 33x or 325:
+ * <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5 (33x)
+ * <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5 (325)
+ * simply by comparing if the signal level is 'S' or 'R'
+ */
+
+ if (sscanf(pp->a_lastcode, "%c%*31c",
+ &siglchar) == 1) {
+
+ if(siglchar == SIGLCHAR325) {
+
+ /*
+ * decode for a Model 325 decoder.
+ * Timecode format from January 23, 2004 datasheet is:
+ *
+ * <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5
+ *
+ * R WWVB decodersignal readability R1 - R5
+ * 5 R1 is unreadable, R5 is best
+ * space a space (0x20)
+ * 1 Data bit 0, 1, M (pos mark), or ? (unknown).
+ * C Reception from either (C)olorado or (H)awaii
+ * 00 Hours since last good WWVB frame sync. Will
+ * be 00-99
+ * space Space char (0x20) or (0xa5) if locked to wwvb
+ * YYYY Current year, 2000-2099
+ * + Leap year indicator. '+' if a leap year,
+ * a space (0x20) if not.
+ * DDD Day of year, 000 - 365.
+ * UTC Timezone (always 'UTC').
+ * S Daylight savings indicator
+ * S - standard time (STD) in effect
+ * O - during STD to DST day 0000-2400
+ * D - daylight savings time (DST) in effect
+ * I - during DST to STD day 0000-2400
+ * space Space character (0x20)
+ * HH Hours 00-23
+ * : This is the REAL in sync indicator (: = insync)
+ * MM Minutes 00-59
+ * : : = in sync ? = NOT in sync
+ * SS Seconds 00-59
+ * L Leap second flag. Changes from space (0x20)
+ * to 'I' or 'D' during month preceding leap
+ * second adjustment. (I)nsert or (D)elete
+ * +5 UT1 correction (sign + digit ))
+ */
+
+ if (sscanf(pp->a_lastcode,
+ "%*2c %*2c%2c%*c%4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
+ char_quality, &pp->year, &pp->day,
+ &pp->hour, &syncchar, &pp->minute, &pp->second,
+ &leapchar) == 8) {
+
+ if (char_quality[0] == '0') {
+ quality = 0;
+ } else if (char_quality[0] == '0') {
+ quality = (char_quality[1] & 0x0f);
+ } else {
+ quality = 99;
+ }
+
+ if (leapchar == 'I' ) leapchar = '+';
+ if (leapchar == 'D' ) leapchar = '-';
+
+ /*
+ #ifdef DEBUG
+ if (debug) {
+ printf("ulink: char_quality %c %c\n",
+ char_quality[0], char_quality[1]);
+ printf("ulink: quality %d\n", quality);
+ printf("ulink: syncchar %x\n", syncchar);
+ printf("ulink: leapchar %x\n", leapchar);
+ }
+ #endif
+ */
+
+ }
+
+ }
+ if(siglchar == SIGLCHAR33x) {
+
+ /*
+ * We got a Model 33X decoder.
+ * Timecode format from January 29, 2001 datasheet is:
+ * <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5
+ * S WWVB decoder sync indicator. S for in-sync(?)
+ * or N for noisy signal.
+ * 9+ RF signal level in S-units, 0-9 followed by
+ * a space (0x20). The space turns to '+' if the
+ * level is over 9.
+ * D Data bit 0, 1, 2 (position mark), or
+ * 3 (unknown).
+ * space Space character (0x20)
+ * 00 Hours since last good WWVB frame sync. Will
+ * be 00-23 hrs, or '1d' to '7d'. Will be 'Lk'
+ * if currently in sync.
+ * space Space character (0x20)
+ * YYYY Current year, 1990-2089
+ * + Leap year indicator. '+' if a leap year,
+ * a space (0x20) if not.
+ * DDD Day of year, 001 - 366.
+ * UTC Timezone (always 'UTC').
+ * S Daylight savings indicator
+ * S - standard time (STD) in effect
+ * O - during STD to DST day 0000-2400
+ * D - daylight savings time (DST) in effect
+ * I - during DST to STD day 0000-2400
+ * space Space character (0x20)
+ * HH Hours 00-23
+ * : This is the REAL in sync indicator (: = insync)
+ * MM Minutes 00-59
+ * : : = in sync ? = NOT in sync
+ * SS Seconds 00-59
+ * L Leap second flag. Changes from space (0x20)
+ * to '+' or '-' during month preceding leap
+ * second adjustment.
+ * +5 UT1 correction (sign + digit ))
+ */
+
+ if (sscanf(pp->a_lastcode,
+ "%*4c %2c %4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
+ char_quality, &pp->year, &pp->day,
+ &pp->hour, &syncchar, &pp->minute, &pp->second,
+ &leapchar) == 8) {
+
+ if (char_quality[0] == 'L') {
+ quality = 0;
+ } else if (char_quality[0] == '0') {
+ quality = (char_quality[1] & 0x0f);
+ } else {
+ quality = 99;
+ }
+
+ /*
+ #ifdef DEBUG
+ if (debug) {
+ printf("ulink: char_quality %c %c\n",
+ char_quality[0], char_quality[1]);
+ printf("ulink: quality %d\n", quality);
+ printf("ulink: syncchar %x\n", syncchar);
+ printf("ulink: leapchar %x\n", leapchar);
+ }
+ #endif
+ */
+
+ }
+ }
+ break;
+ }
+
+ case LEN320:
+
+ /*
+ * Model 320 Decoder
+ * The timecode format is:
+ *
+ * <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr>
+ *
+ * where:
+ *
+ * S = 'S' -- sync'd in last hour,
+ * '0'-'9' - hours x 10 since last update,
+ * '?' -- not in sync
+ * Q = Number of correlating time-frames, from 0 to 5
+ * R = 'R' -- reception in progress,
+ * 'N' -- Noisy reception,
+ * ' ' -- standby mode
+ * YYYY = year from 1990 to 2089
+ * DDD = current day from 1 to 366
+ * + = '+' if current year is a leap year, else ' '
+ * HH = UTC hour 0 to 23
+ * MM = Minutes of current hour from 0 to 59
+ * SS = Seconds of current minute from 0 to 59
+ * mm = 10's milliseconds of the current second from 00 to 99
+ * L = Leap second pending at end of month
+ * 'I' = insert, 'D'= delete
+ * T = DST <-> STD transition indicators
+ *
+ */
+
+ if (sscanf(pp->a_lastcode, "%c%1d%c%4d%3d%*c%2d:%2d:%2d.%2ld%c",
+ &syncchar, &quality, &modechar, &pp->year, &pp->day,
+ &pp->hour, &pp->minute, &pp->second,
+ &pp->nsec, &leapchar) == 10) {
+ pp->nsec *= 10000000; /* M320 returns 10's of msecs */
+ if (leapchar == 'I' ) leapchar = '+';
+ if (leapchar == 'D' ) leapchar = '-';
+ if (syncchar != '?' ) syncchar = ':';
+
+ break;
+ }
+
+ default:
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Decode quality indicator
+ * For the 325 & 33x series, the lower the number the "better"
+ * the time is. I used the dispersion as the measure of time
+ * quality. The quality indicator in the 320 is the number of
+ * correlating time frames (the more the better)
+ */
+
+ /*
+ * The spec sheet for the 325 & 33x series states the clock will
+ * maintain +/-0.002 seconds accuracy when locked to WWVB. This
+ * is indicated by 'Lk' in the quality portion of the incoming
+ * string. When not in lock, a drift of +/-0.015 seconds should
+ * be allowed for.
+ * With the quality indicator decoding scheme above, the 'Lk'
+ * condition will produce a quality value of 0. If the quality
+ * indicator starts with '0' then the second character is the
+ * number of hours since we were last locked. If the first
+ * character is anything other than 'L' or '0' then we have been
+ * out of lock for more than 9 hours so we assume the worst and
+ * force a quality value that selects the 'default' maximum
+ * dispersion. The dispersion values below are what came with the
+ * driver. They're not unreasonable so they've not been changed.
+ */
+
+ if (pp->lencode == LEN33X) {
+ switch (quality) {
+ case 0 :
+ pp->disp=.002;
+ break;
+ case 1 :
+ pp->disp=.02;
+ break;
+ case 2 :
+ pp->disp=.04;
+ break;
+ case 3 :
+ pp->disp=.08;
+ break;
+ default:
+ pp->disp=MAXDISPERSE;
+ break;
+ }
+ } else {
+ switch (quality) {
+ case 5 :
+ pp->disp=.002;
+ break;
+ case 4 :
+ pp->disp=.02;
+ break;
+ case 3 :
+ pp->disp=.04;
+ break;
+ case 2 :
+ pp->disp=.08;
+ break;
+ case 1 :
+ pp->disp=.16;
+ break;
+ default:
+ pp->disp=MAXDISPERSE;
+ break;
+ }
+
+ }
+
+ /*
+ * Decode synchronization, and leap characters. If
+ * unsynchronized, set the leap bits accordingly and exit.
+ * Otherwise, set the leap bits according to the leap character.
+ */
+
+ if (syncchar != ':')
+ pp->leap = LEAP_NOTINSYNC;
+ else if (leapchar == '+')
+ pp->leap = LEAP_ADDSECOND;
+ else if (leapchar == '-')
+ pp->leap = LEAP_DELSECOND;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ }
+
+}
+
+/*
+ * ulink_poll - called by the transmit procedure
+ */
+
+static void
+ulink_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ char pollchar;
+
+ pp = peer->procptr;
+ pollchar = 'T';
+ if (pp->sloppyclockflag & CLK_FLAG1) {
+ if (write(pp->io.fd, &pollchar, 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+ }
+ else
+ pp->polls++;
+
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+}
+
+#else
+int refclock_ulink_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_wwv.c b/ntpd/refclock_wwv.c
new file mode 100644
index 0000000..14183b4
--- /dev/null
+++ b/ntpd/refclock_wwv.c
@@ -0,0 +1,2709 @@
+/*
+ * refclock_wwv - clock driver for NIST WWV/H time/frequency station
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_WWV)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+#include "audio.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#define ICOM 1
+
+#ifdef ICOM
+#include "icom.h"
+#endif /* ICOM */
+
+/*
+ * Audio WWV/H demodulator/decoder
+ *
+ * This driver synchronizes the computer time using data encoded in
+ * radio transmissions from NIST time/frequency stations WWV in Boulder,
+ * CO, and WWVH in Kauai, HI. Transmissions are made continuously on
+ * 2.5, 5, 10 and 15 MHz from WWV and WWVH, and 20 MHz from WWV. An
+ * ordinary AM shortwave receiver can be tuned manually to one of these
+ * frequencies or, in the case of ICOM receivers, the receiver can be
+ * tuned automatically using this program as propagation conditions
+ * change throughout the weasons, both day and night.
+ *
+ * The driver requires an audio codec or sound card with sampling rate 8
+ * kHz and mu-law companding. This is the same standard as used by the
+ * telephone industry and is supported by most hardware and operating
+ * systems, including Solaris, SunOS, FreeBSD, NetBSD and Linux. In this
+ * implementation, only one audio driver and codec can be supported on a
+ * single machine.
+ *
+ * The demodulation and decoding algorithms used in this driver are
+ * based on those developed for the TAPR DSP93 development board and the
+ * TI 320C25 digital signal processor described in: Mills, D.L. A
+ * precision radio clock for WWV transmissions. Electrical Engineering
+ * Report 97-8-1, University of Delaware, August 1997, 25 pp., available
+ * from www.eecis.udel.edu/~mills/reports.html. The algorithms described
+ * in this report have been modified somewhat to improve performance
+ * under weak signal conditions and to provide an automatic station
+ * identification feature.
+ *
+ * The ICOM code is normally compiled in the driver. It isn't used,
+ * unless the mode keyword on the server configuration command specifies
+ * a nonzero ICOM ID select code. The C-IV trace is turned on if the
+ * debug level is greater than one.
+ *
+ * Fudge factors
+ *
+ * Fudge flag4 causes the debugging output described above to be
+ * recorded in the clockstats file. Fudge flag2 selects the audio input
+ * port, where 0 is the mike port (default) and 1 is the line-in port.
+ * It does not seem useful to select the compact disc player port. Fudge
+ * flag3 enables audio monitoring of the input signal. For this purpose,
+ * the monitor gain is set to a default value.
+ *
+ * CEVNT_BADTIME invalid date or time
+ * CEVNT_PROP propagation failure - no stations heard
+ * CEVNT_TIMEOUT timeout (see newgame() below)
+ */
+/*
+ * General definitions. These ordinarily do not need to be changed.
+ */
+#define DEVICE_AUDIO "/dev/audio" /* audio device name */
+#define AUDIO_BUFSIZ 320 /* audio buffer size (50 ms) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define DESCRIPTION "WWV/H Audio Demodulator/Decoder" /* WRU */
+#define WWV_SEC 8000 /* second epoch (sample rate) (Hz) */
+#define WWV_MIN (WWV_SEC * 60) /* minute epoch */
+#define OFFSET 128 /* companded sample offset */
+#define SIZE 256 /* decompanding table size */
+#define MAXAMP 6000. /* max signal level reference */
+#define MAXCLP 100 /* max clips above reference per s */
+#define MAXSNR 40. /* max SNR reference */
+#define MAXFREQ 1.5 /* max frequency tolerance (187 PPM) */
+#define DATCYC 170 /* data filter cycles */
+#define DATSIZ (DATCYC * MS) /* data filter size */
+#define SYNCYC 800 /* minute filter cycles */
+#define SYNSIZ (SYNCYC * MS) /* minute filter size */
+#define TCKCYC 5 /* tick filter cycles */
+#define TCKSIZ (TCKCYC * MS) /* tick filter size */
+#define NCHAN 5 /* number of radio channels */
+#define AUDIO_PHI 5e-6 /* dispersion growth factor */
+#define TBUF 128 /* max monitor line length */
+
+/*
+ * Tunable parameters. The DGAIN parameter can be changed to fit the
+ * audio response of the radio at 100 Hz. The WWV/WWVH data subcarrier
+ * is transmitted at about 20 percent percent modulation; the matched
+ * filter boosts it by a factor of 17 and the receiver response does
+ * what it does. The compromise value works for ICOM radios. If the
+ * radio is not tunable, the DCHAN parameter can be changed to fit the
+ * expected best propagation frequency: higher if further from the
+ * transmitter, lower if nearer. The compromise value works for the US
+ * right coast.
+ */
+#define DCHAN 3 /* default radio channel (15 Mhz) */
+#define DGAIN 5. /* subcarrier gain */
+
+/*
+ * General purpose status bits (status)
+ *
+ * SELV and/or SELH are set when WWV or WWVH have been heard and cleared
+ * on signal loss. SSYNC is set when the second sync pulse has been
+ * acquired and cleared by signal loss. MSYNC is set when the minute
+ * sync pulse has been acquired. DSYNC is set when the units digit has
+ * has reached the threshold and INSYNC is set when all nine digits have
+ * reached the threshold. The MSYNC, DSYNC and INSYNC bits are cleared
+ * only by timeout, upon which the driver starts over from scratch.
+ *
+ * DGATE is lit if the data bit amplitude or SNR is below thresholds and
+ * BGATE is lit if the pulse width amplitude or SNR is below thresolds.
+ * LEPSEC is set during the last minute of the leap day. At the end of
+ * this minute the driver inserts second 60 in the seconds state machine
+ * and the minute sync slips a second.
+ */
+#define MSYNC 0x0001 /* minute epoch sync */
+#define SSYNC 0x0002 /* second epoch sync */
+#define DSYNC 0x0004 /* minute units sync */
+#define INSYNC 0x0008 /* clock synchronized */
+#define FGATE 0x0010 /* frequency gate */
+#define DGATE 0x0020 /* data pulse amplitude error */
+#define BGATE 0x0040 /* data pulse width error */
+#define METRIC 0x0080 /* one or more stations heard */
+#define LEPSEC 0x1000 /* leap minute */
+
+/*
+ * Station scoreboard bits
+ *
+ * These are used to establish the signal quality for each of the five
+ * frequencies and two stations.
+ */
+#define SELV 0x0100 /* WWV station select */
+#define SELH 0x0200 /* WWVH station select */
+
+/*
+ * Alarm status bits (alarm)
+ *
+ * These bits indicate various alarm conditions, which are decoded to
+ * form the quality character included in the timecode.
+ */
+#define CMPERR 0x1 /* digit or misc bit compare error */
+#define LOWERR 0x2 /* low bit or digit amplitude or SNR */
+#define NINERR 0x4 /* less than nine digits in minute */
+#define SYNERR 0x8 /* not tracking second sync */
+
+/*
+ * Watchcat timeouts (watch)
+ *
+ * If these timeouts expire, the status bits are mashed to zero and the
+ * driver starts from scratch. Suitably more refined procedures may be
+ * developed in future. All these are in minutes.
+ */
+#define ACQSN 6 /* station acquisition timeout */
+#define DATA 15 /* unit minutes timeout */
+#define SYNCH 40 /* station sync timeout */
+#define PANIC (2 * 1440) /* panic timeout */
+
+/*
+ * Thresholds. These establish the minimum signal level, minimum SNR and
+ * maximum jitter thresholds which establish the error and false alarm
+ * rates of the driver. The values defined here may be on the
+ * adventurous side in the interest of the highest sensitivity.
+ */
+#define MTHR 13. /* minute sync gate (percent) */
+#define TTHR 50. /* minute sync threshold (percent) */
+#define AWND 20 /* minute sync jitter threshold (ms) */
+#define ATHR 2500. /* QRZ minute sync threshold */
+#define ASNR 20. /* QRZ minute sync SNR threshold (dB) */
+#define QTHR 2500. /* QSY minute sync threshold */
+#define QSNR 20. /* QSY minute sync SNR threshold (dB) */
+#define STHR 2500. /* second sync threshold */
+#define SSNR 15. /* second sync SNR threshold (dB) */
+#define SCMP 10 /* second sync compare threshold */
+#define DTHR 1000. /* bit threshold */
+#define DSNR 10. /* bit SNR threshold (dB) */
+#define AMIN 3 /* min bit count */
+#define AMAX 6 /* max bit count */
+#define BTHR 1000. /* digit threshold */
+#define BSNR 3. /* digit likelihood threshold (dB) */
+#define BCMP 3 /* digit compare threshold */
+#define MAXERR 40 /* maximum error alarm */
+
+/*
+ * Tone frequency definitions. The increments are for 4.5-deg sine
+ * table.
+ */
+#define MS (WWV_SEC / 1000) /* samples per millisecond */
+#define IN100 ((100 * 80) / WWV_SEC) /* 100 Hz increment */
+#define IN1000 ((1000 * 80) / WWV_SEC) /* 1000 Hz increment */
+#define IN1200 ((1200 * 80) / WWV_SEC) /* 1200 Hz increment */
+
+/*
+ * Acquisition and tracking time constants
+ */
+#define MINAVG 8 /* min averaging time */
+#define MAXAVG 1024 /* max averaging time */
+#define FCONST 3 /* frequency time constant */
+#define TCONST 16 /* data bit/digit time constant */
+
+/*
+ * Miscellaneous status bits (misc)
+ *
+ * These bits correspond to designated bits in the WWV/H timecode. The
+ * bit probabilities are exponentially averaged over several minutes and
+ * processed by a integrator and threshold.
+ */
+#define DUT1 0x01 /* 56 DUT .1 */
+#define DUT2 0x02 /* 57 DUT .2 */
+#define DUT4 0x04 /* 58 DUT .4 */
+#define DUTS 0x08 /* 50 DUT sign */
+#define DST1 0x10 /* 55 DST1 leap warning */
+#define DST2 0x20 /* 2 DST2 DST1 delayed one day */
+#define SECWAR 0x40 /* 3 leap second warning */
+
+/*
+ * The on-time synchronization point is the positive-going zero crossing
+ * of the first cycle of the 5-ms second pulse. The IIR baseband filter
+ * phase delay is 0.91 ms, while the receiver delay is approximately 4.7
+ * ms at 1000 Hz. The fudge value -0.45 ms due to the codec and other
+ * causes was determined by calibrating to a PPS signal from a GPS
+ * receiver. The additional propagation delay specific to each receiver
+ * location can be programmed in the fudge time1 and time2 values for
+ * WWV and WWVH, respectively.
+ *
+ * The resulting offsets with a 2.4-GHz P4 running FreeBSD 6.1 are
+ * generally within .02 ms short-term with .02 ms jitter. The long-term
+ * offsets vary up to 0.3 ms due to ionosperhic layer height variations.
+ * The processor load due to the driver is 5.8 percent.
+ */
+#define PDELAY ((.91 + 4.7 - 0.45) / 1000) /* system delay (s) */
+
+/*
+ * Table of sine values at 4.5-degree increments. This is used by the
+ * synchronous matched filter demodulators.
+ */
+double sintab[] = {
+ 0.000000e+00, 7.845910e-02, 1.564345e-01, 2.334454e-01, /* 0-3 */
+ 3.090170e-01, 3.826834e-01, 4.539905e-01, 5.224986e-01, /* 4-7 */
+ 5.877853e-01, 6.494480e-01, 7.071068e-01, 7.604060e-01, /* 8-11 */
+ 8.090170e-01, 8.526402e-01, 8.910065e-01, 9.238795e-01, /* 12-15 */
+ 9.510565e-01, 9.723699e-01, 9.876883e-01, 9.969173e-01, /* 16-19 */
+ 1.000000e+00, 9.969173e-01, 9.876883e-01, 9.723699e-01, /* 20-23 */
+ 9.510565e-01, 9.238795e-01, 8.910065e-01, 8.526402e-01, /* 24-27 */
+ 8.090170e-01, 7.604060e-01, 7.071068e-01, 6.494480e-01, /* 28-31 */
+ 5.877853e-01, 5.224986e-01, 4.539905e-01, 3.826834e-01, /* 32-35 */
+ 3.090170e-01, 2.334454e-01, 1.564345e-01, 7.845910e-02, /* 36-39 */
+-0.000000e+00, -7.845910e-02, -1.564345e-01, -2.334454e-01, /* 40-43 */
+-3.090170e-01, -3.826834e-01, -4.539905e-01, -5.224986e-01, /* 44-47 */
+-5.877853e-01, -6.494480e-01, -7.071068e-01, -7.604060e-01, /* 48-51 */
+-8.090170e-01, -8.526402e-01, -8.910065e-01, -9.238795e-01, /* 52-55 */
+-9.510565e-01, -9.723699e-01, -9.876883e-01, -9.969173e-01, /* 56-59 */
+-1.000000e+00, -9.969173e-01, -9.876883e-01, -9.723699e-01, /* 60-63 */
+-9.510565e-01, -9.238795e-01, -8.910065e-01, -8.526402e-01, /* 64-67 */
+-8.090170e-01, -7.604060e-01, -7.071068e-01, -6.494480e-01, /* 68-71 */
+-5.877853e-01, -5.224986e-01, -4.539905e-01, -3.826834e-01, /* 72-75 */
+-3.090170e-01, -2.334454e-01, -1.564345e-01, -7.845910e-02, /* 76-79 */
+ 0.000000e+00}; /* 80 */
+
+/*
+ * Decoder operations at the end of each second are driven by a state
+ * machine. The transition matrix consists of a dispatch table indexed
+ * by second number. Each entry in the table contains a case switch
+ * number and argument.
+ */
+struct progx {
+ int sw; /* case switch number */
+ int arg; /* argument */
+};
+
+/*
+ * Case switch numbers
+ */
+#define IDLE 0 /* no operation */
+#define COEF 1 /* BCD bit */
+#define COEF1 2 /* BCD bit for minute unit */
+#define COEF2 3 /* BCD bit not used */
+#define DECIM9 4 /* BCD digit 0-9 */
+#define DECIM6 5 /* BCD digit 0-6 */
+#define DECIM3 6 /* BCD digit 0-3 */
+#define DECIM2 7 /* BCD digit 0-2 */
+#define MSCBIT 8 /* miscellaneous bit */
+#define MSC20 9 /* miscellaneous bit */
+#define MSC21 10 /* QSY probe channel */
+#define MIN1 11 /* latch time */
+#define MIN2 12 /* leap second */
+#define SYNC2 13 /* latch minute sync pulse */
+#define SYNC3 14 /* latch data pulse */
+
+/*
+ * Offsets in decoding matrix
+ */
+#define MN 0 /* minute digits (2) */
+#define HR 2 /* hour digits (2) */
+#define DA 4 /* day digits (3) */
+#define YR 7 /* year digits (2) */
+
+struct progx progx[] = {
+ {SYNC2, 0}, /* 0 latch minute sync pulse */
+ {SYNC3, 0}, /* 1 latch data pulse */
+ {MSCBIT, DST2}, /* 2 dst2 */
+ {MSCBIT, SECWAR}, /* 3 lw */
+ {COEF, 0}, /* 4 1 year units */
+ {COEF, 1}, /* 5 2 */
+ {COEF, 2}, /* 6 4 */
+ {COEF, 3}, /* 7 8 */
+ {DECIM9, YR}, /* 8 */
+ {IDLE, 0}, /* 9 p1 */
+ {COEF1, 0}, /* 10 1 minute units */
+ {COEF1, 1}, /* 11 2 */
+ {COEF1, 2}, /* 12 4 */
+ {COEF1, 3}, /* 13 8 */
+ {DECIM9, MN}, /* 14 */
+ {COEF, 0}, /* 15 10 minute tens */
+ {COEF, 1}, /* 16 20 */
+ {COEF, 2}, /* 17 40 */
+ {COEF2, 3}, /* 18 80 (not used) */
+ {DECIM6, MN + 1}, /* 19 p2 */
+ {COEF, 0}, /* 20 1 hour units */
+ {COEF, 1}, /* 21 2 */
+ {COEF, 2}, /* 22 4 */
+ {COEF, 3}, /* 23 8 */
+ {DECIM9, HR}, /* 24 */
+ {COEF, 0}, /* 25 10 hour tens */
+ {COEF, 1}, /* 26 20 */
+ {COEF2, 2}, /* 27 40 (not used) */
+ {COEF2, 3}, /* 28 80 (not used) */
+ {DECIM2, HR + 1}, /* 29 p3 */
+ {COEF, 0}, /* 30 1 day units */
+ {COEF, 1}, /* 31 2 */
+ {COEF, 2}, /* 32 4 */
+ {COEF, 3}, /* 33 8 */
+ {DECIM9, DA}, /* 34 */
+ {COEF, 0}, /* 35 10 day tens */
+ {COEF, 1}, /* 36 20 */
+ {COEF, 2}, /* 37 40 */
+ {COEF, 3}, /* 38 80 */
+ {DECIM9, DA + 1}, /* 39 p4 */
+ {COEF, 0}, /* 40 100 day hundreds */
+ {COEF, 1}, /* 41 200 */
+ {COEF2, 2}, /* 42 400 (not used) */
+ {COEF2, 3}, /* 43 800 (not used) */
+ {DECIM3, DA + 2}, /* 44 */
+ {IDLE, 0}, /* 45 */
+ {IDLE, 0}, /* 46 */
+ {IDLE, 0}, /* 47 */
+ {IDLE, 0}, /* 48 */
+ {IDLE, 0}, /* 49 p5 */
+ {MSCBIT, DUTS}, /* 50 dut+- */
+ {COEF, 0}, /* 51 10 year tens */
+ {COEF, 1}, /* 52 20 */
+ {COEF, 2}, /* 53 40 */
+ {COEF, 3}, /* 54 80 */
+ {MSC20, DST1}, /* 55 dst1 */
+ {MSCBIT, DUT1}, /* 56 0.1 dut */
+ {MSCBIT, DUT2}, /* 57 0.2 */
+ {MSC21, DUT4}, /* 58 0.4 QSY probe channel */
+ {MIN1, 0}, /* 59 p6 latch time */
+ {MIN2, 0} /* 60 leap second */
+};
+
+/*
+ * BCD coefficients for maximum-likelihood digit decode
+ */
+#define P15 1. /* max positive number */
+#define N15 -1. /* max negative number */
+
+/*
+ * Digits 0-9
+ */
+#define P9 (P15 / 4) /* mark (+1) */
+#define N9 (N15 / 4) /* space (-1) */
+
+double bcd9[][4] = {
+ {N9, N9, N9, N9}, /* 0 */
+ {P9, N9, N9, N9}, /* 1 */
+ {N9, P9, N9, N9}, /* 2 */
+ {P9, P9, N9, N9}, /* 3 */
+ {N9, N9, P9, N9}, /* 4 */
+ {P9, N9, P9, N9}, /* 5 */
+ {N9, P9, P9, N9}, /* 6 */
+ {P9, P9, P9, N9}, /* 7 */
+ {N9, N9, N9, P9}, /* 8 */
+ {P9, N9, N9, P9}, /* 9 */
+ {0, 0, 0, 0} /* backstop */
+};
+
+/*
+ * Digits 0-6 (minute tens)
+ */
+#define P6 (P15 / 3) /* mark (+1) */
+#define N6 (N15 / 3) /* space (-1) */
+
+double bcd6[][4] = {
+ {N6, N6, N6, 0}, /* 0 */
+ {P6, N6, N6, 0}, /* 1 */
+ {N6, P6, N6, 0}, /* 2 */
+ {P6, P6, N6, 0}, /* 3 */
+ {N6, N6, P6, 0}, /* 4 */
+ {P6, N6, P6, 0}, /* 5 */
+ {N6, P6, P6, 0}, /* 6 */
+ {0, 0, 0, 0} /* backstop */
+};
+
+/*
+ * Digits 0-3 (day hundreds)
+ */
+#define P3 (P15 / 2) /* mark (+1) */
+#define N3 (N15 / 2) /* space (-1) */
+
+double bcd3[][4] = {
+ {N3, N3, 0, 0}, /* 0 */
+ {P3, N3, 0, 0}, /* 1 */
+ {N3, P3, 0, 0}, /* 2 */
+ {P3, P3, 0, 0}, /* 3 */
+ {0, 0, 0, 0} /* backstop */
+};
+
+/*
+ * Digits 0-2 (hour tens)
+ */
+#define P2 (P15 / 2) /* mark (+1) */
+#define N2 (N15 / 2) /* space (-1) */
+
+double bcd2[][4] = {
+ {N2, N2, 0, 0}, /* 0 */
+ {P2, N2, 0, 0}, /* 1 */
+ {N2, P2, 0, 0}, /* 2 */
+ {0, 0, 0, 0} /* backstop */
+};
+
+/*
+ * DST decode (DST2 DST1) for prettyprint
+ */
+char dstcod[] = {
+ 'S', /* 00 standard time */
+ 'I', /* 01 set clock ahead at 0200 local */
+ 'O', /* 10 set clock back at 0200 local */
+ 'D' /* 11 daylight time */
+};
+
+/*
+ * The decoding matrix consists of nine row vectors, one for each digit
+ * of the timecode. The digits are stored from least to most significant
+ * order. The maximum-likelihood timecode is formed from the digits
+ * corresponding to the maximum-likelihood values reading in the
+ * opposite order: yy ddd hh:mm.
+ */
+struct decvec {
+ int radix; /* radix (3, 4, 6, 10) */
+ int digit; /* current clock digit */
+ int count; /* match count */
+ double digprb; /* max digit probability */
+ double digsnr; /* likelihood function (dB) */
+ double like[10]; /* likelihood integrator 0-9 */
+};
+
+/*
+ * The station structure (sp) is used to acquire the minute pulse from
+ * WWV and/or WWVH. These stations are distinguished by the frequency
+ * used for the second and minute sync pulses, 1000 Hz for WWV and 1200
+ * Hz for WWVH. Other than frequency, the format is the same.
+ */
+struct sync {
+ double epoch; /* accumulated epoch differences */
+ double maxeng; /* sync max energy */
+ double noieng; /* sync noise energy */
+ long pos; /* max amplitude position */
+ long lastpos; /* last max position */
+ long mepoch; /* minute synch epoch */
+
+ double amp; /* sync signal */
+ double syneng; /* sync signal max */
+ double synmax; /* sync signal max latched at 0 s */
+ double synsnr; /* sync signal SNR */
+ double metric; /* signal quality metric */
+ int reach; /* reachability register */
+ int count; /* bit counter */
+ int select; /* select bits */
+ char refid[5]; /* reference identifier */
+};
+
+/*
+ * The channel structure (cp) is used to mitigate between channels.
+ */
+struct chan {
+ int gain; /* audio gain */
+ struct sync wwv; /* wwv station */
+ struct sync wwvh; /* wwvh station */
+};
+
+/*
+ * WWV unit control structure (up)
+ */
+struct wwvunit {
+ l_fp timestamp; /* audio sample timestamp */
+ l_fp tick; /* audio sample increment */
+ double phase, freq; /* logical clock phase and frequency */
+ double monitor; /* audio monitor point */
+ double pdelay; /* propagation delay (s) */
+#ifdef ICOM
+ int fd_icom; /* ICOM file descriptor */
+#endif /* ICOM */
+ int errflg; /* error flags */
+ int watch; /* watchcat */
+
+ /*
+ * Audio codec variables
+ */
+ double comp[SIZE]; /* decompanding table */
+ int port; /* codec port */
+ int gain; /* codec gain */
+ int mongain; /* codec monitor gain */
+ int clipcnt; /* sample clipped count */
+
+ /*
+ * Variables used to establish basic system timing
+ */
+ int avgint; /* master time constant */
+ int yepoch; /* sync epoch */
+ int repoch; /* buffered sync epoch */
+ double epomax; /* second sync amplitude */
+ double eposnr; /* second sync SNR */
+ double irig; /* data I channel amplitude */
+ double qrig; /* data Q channel amplitude */
+ int datapt; /* 100 Hz ramp */
+ double datpha; /* 100 Hz VFO control */
+ int rphase; /* second sample counter */
+ long mphase; /* minute sample counter */
+
+ /*
+ * Variables used to mitigate which channel to use
+ */
+ struct chan mitig[NCHAN]; /* channel data */
+ struct sync *sptr; /* station pointer */
+ int dchan; /* data channel */
+ int schan; /* probe channel */
+ int achan; /* active channel */
+
+ /*
+ * Variables used by the clock state machine
+ */
+ struct decvec decvec[9]; /* decoding matrix */
+ int rsec; /* seconds counter */
+ int digcnt; /* count of digits synchronized */
+
+ /*
+ * Variables used to estimate signal levels and bit/digit
+ * probabilities
+ */
+ double datsig; /* data signal max */
+ double datsnr; /* data signal SNR (dB) */
+
+ /*
+ * Variables used to establish status and alarm conditions
+ */
+ int status; /* status bits */
+ int alarm; /* alarm flashers */
+ int misc; /* miscellaneous timecode bits */
+ int errcnt; /* data bit error counter */
+};
+
+/*
+ * Function prototypes
+ */
+static int wwv_start (int, struct peer *);
+static void wwv_shutdown (int, struct peer *);
+static void wwv_receive (struct recvbuf *);
+static void wwv_poll (int, struct peer *);
+
+/*
+ * More function prototypes
+ */
+static void wwv_epoch (struct peer *);
+static void wwv_rf (struct peer *, double);
+static void wwv_endpoc (struct peer *, int);
+static void wwv_rsec (struct peer *, double);
+static void wwv_qrz (struct peer *, struct sync *, int);
+static void wwv_corr4 (struct peer *, struct decvec *,
+ double [], double [][4]);
+static void wwv_gain (struct peer *);
+static void wwv_tsec (struct peer *);
+static int timecode (struct wwvunit *, char *, size_t);
+static double wwv_snr (double, double);
+static int carry (struct decvec *);
+static int wwv_newchan (struct peer *);
+static void wwv_newgame (struct peer *);
+static double wwv_metric (struct sync *);
+static void wwv_clock (struct peer *);
+#ifdef ICOM
+static int wwv_qsy (struct peer *, int);
+#endif /* ICOM */
+
+static double qsy[NCHAN] = {2.5, 5, 10, 15, 20}; /* frequencies (MHz) */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_wwv = {
+ wwv_start, /* start up driver */
+ wwv_shutdown, /* shut down driver */
+ wwv_poll, /* transmit poll message */
+ noentry, /* not used (old wwv_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old wwv_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * wwv_start - open the devices and initialize data for processing
+ */
+static int
+wwv_start(
+ int unit, /* instance number (used by PCM) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+#ifdef ICOM
+ int temp;
+#endif /* ICOM */
+
+ /*
+ * Local variables
+ */
+ int fd; /* file descriptor */
+ int i; /* index */
+ double step; /* codec adjustment */
+
+ /*
+ * Open audio device
+ */
+ fd = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit);
+ if (fd < 0)
+ return (0);
+#ifdef DEBUG
+ if (debug)
+ audio_show();
+#endif /* DEBUG */
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->io.clock_recv = wwv_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+
+ /*
+ * The companded samples are encoded sign-magnitude. The table
+ * contains all the 256 values in the interest of speed.
+ */
+ up->comp[0] = up->comp[OFFSET] = 0.;
+ up->comp[1] = 1.; up->comp[OFFSET + 1] = -1.;
+ up->comp[2] = 3.; up->comp[OFFSET + 2] = -3.;
+ step = 2.;
+ for (i = 3; i < OFFSET; i++) {
+ up->comp[i] = up->comp[i - 1] + step;
+ up->comp[OFFSET + i] = -up->comp[i];
+ if (i % 16 == 0)
+ step *= 2.;
+ }
+ DTOLFP(1. / WWV_SEC, &up->tick);
+
+ /*
+ * Initialize the decoding matrix with the radix for each digit
+ * position.
+ */
+ up->decvec[MN].radix = 10; /* minutes */
+ up->decvec[MN + 1].radix = 6;
+ up->decvec[HR].radix = 10; /* hours */
+ up->decvec[HR + 1].radix = 3;
+ up->decvec[DA].radix = 10; /* days */
+ up->decvec[DA + 1].radix = 10;
+ up->decvec[DA + 2].radix = 4;
+ up->decvec[YR].radix = 10; /* years */
+ up->decvec[YR + 1].radix = 10;
+
+#ifdef ICOM
+ /*
+ * Initialize autotune if available. Note that the ICOM select
+ * code must be less than 128, so the high order bit can be used
+ * to select the line speed 0 (9600 bps) or 1 (1200 bps). Note
+ * we don't complain if the ICOM device is not there; but, if it
+ * is, the radio better be working.
+ */
+ temp = 0;
+#ifdef DEBUG
+ if (debug > 1)
+ temp = P_TRACE;
+#endif /* DEBUG */
+ if (peer->ttl != 0) {
+ if (peer->ttl & 0x80)
+ up->fd_icom = icom_init("/dev/icom", B1200,
+ temp);
+ else
+ up->fd_icom = icom_init("/dev/icom", B9600,
+ temp);
+ }
+ if (up->fd_icom > 0) {
+ if (wwv_qsy(peer, DCHAN) != 0) {
+ msyslog(LOG_NOTICE, "icom: radio not found");
+ close(up->fd_icom);
+ up->fd_icom = 0;
+ } else {
+ msyslog(LOG_NOTICE, "icom: autotune enabled");
+ }
+ }
+#endif /* ICOM */
+
+ /*
+ * Let the games begin.
+ */
+ wwv_newgame(peer);
+ return (1);
+}
+
+
+/*
+ * wwv_shutdown - shut down the clock
+ */
+static void
+wwv_shutdown(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (up == NULL)
+ return;
+
+ io_closeclock(&pp->io);
+#ifdef ICOM
+ if (up->fd_icom > 0)
+ close(up->fd_icom);
+#endif /* ICOM */
+ free(up);
+}
+
+
+/*
+ * wwv_receive - receive data from the audio device
+ *
+ * This routine reads input samples and adjusts the logical clock to
+ * track the A/D sample clock by dropping or duplicating codec samples.
+ * It also controls the A/D signal level with an AGC loop to mimimize
+ * quantization noise and avoid overload.
+ */
+static void
+wwv_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ /*
+ * Local variables
+ */
+ double sample; /* codec sample */
+ u_char *dpt; /* buffer pointer */
+ int bufcnt; /* buffer counter */
+ l_fp ltemp;
+
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Main loop - read until there ain't no more. Note codec
+ * samples are bit-inverted.
+ */
+ DTOLFP((double)rbufp->recv_length / WWV_SEC, &ltemp);
+ L_SUB(&rbufp->recv_time, &ltemp);
+ up->timestamp = rbufp->recv_time;
+ dpt = rbufp->recv_buffer;
+ for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) {
+ sample = up->comp[~*dpt++ & 0xff];
+
+ /*
+ * Clip noise spikes greater than MAXAMP (6000) and
+ * record the number of clips to be used later by the
+ * AGC.
+ */
+ if (sample > MAXAMP) {
+ sample = MAXAMP;
+ up->clipcnt++;
+ } else if (sample < -MAXAMP) {
+ sample = -MAXAMP;
+ up->clipcnt++;
+ }
+
+ /*
+ * Variable frequency oscillator. The codec oscillator
+ * runs at the nominal rate of 8000 samples per second,
+ * or 125 us per sample. A frequency change of one unit
+ * results in either duplicating or deleting one sample
+ * per second, which results in a frequency change of
+ * 125 PPM.
+ */
+ up->phase += (up->freq + clock_codec) / WWV_SEC;
+ if (up->phase >= .5) {
+ up->phase -= 1.;
+ } else if (up->phase < -.5) {
+ up->phase += 1.;
+ wwv_rf(peer, sample);
+ wwv_rf(peer, sample);
+ } else {
+ wwv_rf(peer, sample);
+ }
+ L_ADD(&up->timestamp, &up->tick);
+ }
+
+ /*
+ * Set the input port and monitor gain for the next buffer.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ up->port = 2;
+ else
+ up->port = 1;
+ if (pp->sloppyclockflag & CLK_FLAG3)
+ up->mongain = MONGAIN;
+ else
+ up->mongain = 0;
+}
+
+
+/*
+ * wwv_poll - called by the transmit procedure
+ *
+ * This routine keeps track of status. If no offset samples have been
+ * processed during a poll interval, a timeout event is declared. If
+ * errors have have occurred during the interval, they are reported as
+ * well.
+ */
+static void
+wwv_poll(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (up->errflg)
+ refclock_report(peer, up->errflg);
+ up->errflg = 0;
+ pp->polls++;
+}
+
+
+/*
+ * wwv_rf - process signals and demodulate to baseband
+ *
+ * This routine grooms and filters decompanded raw audio samples. The
+ * output signal is the 100-Hz filtered baseband data signal in
+ * quadrature phase. The routine also determines the minute synch epoch,
+ * as well as certain signal maxima, minima and related values.
+ *
+ * There are two 1-s ramps used by this program. Both count the 8000
+ * logical clock samples spanning exactly one second. The epoch ramp
+ * counts the samples starting at an arbitrary time. The rphase ramp
+ * counts the samples starting at the 5-ms second sync pulse found
+ * during the epoch ramp.
+ *
+ * There are two 1-m ramps used by this program. The mphase ramp counts
+ * the 480,000 logical clock samples spanning exactly one minute and
+ * starting at an arbitrary time. The rsec ramp counts the 60 seconds of
+ * the minute starting at the 800-ms minute sync pulse found during the
+ * mphase ramp. The rsec ramp drives the seconds state machine to
+ * determine the bits and digits of the timecode.
+ *
+ * Demodulation operations are based on three synthesized quadrature
+ * sinusoids: 100 Hz for the data signal, 1000 Hz for the WWV sync
+ * signal and 1200 Hz for the WWVH sync signal. These drive synchronous
+ * matched filters for the data signal (170 ms at 100 Hz), WWV minute
+ * sync signal (800 ms at 1000 Hz) and WWVH minute sync signal (800 ms
+ * at 1200 Hz). Two additional matched filters are switched in
+ * as required for the WWV second sync signal (5 cycles at 1000 Hz) and
+ * WWVH second sync signal (6 cycles at 1200 Hz).
+ */
+static void
+wwv_rf(
+ struct peer *peer, /* peerstructure pointer */
+ double isig /* input signal */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct sync *sp, *rp;
+
+ static double lpf[5]; /* 150-Hz lpf delay line */
+ double data; /* lpf output */
+ static double bpf[9]; /* 1000/1200-Hz bpf delay line */
+ double syncx; /* bpf output */
+ static double mf[41]; /* 1000/1200-Hz mf delay line */
+ double mfsync; /* mf output */
+
+ static int iptr; /* data channel pointer */
+ static double ibuf[DATSIZ]; /* data I channel delay line */
+ static double qbuf[DATSIZ]; /* data Q channel delay line */
+
+ static int jptr; /* sync channel pointer */
+ static int kptr; /* tick channel pointer */
+
+ static int csinptr; /* wwv channel phase */
+ static double cibuf[SYNSIZ]; /* wwv I channel delay line */
+ static double cqbuf[SYNSIZ]; /* wwv Q channel delay line */
+ static double ciamp; /* wwv I channel amplitude */
+ static double cqamp; /* wwv Q channel amplitude */
+
+ static double csibuf[TCKSIZ]; /* wwv I tick delay line */
+ static double csqbuf[TCKSIZ]; /* wwv Q tick delay line */
+ static double csiamp; /* wwv I tick amplitude */
+ static double csqamp; /* wwv Q tick amplitude */
+
+ static int hsinptr; /* wwvh channel phase */
+ static double hibuf[SYNSIZ]; /* wwvh I channel delay line */
+ static double hqbuf[SYNSIZ]; /* wwvh Q channel delay line */
+ static double hiamp; /* wwvh I channel amplitude */
+ static double hqamp; /* wwvh Q channel amplitude */
+
+ static double hsibuf[TCKSIZ]; /* wwvh I tick delay line */
+ static double hsqbuf[TCKSIZ]; /* wwvh Q tick delay line */
+ static double hsiamp; /* wwvh I tick amplitude */
+ static double hsqamp; /* wwvh Q tick amplitude */
+
+ static double epobuf[WWV_SEC]; /* second sync comb filter */
+ static double epomax, nxtmax; /* second sync amplitude buffer */
+ static int epopos; /* epoch second sync position buffer */
+
+ static int iniflg; /* initialization flag */
+ int epoch; /* comb filter index */
+ double dtemp;
+ int i;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ if (!iniflg) {
+ iniflg = 1;
+ memset((char *)lpf, 0, sizeof(lpf));
+ memset((char *)bpf, 0, sizeof(bpf));
+ memset((char *)mf, 0, sizeof(mf));
+ memset((char *)ibuf, 0, sizeof(ibuf));
+ memset((char *)qbuf, 0, sizeof(qbuf));
+ memset((char *)cibuf, 0, sizeof(cibuf));
+ memset((char *)cqbuf, 0, sizeof(cqbuf));
+ memset((char *)csibuf, 0, sizeof(csibuf));
+ memset((char *)csqbuf, 0, sizeof(csqbuf));
+ memset((char *)hibuf, 0, sizeof(hibuf));
+ memset((char *)hqbuf, 0, sizeof(hqbuf));
+ memset((char *)hsibuf, 0, sizeof(hsibuf));
+ memset((char *)hsqbuf, 0, sizeof(hsqbuf));
+ memset((char *)epobuf, 0, sizeof(epobuf));
+ }
+
+ /*
+ * Baseband data demodulation. The 100-Hz subcarrier is
+ * extracted using a 150-Hz IIR lowpass filter. This attenuates
+ * the 1000/1200-Hz sync signals, as well as the 440-Hz and
+ * 600-Hz tones and most of the noise and voice modulation
+ * components.
+ *
+ * The subcarrier is transmitted 10 dB down from the carrier.
+ * The DGAIN parameter can be adjusted for this and to
+ * compensate for the radio audio response at 100 Hz.
+ *
+ * Matlab IIR 4th-order IIR elliptic, 150 Hz lowpass, 0.2 dB
+ * passband ripple, -50 dB stopband ripple, phase delay 0.97 ms.
+ */
+ data = (lpf[4] = lpf[3]) * 8.360961e-01;
+ data += (lpf[3] = lpf[2]) * -3.481740e+00;
+ data += (lpf[2] = lpf[1]) * 5.452988e+00;
+ data += (lpf[1] = lpf[0]) * -3.807229e+00;
+ lpf[0] = isig * DGAIN - data;
+ data = lpf[0] * 3.281435e-03
+ + lpf[1] * -1.149947e-02
+ + lpf[2] * 1.654858e-02
+ + lpf[3] * -1.149947e-02
+ + lpf[4] * 3.281435e-03;
+
+ /*
+ * The 100-Hz data signal is demodulated using a pair of
+ * quadrature multipliers, matched filters and a phase lock
+ * loop. The I and Q quadrature data signals are produced by
+ * multiplying the filtered signal by 100-Hz sine and cosine
+ * signals, respectively. The signals are processed by 170-ms
+ * synchronous matched filters to produce the amplitude and
+ * phase signals used by the demodulator. The signals are scaled
+ * to produce unit energy at the maximum value.
+ */
+ i = up->datapt;
+ up->datapt = (up->datapt + IN100) % 80;
+ dtemp = sintab[i] * data / (MS / 2. * DATCYC);
+ up->irig -= ibuf[iptr];
+ ibuf[iptr] = dtemp;
+ up->irig += dtemp;
+
+ i = (i + 20) % 80;
+ dtemp = sintab[i] * data / (MS / 2. * DATCYC);
+ up->qrig -= qbuf[iptr];
+ qbuf[iptr] = dtemp;
+ up->qrig += dtemp;
+ iptr = (iptr + 1) % DATSIZ;
+
+ /*
+ * Baseband sync demodulation. The 1000/1200 sync signals are
+ * extracted using a 600-Hz IIR bandpass filter. This removes
+ * the 100-Hz data subcarrier, as well as the 440-Hz and 600-Hz
+ * tones and most of the noise and voice modulation components.
+ *
+ * Matlab 4th-order IIR elliptic, 800-1400 Hz bandpass, 0.2 dB
+ * passband ripple, -50 dB stopband ripple, phase delay 0.91 ms.
+ */
+ syncx = (bpf[8] = bpf[7]) * 4.897278e-01;
+ syncx += (bpf[7] = bpf[6]) * -2.765914e+00;
+ syncx += (bpf[6] = bpf[5]) * 8.110921e+00;
+ syncx += (bpf[5] = bpf[4]) * -1.517732e+01;
+ syncx += (bpf[4] = bpf[3]) * 1.975197e+01;
+ syncx += (bpf[3] = bpf[2]) * -1.814365e+01;
+ syncx += (bpf[2] = bpf[1]) * 1.159783e+01;
+ syncx += (bpf[1] = bpf[0]) * -4.735040e+00;
+ bpf[0] = isig - syncx;
+ syncx = bpf[0] * 8.203628e-03
+ + bpf[1] * -2.375732e-02
+ + bpf[2] * 3.353214e-02
+ + bpf[3] * -4.080258e-02
+ + bpf[4] * 4.605479e-02
+ + bpf[5] * -4.080258e-02
+ + bpf[6] * 3.353214e-02
+ + bpf[7] * -2.375732e-02
+ + bpf[8] * 8.203628e-03;
+
+ /*
+ * The 1000/1200 sync signals are demodulated using a pair of
+ * quadrature multipliers and matched filters. However,
+ * synchronous demodulation at these frequencies is impractical,
+ * so only the signal amplitude is used. The I and Q quadrature
+ * sync signals are produced by multiplying the filtered signal
+ * by 1000-Hz (WWV) and 1200-Hz (WWVH) sine and cosine signals,
+ * respectively. The WWV and WWVH signals are processed by 800-
+ * ms synchronous matched filters and combined to produce the
+ * minute sync signal and detect which one (or both) the WWV or
+ * WWVH signal is present. The WWV and WWVH signals are also
+ * processed by 5-ms synchronous matched filters and combined to
+ * produce the second sync signal. The signals are scaled to
+ * produce unit energy at the maximum value.
+ *
+ * Note the master timing ramps, which run continuously. The
+ * minute counter (mphase) counts the samples in the minute,
+ * while the second counter (epoch) counts the samples in the
+ * second.
+ */
+ up->mphase = (up->mphase + 1) % WWV_MIN;
+ epoch = up->mphase % WWV_SEC;
+
+ /*
+ * WWV
+ */
+ i = csinptr;
+ csinptr = (csinptr + IN1000) % 80;
+
+ dtemp = sintab[i] * syncx / (MS / 2.);
+ ciamp -= cibuf[jptr];
+ cibuf[jptr] = dtemp;
+ ciamp += dtemp;
+ csiamp -= csibuf[kptr];
+ csibuf[kptr] = dtemp;
+ csiamp += dtemp;
+
+ i = (i + 20) % 80;
+ dtemp = sintab[i] * syncx / (MS / 2.);
+ cqamp -= cqbuf[jptr];
+ cqbuf[jptr] = dtemp;
+ cqamp += dtemp;
+ csqamp -= csqbuf[kptr];
+ csqbuf[kptr] = dtemp;
+ csqamp += dtemp;
+
+ sp = &up->mitig[up->achan].wwv;
+ sp->amp = sqrt(ciamp * ciamp + cqamp * cqamp) / SYNCYC;
+ if (!(up->status & MSYNC))
+ wwv_qrz(peer, sp, (int)(pp->fudgetime1 * WWV_SEC));
+
+ /*
+ * WWVH
+ */
+ i = hsinptr;
+ hsinptr = (hsinptr + IN1200) % 80;
+
+ dtemp = sintab[i] * syncx / (MS / 2.);
+ hiamp -= hibuf[jptr];
+ hibuf[jptr] = dtemp;
+ hiamp += dtemp;
+ hsiamp -= hsibuf[kptr];
+ hsibuf[kptr] = dtemp;
+ hsiamp += dtemp;
+
+ i = (i + 20) % 80;
+ dtemp = sintab[i] * syncx / (MS / 2.);
+ hqamp -= hqbuf[jptr];
+ hqbuf[jptr] = dtemp;
+ hqamp += dtemp;
+ hsqamp -= hsqbuf[kptr];
+ hsqbuf[kptr] = dtemp;
+ hsqamp += dtemp;
+
+ rp = &up->mitig[up->achan].wwvh;
+ rp->amp = sqrt(hiamp * hiamp + hqamp * hqamp) / SYNCYC;
+ if (!(up->status & MSYNC))
+ wwv_qrz(peer, rp, (int)(pp->fudgetime2 * WWV_SEC));
+ jptr = (jptr + 1) % SYNSIZ;
+ kptr = (kptr + 1) % TCKSIZ;
+
+ /*
+ * The following section is called once per minute. It does
+ * housekeeping and timeout functions and empties the dustbins.
+ */
+ if (up->mphase == 0) {
+ up->watch++;
+ if (!(up->status & MSYNC)) {
+
+ /*
+ * If minute sync has not been acquired before
+ * ACQSN timeout (6 min), or if no signal is
+ * heard, the program cycles to the next
+ * frequency and tries again.
+ */
+ if (!wwv_newchan(peer))
+ up->watch = 0;
+ } else {
+
+ /*
+ * If the leap bit is set, set the minute epoch
+ * back one second so the station processes
+ * don't miss a beat.
+ */
+ if (up->status & LEPSEC) {
+ up->mphase -= WWV_SEC;
+ if (up->mphase < 0)
+ up->mphase += WWV_MIN;
+ }
+ }
+ }
+
+ /*
+ * When the channel metric reaches threshold and the second
+ * counter matches the minute epoch within the second, the
+ * driver has synchronized to the station. The second number is
+ * the remaining seconds until the next minute epoch, while the
+ * sync epoch is zero. Watch out for the first second; if
+ * already synchronized to the second, the buffered sync epoch
+ * must be set.
+ *
+ * Note the guard interval is 200 ms; if for some reason the
+ * clock drifts more than that, it might wind up in the wrong
+ * second. If the maximum frequency error is not more than about
+ * 1 PPM, the clock can go as much as two days while still in
+ * the same second.
+ */
+ if (up->status & MSYNC) {
+ wwv_epoch(peer);
+ } else if (up->sptr != NULL) {
+ sp = up->sptr;
+ if (sp->metric >= TTHR && epoch == sp->mepoch % WWV_SEC)
+ {
+ up->rsec = (60 - sp->mepoch / WWV_SEC) % 60;
+ up->rphase = 0;
+ up->status |= MSYNC;
+ up->watch = 0;
+ if (!(up->status & SSYNC))
+ up->repoch = up->yepoch = epoch;
+ else
+ up->repoch = up->yepoch;
+
+ }
+ }
+
+ /*
+ * The second sync pulse is extracted using 5-ms (40 sample) FIR
+ * matched filters at 1000 Hz for WWV or 1200 Hz for WWVH. This
+ * pulse is used for the most precise synchronization, since if
+ * provides a resolution of one sample (125 us). The filters run
+ * only if the station has been reliably determined.
+ */
+ if (up->status & SELV)
+ mfsync = sqrt(csiamp * csiamp + csqamp * csqamp) /
+ TCKCYC;
+ else if (up->status & SELH)
+ mfsync = sqrt(hsiamp * hsiamp + hsqamp * hsqamp) /
+ TCKCYC;
+ else
+ mfsync = 0;
+
+ /*
+ * Enhance the seconds sync pulse using a 1-s (8000-sample) comb
+ * filter. Correct for the FIR matched filter delay, which is 5
+ * ms for both the WWV and WWVH filters, and also for the
+ * propagation delay. Once each second look for second sync. If
+ * not in minute sync, fiddle the codec gain. Note the SNR is
+ * computed from the maximum sample and the envelope of the
+ * sample 6 ms before it, so if we slip more than a cycle the
+ * SNR should plummet. The signal is scaled to produce unit
+ * energy at the maximum value.
+ */
+ dtemp = (epobuf[epoch] += (mfsync - epobuf[epoch]) /
+ up->avgint);
+ if (dtemp > epomax) {
+ int j;
+
+ epomax = dtemp;
+ epopos = epoch;
+ j = epoch - 6 * MS;
+ if (j < 0)
+ j += WWV_SEC;
+ nxtmax = fabs(epobuf[j]);
+ }
+ if (epoch == 0) {
+ up->epomax = epomax;
+ up->eposnr = wwv_snr(epomax, nxtmax);
+ epopos -= TCKCYC * MS;
+ if (epopos < 0)
+ epopos += WWV_SEC;
+ wwv_endpoc(peer, epopos);
+ if (!(up->status & SSYNC))
+ up->alarm |= SYNERR;
+ epomax = 0;
+ if (!(up->status & MSYNC))
+ wwv_gain(peer);
+ }
+}
+
+
+/*
+ * wwv_qrz - identify and acquire WWV/WWVH minute sync pulse
+ *
+ * This routine implements a virtual station process used to acquire
+ * minute sync and to mitigate among the ten frequency and station
+ * combinations. During minute sync acquisition the process probes each
+ * frequency and station in turn for the minute pulse, which
+ * involves searching through the entire 480,000-sample minute. The
+ * process finds the maximum signal and RMS noise plus signal. Then, the
+ * actual noise is determined by subtracting the energy of the matched
+ * filter.
+ *
+ * Students of radar receiver technology will discover this algorithm
+ * amounts to a range-gate discriminator. A valid pulse must have peak
+ * amplitude at least QTHR (2500) and SNR at least QSNR (20) dB and the
+ * difference between the current and previous epoch must be less than
+ * AWND (20 ms). Note that the discriminator peak occurs about 800 ms
+ * into the second, so the timing is retarded to the previous second
+ * epoch.
+ */
+static void
+wwv_qrz(
+ struct peer *peer, /* peer structure pointer */
+ struct sync *sp, /* sync channel structure */
+ int pdelay /* propagation delay (samples) */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ char tbuf[TBUF]; /* monitor buffer */
+ long epoch;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Find the sample with peak amplitude, which defines the minute
+ * epoch. Accumulate all samples to determine the total noise
+ * energy.
+ */
+ epoch = up->mphase - pdelay - SYNSIZ;
+ if (epoch < 0)
+ epoch += WWV_MIN;
+ if (sp->amp > sp->maxeng) {
+ sp->maxeng = sp->amp;
+ sp->pos = epoch;
+ }
+ sp->noieng += sp->amp;
+
+ /*
+ * At the end of the minute, determine the epoch of the minute
+ * sync pulse, as well as the difference between the current and
+ * previous epoches due to the intrinsic frequency error plus
+ * jitter. When calculating the SNR, subtract the pulse energy
+ * from the total noise energy and then normalize.
+ */
+ if (up->mphase == 0) {
+ sp->synmax = sp->maxeng;
+ sp->synsnr = wwv_snr(sp->synmax, (sp->noieng -
+ sp->synmax) / WWV_MIN);
+ if (sp->count == 0)
+ sp->lastpos = sp->pos;
+ epoch = (sp->pos - sp->lastpos) % WWV_MIN;
+ sp->reach <<= 1;
+ if (sp->reach & (1 << AMAX))
+ sp->count--;
+ if (sp->synmax > ATHR && sp->synsnr > ASNR) {
+ if (abs(epoch) < AWND * MS) {
+ sp->reach |= 1;
+ sp->count++;
+ sp->mepoch = sp->lastpos = sp->pos;
+ } else if (sp->count == 1) {
+ sp->lastpos = sp->pos;
+ }
+ }
+ if (up->watch > ACQSN)
+ sp->metric = 0;
+ else
+ sp->metric = wwv_metric(sp);
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ snprintf(tbuf, sizeof(tbuf),
+ "wwv8 %04x %3d %s %04x %.0f %.0f/%.1f %ld %ld",
+ up->status, up->gain, sp->refid,
+ sp->reach & 0xffff, sp->metric, sp->synmax,
+ sp->synsnr, sp->pos % WWV_SEC, epoch);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+ sp->maxeng = sp->noieng = 0;
+ }
+}
+
+
+/*
+ * wwv_endpoc - identify and acquire second sync pulse
+ *
+ * This routine is called at the end of the second sync interval. It
+ * determines the second sync epoch position within the second and
+ * disciplines the sample clock using a frequency-lock loop (FLL).
+ *
+ * Second sync is determined in the RF input routine as the maximum
+ * over all 8000 samples in the second comb filter. To assure accurate
+ * and reliable time and frequency discipline, this routine performs a
+ * great deal of heavy-handed heuristic data filtering and grooming.
+ */
+static void
+wwv_endpoc(
+ struct peer *peer, /* peer structure pointer */
+ int epopos /* epoch max position */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ static int epoch_mf[3]; /* epoch median filter */
+ static int tepoch; /* current second epoch */
+ static int xepoch; /* last second epoch */
+ static int zepoch; /* last run epoch */
+ static int zcount; /* last run end time */
+ static int scount; /* seconds counter */
+ static int syncnt; /* run length counter */
+ static int maxrun; /* longest run length */
+ static int mepoch; /* longest run end epoch */
+ static int mcount; /* longest run end time */
+ static int avgcnt; /* averaging interval counter */
+ static int avginc; /* averaging ratchet */
+ static int iniflg; /* initialization flag */
+ char tbuf[TBUF]; /* monitor buffer */
+ double dtemp;
+ int tmp2;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (!iniflg) {
+ iniflg = 1;
+ ZERO(epoch_mf);
+ }
+
+ /*
+ * If the signal amplitude or SNR fall below thresholds, dim the
+ * second sync lamp and wait for hotter ions. If no stations are
+ * heard, we are either in a probe cycle or the ions are really
+ * cold.
+ */
+ scount++;
+ if (up->epomax < STHR || up->eposnr < SSNR) {
+ up->status &= ~(SSYNC | FGATE);
+ avgcnt = syncnt = maxrun = 0;
+ return;
+ }
+ if (!(up->status & (SELV | SELH)))
+ return;
+
+ /*
+ * A three-stage median filter is used to help denoise the
+ * second sync pulse. The median sample becomes the candidate
+ * epoch.
+ */
+ epoch_mf[2] = epoch_mf[1];
+ epoch_mf[1] = epoch_mf[0];
+ epoch_mf[0] = epopos;
+ if (epoch_mf[0] > epoch_mf[1]) {
+ if (epoch_mf[1] > epoch_mf[2])
+ tepoch = epoch_mf[1]; /* 0 1 2 */
+ else if (epoch_mf[2] > epoch_mf[0])
+ tepoch = epoch_mf[0]; /* 2 0 1 */
+ else
+ tepoch = epoch_mf[2]; /* 0 2 1 */
+ } else {
+ if (epoch_mf[1] < epoch_mf[2])
+ tepoch = epoch_mf[1]; /* 2 1 0 */
+ else if (epoch_mf[2] < epoch_mf[0])
+ tepoch = epoch_mf[0]; /* 1 0 2 */
+ else
+ tepoch = epoch_mf[2]; /* 1 2 0 */
+ }
+
+
+ /*
+ * If the epoch candidate is the same as the last one, increment
+ * the run counter. If not, save the length, epoch and end
+ * time of the current run for use later and reset the counter.
+ * The epoch is considered valid if the run is at least SCMP
+ * (10) s, the minute is synchronized and the interval since the
+ * last epoch is not greater than the averaging interval. Thus,
+ * after a long absence, the program will wait a full averaging
+ * interval while the comb filter charges up and noise
+ * dissapates..
+ */
+ tmp2 = (tepoch - xepoch) % WWV_SEC;
+ if (tmp2 == 0) {
+ syncnt++;
+ if (syncnt > SCMP && up->status & MSYNC && (up->status &
+ FGATE || scount - zcount <= up->avgint)) {
+ up->status |= SSYNC;
+ up->yepoch = tepoch;
+ }
+ } else if (syncnt >= maxrun) {
+ maxrun = syncnt;
+ mcount = scount;
+ mepoch = xepoch;
+ syncnt = 0;
+ }
+ if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status &
+ MSYNC)) {
+ snprintf(tbuf, sizeof(tbuf),
+ "wwv1 %04x %3d %4d %5.0f %5.1f %5d %4d %4d %4d",
+ up->status, up->gain, tepoch, up->epomax,
+ up->eposnr, tmp2, avgcnt, syncnt,
+ maxrun);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+ avgcnt++;
+ if (avgcnt < up->avgint) {
+ xepoch = tepoch;
+ return;
+ }
+
+ /*
+ * The sample clock frequency is disciplined using a first-order
+ * feedback loop with time constant consistent with the Allan
+ * intercept of typical computer clocks. During each averaging
+ * interval the candidate epoch at the end of the longest run is
+ * determined. If the longest run is zero, all epoches in the
+ * interval are different, so the candidate epoch is the current
+ * epoch. The frequency update is computed from the candidate
+ * epoch difference (125-us units) and time difference (seconds)
+ * between updates.
+ */
+ if (syncnt >= maxrun) {
+ maxrun = syncnt;
+ mcount = scount;
+ mepoch = xepoch;
+ }
+ xepoch = tepoch;
+ if (maxrun == 0) {
+ mepoch = tepoch;
+ mcount = scount;
+ }
+
+ /*
+ * The master clock runs at the codec sample frequency of 8000
+ * Hz, so the intrinsic time resolution is 125 us. The frequency
+ * resolution ranges from 18 PPM at the minimum averaging
+ * interval of 8 s to 0.12 PPM at the maximum interval of 1024
+ * s. An offset update is determined at the end of the longest
+ * run in each averaging interval. The frequency adjustment is
+ * computed from the difference between offset updates and the
+ * interval between them.
+ *
+ * The maximum frequency adjustment ranges from 187 PPM at the
+ * minimum interval to 1.5 PPM at the maximum. If the adjustment
+ * exceeds the maximum, the update is discarded and the
+ * hysteresis counter is decremented. Otherwise, the frequency
+ * is incremented by the adjustment, but clamped to the maximum
+ * 187.5 PPM. If the update is less than half the maximum, the
+ * hysteresis counter is incremented. If the counter increments
+ * to +3, the averaging interval is doubled and the counter set
+ * to zero; if it decrements to -3, the interval is halved and
+ * the counter set to zero.
+ */
+ dtemp = (mepoch - zepoch) % WWV_SEC;
+ if (up->status & FGATE) {
+ if (abs(dtemp) < MAXFREQ * MINAVG) {
+ up->freq += (dtemp / 2.) / ((mcount - zcount) *
+ FCONST);
+ if (up->freq > MAXFREQ)
+ up->freq = MAXFREQ;
+ else if (up->freq < -MAXFREQ)
+ up->freq = -MAXFREQ;
+ if (abs(dtemp) < MAXFREQ * MINAVG / 2.) {
+ if (avginc < 3) {
+ avginc++;
+ } else {
+ if (up->avgint < MAXAVG) {
+ up->avgint <<= 1;
+ avginc = 0;
+ }
+ }
+ }
+ } else {
+ if (avginc > -3) {
+ avginc--;
+ } else {
+ if (up->avgint > MINAVG) {
+ up->avgint >>= 1;
+ avginc = 0;
+ }
+ }
+ }
+ }
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ snprintf(tbuf, sizeof(tbuf),
+ "wwv2 %04x %5.0f %5.1f %5d %4d %4d %4d %4.0f %7.2f",
+ up->status, up->epomax, up->eposnr, mepoch,
+ up->avgint, maxrun, mcount - zcount, dtemp,
+ up->freq * 1e6 / WWV_SEC);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+
+ /*
+ * This is a valid update; set up for the next interval.
+ */
+ up->status |= FGATE;
+ zepoch = mepoch;
+ zcount = mcount;
+ avgcnt = syncnt = maxrun = 0;
+}
+
+
+/*
+ * wwv_epoch - epoch scanner
+ *
+ * This routine extracts data signals from the 100-Hz subcarrier. It
+ * scans the receiver second epoch to determine the signal amplitudes
+ * and pulse timings. Receiver synchronization is determined by the
+ * minute sync pulse detected in the wwv_rf() routine and the second
+ * sync pulse detected in the wwv_epoch() routine. The transmitted
+ * signals are delayed by the propagation delay, receiver delay and
+ * filter delay of this program. Delay corrections are introduced
+ * separately for WWV and WWVH.
+ *
+ * Most communications radios use a highpass filter in the audio stages,
+ * which can do nasty things to the subcarrier phase relative to the
+ * sync pulses. Therefore, the data subcarrier reference phase is
+ * disciplined using the hardlimited quadrature-phase signal sampled at
+ * the same time as the in-phase signal. The phase tracking loop uses
+ * phase adjustments of plus-minus one sample (125 us).
+ */
+static void
+wwv_epoch(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct chan *cp;
+ static double sigmin, sigzer, sigone, engmax, engmin;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Find the maximum minute sync pulse energy for both the
+ * WWV and WWVH stations. This will be used later for channel
+ * and station mitigation. Also set the seconds epoch at 800 ms
+ * well before the end of the second to make sure we never set
+ * the epoch backwards.
+ */
+ cp = &up->mitig[up->achan];
+ if (cp->wwv.amp > cp->wwv.syneng)
+ cp->wwv.syneng = cp->wwv.amp;
+ if (cp->wwvh.amp > cp->wwvh.syneng)
+ cp->wwvh.syneng = cp->wwvh.amp;
+ if (up->rphase == 800 * MS)
+ up->repoch = up->yepoch;
+
+ /*
+ * Use the signal amplitude at epoch 15 ms as the noise floor.
+ * This gives a guard time of +-15 ms from the beginning of the
+ * second until the second pulse rises at 30 ms. There is a
+ * compromise here; we want to delay the sample as long as
+ * possible to give the radio time to change frequency and the
+ * AGC to stabilize, but as early as possible if the second
+ * epoch is not exact.
+ */
+ if (up->rphase == 15 * MS)
+ sigmin = sigzer = sigone = up->irig;
+
+ /*
+ * Latch the data signal at 200 ms. Keep this around until the
+ * end of the second. Use the signal energy as the peak to
+ * compute the SNR. Use the Q sample to adjust the 100-Hz
+ * reference oscillator phase.
+ */
+ if (up->rphase == 200 * MS) {
+ sigzer = up->irig;
+ engmax = sqrt(up->irig * up->irig + up->qrig *
+ up->qrig);
+ up->datpha = up->qrig / up->avgint;
+ if (up->datpha >= 0) {
+ up->datapt++;
+ if (up->datapt >= 80)
+ up->datapt -= 80;
+ } else {
+ up->datapt--;
+ if (up->datapt < 0)
+ up->datapt += 80;
+ }
+ }
+
+
+ /*
+ * Latch the data signal at 500 ms. Keep this around until the
+ * end of the second.
+ */
+ else if (up->rphase == 500 * MS)
+ sigone = up->irig;
+
+ /*
+ * At the end of the second crank the clock state machine and
+ * adjust the codec gain. Note the epoch is buffered from the
+ * center of the second in order to avoid jitter while the
+ * seconds synch is diddling the epoch. Then, determine the true
+ * offset and update the median filter in the driver interface.
+ *
+ * Use the energy at the end of the second as the noise to
+ * compute the SNR for the data pulse. This gives a better
+ * measurement than the beginning of the second, especially when
+ * returning from the probe channel. This gives a guard time of
+ * 30 ms from the decay of the longest pulse to the rise of the
+ * next pulse.
+ */
+ up->rphase++;
+ if (up->mphase % WWV_SEC == up->repoch) {
+ up->status &= ~(DGATE | BGATE);
+ engmin = sqrt(up->irig * up->irig + up->qrig *
+ up->qrig);
+ up->datsig = engmax;
+ up->datsnr = wwv_snr(engmax, engmin);
+
+ /*
+ * If the amplitude or SNR is below threshold, average a
+ * 0 in the the integrators; otherwise, average the
+ * bipolar signal. This is done to avoid noise polution.
+ */
+ if (engmax < DTHR || up->datsnr < DSNR) {
+ up->status |= DGATE;
+ wwv_rsec(peer, 0);
+ } else {
+ sigzer -= sigone;
+ sigone -= sigmin;
+ wwv_rsec(peer, sigone - sigzer);
+ }
+ if (up->status & (DGATE | BGATE))
+ up->errcnt++;
+ if (up->errcnt > MAXERR)
+ up->alarm |= LOWERR;
+ wwv_gain(peer);
+ cp = &up->mitig[up->achan];
+ cp->wwv.syneng = 0;
+ cp->wwvh.syneng = 0;
+ up->rphase = 0;
+ }
+}
+
+
+/*
+ * wwv_rsec - process receiver second
+ *
+ * This routine is called at the end of each receiver second to
+ * implement the per-second state machine. The machine assembles BCD
+ * digit bits, decodes miscellaneous bits and dances the leap seconds.
+ *
+ * Normally, the minute has 60 seconds numbered 0-59. If the leap
+ * warning bit is set, the last minute (1439) of 30 June (day 181 or 182
+ * for leap years) or 31 December (day 365 or 366 for leap years) is
+ * augmented by one second numbered 60. This is accomplished by
+ * extending the minute interval by one second and teaching the state
+ * machine to ignore it.
+ */
+static void
+wwv_rsec(
+ struct peer *peer, /* peer structure pointer */
+ double bit
+ )
+{
+ static int iniflg; /* initialization flag */
+ static double bcddld[4]; /* BCD data bits */
+ static double bitvec[61]; /* bit integrator for misc bits */
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct chan *cp;
+ struct sync *sp, *rp;
+ char tbuf[TBUF]; /* monitor buffer */
+ int sw, arg, nsec;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (!iniflg) {
+ iniflg = 1;
+ ZERO(bitvec);
+ }
+
+ /*
+ * The bit represents the probability of a hit on zero (negative
+ * values), a hit on one (positive values) or a miss (zero
+ * value). The likelihood vector is the exponential average of
+ * these probabilities. Only the bits of this vector
+ * corresponding to the miscellaneous bits of the timecode are
+ * used, but it's easier to do them all. After that, crank the
+ * seconds state machine.
+ */
+ nsec = up->rsec;
+ up->rsec++;
+ bitvec[nsec] += (bit - bitvec[nsec]) / TCONST;
+ sw = progx[nsec].sw;
+ arg = progx[nsec].arg;
+
+ /*
+ * The minute state machine. Fly off to a particular section as
+ * directed by the transition matrix and second number.
+ */
+ switch (sw) {
+
+ /*
+ * Ignore this second.
+ */
+ case IDLE: /* 9, 45-49 */
+ break;
+
+ /*
+ * Probe channel stuff
+ *
+ * The WWV/H format contains data pulses in second 59 (position
+ * identifier) and second 1, but not in second 0. The minute
+ * sync pulse is contained in second 0. At the end of second 58
+ * QSY to the probe channel, which rotates in turn over all
+ * WWV/H frequencies. At the end of second 0 measure the minute
+ * sync pulse. At the end of second 1 measure the data pulse and
+ * QSY back to the data channel. Note that the actions commented
+ * here happen at the end of the second numbered as shown.
+ *
+ * At the end of second 0 save the minute sync amplitude latched
+ * at 800 ms as the signal later used to calculate the SNR.
+ */
+ case SYNC2: /* 0 */
+ cp = &up->mitig[up->achan];
+ cp->wwv.synmax = cp->wwv.syneng;
+ cp->wwvh.synmax = cp->wwvh.syneng;
+ break;
+
+ /*
+ * At the end of second 1 use the minute sync amplitude latched
+ * at 800 ms as the noise to calculate the SNR. If the minute
+ * sync pulse and SNR are above thresholds and the data pulse
+ * amplitude and SNR are above thresolds, shift a 1 into the
+ * station reachability register; otherwise, shift a 0. The
+ * number of 1 bits in the last six intervals is a component of
+ * the channel metric computed by the wwv_metric() routine.
+ * Finally, QSY back to the data channel.
+ */
+ case SYNC3: /* 1 */
+ cp = &up->mitig[up->achan];
+
+ /*
+ * WWV station
+ */
+ sp = &cp->wwv;
+ sp->synsnr = wwv_snr(sp->synmax, sp->amp);
+ sp->reach <<= 1;
+ if (sp->reach & (1 << AMAX))
+ sp->count--;
+ if (sp->synmax >= QTHR && sp->synsnr >= QSNR &&
+ !(up->status & (DGATE | BGATE))) {
+ sp->reach |= 1;
+ sp->count++;
+ }
+ sp->metric = wwv_metric(sp);
+
+ /*
+ * WWVH station
+ */
+ rp = &cp->wwvh;
+ rp->synsnr = wwv_snr(rp->synmax, rp->amp);
+ rp->reach <<= 1;
+ if (rp->reach & (1 << AMAX))
+ rp->count--;
+ if (rp->synmax >= QTHR && rp->synsnr >= QSNR &&
+ !(up->status & (DGATE | BGATE))) {
+ rp->reach |= 1;
+ rp->count++;
+ }
+ rp->metric = wwv_metric(rp);
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ snprintf(tbuf, sizeof(tbuf),
+ "wwv5 %04x %3d %4d %.0f/%.1f %.0f/%.1f %s %04x %.0f %.0f/%.1f %s %04x %.0f %.0f/%.1f",
+ up->status, up->gain, up->yepoch,
+ up->epomax, up->eposnr, up->datsig,
+ up->datsnr,
+ sp->refid, sp->reach & 0xffff,
+ sp->metric, sp->synmax, sp->synsnr,
+ rp->refid, rp->reach & 0xffff,
+ rp->metric, rp->synmax, rp->synsnr);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+ up->errcnt = up->digcnt = up->alarm = 0;
+
+ /*
+ * If synchronized to a station, restart if no stations
+ * have been heard within the PANIC timeout (2 days). If
+ * not and the minute digit has been found, restart if
+ * not synchronized withing the SYNCH timeout (40 m). If
+ * not, restart if the unit digit has not been found
+ * within the DATA timeout (15 m).
+ */
+ if (up->status & INSYNC) {
+ if (up->watch > PANIC) {
+ wwv_newgame(peer);
+ return;
+ }
+ } else if (up->status & DSYNC) {
+ if (up->watch > SYNCH) {
+ wwv_newgame(peer);
+ return;
+ }
+ } else if (up->watch > DATA) {
+ wwv_newgame(peer);
+ return;
+ }
+ wwv_newchan(peer);
+ break;
+
+ /*
+ * Save the bit probability in the BCD data vector at the index
+ * given by the argument. Bits not used in the digit are forced
+ * to zero.
+ */
+ case COEF1: /* 4-7 */
+ bcddld[arg] = bit;
+ break;
+
+ case COEF: /* 10-13, 15-17, 20-23, 25-26,
+ 30-33, 35-38, 40-41, 51-54 */
+ if (up->status & DSYNC)
+ bcddld[arg] = bit;
+ else
+ bcddld[arg] = 0;
+ break;
+
+ case COEF2: /* 18, 27-28, 42-43 */
+ bcddld[arg] = 0;
+ break;
+
+ /*
+ * Correlate coefficient vector with each valid digit vector and
+ * save in decoding matrix. We step through the decoding matrix
+ * digits correlating each with the coefficients and saving the
+ * greatest and the next lower for later SNR calculation.
+ */
+ case DECIM2: /* 29 */
+ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd2);
+ break;
+
+ case DECIM3: /* 44 */
+ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd3);
+ break;
+
+ case DECIM6: /* 19 */
+ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd6);
+ break;
+
+ case DECIM9: /* 8, 14, 24, 34, 39 */
+ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd9);
+ break;
+
+ /*
+ * Miscellaneous bits. If above the positive threshold, declare
+ * 1; if below the negative threshold, declare 0; otherwise
+ * raise the BGATE bit. The design is intended to avoid
+ * integrating noise under low SNR conditions.
+ */
+ case MSC20: /* 55 */
+ wwv_corr4(peer, &up->decvec[YR + 1], bcddld, bcd9);
+ /* fall through */
+
+ case MSCBIT: /* 2-3, 50, 56-57 */
+ if (bitvec[nsec] > BTHR) {
+ if (!(up->misc & arg))
+ up->alarm |= CMPERR;
+ up->misc |= arg;
+ } else if (bitvec[nsec] < -BTHR) {
+ if (up->misc & arg)
+ up->alarm |= CMPERR;
+ up->misc &= ~arg;
+ } else {
+ up->status |= BGATE;
+ }
+ break;
+
+ /*
+ * Save the data channel gain, then QSY to the probe channel and
+ * dim the seconds comb filters. The www_newchan() routine will
+ * light them back up.
+ */
+ case MSC21: /* 58 */
+ if (bitvec[nsec] > BTHR) {
+ if (!(up->misc & arg))
+ up->alarm |= CMPERR;
+ up->misc |= arg;
+ } else if (bitvec[nsec] < -BTHR) {
+ if (up->misc & arg)
+ up->alarm |= CMPERR;
+ up->misc &= ~arg;
+ } else {
+ up->status |= BGATE;
+ }
+ up->status &= ~(SELV | SELH);
+#ifdef ICOM
+ if (up->fd_icom > 0) {
+ up->schan = (up->schan + 1) % NCHAN;
+ wwv_qsy(peer, up->schan);
+ } else {
+ up->mitig[up->achan].gain = up->gain;
+ }
+#else
+ up->mitig[up->achan].gain = up->gain;
+#endif /* ICOM */
+ break;
+
+ /*
+ * The endgames
+ *
+ * During second 59 the receiver and codec AGC are settling
+ * down, so the data pulse is unusable as quality metric. If
+ * LEPSEC is set on the last minute of 30 June or 31 December,
+ * the transmitter and receiver insert an extra second (60) in
+ * the timescale and the minute sync repeats the second. Once
+ * leaps occurred at intervals of about 18 months, but the last
+ * leap before the most recent leap in 1995 was in 1998.
+ */
+ case MIN1: /* 59 */
+ if (up->status & LEPSEC)
+ break;
+
+ /* fall through */
+
+ case MIN2: /* 60 */
+ up->status &= ~LEPSEC;
+ wwv_tsec(peer);
+ up->rsec = 0;
+ wwv_clock(peer);
+ break;
+ }
+ if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status &
+ DSYNC)) {
+ snprintf(tbuf, sizeof(tbuf),
+ "wwv3 %2d %04x %3d %4d %5.0f %5.1f %5.0f %5.1f %5.0f",
+ nsec, up->status, up->gain, up->yepoch, up->epomax,
+ up->eposnr, up->datsig, up->datsnr, bit);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+ pp->disp += AUDIO_PHI;
+}
+
+/*
+ * The radio clock is set if the alarm bits are all zero. After that,
+ * the time is considered valid if the second sync bit is lit. It should
+ * not be a surprise, especially if the radio is not tunable, that
+ * sometimes no stations are above the noise and the integrators
+ * discharge below the thresholds. We assume that, after a day of signal
+ * loss, the minute sync epoch will be in the same second. This requires
+ * the codec frequency be accurate within 6 PPM. Practical experience
+ * shows the frequency typically within 0.1 PPM, so after a day of
+ * signal loss, the time should be within 8.6 ms..
+ */
+static void
+wwv_clock(
+ struct peer *peer /* peer unit pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ l_fp offset; /* offset in NTP seconds */
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (!(up->status & SSYNC))
+ up->alarm |= SYNERR;
+ if (up->digcnt < 9)
+ up->alarm |= NINERR;
+ if (!(up->alarm))
+ up->status |= INSYNC;
+ if (up->status & INSYNC && up->status & SSYNC) {
+ if (up->misc & SECWAR)
+ pp->leap = LEAP_ADDSECOND;
+ else
+ pp->leap = LEAP_NOWARNING;
+ pp->second = up->rsec;
+ pp->minute = up->decvec[MN].digit + up->decvec[MN +
+ 1].digit * 10;
+ pp->hour = up->decvec[HR].digit + up->decvec[HR +
+ 1].digit * 10;
+ pp->day = up->decvec[DA].digit + up->decvec[DA +
+ 1].digit * 10 + up->decvec[DA + 2].digit * 100;
+ pp->year = up->decvec[YR].digit + up->decvec[YR +
+ 1].digit * 10;
+ pp->year += 2000;
+ L_CLR(&offset);
+ if (!clocktime(pp->day, pp->hour, pp->minute,
+ pp->second, GMT, up->timestamp.l_ui,
+ &pp->yearstart, &offset.l_ui)) {
+ up->errflg = CEVNT_BADTIME;
+ } else {
+ up->watch = 0;
+ pp->disp = 0;
+ pp->lastref = up->timestamp;
+ refclock_process_offset(pp, offset,
+ up->timestamp, PDELAY + up->pdelay);
+ refclock_receive(peer);
+ }
+ }
+ pp->lencode = timecode(up, pp->a_lastcode,
+ sizeof(pp->a_lastcode));
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("wwv: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif /* DEBUG */
+}
+
+
+/*
+ * wwv_corr4 - determine maximum-likelihood digit
+ *
+ * This routine correlates the received digit vector with the BCD
+ * coefficient vectors corresponding to all valid digits at the given
+ * position in the decoding matrix. The maximum value corresponds to the
+ * maximum-likelihood digit, while the ratio of this value to the next
+ * lower value determines the likelihood function. Note that, if the
+ * digit is invalid, the likelihood vector is averaged toward a miss.
+ */
+static void
+wwv_corr4(
+ struct peer *peer, /* peer unit pointer */
+ struct decvec *vp, /* decoding table pointer */
+ double data[], /* received data vector */
+ double tab[][4] /* correlation vector array */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ double topmax, nxtmax; /* metrics */
+ double acc; /* accumulator */
+ char tbuf[TBUF]; /* monitor buffer */
+ int mldigit; /* max likelihood digit */
+ int i, j;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Correlate digit vector with each BCD coefficient vector. If
+ * any BCD digit bit is bad, consider all bits a miss. Until the
+ * minute units digit has been resolved, don't to anything else.
+ * Note the SNR is calculated as the ratio of the largest
+ * likelihood value to the next largest likelihood value.
+ */
+ mldigit = 0;
+ topmax = nxtmax = -MAXAMP;
+ for (i = 0; tab[i][0] != 0; i++) {
+ acc = 0;
+ for (j = 0; j < 4; j++)
+ acc += data[j] * tab[i][j];
+ acc = (vp->like[i] += (acc - vp->like[i]) / TCONST);
+ if (acc > topmax) {
+ nxtmax = topmax;
+ topmax = acc;
+ mldigit = i;
+ } else if (acc > nxtmax) {
+ nxtmax = acc;
+ }
+ }
+ vp->digprb = topmax;
+ vp->digsnr = wwv_snr(topmax, nxtmax);
+
+ /*
+ * The current maximum-likelihood digit is compared to the last
+ * maximum-likelihood digit. If different, the compare counter
+ * and maximum-likelihood digit are reset. When the compare
+ * counter reaches the BCMP threshold (3), the digit is assumed
+ * correct. When the compare counter of all nine digits have
+ * reached threshold, the clock is assumed correct.
+ *
+ * Note that the clock display digit is set before the compare
+ * counter has reached threshold; however, the clock display is
+ * not considered correct until all nine clock digits have
+ * reached threshold. This is intended as eye candy, but avoids
+ * mistakes when the signal is low and the SNR is very marginal.
+ */
+ if (vp->digprb < BTHR || vp->digsnr < BSNR) {
+ up->status |= BGATE;
+ } else {
+ if (vp->digit != mldigit) {
+ up->alarm |= CMPERR;
+ if (vp->count > 0)
+ vp->count--;
+ if (vp->count == 0)
+ vp->digit = mldigit;
+ } else {
+ if (vp->count < BCMP)
+ vp->count++;
+ if (vp->count == BCMP) {
+ up->status |= DSYNC;
+ up->digcnt++;
+ }
+ }
+ }
+ if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status &
+ INSYNC)) {
+ snprintf(tbuf, sizeof(tbuf),
+ "wwv4 %2d %04x %3d %4d %5.0f %2d %d %d %d %5.0f %5.1f",
+ up->rsec - 1, up->status, up->gain, up->yepoch,
+ up->epomax, vp->radix, vp->digit, mldigit,
+ vp->count, vp->digprb, vp->digsnr);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+}
+
+
+/*
+ * wwv_tsec - transmitter minute processing
+ *
+ * This routine is called at the end of the transmitter minute. It
+ * implements a state machine that advances the logical clock subject to
+ * the funny rules that govern the conventional clock and calendar.
+ */
+static void
+wwv_tsec(
+ struct peer *peer /* driver structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ int minute, day, isleap;
+ int temp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Advance minute unit of the day. Don't propagate carries until
+ * the unit minute digit has been found.
+ */
+ temp = carry(&up->decvec[MN]); /* minute units */
+ if (!(up->status & DSYNC))
+ return;
+
+ /*
+ * Propagate carries through the day.
+ */
+ if (temp == 0) /* carry minutes */
+ temp = carry(&up->decvec[MN + 1]);
+ if (temp == 0) /* carry hours */
+ temp = carry(&up->decvec[HR]);
+ if (temp == 0)
+ temp = carry(&up->decvec[HR + 1]);
+
+ /*
+ * Decode the current minute and day. Set leap day if the
+ * timecode leap bit is set on 30 June or 31 December. Set leap
+ * minute if the last minute on leap day, but only if the clock
+ * is syncrhronized. This code fails in 2400 AD.
+ */
+ minute = up->decvec[MN].digit + up->decvec[MN + 1].digit *
+ 10 + up->decvec[HR].digit * 60 + up->decvec[HR +
+ 1].digit * 600;
+ day = up->decvec[DA].digit + up->decvec[DA + 1].digit * 10 +
+ up->decvec[DA + 2].digit * 100;
+
+ /*
+ * Set the leap bit on the last minute of the leap day.
+ */
+ isleap = up->decvec[YR].digit & 0x3;
+ if (up->misc & SECWAR && up->status & INSYNC) {
+ if ((day == (isleap ? 182 : 183) || day == (isleap ?
+ 365 : 366)) && minute == 1439)
+ up->status |= LEPSEC;
+ }
+
+ /*
+ * Roll the day if this the first minute and propagate carries
+ * through the year.
+ */
+ if (minute != 1440)
+ return;
+
+ minute = 0;
+ while (carry(&up->decvec[HR]) != 0); /* advance to minute 0 */
+ while (carry(&up->decvec[HR + 1]) != 0);
+ day++;
+ temp = carry(&up->decvec[DA]); /* carry days */
+ if (temp == 0)
+ temp = carry(&up->decvec[DA + 1]);
+ if (temp == 0)
+ temp = carry(&up->decvec[DA + 2]);
+
+ /*
+ * Roll the year if this the first day and propagate carries
+ * through the century.
+ */
+ if (day != (isleap ? 365 : 366))
+ return;
+
+ day = 1;
+ while (carry(&up->decvec[DA]) != 1); /* advance to day 1 */
+ while (carry(&up->decvec[DA + 1]) != 0);
+ while (carry(&up->decvec[DA + 2]) != 0);
+ temp = carry(&up->decvec[YR]); /* carry years */
+ if (temp == 0)
+ carry(&up->decvec[YR + 1]);
+}
+
+
+/*
+ * carry - process digit
+ *
+ * This routine rotates a likelihood vector one position and increments
+ * the clock digit modulo the radix. It returns the new clock digit or
+ * zero if a carry occurred. Once synchronized, the clock digit will
+ * match the maximum-likelihood digit corresponding to that position.
+ */
+static int
+carry(
+ struct decvec *dp /* decoding table pointer */
+ )
+{
+ int temp;
+ int j;
+
+ dp->digit++;
+ if (dp->digit == dp->radix)
+ dp->digit = 0;
+ temp = dp->like[dp->radix - 1];
+ for (j = dp->radix - 1; j > 0; j--)
+ dp->like[j] = dp->like[j - 1];
+ dp->like[0] = temp;
+ return (dp->digit);
+}
+
+
+/*
+ * wwv_snr - compute SNR or likelihood function
+ */
+static double
+wwv_snr(
+ double signal, /* signal */
+ double noise /* noise */
+ )
+{
+ double rval;
+
+ /*
+ * This is a little tricky. Due to the way things are measured,
+ * either or both the signal or noise amplitude can be negative
+ * or zero. The intent is that, if the signal is negative or
+ * zero, the SNR must always be zero. This can happen with the
+ * subcarrier SNR before the phase has been aligned. On the
+ * other hand, in the likelihood function the "noise" is the
+ * next maximum down from the peak and this could be negative.
+ * However, in this case the SNR is truly stupendous, so we
+ * simply cap at MAXSNR dB (40).
+ */
+ if (signal <= 0) {
+ rval = 0;
+ } else if (noise <= 0) {
+ rval = MAXSNR;
+ } else {
+ rval = 20. * log10(signal / noise);
+ if (rval > MAXSNR)
+ rval = MAXSNR;
+ }
+ return (rval);
+}
+
+
+/*
+ * wwv_newchan - change to new data channel
+ *
+ * The radio actually appears to have ten channels, one channel for each
+ * of five frequencies and each of two stations (WWV and WWVH), although
+ * if not tunable only the DCHAN channel appears live. While the radio
+ * is tuned to the working data channel frequency and station for most
+ * of the minute, during seconds 59, 0 and 1 the radio is tuned to a
+ * probe frequency in order to search for minute sync pulse and data
+ * subcarrier from other transmitters.
+ *
+ * The search for WWV and WWVH operates simultaneously, with WWV minute
+ * sync pulse at 1000 Hz and WWVH at 1200 Hz. The probe frequency
+ * rotates each minute over 2.5, 5, 10, 15 and 20 MHz in order and yes,
+ * we all know WWVH is dark on 20 MHz, but few remember when WWV was lit
+ * on 25 MHz.
+ *
+ * This routine selects the best channel using a metric computed from
+ * the reachability register and minute pulse amplitude. Normally, the
+ * award goes to the the channel with the highest metric; but, in case
+ * of ties, the award goes to the channel with the highest minute sync
+ * pulse amplitude and then to the highest frequency.
+ *
+ * The routine performs an important squelch function to keep dirty data
+ * from polluting the integrators. In order to consider a station valid,
+ * the metric must be at least MTHR (13); otherwise, the station select
+ * bits are cleared so the second sync is disabled and the data bit
+ * integrators averaged to a miss.
+ */
+static int
+wwv_newchan(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct sync *sp, *rp;
+ double rank, dtemp;
+ int i, j, rval;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Search all five station pairs looking for the channel with
+ * maximum metric.
+ */
+ sp = NULL;
+ j = 0;
+ rank = 0;
+ for (i = 0; i < NCHAN; i++) {
+ rp = &up->mitig[i].wwvh;
+ dtemp = rp->metric;
+ if (dtemp >= rank) {
+ rank = dtemp;
+ sp = rp;
+ j = i;
+ }
+ rp = &up->mitig[i].wwv;
+ dtemp = rp->metric;
+ if (dtemp >= rank) {
+ rank = dtemp;
+ sp = rp;
+ j = i;
+ }
+ }
+
+ /*
+ * If the strongest signal is less than the MTHR threshold (13),
+ * we are beneath the waves, so squelch the second sync and
+ * advance to the next station. This makes sure all stations are
+ * scanned when the ions grow dim. If the strongest signal is
+ * greater than the threshold, tune to that frequency and
+ * transmitter QTH.
+ */
+ up->status &= ~(SELV | SELH);
+ if (rank < MTHR) {
+ up->dchan = (up->dchan + 1) % NCHAN;
+ if (up->status & METRIC) {
+ up->status &= ~METRIC;
+ refclock_report(peer, CEVNT_PROP);
+ }
+ rval = FALSE;
+ } else {
+ up->dchan = j;
+ up->sptr = sp;
+ memcpy(&pp->refid, sp->refid, 4);
+ peer->refid = pp->refid;
+ up->status |= METRIC;
+ if (sp->select & SELV) {
+ up->status |= SELV;
+ up->pdelay = pp->fudgetime1;
+ } else if (sp->select & SELH) {
+ up->status |= SELH;
+ up->pdelay = pp->fudgetime2;
+ } else {
+ up->pdelay = 0;
+ }
+ rval = TRUE;
+ }
+#ifdef ICOM
+ if (up->fd_icom > 0)
+ wwv_qsy(peer, up->dchan);
+#endif /* ICOM */
+ return (rval);
+}
+
+
+/*
+ * wwv_newgame - reset and start over
+ *
+ * There are three conditions resulting in a new game:
+ *
+ * 1 After finding the minute pulse (MSYNC lit), going 15 minutes
+ * (DATA) without finding the unit seconds digit.
+ *
+ * 2 After finding good data (DSYNC lit), going more than 40 minutes
+ * (SYNCH) without finding station sync (INSYNC lit).
+ *
+ * 3 After finding station sync (INSYNC lit), going more than 2 days
+ * (PANIC) without finding any station.
+ */
+static void
+wwv_newgame(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct chan *cp;
+ int i;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Initialize strategic values. Note we set the leap bits
+ * NOTINSYNC and the refid "NONE".
+ */
+ if (up->status)
+ up->errflg = CEVNT_TIMEOUT;
+ peer->leap = LEAP_NOTINSYNC;
+ up->watch = up->status = up->alarm = 0;
+ up->avgint = MINAVG;
+ up->freq = 0;
+ up->gain = MAXGAIN / 2;
+
+ /*
+ * Initialize the station processes for audio gain, select bit,
+ * station/frequency identifier and reference identifier. Start
+ * probing at the strongest channel or the default channel if
+ * nothing heard.
+ */
+ memset(up->mitig, 0, sizeof(up->mitig));
+ for (i = 0; i < NCHAN; i++) {
+ cp = &up->mitig[i];
+ cp->gain = up->gain;
+ cp->wwv.select = SELV;
+ snprintf(cp->wwv.refid, sizeof(cp->wwv.refid), "WV%.0f",
+ floor(qsy[i]));
+ cp->wwvh.select = SELH;
+ snprintf(cp->wwvh.refid, sizeof(cp->wwvh.refid), "WH%.0f",
+ floor(qsy[i]));
+ }
+ up->dchan = (DCHAN + NCHAN - 1) % NCHAN;
+ wwv_newchan(peer);
+ up->schan = up->dchan;
+}
+
+/*
+ * wwv_metric - compute station metric
+ *
+ * The most significant bits represent the number of ones in the
+ * station reachability register. The least significant bits represent
+ * the minute sync pulse amplitude. The combined value is scaled 0-100.
+ */
+double
+wwv_metric(
+ struct sync *sp /* station pointer */
+ )
+{
+ double dtemp;
+
+ dtemp = sp->count * MAXAMP;
+ if (sp->synmax < MAXAMP)
+ dtemp += sp->synmax;
+ else
+ dtemp += MAXAMP - 1;
+ dtemp /= (AMAX + 1) * MAXAMP;
+ return (dtemp * 100.);
+}
+
+
+#ifdef ICOM
+/*
+ * wwv_qsy - Tune ICOM receiver
+ *
+ * This routine saves the AGC for the current channel, switches to a new
+ * channel and restores the AGC for that channel. If a tunable receiver
+ * is not available, just fake it.
+ */
+static int
+wwv_qsy(
+ struct peer *peer, /* peer structure pointer */
+ int chan /* channel */
+ )
+{
+ int rval = 0;
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (up->fd_icom > 0) {
+ up->mitig[up->achan].gain = up->gain;
+ rval = icom_freq(up->fd_icom, peer->ttl & 0x7f,
+ qsy[chan]);
+ up->achan = chan;
+ up->gain = up->mitig[up->achan].gain;
+ }
+ return (rval);
+}
+#endif /* ICOM */
+
+
+/*
+ * timecode - assemble timecode string and length
+ *
+ * Prettytime format - similar to Spectracom
+ *
+ * sq yy ddd hh:mm:ss ld dut lset agc iden sig errs freq avgt
+ *
+ * s sync indicator ('?' or ' ')
+ * q error bits (hex 0-F)
+ * yyyy year of century
+ * ddd day of year
+ * hh hour of day
+ * mm minute of hour
+ * ss second of minute)
+ * l leap second warning (' ' or 'L')
+ * d DST state ('S', 'D', 'I', or 'O')
+ * dut DUT sign and magnitude (0.1 s)
+ * lset minutes since last clock update
+ * agc audio gain (0-255)
+ * iden reference identifier (station and frequency)
+ * sig signal quality (0-100)
+ * errs bit errors in last minute
+ * freq frequency offset (PPM)
+ * avgt averaging time (s)
+ */
+static int
+timecode(
+ struct wwvunit *up, /* driver structure pointer */
+ char * tc, /* target string */
+ size_t tcsiz /* target max chars */
+ )
+{
+ struct sync *sp;
+ int year, day, hour, minute, second, dut;
+ char synchar, leapchar, dst;
+ char cptr[50];
+
+
+ /*
+ * Common fixed-format fields
+ */
+ synchar = (up->status & INSYNC) ? ' ' : '?';
+ year = up->decvec[YR].digit + up->decvec[YR + 1].digit * 10 +
+ 2000;
+ day = up->decvec[DA].digit + up->decvec[DA + 1].digit * 10 +
+ up->decvec[DA + 2].digit * 100;
+ hour = up->decvec[HR].digit + up->decvec[HR + 1].digit * 10;
+ minute = up->decvec[MN].digit + up->decvec[MN + 1].digit * 10;
+ second = 0;
+ leapchar = (up->misc & SECWAR) ? 'L' : ' ';
+ dst = dstcod[(up->misc >> 4) & 0x3];
+ dut = up->misc & 0x7;
+ if (!(up->misc & DUTS))
+ dut = -dut;
+ snprintf(tc, tcsiz, "%c%1X", synchar, up->alarm);
+ snprintf(cptr, sizeof(cptr),
+ " %4d %03d %02d:%02d:%02d %c%c %+d",
+ year, day, hour, minute, second, leapchar, dst, dut);
+ strlcat(tc, cptr, tcsiz);
+
+ /*
+ * Specific variable-format fields
+ */
+ sp = up->sptr;
+ snprintf(cptr, sizeof(cptr), " %d %d %s %.0f %d %.1f %d",
+ up->watch, up->mitig[up->dchan].gain, sp->refid,
+ sp->metric, up->errcnt, up->freq / WWV_SEC * 1e6,
+ up->avgint);
+ strlcat(tc, cptr, tcsiz);
+
+ return strlen(tc);
+}
+
+
+/*
+ * wwv_gain - adjust codec gain
+ *
+ * This routine is called at the end of each second. During the second
+ * the number of signal clips above the MAXAMP threshold (6000). If
+ * there are no clips, the gain is bumped up; if there are more than
+ * MAXCLP clips (100), it is bumped down. The decoder is relatively
+ * insensitive to amplitude, so this crudity works just peachy. The
+ * routine also jiggles the input port and selectively mutes the
+ * monitor.
+ */
+static void
+wwv_gain(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ /*
+ * Apparently, the codec uses only the high order bits of the
+ * gain control field. Thus, it may take awhile for changes to
+ * wiggle the hardware bits.
+ */
+ if (up->clipcnt == 0) {
+ up->gain += 4;
+ if (up->gain > MAXGAIN)
+ up->gain = MAXGAIN;
+ } else if (up->clipcnt > MAXCLP) {
+ up->gain -= 4;
+ if (up->gain < 0)
+ up->gain = 0;
+ }
+ audio_gain(up->gain, up->mongain, up->port);
+ up->clipcnt = 0;
+#if DEBUG
+ if (debug > 1)
+ audio_show();
+#endif
+}
+
+
+#else
+int refclock_wwv_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_wwvb.c b/ntpd/refclock_wwvb.c
new file mode 100644
index 0000000..75897fc
--- /dev/null
+++ b/ntpd/refclock_wwvb.c
@@ -0,0 +1,603 @@
+/*
+ * refclock_wwvb - clock driver for Spectracom WWVB and GPS receivers
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_SPECTRACOM)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef HAVE_PPSAPI
+#include "ppsapi_timepps.h"
+#include "refclock_atom.h"
+#endif /* HAVE_PPSAPI */
+
+/*
+ * This driver supports the Spectracom Model 8170 and Netclock/2 WWVB
+ * Synchronized Clocks and the Netclock/GPS Master Clock. Both the WWVB
+ * and GPS clocks have proven reliable sources of time; however, the
+ * WWVB clocks have proven vulnerable to high ambient conductive RF
+ * interference. The claimed accuracy of the WWVB clocks is 100 us
+ * relative to the broadcast signal, while the claimed accuracy of the
+ * GPS clock is 50 ns; however, in most cases the actual accuracy is
+ * limited by the resolution of the timecode and the latencies of the
+ * serial interface and operating system.
+ *
+ * The WWVB and GPS clocks should be configured for 24-hour display,
+ * AUTO DST off, time zone 0 (UTC), data format 0 or 2 (see below) and
+ * baud rate 9600. If the clock is to used as the source for the IRIG
+ * Audio Decoder (refclock_irig.c in this distribution), it should be
+ * configured for AM IRIG output and IRIG format 1 (IRIG B with
+ * signature control). The GPS clock can be configured either to respond
+ * to a 'T' poll character or left running continuously.
+ *
+ * There are two timecode formats used by these clocks. Format 0, which
+ * is available with both the Netclock/2 and 8170, and format 2, which
+ * is available only with the Netclock/2, specially modified 8170 and
+ * GPS.
+ *
+ * Format 0 (22 ASCII printing characters):
+ *
+ * <cr><lf>i ddd hh:mm:ss TZ=zz<cr><lf>
+ *
+ * on-time = first <cr>
+ * hh:mm:ss = hours, minutes, seconds
+ * i = synchronization flag (' ' = in synch, '?' = out of synch)
+ *
+ * The alarm condition is indicated by other than ' ' at i, which occurs
+ * during initial synchronization and when received signal is lost for
+ * about ten hours.
+ *
+ * Format 2 (24 ASCII printing characters):
+ *
+ * <cr><lf>iqyy ddd hh:mm:ss.fff ld
+ *
+ * on-time = <cr>
+ * i = synchronization flag (' ' = in synch, '?' = out of synch)
+ * q = quality indicator (' ' = locked, 'A'...'D' = unlocked)
+ * yy = year (as broadcast)
+ * ddd = day of year
+ * hh:mm:ss.fff = hours, minutes, seconds, milliseconds
+ *
+ * The alarm condition is indicated by other than ' ' at i, which occurs
+ * during initial synchronization and when received signal is lost for
+ * about ten hours. The unlock condition is indicated by other than ' '
+ * at q.
+ *
+ * The q is normally ' ' when the time error is less than 1 ms and a
+ * character in the set 'A'...'D' when the time error is less than 10,
+ * 100, 500 and greater than 500 ms respectively. The l is normally ' ',
+ * but is set to 'L' early in the month of an upcoming UTC leap second
+ * and reset to ' ' on the first day of the following month. The d is
+ * set to 'S' for standard time 'I' on the day preceding a switch to
+ * daylight time, 'D' for daylight time and 'O' on the day preceding a
+ * switch to standard time. The start bit of the first <cr> is
+ * synchronized to the indicated time as returned.
+ *
+ * This driver does not need to be told which format is in use - it
+ * figures out which one from the length of the message. The driver
+ * makes no attempt to correct for the intrinsic jitter of the radio
+ * itself, which is a known problem with the older radios.
+ *
+ * PPS Signal Processing
+ *
+ * When PPS signal processing is enabled, and when the system clock has
+ * been set by this or another driver and the PPS signal offset is
+ * within 0.4 s of the system clock offset, the PPS signal replaces the
+ * timecode for as long as the PPS signal is active. If for some reason
+ * the PPS signal fails for one or more poll intervals, the driver
+ * reverts to the timecode. If the timecode fails for one or more poll
+ * intervals, the PPS signal is disconnected.
+ *
+ * Fudge Factors
+ *
+ * This driver can retrieve a table of quality data maintained
+ * internally by the Netclock/2 clock. If flag4 of the fudge
+ * configuration command is set to 1, the driver will retrieve this
+ * table and write it to the clockstats file when the first timecode
+ * message of a new day is received.
+ *
+ * PPS calibration fudge time 1: format 0 .003134, format 2 .004034
+ */
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/wwvb%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-13) /* precision assumed (about 100 us) */
+#define PPS_PRECISION (-13) /* precision assumed (about 100 us) */
+#define REFID "WWVB" /* reference ID */
+#define DESCRIPTION "Spectracom WWVB/GPS Receiver" /* WRU */
+
+#define LENWWVB0 22 /* format 0 timecode length */
+#define LENWWVB2 24 /* format 2 timecode length */
+#define LENWWVB3 29 /* format 3 timecode length */
+#define MONLIN 15 /* number of monitoring lines */
+
+/*
+ * WWVB unit control structure
+ */
+struct wwvbunit {
+#ifdef HAVE_PPSAPI
+ struct refclock_atom atom; /* PPSAPI structure */
+ int ppsapi_tried; /* attempt PPSAPI once */
+ int ppsapi_lit; /* time_pps_create() worked */
+ int tcount; /* timecode sample counter */
+ int pcount; /* PPS sample counter */
+#endif /* HAVE_PPSAPI */
+ l_fp laststamp; /* last <CR> timestamp */
+ int prev_eol_cr; /* was last EOL <CR> (not <LF>)? */
+ u_char lasthour; /* last hour (for monitor) */
+ u_char linect; /* count ignored lines (for monitor */
+};
+
+/*
+ * Function prototypes
+ */
+static int wwvb_start (int, struct peer *);
+static void wwvb_shutdown (int, struct peer *);
+static void wwvb_receive (struct recvbuf *);
+static void wwvb_poll (int, struct peer *);
+static void wwvb_timer (int, struct peer *);
+#ifdef HAVE_PPSAPI
+static void wwvb_control (int, const struct refclockstat *,
+ struct refclockstat *, struct peer *);
+#define WWVB_CONTROL wwvb_control
+#else
+#define WWVB_CONTROL noentry
+#endif /* HAVE_PPSAPI */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_wwvb = {
+ wwvb_start, /* start up driver */
+ wwvb_shutdown, /* shut down driver */
+ wwvb_poll, /* transmit poll message */
+ WWVB_CONTROL, /* fudge set/change notification */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old wwvb_buginfo) */
+ wwvb_timer /* called once per second */
+};
+
+
+/*
+ * wwvb_start - open the devices and initialize data for processing
+ */
+static int
+wwvb_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+ fd = refclock_open(device, SPEED232, LDISC_CLK);
+ if (fd <= 0)
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc_zero(sizeof(*up));
+ pp = peer->procptr;
+ pp->io.clock_recv = wwvb_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ return (0);
+ }
+ pp->unitptr = up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy(&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * wwvb_shutdown - shut down the clock
+ */
+static void
+wwvb_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc * pp;
+ struct wwvbunit * up;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (-1 != pp->io.fd)
+ io_closeclock(&pp->io);
+ if (NULL != up)
+ free(up);
+}
+
+
+/*
+ * wwvb_receive - receive data from the serial interface
+ */
+static void
+wwvb_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct wwvbunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ l_fp trtmp; /* arrival timestamp */
+ int tz; /* time zone */
+ int day, month; /* ddd conversion */
+ int temp; /* int temp */
+ char syncchar; /* synchronization indicator */
+ char qualchar; /* quality indicator */
+ char leapchar; /* leap indicator */
+ char dstchar; /* daylight/standard indicator */
+ char tmpchar; /* trashbin */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ /*
+ * Note we get a buffer and timestamp for both a <cr> and <lf>,
+ * but only the <cr> timestamp is retained. Note: in format 0 on
+ * a Netclock/2 or upgraded 8170 the start bit is delayed 100
+ * +-50 us relative to the pps; however, on an unmodified 8170
+ * the start bit can be delayed up to 10 ms. In format 2 the
+ * reading precision is only to the millisecond. Thus, unless
+ * you have a PPS gadget and don't have to have the year, format
+ * 0 provides the lowest jitter.
+ * Save the timestamp of each <CR> in up->laststamp. Lines with
+ * no characters occur for every <LF>, and for some <CR>s when
+ * format 0 is used. Format 0 starts and ends each cycle with a
+ * <CR><LF> pair, format 2 starts each cycle with its only pair.
+ * The preceding <CR> is the on-time character for both formats.
+ * The timestamp provided with non-empty lines corresponds to
+ * the <CR> following the timecode, which is ultimately not used
+ * with format 0 and is used for the following timecode for
+ * format 2.
+ */
+ if (temp == 0) {
+ if (up->prev_eol_cr) {
+ DPRINTF(2, ("wwvb: <LF> @ %s\n",
+ prettydate(&trtmp)));
+ } else {
+ up->laststamp = trtmp;
+ DPRINTF(2, ("wwvb: <CR> @ %s\n",
+ prettydate(&trtmp)));
+ }
+ up->prev_eol_cr = !up->prev_eol_cr;
+ return;
+ }
+ pp->lencode = temp;
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ up->prev_eol_cr = TRUE;
+ DPRINTF(2, ("wwvb: code @ %s\n"
+ " using %s minus one char\n",
+ prettydate(&trtmp), prettydate(&pp->lastrec)));
+ if (L_ISZERO(&pp->lastrec))
+ return;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. This code uses the timecode length to determine
+ * format 0, 2 or 3. If the timecode has invalid length or is
+ * not in proper format, we declare bad format and exit.
+ */
+ syncchar = qualchar = leapchar = dstchar = ' ';
+ tz = 0;
+ switch (pp->lencode) {
+
+ case LENWWVB0:
+
+ /*
+ * Timecode format 0: "I ddd hh:mm:ss DTZ=nn"
+ */
+ if (sscanf(pp->a_lastcode,
+ "%c %3d %2d:%2d:%2d%c%cTZ=%2d",
+ &syncchar, &pp->day, &pp->hour, &pp->minute,
+ &pp->second, &tmpchar, &dstchar, &tz) == 8) {
+ pp->nsec = 0;
+ break;
+ }
+ goto bad_format;
+
+ case LENWWVB2:
+
+ /*
+ * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD" */
+ if (sscanf(pp->a_lastcode,
+ "%c%c %2d %3d %2d:%2d:%2d.%3ld %c",
+ &syncchar, &qualchar, &pp->year, &pp->day,
+ &pp->hour, &pp->minute, &pp->second, &pp->nsec,
+ &leapchar) == 9) {
+ pp->nsec *= 1000000;
+ break;
+ }
+ goto bad_format;
+
+ case LENWWVB3:
+
+ /*
+ * Timecode format 3: "0003I yyyymmdd hhmmss+0000SL#"
+ * WARNING: Undocumented, and the on-time character # is
+ * not yet handled correctly by this driver. It may be
+ * as simple as compensating for an additional 1/960 s.
+ */
+ if (sscanf(pp->a_lastcode,
+ "0003%c %4d%2d%2d %2d%2d%2d+0000%c%c",
+ &syncchar, &pp->year, &month, &day, &pp->hour,
+ &pp->minute, &pp->second, &dstchar, &leapchar) == 8)
+ {
+ pp->day = ymd2yd(pp->year, month, day);
+ pp->nsec = 0;
+ break;
+ }
+ goto bad_format;
+
+ default:
+ bad_format:
+
+ /*
+ * Unknown format: If dumping internal table, record
+ * stats; otherwise, declare bad format.
+ */
+ if (up->linect > 0) {
+ up->linect--;
+ record_clock_stats(&peer->srcadr,
+ pp->a_lastcode);
+ } else {
+ refclock_report(peer, CEVNT_BADREPLY);
+ }
+ return;
+ }
+
+ /*
+ * Decode synchronization, quality and leap characters. If
+ * unsynchronized, set the leap bits accordingly and exit.
+ * Otherwise, set the leap bits according to the leap character.
+ * Once synchronized, the dispersion depends only on the
+ * quality character.
+ */
+ switch (qualchar) {
+
+ case ' ':
+ pp->disp = .001;
+ pp->lastref = pp->lastrec;
+ break;
+
+ case 'A':
+ pp->disp = .01;
+ break;
+
+ case 'B':
+ pp->disp = .1;
+ break;
+
+ case 'C':
+ pp->disp = .5;
+ break;
+
+ case 'D':
+ pp->disp = MAXDISPERSE;
+ break;
+
+ default:
+ pp->disp = MAXDISPERSE;
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+ if (syncchar != ' ')
+ pp->leap = LEAP_NOTINSYNC;
+ else if (leapchar == 'L')
+ pp->leap = LEAP_ADDSECOND;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp, but only if the PPS is not in control.
+ */
+#ifdef HAVE_PPSAPI
+ up->tcount++;
+ if (peer->flags & FLAG_PPS)
+ return;
+
+#endif /* HAVE_PPSAPI */
+ if (!refclock_process_f(pp, pp->fudgetime2))
+ refclock_report(peer, CEVNT_BADTIME);
+}
+
+
+/*
+ * wwvb_timer - called once per second by the transmit procedure
+ */
+static void
+wwvb_timer(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ char pollchar; /* character sent to clock */
+#ifdef DEBUG
+ l_fp now;
+#endif
+
+ /*
+ * Time to poll the clock. The Spectracom clock responds to a
+ * 'T' by returning a timecode in the format(s) specified above.
+ * Note there is no checking on state, since this may not be the
+ * only customer reading the clock. Only one customer need poll
+ * the clock; all others just listen in.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (up->linect > 0)
+ pollchar = 'R';
+ else
+ pollchar = 'T';
+ if (write(pp->io.fd, &pollchar, 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+#ifdef DEBUG
+ get_systime(&now);
+ if (debug)
+ printf("%c poll at %s\n", pollchar, prettydate(&now));
+#endif
+#ifdef HAVE_PPSAPI
+ if (up->ppsapi_lit &&
+ refclock_pps(peer, &up->atom, pp->sloppyclockflag) > 0) {
+ up->pcount++,
+ peer->flags |= FLAG_PPS;
+ peer->precision = PPS_PRECISION;
+ }
+#endif /* HAVE_PPSAPI */
+}
+
+
+/*
+ * wwvb_poll - called by the transmit procedure
+ */
+static void
+wwvb_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Sweep up the samples received since the last poll. If none
+ * are received, declare a timeout and keep going.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+ pp->polls++;
+
+ /*
+ * If the monitor flag is set (flag4), we dump the internal
+ * quality table at the first timecode beginning the day.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour <
+ (int)up->lasthour)
+ up->linect = MONLIN;
+ up->lasthour = (u_char)pp->hour;
+
+ /*
+ * Process median filter samples. If none received, declare a
+ * timeout and keep going.
+ */
+#ifdef HAVE_PPSAPI
+ if (up->pcount == 0) {
+ peer->flags &= ~FLAG_PPS;
+ peer->precision = PRECISION;
+ }
+ if (up->tcount == 0) {
+ pp->coderecv = pp->codeproc;
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ up->pcount = up->tcount = 0;
+#else /* HAVE_PPSAPI */
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+#endif /* HAVE_PPSAPI */
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("wwvb: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+}
+
+
+/*
+ * wwvb_control - fudge parameters have been set or changed
+ */
+#ifdef HAVE_PPSAPI
+static void
+wwvb_control(
+ int unit,
+ const struct refclockstat *in_st,
+ struct refclockstat *out_st,
+ struct peer *peer
+ )
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+
+ if (!(pp->sloppyclockflag & CLK_FLAG1)) {
+ if (!up->ppsapi_tried)
+ return;
+ up->ppsapi_tried = 0;
+ if (!up->ppsapi_lit)
+ return;
+ peer->flags &= ~FLAG_PPS;
+ peer->precision = PRECISION;
+ time_pps_destroy(up->atom.handle);
+ up->atom.handle = 0;
+ up->ppsapi_lit = 0;
+ return;
+ }
+
+ if (up->ppsapi_tried)
+ return;
+ /*
+ * Light up the PPSAPI interface.
+ */
+ up->ppsapi_tried = 1;
+ if (refclock_ppsapi(pp->io.fd, &up->atom)) {
+ up->ppsapi_lit = 1;
+ return;
+ }
+
+ msyslog(LOG_WARNING, "%s flag1 1 but PPSAPI fails",
+ refnumtoa(&peer->srcadr));
+}
+#endif /* HAVE_PPSAPI */
+
+#else
+int refclock_wwvb_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_zyfer.c b/ntpd/refclock_zyfer.c
new file mode 100644
index 0000000..7b79da3
--- /dev/null
+++ b/ntpd/refclock_zyfer.c
@@ -0,0 +1,331 @@
+/*
+ * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock
+ *
+ * Harlan Stenn, Jan 2002
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ZYFER)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#if defined(HAVE_TERMIOS_H)
+# include <termios.h>
+#elif defined(HAVE_SYS_TERMIOS_H)
+# include <sys/termios.h>
+#endif
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+
+/*
+ * This driver provides support for the TOD serial port of a Zyfer GPStarplus.
+ * This clock also provides PPS as well as IRIG outputs.
+ * Precision is limited by the serial driver, etc.
+ *
+ * If I was really brave I'd hack/generalize the serial driver to deal
+ * with arbitrary on-time characters. This clock *begins* the stream with
+ * `!`, the on-time character, and the string is *not* EOL-terminated.
+ *
+ * Configure the beast for 9600, 8N1. While I see leap-second stuff
+ * in the documentation, the published specs on the TOD format only show
+ * the seconds going to '59'. I see no leap warning in the TOD format.
+ *
+ * The clock sends the following message once per second:
+ *
+ * !TIME,2002,017,07,59,32,2,4,1
+ * YYYY DDD HH MM SS m T O
+ *
+ * ! On-time character
+ * YYYY Year
+ * DDD 001-366 Day of Year
+ * HH 00-23 Hour
+ * MM 00-59 Minute
+ * SS 00-59 Second (probably 00-60)
+ * m 1-5 Time Mode:
+ * 1 = GPS time
+ * 2 = UTC time
+ * 3 = LGPS time (Local GPS)
+ * 4 = LUTC time (Local UTC)
+ * 5 = Manual time
+ * T 4-9 Time Figure Of Merit:
+ * 4 x <= 1us
+ * 5 1us < x <= 10 us
+ * 6 10us < x <= 100us
+ * 7 100us < x <= 1ms
+ * 8 1ms < x <= 10ms
+ * 9 10ms < x
+ * O 0-4 Operation Mode:
+ * 0 Warm-up
+ * 1 Time Locked
+ * 2 Coasting
+ * 3 Recovering
+ * 4 Manual
+ *
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/zyfer%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference ID */
+#define DESCRIPTION "Zyfer GPStarplus" /* WRU */
+
+#define LENZYFER 29 /* timecode length */
+
+/*
+ * Unit control structure
+ */
+struct zyferunit {
+ u_char Rcvbuf[LENZYFER + 1];
+ u_char polled; /* poll message flag */
+ int pollcnt;
+ l_fp tstamp; /* timestamp of last poll */
+ int Rcvptr;
+};
+
+/*
+ * Function prototypes
+ */
+static int zyfer_start (int, struct peer *);
+static void zyfer_shutdown (int, struct peer *);
+static void zyfer_receive (struct recvbuf *);
+static void zyfer_poll (int, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_zyfer = {
+ zyfer_start, /* start up driver */
+ zyfer_shutdown, /* shut down driver */
+ zyfer_poll, /* transmit poll message */
+ noentry, /* not used (old zyfer_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old zyfer_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * zyfer_start - open the devices and initialize data for processing
+ */
+static int
+zyfer_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct zyferunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port.
+ * Something like LDISC_ACTS that looked for ! would be nice...
+ */
+ snprintf(device, sizeof(device), DEVICE, unit);
+ fd = refclock_open(device, SPEED232, LDISC_RAW);
+ if (fd <= 0)
+ return (0);
+
+ msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc(sizeof(struct zyferunit));
+ memset(up, 0, sizeof(struct zyferunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = zyfer_receive;
+ pp->io.srcclock = peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ pp->io.fd = -1;
+ free(up);
+ return (0);
+ }
+ pp->unitptr = up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ up->polled = 0; /* May not be needed... */
+
+ return (1);
+}
+
+
+/*
+ * zyfer_shutdown - shut down the clock
+ */
+static void
+zyfer_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct zyferunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (pp->io.fd != -1)
+ io_closeclock(&pp->io);
+ if (up != NULL)
+ free(up);
+}
+
+
+/*
+ * zyfer_receive - receive data from the serial interface
+ */
+static void
+zyfer_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct zyferunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ int tmode; /* Time mode */
+ int tfom; /* Time Figure Of Merit */
+ int omode; /* Operation mode */
+ u_char *p;
+
+ peer = rbufp->recv_peer;
+ pp = peer->procptr;
+ up = pp->unitptr;
+ p = (u_char *) &rbufp->recv_space;
+ /*
+ * If lencode is 0:
+ * - if *rbufp->recv_space is !
+ * - - call refclock_gtlin to get things going
+ * - else flush
+ * else stuff it on the end of lastcode
+ * If we don't have LENZYFER bytes
+ * - wait for more data
+ * Crack the beast, and if it's OK, process it.
+ *
+ * We use refclock_gtlin() because we might use LDISC_CLK.
+ *
+ * Under FreeBSD, we get the ! followed by two 14-byte packets.
+ */
+
+ if (pp->lencode >= LENZYFER)
+ pp->lencode = 0;
+
+ if (!pp->lencode) {
+ if (*p == '!')
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode,
+ BMAX, &pp->lastrec);
+ else
+ return;
+ } else {
+ memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length);
+ pp->lencode += rbufp->recv_length;
+ pp->a_lastcode[pp->lencode] = '\0';
+ }
+
+ if (pp->lencode < LENZYFER)
+ return;
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+
+ if (pp->lencode != LENZYFER) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1"
+ */
+ if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d",
+ &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second,
+ &tmode, &tfom, &omode) != 8) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ if (tmode != 2) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /* Should we make sure tfom is 4? */
+
+ if (omode != 1) {
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Good place for record_clock_stats()
+ */
+ up->pollcnt = 2;
+
+ if (up->polled) {
+ up->polled = 0;
+ refclock_receive(peer);
+ }
+}
+
+
+/*
+ * zyfer_poll - called by the transmit procedure
+ */
+static void
+zyfer_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct zyferunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * We don't really do anything here, except arm the receiving
+ * side to capture a sample and check for timeouts.
+ */
+ pp = peer->procptr;
+ up = pp->unitptr;
+ if (!up->pollcnt)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ pp->polls++;
+ up->polled = 1;
+}
+
+#else
+int refclock_zyfer_bs;
+#endif /* REFCLOCK */