summaryrefslogtreecommitdiffstats
path: root/contrib/ntp/ntpd/refclock_local.c
blob: 12184dbc97cdd96e02330ae4dc5d7ee99d7474c8 (plain)
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/* wjm 17-aug-1995: add a hook for special treatment of VMS_LOCALUNIT */

/*
 * refclock_local - local pseudo-clock driver
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef REFCLOCK
#include <stdio.h>
#include <ctype.h>
#include <sys/time.h>

#include "ntpd.h"
#include "ntp_refclock.h"
#include "ntp_stdlib.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. 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. For this you would
 * configure this driver at a higher stratum (say 3 or 4) to prevent the
 * server's stratum from falling below that.
 *
 * 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.
 *
 * THis driver includes provisions to telegraph synchronization state
 * and related variables by means of kernel variables with specially
 * modified kernels. This is done using the ntp_adjtime() syscall.
 * In the cases where another protocol or device synchronizes the local
 * host, the data given to the kernel can be slurped up by this driver
 * and distributed to clients by ordinary NTP messaging.
 *
 * In the default mode the behavior of the clock selection algorithm is
 * modified when this driver is in use. The algorithm is designed so
 * that this driver will never be selected unless no other discipline
 * source is available. This can be overriden with the prefer keyword of
 * the server configuration command, in which case only this driver will
 * be selected for synchronization and all other discipline sources will
 * be ignored. This behavior is intended for use when an external
 * discipline source controls the system clock.
 *
 * Fudge Factors
 *
 * The stratum for this driver set at 3 by default, but it can be changed
 * by the fudge command and/or the ntpdc utility. The reference ID is
 * "LCL" by default, but can be changed using the same mechanism. *NEVER*
 * configure this driver to operate at a stratum which might possibly
 * disrupt a client with access to a bona fide primary server, unless the
 * local clock oscillator is reliably disciplined by another source.
 * *NEVER NEVER* configure a server which might devolve to an undisciplined
 * local clock to use multicast mode. Always remember that an improperly
 * configured local clock driver let loose in the Internet can cause
 * very serious disruption. This is why most of us who care about good
 * time use cryptographic authentication.
 *
 * This driver provides a mechanism to trim the local clock in both time
 * and frequency, as well as a way to manipulate the leap bits. The
 * fudge time1 parameter adjusts the time, in seconds, and the fudge
 * time2 parameter adjusts the frequency, in ppm. The fudge time1 parameter
 * is additive; that is, it adds an increment to the current time. The
 * fudge time2 parameter directly sets the frequency.
 */

/*
 * Local interface definitions
 */
#define PRECISION	(-7)	/* about 10 ms precision */
#define REFID		"LCL\0" /* reference ID */
#define DESCRIPTION "Undisciplined local clock" /* WRU */

#define STRATUM 	3	/* 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;

#ifdef KERNEL_PLL
/*
 * Imported from ntp_loopfilter
 */
extern int pll_control; 	/* kernel pll control */
extern int kern_enable;		/* kernel pll enabled */
extern int ext_enable;		/* external clock enable */
#endif /* KERNEL_PLL */

/*
 * Function prototypes
 */
static	int local_start P((int, struct peer *));
static	void	local_poll	P((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;
	peer->stratum = STRATUM;
	pp->clockdesc = DESCRIPTION;
	memcpy((char *)&pp->refid, REFID, 4);
#if defined(VMS) && defined(VMS_LOCALUNIT)
	/* provide a non-standard REFID */
	if(unit == VMS_LOCALUNIT)
		memcpy((char *)&pp->refid,"LCLv",4);
#endif	/* VMS && VMS_LOCALUNIT */
	poll_time = current_time;
	return (1);
}


/*
 * local_poll - called by the transmit procedure
 */
static void
local_poll(
	int unit,
	struct peer *peer
	)
{
	struct refclockproc *pp;
#if defined(KERNEL_PLL) && defined(STA_CLK)
	struct timex ntv;
	int retval;
#endif /* KERNEL_PLL STA_CLK */

#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).
	 */
	get_systime(&pp->lastrec);
	pp->fudgetime1 += pp->fudgetime2 * 1e-6 * (current_time -
	    poll_time);
	poll_time = current_time;
	refclock_process_offset(pp, pp->lastrec, pp->lastrec, pp->fudgetime1);
	pp->leap = LEAP_NOWARNING;
	pp->disp = DISPERSION;
	pp->variance = 0;
#if defined(KERNEL_PLL) && defined(STA_CLK)

	/*
	 * If the kernel pll code is up and running, somebody else
	 * may come diddle the clock. If so, they better use ntp_adjtime(),
	 * and set the STA_CLK bit in the status word. In this case, the
	 * performance information is read from the kernel and becomes the
	 * variables presented to the clock mitigation process.
	 */
	if (pll_control && kern_enable && (peer->flags & FLAG_PREFER)) {
		memset((char *)&ntv,  0, sizeof ntv);
		retval = ntp_adjtime(&ntv);
		if (ntv.status & STA_CLK) {
			ext_enable = 1;
			switch(retval) {

				case TIME_OK:
				pp->leap = LEAP_NOWARNING;
				break;

				case TIME_INS:
				pp->leap = LEAP_ADDSECOND;
				break;

				case TIME_DEL:
				pp->leap = LEAP_DELSECOND;
				break;

				case TIME_ERROR:
				pp->leap = LEAP_NOTINSYNC;
			}
			pp->disp = ntv.maxerror / 1e6;
			pp->variance = SQUARE(ntv.esterror / 1e6);
		}
	} else {
		ext_enable = 0;
	}
#endif /* KERNEL_PLL STA_CLK */
	refclock_receive(peer);
	pp->fudgetime1 = 0;
}
#endif /* REFCLOCK */
OpenPOWER on IntegriCloud