Figure 24. NPv4 Header Format

Figure 25. NTPv4 Extension Field Format

Appendix A. NTPv4 Packet Formats

The NTP packet consists of a number of 32-bit (4 octet) words in network byte order. The packet 
format consists of three components, the header itself, one or more optional extension fields and 
an optional message authentication code (MAC). The header component is identical to the 
NTPv3 header and previous versions. The optional extension fields are used by the Autokey 
public key cryptographic algorithms described in [3]. The optional MAC is used by both 
Autokey and the symmetric key cryptographic algorithms described in the main body of this 
report.

A.1 NTP Header Field Format

The NTP header format is shown in Figure 24, where the size of some multiple-word fields is 
shown in bits if not the default 32 bits. The header extends from the beginning of the packet to 
the end of the Transmit Timestamp field. The interpretation of the header fields is shown in the 
main body of this report. When using the IPv4 address family these fields are backwards 
compatible with NTPv3. When using the IPv6 address family on an NTPv4 server with a NTPv3 
client, the Reference Identifier field appears to be a random value and a timing loop might not be 
detected. The incidence of this, which would be considered a birthday event, will be very rare.

The message authentication code (MAC) consists of a 32-bit Key Identifier followed by a 128-
bit Message Digest. The message digest, or cryptosum, is calculated as in RFC-1321 [7] over the 
fields shown in the figure.

A.2 NTPv4 Extension Field Format

In NTPv4 one or more extension fields can be inserted after the header and before the MAC, 
which is always present when extension fields are present. The extension fields can occur in any 
order; however, in some cases there is a preferred order which improves the protocol efficiency. 
While previous versions of the Autokey protocol used several different extension field formats, 
in version 2 of the protocol only a single extension field format is used.

An extension field contains a request or response message in the format shown in Figure 25. All 
extension fields are zero-padded to a word (4 octets) boundary. The Length field covers the 
entire extension field, including the Length and Padding fields. While the minimum field length 
is 4 words (16 octets), a maximum field length remains to be established. The reference 
implementation discards any packet with an extension field length more than 1024 octets.

The presence of the MAC and extension fields in the packet is determined from the length of the 
remaining area after the header to the end of the packet. The parser initializes a pointer just after 
the header. If the Length field is not a multiple of 4, a format error has occurred and the packet is 
discarded. The following cases are possible based on the remaining length in words.

0	The packet is not authenticated.
1	The packet is an error report or crypto-NAK.
2, 3, 4	 The packet is discarded with a format error.
5	The remainder of the packet is the MAC.
>5	One or more extension fields are present.

If an extension field is present, the parser examines the Length field. If the length is less than 4 or 
not a multiple of 4, a format error has occurred and the packet is discarded; otherwise, the parser 
increments the pointer by this value. The parser now uses the same rules as above to determine 
whether a MAC is present and/or another extension field. An additional implementation-
dependent test is necessary to ensure the pointer does not stray outside the buffer space occupied 
by the packet.

In the Autokey Version 2 format, the 8-bit Code field specifies the request or response operation, 
while the 6-bit Version Number (VN) field is 2 for the current protocol version. The R bit is lit 
for a response and the E bit lit for an error. The Timestamp and Filestamp fields carry the 
seconds field of an NTP timestamp. The Timestamp field establishes the signature epoch of the 
data field in the message, while the Filestamp field establishes the generation epoch of the file 
that ultimately produced the data that is signed. The optional Value field carries the data and the 
optional Signature field the signature that validates the data. Further details are in [3].

Appendix B. Code Skeleton

This appendix is intended to describe the protocol and algorithms of the reference 
implementation in a general way using what is called a code skeleton program. This consists of a 
set of definitions, structures and code segments which illustrate the protocol operations without 
the complexities of the actual reference implementation code. This program is not an executable 
and is not designed to run in the ordinary sense. It is designed to be compiled only in order to 
verify consistent variable and type usage. The program is not intended to be fast or compact, just 
to demonstrate the algorithms with sufficient fidelity to understand how they work.

Most of the features of the reference implementation are included in the code skeleton, with the 
following exceptions: There are no provisions for reference clocks, server discovery or public 
key (Autokey) cryptography. There is no huff-n’-puff filter, anti-clockhop hysteresis or 
monitoring provisions. Many of the values that can be tinkered in the reference implementation 
are assumed constants here. There are only minimal provisions for the kiss-o-death packet and no 
responding code.

The code skeleton consists of five segments, a header segment included by each of the other 
segments, plus a code segment for the main program and peer, system, clock_adjust and poll 
processes. These are presented in order below along with definitions and variables specific to 
each process.

B.1 Global Definitions

Following are definitions and other data shared by all programs. These values are defined in a 
header file ntp4.h which is included in all files.

B.1.1  Definitions, Constants and Parameters

#include <math.h>			/* avoids complaints about sqrt() */

#include <sys/time.h>			/* for gettimeofday() and friends */

#include <stdlib.h>			/* for malloc() and friends */



/*

 * Data types

 *

 * This program assumes the int data type is 32 bitsand the long data

 * type is 64 bits. The native data type used in most calculations is

 * floating double. The data types used in some packet header fields

 * require conversion to and from this representation. Some header

 * fields involve partitioning an octet, here represeted by individual

 * octets.

 *

 * The 64-bit NTP timestamp format used in timestamp calculations is

 * unsigned seconds and fraction with the decimal point to the left of

 * bit 32. The only operation permitted with these values is

 * subtraction, yielding a signed 31-bit difference. The 32-bit NTP

 * short format used in delay and dispersion calculations is seconds and

 * fraction with the decimal point to the left of bit 16. The only

 * operations permitted with these values are addition and

 * multiplication by a constant.

 *

 * The IPv4 address is 32 bits, while the IPv6 address is 128 bits. The

 * message digest field is 128 bits as constructed by the MD5 algorithm.

 * The precision and poll interval fields are signed log2 seconds. 

 */

typedef unsigned long tstamp;				/* NTP timestamp format */

typedef unsigned int tdist;				/* NTP short format */

typedef unsigned long ipaddr;				/* IPv4 or IPv6 address */

typedef unsinged int ipport;				/* IP port number */

typedef unsigned long digest;				/* md5 digest */

typedef signed char s_char;				/* precision and poll interval (log2) */



/*

 * Arithmetic conversion macroni

 */

#define	LOG2D(a)	((a) < 0 ? 1. / (1L << -(a)) : \

		1L << (a))		     /* poll, etc. */

#define	LFP2D(a)	((double)(a) / 0x100000000L) /* NTP timestamp */

#define	D2LFP(a)	((tstamp)((a) * 0x100000000L))

#define	FP2D(a)	(double)(a) / 0x10000L)     /* NTP short */

#define	D2FP(a)	((tdist)((a) * 0x10000L))

#define	SQUARE(x)	(x * x)

#define	SQRT(x)	(sqrt(x))



/*

 * Global constants. Some of these might be converted to variables

 * which can be tinkered by configuration or computed on-fly. For

 * instance, the reference implementation computes PRECISION on-fly and

 * provides performance tuning for the defines marked with % below. 

 */

#define	VERSION		4	/* version number */

#define	PORT		123	/* NTP poert number */

#define	MINDISP		.01	/* % minimum dispersion (s) */

#define	MAXDISP		16	/* % maximum dispersion (s) */

#define	MAXDIST		1	/* % distance threshold (s) */

#define	NOSYNC		3	/* leap unsync */

#define	MAXSTRAT		16	/* maximum stratum (infinity metric) */

#define	MINPOLL		4	/* % minimum poll interval (64 s)*/

#define	MAXPOLL		17	/* % maximum poll interval (36.4 h) */

#define	PHI		15e-6	/* % frequency tolerance (15 PPM) */

#define	NSTAGE		8	/* clock register stages */

#define	NMAX		50	/* % maximum number of peers */

#define	NSANE		1	/* % minimum intersection survivors */

#define	NMIN		3	/* % minimum cluster survivors */



/*

 * Global return values

 */

#define	TRUE		1	/* boolean true */

#define	FALSE		0	/* boolean false */

#define	NULL		0	/* empty pointer */



/*

 * Local clock process return codes

 */

#define	IGNORE		0	/* ignore */

#define	SLEW		1	/* slew adjustment */

#define	STEP		2	/* step adjustment */

#define	PANIC		3	/* panic - no adjustment */



/*

 * System flags

 */

#define	S_FLAGS		0	/* any system flags */

#define	S_BCSTENAB		0x1	/* enable broadcast client */



/*

 * Peer flags

 */

#define	P_FLAGS		0	/* any peer flags */

#define	P_EPHEM		0x01	/* association is ephemeral */

#define	P_BURST		0x02	/* burst enable */

#define	P_IBURST		0x04	/* intial burst enable */

#define	P_NOTRUST		0x08	/* authenticated access */

#define	P_NOPEER		0x10	/* authenticated mobilization */



/*

 * Authentication codes

 */

#define	A_NONE		0	/* no authentication */

#define	A_OK		1	/* authentication OK */

#define	A_ERROR		2	/* authentication error */

#define	A_CRYPTO		3	/* crypto-NAK */



/*

 * Association state codes

 */

#define	X_INIT		0	/* initialization */

#define	X_STALE		1	/* timeout */

#define	X_STEP		2	/* time step */

#define	X_ERROR		3	/* authentication error */

#define	X_CRYPTO		4	/* crypto-NAK received */

#define	X_NKEY		5	/* untrusted key */



/*

 * Protocol mode definitionss

 */

#define	M_RSVD		0	/* reserved */

#define	M_SACT		1	/* symmetric active */

#define	M_PASV		2	/* symmetric passive */

#define	M_CLNT		3	/* client */

#define	M_SERV		4	/* server */

#define	M_BCST		5	/* broadcast server */

#define	M_BCLN		6	/* broadcast client */



/*

 * Clock state definitions

 */

#define NSET		0	/* clock never set */

#define FSET		1	/* frequency set from file */

#define SPIK		2	/* spike detected */

#define FREQ		3	/* frequency mode */

#define SYNC		4	/* clock synchronized */



B.1.2  Packet Data Structures

/*

 * The receive and transmit packets may contain an optional message

 * authentication code (MAC) consisting of a key identifier (keyid) and

 * message digest (mac). NTPv4 supports optional extension fields which

 * are inserted after the the header and before the MAC, but these are

 * not described here.

 *

 * Receive packet

 *

 * Note the dst timestamp is not part of the packet itself. It is

 * captured upon arrival and returned in the receive buffer along with

 * the buffer length and data. Note that some of the char fields are

 * packed in the actual header, but the details are omited here.

 */

struct r {

	ipaddr	srcaddr;		/* source (remote) address */

	ipaddr	dstaddr;		/* destination (local) address */

	char	version;		/* version number */

	char	leap;		/* leap indicator */

	char	mode;		/* mode */

	char	stratum;		/* stratum */

	char	poll;		/* poll interval */

	s_char	precision;		/* precision */

	tdist	rootdelay;		/* root delay */

	tdist	rootdisp;		/* root dispersion */

	char	refid;		/* reference ID */

	tstamp	reftime;		/* reference time */

	tstamp	org;		/* origin timestamp */

	tstamp	rec;		/* receive timestamp */

	tstamp	xmt;		/* transmit timestamp */

	int	keyid;		/* key ID */

	digest	digest;		/* message digest */

	tstamp	dst;		/* destination timestamp */

} r;



/*

 * Transmit packet

 */

struct x {

	ipaddr	dstaddr;		/* source (local) address */

	ipaddr	srcaddr;		/* destination (remote) address */

	char	version;		/* version number */

	char	leap;		/* leap indicator */

	char	mode;		/* mode */

	char	stratum;		/* stratum */

	char	poll;		/* poll interval */

	s_char	precision;		/* precision */

	tdist	rootdelay;		/* root delay */

	tdist	rootdisp;		/* root dispersion */

	char	refid;		/* reference ID */

	tstamp	reftime;		/* reference time */

	tstamp	org;		/* origin timestamp */

	tstamp	rec;		/* receive timestamp */

	tstamp	xmt;		/* transmit timestamp */

	int	keyid;		/* key ID */

	digest	digest;		/* message digest */

} x;





B.1.3  Association Data Structures

/*

 * Filter stage structure. Note the t member in this and other

 * structures refers to process time, not real time. Process time

 * increments by one second for every elapsed second of real time.

 */

struct f {

	tstamp	t;		/* update time */

	double	offset;		/* clock ofset */

	double	delay;		/* roundtrip delay */

	double	disp;		/* dispersion */

} f;



/*

 * Association structure. This is shared between the peer process and

 * poll process.

 */

struct p {



	/*

	 * Variables set by configuration

	 */

	ipaddr	srcaddr;		/* source (remote) address */

	ipport	srcport;		/* source port number *.

	ipaddr	dstaddr;		/* destination (local) address */

	ipport	dstport;		/* destination port number */

	char	version;		/* version number */

	char	mode;		/* mode */

	int	keyid;		/* key identifier */

	int	flags;		/* option flags */



	/*

	 * Variables set by received packet

	 */

	char	leap;		/* leap indicator */

	char	mode;		/* mode */

	char	stratum;		/* stratum */

	char	ppoll;		/* peer poll interval */

	double	rootdelay;		/* root delay */

	double	rootdisp;		/* root dispersion */

	char	refid;		/* reference ID */

	tstamp	reftime;		/* reference time */

#define	begin_clear org			/* beginning of clear area */

	tstamp	org;		/* originate timestamp */

	tstamp	rec;		/* receive timestamp */

	tstamp	xmt;		/* transmit timestamp */



	/*

	 * Computed data

	 */

	double	t;		/* update time */

	struct f f[NSTAGE];			/* clock filter */

	double	offset;		/* peer offset */

	double	delay;		/* peer delay */

	double	disp;		/* peer dispersion */

	double	jitter;		/* RMS jitter */



	/*

	 * Poll process variables

	 */

	char	hpoll;		/* host poll interval */

	int	burst;		/* burst counter */

	int	reach;		/* reach register */

#define	end_clear unreach			/* end of clear area */

	int	unreach;		/* unreach counter */

	int	last;		/* last poll time */

	int	next;		/* next poll time */

} p;



B.1.4  System Data Structures

/*

 * Chime list. This is used by the intersection algorithm.

 */

struct m {				/* m is for Marzullo */

	struct p *p;			/* peer structure pointer */

	int	type;		/* high +1, mid 0, low -1 */

	double	edge;		/* correctness interval edge */

} m;



/*

 * Survivor list. This is used by the clustering algorithm.

 */

struct v {

	struct p *p;			/* peer structure pointer */

	double	metric;		/* sort metric */

} v;





/*

 * System structure

 */

struct s {

	tstamp	t;		/* update time */

	char	leap;		/* leap indicator */

	char	stratum;		/* stratum */

	char	poll;		/* poll interval */

	char	precision;		/* precision */

	double	rootdelay;		/* root delay */

	double	rootdisp;		/* root dispersion */

	char	refid;		/* reference ID */

	tstamp	reftime;		/* reference time */

	struct m m[NMAX];			/* chime list */

	struct v v[NMAX];			/* survivor list */

	struct p *p;			/* association ID */

	double	offset;		/* combined offset */

	double	jitter;		/* combined jitter */

	int	flags;		/* option flags */

} s;



B.1.5  Local Clock Data Structure

/*

 * Local clock structure

 */

struct c {

	tstamp	t;		/* update time */

	int	state;		/* current state */

	double	offset;		/* current offset */

	double	base;		/* base offset */

	double	last;		/* previous offset */

	int	count;		/* jiggle counter */

	double	freq;		/* frequency */

	double	jitter;		/* RMS jitter */

	double	wander;		/* RMS wander */

} c;



B.1.6  Function Prototypes

/*

 * Peer process

 */

void	receive(struct r *);			/* receive packet */

void	fast_xmit(struct r *, int, int); /* transmit a reply packet */

struct p *find_assoc(struct r *);				 /* search the association table */

void	packet(struct p *, struct r *); /* process packet */

void	clock_filter(struct p *, double, double, double); /* filter */

int	accept(struct p *);			/* determine fitness of server */

int	access(struct r *);			/* determine access restrictions */



/*

 * System process

 */

void	clock_select();			/* find the best clocks */

void	clock_update(struct p *);			/* update the system clock */

void	clock_combine();			/* combine the offsets */

double	root_dist(struct p *);			/* calculate root distance */



/*

 * Clock discipline process

 */

int	local_clock(struct p *, double); /* clock discipline */

void	rstclock(int, double, double); /* clock state transition */



/*

 * Clock adjust process

 */ 

void	clock_adjust();		/* one-second timer process */



/*

 * Poll process

 */

void	poll(struct p *);			/* poll process */

void	poll_update(struct p *, int); /* update the poll interval */

void	peer_xmit(struct p *);			/* transmit a packet */



/*

 * Main program and utility routines

 */

int	main();			/* main program */

struct p *mobilize(ipaddr, ipaddr, int, int, int, int); /* mobilize */

void	clear(struct p *, int);			/* clear association */

digest	md5(int);			/* generate a message digest */



/*

 * Kernel I/O Interface

 */

struct r *recv_packet();				/* wait for packet */

void	xmit_packet(struct x *);			/* send packet */



.*

 * Kernel system clock interface

 */

void	step_time(double);			/* step time */

void	adjust_time(double);			/* adjust (slew) time */

tstamp	get_time();			/* read time */



B.2 Main Program and Utility Routines

#include “ntp4.h”



/*

 * Definitions

 */

#define	PRECISION	-18		/* precision (log2 s)  */

#define IPADDR		0		/* any IP address */

#define MODE		0		/* any NTP mode */

#define KEYID		0		/* any key identifier */



/*

 * main() - main program

 */

int

main()

{

	struct p *p;			/* peer structure pointer */

	struct r *r;			/* receive packet pointer */



	/*

	 * Read command line options and initialize system variables.

	 * The reference implementation measures the precision specific

	 * to each machine by measuring the clock increments to read the

	 * system clock.

	 */

	memset(&s, sizeof(s), 0);

	s.leap = NOSYNC;

	s.stratum = MAXSTRAT;

	s.poll = MINPOLL;

	s.precision = PRECISION;

	s.p = NULL;



	/*

	 * Initialize local clock variables

	 */

	memset(&c, sizeof(c), 0);

	if (/* frequency file */ 0) {

		c.freq = /* freq */ 0;

		rstclock(FSET, 0, 0);

	} else {

		rstclock(NSET, 0, 0);

	}

	c.jitter = LOG2D(s.precision);



	/*

	 * Read the configuration file and mobilize persistent

	 * associations with spcified addresses, version, mode, key ID

	 * and flags.

	 */ 

	while (/* mobilize configurated associations */ 0) {		

		p = mobilize(IPADDR, IPADDR, VERSION, MODE, KEYID,

		    P_FLAGS);

	}



	/*

	 * Start the system timer, which ticks once per second. Then

	 * read packets as they arrive, strike receive timestamp and

	 * call the receive() routine.

	 */

	while (0) {

		r = recv_packet();

		r->dst = get_time();

		receive(r);

	}

}



/*
 * mobilize() - mobilize and initialize an association
 */
struct p

*mobilize(

	ipaddr	srcaddr,		/* IP source address */

	ipaddr	dstaddr,		/* IP destination address */

	int	version,		/* version */

	int	mode,		/* host mode */

	int	keyid,		/* key identifier */

	int	flags		/* peer flags */

	)

{

	struct p *p;			/* peer process pointer */



	/*

	 * Allocate and initialize association memory

	 */

	p = malloc(sizeof(struct p));

	p->srcaddr = srcaddr;

	p->srcport = PORT;

	p->dstaddr = dstaddr;

	p->dstport = PORT;

	p->version = version;

	p->mode = mode;

	p->keyid = keyid;

	p->hpoll = MINPOLL;

	clear(p, X_INIT);

	p->flags == flags;

	return (p);

}



/*

 * clear() - reinitialize for persistent association, demobilize

 * for ephemeral association.

 */

void

clear(

	struct p *p,			/* peer structure pointer */

	int	kiss		/* kiss code */

	)

{

	int i;



	/*

	 * The first thing to do is return all resources to the bank.

	 * Typical resources are not detailed here, but they include

	 * dynamically allocated structures for keys, certificates, etc.

	 * If an ephemeral association and not initialization, return

	 * the association memory as well.

	 */

	/* return resources */

	if (s.p == p)

		s.p = NULL;

	if (kiss != X_INIT && (p->flags & P_EPHEM)) {

		free(p);

		return;

	}



	/*

	 * Initialize the association fields for general reset.

	 */

	memset(BEGIN_CLEAR(p), LEN_CLEAR, 0);

	p->leap = NOSYNC;

	p->stratum = MAXSTRAT;

	p->ppoll = MAXPOLL;

	p->hpoll = MINPOLL;

	p->disp = MAXDISP;

	p->jitter = LOG2D(s.precision);

	p->refid = kiss;

	for (i = 0; i < NSTAGE; i++)

		p->f[i].disp = MAXDISP;



	/*

	 * Randomize the first poll just in case thousands of broadcast

	 * clients have just been stirred up after a long absence of the

	 * broadcast server.

	 */

	p->last = p->t = c.t;

	p->next = p->last + (random() & ((1 << MINPOLL) - 1));

}



/*

 * md5() - compute message digest

 */

digest

md5(

	int	keyid		/* key identifier */

	)

{

	/*

	 * Compute a keyed cryptographic message digest. The key

	 * identifier is associated with a key in the local key cache.

	 * The key is prepended to the packet header and extension fieds

	 * and the result hashed by the MD5 algorithm as described in

	 * RFC-1321. Return a MAC consisting of the 32-bit key ID

	 * concatenated with the 128-bit digest.

	 */

	return (/* MD5 digest */ 0);

}



B.3 Kernel Input/Output Interface

/*

 * Kernel interface to transmit and receive packets. Details are

 * deliberately vague and depend on the operating system.

 *

 * recv_packet - receive packet from network

 */

struct r				/* receive packet pointer*/

*recv_packet() {

	return (/* receive packet r */ 0);

}



/*

 * xmit_packet - transmit packet to network

 */

void

xmit_packet(

	struct x *x		/* transmit packet pointer */

	)

{

	/* send packet x */

}



B.4 Kernel System Clock Interface

*

 * There are three time formats: native (Unix), NTP and floating double.

 * The get_time() routine returns the time in NTP long format. The Unix

 * routines expect arguments as a structure of two signed 32-bit words

 * in seconds and microseconds (timeval) or nanoseconds (timespec). The

 * step_time() and adjust_time() routines expect signed arguments in

 * floating double. The simplified code shown here is for illustration

 * only and has not been verified.

 */

#define	JAN_1970	2208988800UL		/* 1970 - 1900 in seconds */



/*

 * get_time - read system time and convert to NTP format

 */

tstamp

get_time()

{

	struct timeval unix_time;



	/*

	 * There are only two calls on this routine in the program. One

	 * when a packet arrives from the network and the other when a

	 * packet is placed on the send queue. Call the kernel time of

	 * day routine (such as gettimeofday()) and convert to NTP

	 * format.

	 */

	gettimeofday(&unix_time, NULL);



	return ((unix_time.tv_sec + JAN_1970) * 0x100000000L +

	    (unix_time.tv_usec * 0x100000000L) / 1000000);

}



/*

 * step_time() - step system time to given offset valuet

 */

void

step_time(

	double	offset		/* clock offset */

	)

{

	struct timeval unix_time;

	tstamp	ntp_time;



	/*

	 * Convert from double to native format (signed) and add to the

	 * current time. Note the addition is done in native format to

	 * avoid overflow or loss of precision.

	 */

	ntp_time = D2LFP(offset);

	gettimeofday(&unix_time, NULL);

	unix_time.tv_sec += ntp_time / 0x100000000L;

	unix_time.tv_usec += ntp_time % 0x100000000L;

	unix_time.tv_sec += unix_time.tv_usec / 1000000;

	unix_time.tv_usec %= 1000000;

	settimeofday(&unix_time, NULL);

}



/*

 * adjust_time() - slew system clock to given offset value

 */

void

adjust_time(

	double	offset		/* clock offset */

	)

{

	struct timeval unix_time;

	tstamp	ntp_time;



	/*

	 * Convert from double to native format (signed) and add to the

	 * current time.

	 */

	ntp_time = D2LFP(offset);

	unix_time.tv_sec = ntp_time / 0x100000000L;

	unix_time.tv_usec = ntp_time % 0x100000000L;

	unix_time.tv_sec += unix_time.tv_usec / 1000000;

	unix_time.tv_usec %= 1000000;

	adjtime(&unix_time, NULL);

}



B.5 Peer Process

#include “ntp4.h”



/*

 * A crypto-NAK packet includes the NTP header followed by a MAC

 * consisting only of the key identifier with value zero. It tells the

 * receiver that a prior request could not be properly authenticated,

 * but the NTP header fields are correct.

 *

 * A kiss-o’-death packet has an NTP header with leap 3 (NOSYNC) and

 * stratum 0. It tells the receiver that something drastic

 * has happened, as revealled by the kiss code in the refid field. The

 * NTP header fields may or may not be correct.

 */

/*

 * Definitions

 */

#define SGATE		3		/* spike gate (clock filter */

#define BDELAY		.004		/* broadcast delay (s) */



/*

 * Dispatch codes

 */

#define	ERR	-1		/* error */

#define DSCRD		0		/* discard packet */

#define	PROC	1		/* process packet */

#define	BCST	2		/* broadcast packet */

#define	FXMIT	3		/* client packet */

#define	NEWPS	4		/* new symmetric passive client */

#define	NEWBC	5		/* new broadcast client */



/*

 * Dispatch matrix

 *		active  passv  client server bcast */

int table[7][5] = {

/* nopeer  */	{ NEWPS, DSCRD, FXMIT, DSCRD, NEWBC },

/* active  */	{ PROC,  PROC,  DSCRD, DSCRD, DSCRD },

/* passv   */	{ PROC,  ERR,   DSCRD, DSCRD, DSCRD },

/* client  */	{ DSCRD, DSCRD, DSCRD, PROC,  DSCRD },

/* server  */	{ DSCRD, DSCRD, DSCRD, DSCRD, DSCRD },

/* bcast   */	{ DSCRD, DSCRD, DSCRD, DSCRD, DSCRD },

/* bclient */	{ DSCRD, DSCRD, DSCRD, DSCRD, PROC}

};



/*

 * Miscellaneous macroni

 *

 * This macro defines the authentication state. If x is 0,

 * authentication is optional, othewise it is required.

 */

#define	AUTH(x, y)	((x) ? (y) == A_OK : (y) == A_OK || \

			    (y) == A_NONE)



/*

 * These are used by the clear() routine

 */

#define	BEGIN_CLEAR(p)		((char *)&((p)->begin_clear))

#define	END_CLEAR(p)		((char *)&((p)->end_clear))

#define	LEN_CLEAR	(END_CLEAR	 ((struct p *)0) - \

			    BEGIN_CLEAR((struct p *)0))



B.5.1  receive()

/*

 * receive() - receive packet and decode modes

 */

void

receive(

	struct r *r			/* receive packet pointer */

	)

{

	struct p *p;			/* peer structure pointer

	int	auth;		/* authentication code */

	int	has_mac;		/* size of MAC */

	int	synch;		/* synchronized switch */

	int	auth;		/* authentication code */



	/*

	 * Check access control lists. The intent here is to implement a

	 * whitelist of those IP addresses specifically accepted and/or

	 * a blacklist of those IP addresses specifically rejected.

	 * There could be different lists for authenticated clients and

	 * unauthenticated clients.

	 */

	if (!access(r))

		return;			/* access denied */



	/*

	 * The version must not be in the future. Format checks include

	 * packet length, MAC length and extension field lengths, if

	 * present. 

	 */

	if (r->version > VERSION /* or format error */)

		return;			/* format error */



	/*

	 * Authentication is conditioned by two switches which can be

	 * specified on a per-client basis.

	 *

	 * P_NOPEER		do not mobilize an association unless

	 *		authenticated

	 * P_NOTRUST		do not allow access unless authenticated

	 *		(implies P_NOPEER)

	 *

	 * There are four outcomes:

	 *

	 * A_NONE	the packet has no MAC

	 * A_OK		the packet has a MAC and authentication

	 *		succeeds

	 * A_ERROR		the packet has a MAC and authentication fails

	 * A_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.

	 */

	has_mac = /* length of MAC field */ 0;

	if (has_mac == 0) {

		auth = A_NONE;			/* not required */

	} else if (has_mac == 4) {

		auth == A_CRYPTO;			/* crypto-NAK */

	} else {

		if (r->mac != md5(r->keyid))

			auth = A_ERROR;		/* auth error */

		else

			auth = A_OK;		/* auth OK */

	}



	/*

	 * Find association and dispatch code. If there is no

	 * association to match, the value of p->mode is assumed NULL.

	 */

	p = find_assoc(r);

	switch(table[p->mode][r->mode]) {



	/*

	 * Client packet. Send server reply (no association). If

	 * authentication fails, send a crypto-NAK packet.

	 */

	case FXMIT:

		if (AUTH(p->flags & P_NOTRUST, auth))

			fast_xmit(r, M_SERV, auth);

		else if (auth == A_ERROR)

			fast_xmit(r, M_SERV, A_CRYPTO);

		return;			/* M_SERV packet sent */



	/*

	 * New symmetric passive client (ephemeral association). It is

	 * mobilized in the same version as in the packet. If

	 * authentication fails, send a crypto-NAK packet. If restrict

	 * no-moblize, send a symmetric active packet instead.

	 */

	case NEWPS:

		if (!AUTH(p->flags & P_NOTRUST, auth)) {

			if (auth == A_ERROR)

				fast_xmit(r, M_SACT, A_CRYPTO);

			return;		/* crypto-NAK packet sent */

		}

		if (!AUTH(p->flags & P_NOPEER, auth)) {

			fast_xmit(r, M_SACT, auth);

			return;		/* M_SACT packet sent */

		}

		p = mobilize(r->srcaddr, r->dstaddr, r->version, M_PASV,

		    r->keyid, P_EPHEM);

		break;



	/*

	 * New broadcast client (ephemeral association). It is mobilized

	 * in the same version as in the packet. If authentication

	 * error, ignore the packet. Note this code does not support the

	 * initial volley feature in the reference implementation.

	 */

	case NEWBC:

		if (!AUTH(p->flags & (P_NOTRUST | P_NOPEER), auth))

			return;		/* authentication error */



		if (!(s.flags & S_BCSTENAB))

			return;		/* broadcast not enabled */



		p = mobilize(r->srcaddr, r->dstaddr, r->version, M_BCLN,

		    r->keyid, P_EPHEM);

		break;			/* processing continues */



	/*

	 * Process packet. Placeholdler only.

	 */

	case PROC:

		break;			/* processing continues */



	/*

	 * Invalid mode combination. We get here only in case of

	 * ephemeral associations, so the correct action is simply to

	 * toss it.

	 */

	case ERR:

		clear(p, X_ERROR);

		return;			/* invalid mode combination */



	/*

	 * No match; just discard the packet.

	 */

	case DSCRD:

		return;			/* orphan abandoned */

	}



	/*

	 * Next comes a rigorous schedule of timestamp checking. If the

	 * transmit timestamp is zero, the server is horribly broken.

	 */

	if (r->xmt == 0)

		return;			/* invalid timestamp */



	/*

	 * If the transmit timestamp duplicates a previous one, the

	 * packet is a replay.

	 */

	if (r->xmt == p->xmt)

		return;			/* duplicate packet */



	/*

	 * If this is a broadcast mode packet, skip further checking.

	 * If the origin timestamp is zero, the sender has not yet heard

	 * from us. Otherwise, if the origin timestamp does not match

	 * the transmit timestamp, the packet is bogus.

	 */

	synch = TRUE;

	if (r->mode != M_BCST) {

		if (r->org == 0)

			synch = FALSE;	/* unsynchronized */

			

		else if (r->org != p->xmt)

			synch = FALSE;	/* bogus packet */

	}



	/*

	 * Update the origin and destination timestamps. If

	 * unsynchronized or bogus, abandon ship.

	 */

	p->org = r->xmt;

	p->rec = r->dst;

	if (!synch)

		return;			/* unsynch */



	/*

	 * The timestamps are valid and the receive packet matches the

	 * last one sent. If the packet is a crypto-NAK, the server

	 * might have just changed keys. We demobilize the association

	 * and wait for better times.

	 */

	if (auth == A_CRYPTO) {

		clear(p, X_CRYPTO);

		return;			/* crypto-NAK */

	}



	/* 

	 * If the association is authenticated, the key ID is nonzero

	 * and received packets must be authenticated. This is designed

	 * to avoid a bait-and-switch attack, which was possible in past

	 * versions.

	 */

	if (!AUTH(p->keyid || (p->flags & P_NOTRUST), auth))

		return;			/* bad auth */



	/*

	 * Everything possible has been done to validate the timestamps

	 * and prevent bad guys from disrupting the protocol or

	 * injecting bogus data. Earn some revenue.

	 */

	packet(p, r);

}



/*

 * find_assoc() - find a matching association

 */

struct p				/* peer structure pointer or NULL */

*find_assoc(

	struct r *r			/* receive packet pointer */

	)

{

	struct p *p;			/* dummy peer structure pointer */



	/*

	 * Search association table for matching source

	 * address and source port.

	 */

	while (/* all associations */ 0) {		 

		if (r->srcaddr == p->srcaddr && r->port == p->port)

			return(p);

	}

	return (NULL); 

}



B.5.2  packet()

/*

 * packet() - process packet and compute offset, delay and

 * dispersion.

 */

void

packet(

	struct p *p,			/* peer structure pointer */

	struct r *r			/* receive packet pointer */

	)

{

	double	offset;		/* sample offsset */

	double	delay;		/* sample delay */

	double	disp;		/* sample dispersion */



	/*

	 * By golly the packet is valid. Light up the remaining header

	 * fields. Note that we map stratum 0 (unspecified) to MAXSTRAT

	 * to make stratum comparisons simpler and to provide a natural

	 * interface for radio clock drivers that operate for

	 *  convenience at stratum 0.

	 */

	p->leap = r->leap;

	if (r->stratum == 0)

		p->stratum = MAXSTRAT;

	else

		p->stratum = r->stratum;

	p->mode = r->mode;

	p->ppoll = r->poll;

	p->rootdelay = FP2D(r->rootdelay);

	p->rootdisp = FP2D(r->rootdisp);

	p->refid = r->refid;

	p->reftime = r->reftime;



	/*

	 * Verify the server is synchronized with valid stratum and

	 * reference time not later than the transmit time. 

	 */

	if (p->leap == NOSYNC || p->stratum >= MAXSTRAT)

		return;			/* unsynchronized */



	/*

	 * Verify valid root distance.

	 */

	if (r->rootdelay / 2 + r->rootdisp >= MAXDISP || p->reftime >

	    r->xmt)

		return;			/* invalid header values */



	poll_update(p, p->hpoll);

	p->reach |= 1;



	/*

	 * Calculate offset, delay and dispersion, then pass to the

	 * clock filter. Note carefully the implied processing. The

	 * first-order difference is done directly in 64-bit arithmetic,

	 * then the result is converted to floating double. All further

	 * processing is in floating double arithmetic with rounding

	 * done by the hardware. This is necessary in order to avoid

	 * overflow and preseve precision.

	 *

	 * The delay calculation is a special case. In cases where the

	 * server and client clocks are running at different rates and

	 * with very fast networks, the delay can appear negative. In

	 * order to avoid violating the Principle of Least Astonishment,

	 * the delay is clamped not less than the system precision.

	 */

	if (p->mode == M_BCST) {

		offset = LFP2D(r->xmt - r->dst);

		delay = BDELAY;

		disp = LOG2D(r->precision) + LOG2D(s.precision) + PHI *

		    2 * BDELAY;

	} else {

		offset = (LFP2D(r->rec - r->org) + LFP2D(r->dst -

		    r->xmt)) / 2;

		delay = max(LFP2D(r->dst - r->org) - LFP2D(r->rec -

		    r->xmt), LOG2D(s.precision));

		disp = LOG2D(r->precision) + LOG2D(s.precision) + PHI *

		    LFP2D(r->dst - r->org);

	}

	clock_filter(p, offset, delay, disp);

}



B.5.3  clock_filter()

/*

 * clock_filter(p, offset, delay, dispersion) - select the best from the

 * latest eight delay/offset samples.

 */

void

clock_filter(

	struct p *p,			/* peer structure pointer */

	double	offset,		/* clock offset */

	double	delay,		/* roundtrip delay */

	double	disp		/* dispersion */

	)

{

	struct f f[NSTAGE];	/* sorted list */

	double	dtemp;

	int	i;



	/*

	 * The clock filter contents consist of eight tuples (offset,

	 * delay, dispersion, time). Shift each tuple to the left,

	 * discarding the leftmost one. As each tuple is shifted,

	 * increase the dispersion since the last filter update. At the

	 * same time, copy each tuple to a temporary list. After this,

	 * place the (offset, delay, disp, time) in the vacated

	 * rightmost tuple.

	 */

	for (i = 1; i < NSTAGE; i++) {

		p->f[i] = p->f[i - 1];

		p->f[i].disp += PHI * (c.t - p->t);

		f[i] = p->f[i];

	}

	p->f[0].t = c.t;

	p->f[0].offset = offset;

	p->f[0].delay = delay;

	p->f[0].disp = disp;

	f[0] = p->f[0];



	/*

	 * Sort the temporary list of tuples by increasing f[].delay.

	 * The first entry on the sorted list represents the best

	 * sample, but it might be old.

	 */

	dtemp = p->offset;

	p->offset = f[0].offset;

	p->delay = f[0].delay;

	for (i = 0; i < NSTAGE; i++) {

		p->disp += f[i].disp / (2 ^ (i + 1));

		p->jitter += SQUARE(f[i].offset - f[0].offset);

	}

	p->jitter = max(SQRT(p->jitter), LOG2D(s.precision));



	/*

	 * Prime directive: use a sample only once and never a sample

	 * older than the latest one, but anything goes before first 

	 * synchronized.

	 */

	if (f[0].t - p->t <= 0 && s.leap != NOSYNC)

		return;



	/*

	 * Popcorn spike suppressor. Compare the difference between the

	 * last and current offsets to the current jitter. If greater

	 * than SGATE (3) and if the interval since the last offset is

	 * less than twice the system poll interval, dump the spike.

	 * Otherwise, and if not in a burst, shake out the truechimers.

	 */

	if (fabs(p->offset - dtemp) > SGATE * p->jitter && (f[0].t -

	    p->t) < 2 * s.poll)

		return;



	p->t = f[0].t;

	if (p->burst == 0)

		clock_select();

	return;

}



B.5.4  fast_xmit()

/*

 * fast_xmit() - transmit a reply packet for receive packet r

 */

void

fast_xmit(

	struct r *r,			/* receive packet pointer */

	int	mode,		/* association mode */

	int	auth		/* authentication code */

	)

{

	struct x x;



	/*

	 * Initialize header and transmit timestamp. Note that the

	 * transmit version is copied from the receive version. This is

	 * for backward compatibility.

	 */

	x.version = r->version;

	x.srcaddr = r->dstaddr;

	x.dstaddr = r->srcaddr;

	x.leap = s.leap;

	x.mode = mode;

	if (s.stratum == MAXSTRAT)

		x.stratum = 0;

	else

		x.stratum = s.stratum;

	x.poll = r->poll;

	x.precision = s.precision;

	x.rootdelay = D2FP(s.rootdelay);

	x.rootdisp = D2FP(s.rootdisp);

	x.refid = s.refid;

	x.reftime = s.reftime;

	x.org = r->xmt;

	x.rec = r->dst;

	x.xmt = get_time();



	/*

	 * If the authentication code is A.NONE, include only the

	 * header; if A.CRYPTO, send a crypto-NAK; if A.OK, send a valid

	 * MAC. Use the key ID in the received packet and the key in the

	 * local key cache.

	 */

	if (auth != A_NONE) {

		if (auth == A_CRYPTO) {

			x.keyid = 0;

		} else {

			x.keyid = r->keyid;

			x.digest = md5(x.keyid);

		}

	}

	xmit_packet(&x);

}



B.5.5  access()

/*

 * access() - determine access restrictions

 */

int

access(

	struct r *r			/* receive packet pointer */

	)

{

	/*

	 * The access control list is an ordered set of tuples

	 * consisting of an address, mask and restrict word containing

	 * defined bits. The list is searched for the first match on the

	 * source address (r->srcaddr) and the associated restrict word

	 * is returned.

	 */

	return (/* access bits */ 0);

}



B.6 System Process

#include “ntp4.h”



B.6.1  clock_select()

/*

 * clock_select() - find the best clocks

 */

void

clock_select() {

	struct p *p, *osys;			/* peer structure pointers */

	double	low, high;		/* correctness interval extents */

	int	allow, found, chime; /* used by intersecion algorithm */

	int	n, i, j;



	/*

	 * We first cull the falsetickers from the server population,

	 * leaving only the truechimers. The correctness interval for

	 * association p is the interval from offset - root_dist() to

	 * offset + root_dist(). The object of the game is to find a

	 * majority clique; that is, an intersection of correctness

	 * intervals numbering more than half the server population.

	 *

	 * First construct the chime list of tuples (p, type, edge) as

	 * shown below, then sort the list by edge from lowest to

	 * highest.

	 */

	osys = s.p;

	s.p = NULL;

	n = 0;

	while (accept(p)) {

		s.m[n].p = p;

		s.m[n].type = +1;

		s.m[n].edge = p->offset + root_dist(p);

		n++;

		s.m[n].p = p;

		s.m[n].type = 0;

		s.m[n].edge = p->offset;

		n++;

		s.m[n].p = p;

		s.m[n].type = -1;

		s.m[n].edge = p->offset - root_dist(p);

		n++;

	}



	/*

	 * Find the largest contiguous intersection of correctness

	 * intervals. Allow is the number of allowed falsetickers; found

	 * is the number of midpoints. Note that the edge values are

	 * limited to the range +-(2 ^ 30) < +-2e9 by the timestamp

	 * calculations.

	 */

	low = 2e9; high = -2e9;

	for (allow = 0; 2 * allow < n; allow++) {



		/*

		 * Scan the chime list from lowest to highest to find

		 * the lower endpoint.

		 */

		found = 0;

		chime = 0;

		for (i = 0; i < n; i++) {

			chime -= s.m[i].type;

			if (chime >= n - found) {

				low = s.m[i].edge;

				break;

			}

			if (s.m[i].type == 0)

				found++;

		}



		/*

		 * Scan the chime list from highest to lowest to find

		 * the upper endpoint.

		 */

		chime = 0;

		for (i = n - 1; i >= 0; i--) {

			chime += s.m[i].type;

			if (chime >= n - found) {

				high = s.m[i].edge;

				break;

			}

			if (s.m[i].type == 0)

				found++;

		}



		/*

		 * If the number of midpoints is greater than the number

		 * of allowed falsetickers, the intersection contains at

		 * least one truechimer with no midpoint. If so,

		 * increment the number of allowed falsetickers and go

		 * around again. If not and the intersection is

		 * nonempty, declare success.

		 */

		if (found > allow)

			continue;



		if (high > low)

			break;

	}



	/*

	 * Clustering algorithm. Construct a list of survivors (p,

	 * metric) from the chime list, where metric is dominated first

	 * by stratum and then by root distance. All other things being

	 * equal, this is the order of preference.

	 */

	n = 0;

	for (i = 0; i < n; i++) {

		if (s.m[i].edge < low || s.m[i].edge > high)

			continue;



		p = s.m[i].p;

		s.v[n].p = p;

		s.v[n].metric = MAXDIST * p->stratum + root_dist(p);

		n++;

	}



	/*

	 * There must be at least NSANE survivors to satisfy the

	 * correctness assertions. Ordinarily, the Byzantine criteria

	 * require four, susrvivors, but for the demonstration here, one

	 * is acceptable.

	 */

	if (n == NSANE)

		return;



	/*

	 * For each association p in turn, calculate the selection

	 * jitter p->sjitter as the square root of the sum of squares

	 * (p->offset - q->offset) over all q associations. The idea is

	 * to repeatedly discard the survivor with maximum selection

	 * jitter until a termination condition is met.

	 */

	while (1) {

		struct p *p, *q, *qmax;	/* peer structure pointers */

		double	max, min, dtemp;



		max = -2e9; min = 2e9;

		for (i = 0; i < n; i++) {

			p = s.v[i].p;

			if (p->jitter < min)

				min = p->jitter;

			dtemp = 0;

			for (j = 0; j < n; j++) {

				q = s.v[j].p;

				dtemp += SQUARE(p->offset - q->offset);

			}

			dtemp = SQRT(dtemp);

			if (dtemp > max) {

				max = dtemp;

				qmax = q;

			}

		}



		/*

		 * If the maximum selection jitter is less than the

		 * minimum peer jitter, then tossing out more survivors

		 * will not lower the minimum peer jitter, so we might

		 * as well stop. To make sure a few survivors are left

		 * for the clustering algorithm to chew on, we also stop

		 * if the number of survivors is less than or equal to

		 * NMIN (3).

		 */

		if (max < min || n <= NMIN)

			break;



		/*

		 * Delete survivor qmax from the list and go around

		 * again.

		 */

		n--;

	}



	/*

	 * Pick the best clock. If the old system peer is on the list

	 * and at the same stratum as the first survivor on the list,

	 * then don’t do a clock hop. Otherwise, select the first

	 * survivor on the list as the new system peer.

	 */

	if (osys->stratum == s.v[0].p->stratum)

		s.p = osys;

	else

		s.p = s.v[0].p;

	clock_update(s.p);

}



B.6.2  root_dist()

/*

 * root_dist() - calculate root distance

 */

double

root_dist(

	struct p *p		/* peer structure pointer */

	)

{

	/*

	 * The root synchronization distance is the maximum error due to

	 * all causes of the local clock relative to the primary server.

	 * It is defined as half the total delay plus total dispersion

	 * plus peer jitter.

	 */

	return (max(MINDISP, p->rootdelay + p->delay) / 2 +

	    p->rootdisp + p->disp + PHI * (c.t - p->t) + p->jitter);

}



B.6.3  accept()

/*

 * accept() - test if association p is acceptable for synchronization

 */

int

accept(

	struct p *p		/* peer structure pointer */

	)

{

	/*

	 * A stratum error occurs if (1) the server has never been

	 * synchronized, (2) the server stratum is invalid.

	 */

	if (p->leap == NOSYNC || p->stratum >= MAXSTRAT)

		return (FALSE);



	/*

	 * A distance error occurs if the root distance exceeds the

	 * distance threshold plus an increment equal to one poll

	 * interval.

	 */

	if (root_dist(p) > MAXDIST + PHI * LOG2D(s.poll))

		return (FALSE);



	/*

	 * A loop error occurs if the remote peer is synchronized to the

	 * local peer or the remote peer is synchronized to the current

	 * system peer. Note this is the behavior for IPv4; for IPv6 the

	 * MD5 hash is used instead.

	 */

	if (p->refid == p->dstaddr || p->refid == s.refid)

		return (FALSE);



	/*

	 * An unreachable error occurs if the server is unreachable.

	 */

	if (p->reach == 0)

		return (FALSE);



	return (TRUE);

}



B.6.4  clock_update()

/*

 * clock_update() - update the system clock

 */

void

clock_update(

	struct p *p			/* peer structure pointer */

	)

{

	double dtemp;



	/*

	 * If this is an old update, for instance as the result of a

	 * system peer change, avoid it. We never use an old sample or

	 * the same sample twice.

	 *

	if (s.t >= p->t)

		return;



	/*

	 * Combine the survivor offsets and update the system clock; the

	 * local_clock() routine will tell us the good or bad news.

	 */

	s.t = p->t;

	clock_combine();

	switch (local_clock(p, s.offset)) {



	/*

	 * The offset is too large and probably bogus. Complain to the

	 * system log and order the operator to set the clock manually

	 * within PANIC range. The reference implementation includes a

	 * command line option to disable this check and to change the

	 * panic threshold from the default 1000 s as required.

	 */

	case PANIC:

		exit (0);



	/*

	 * The offset is more than the step threshold (0.125 s by

	 * default). After a step, all associations now have

	 * inconsistent time valurs, so they are reset and started

	 * fresh. The step threshold can be changed in the reference

	 * implementation in order to lessen the chance the clock might

	 * be stepped backwards. However, there may be serious

	 * consequences, as noted in the white papers at the NTP project

	 * site.

	 */

	case STEP:

		while (/* all associations */ 0)

			clear(p, X_STEP);

		s.stratum = MAXSTRAT;

		s.poll = MINPOLL;

		break;



	/*

	 * The offset was less than the step threshold, which is the

	 * normal case. Update the system variables from the peer

	 * variables. The lower clamp on the dispersion increase is to

	 * avoid timing loops and clockhopping when highly precise

	 * sources are in play. The clamp can be changed from the

	 * default .01 s in the reference implementation.

	 */

	case SLEW:

		s.leap = p->leap;

		s.stratum = p->stratum + 1;

		s.refid = p->refid;

		s.reftime = p->reftime;

		s.rootdelay = p->rootdelay + p->delay;

		dtemp = SQRT(SQUARE(p->jitter) + SQUARE(s.jitter));

		dtemp += max(p->disp + PHI * (c.t - p->t) +

		    fabs(p->offset), MINDISP);

		s.rootdisp = p->rootdisp + dtemp;

		break;



	/*

	 * Some samples are discarded while, for instance, a direct

	 * frequency measurement is being made.

	 */

	case IGNORE:

		break;

	}

}



B.6.5  clock_combine()

/*

 * clock_combine() - combine offsets

 */

void

clock_combine()

{

	struct p *p;		/* peer structure pointer */

	double x, y, z, w;

	int	i;



	/*

	 * Combine the offsets of the clustering algorithm survivors

	 * using a weighted average with weight determined by the root

	 * distance. Compute the selection jitter as the weighted RMS

	 * difference between the first survivor and the remaining

	 * survivors. In some cases the inherent clock jitter can be

	 * reduced by not using this algorithm, especially when frequent

	 * clockhopping is involved. The reference implementation can be

	 * configured to avoid this algorithm by designating a preferred

	 * peer.

	 */

	y = z = w = 0;

	for (i = 0; s.v[i].p != NULL; i++) {

		p = s.v[i].p;

		x = root_dist(p);

		y += 1 / x;

		z += p->offset / x;

		w += SQUARE(p->offset - s.v[0].p->offset) / x;

	}

	s.offset = z / y;

	s.jitter = SQRT(w / y);

}



B.6.6  local_clock()

#include “ntp4.h”



/*

 * Constants

 */

#define STEPT		.128		/* step threshold (s) */

#define WATCH		900		/* stepout threshold (s) */

#define PANICT		1000		/* panic threshold (s) */

#define PLL		65536		/* PLL loop gain */

#define FLL		MAXPOLL + 1		/* FLL loop gain */

#define AVG		4		/* parameter averaging constant */

#define ALLAN		1500		/* compromise Allan intercept (s) */

#define LIMIT		30		/* poll-adjust threshold */

#define MAXFREQ		500e-6		/* maximum frequency tolerance (s/s) */

#define PGATE		4		/* poll-adjust gate */



/*

 * local_clock() - discipline the local clock

 */

int				/* return code */

local_clock(

	struct p *p,			/* peer structure pointer */

	double	offset		/* clock offset from combine() */

	)

{

	int	state;		/* clock discipline state */

	double	freq;		/* frequency */

	double	mu;		/* interval since last update */

	int	rval;

	double	etemp, dtemp;



	/*

	 * If the offset is too large, give up and go home.

	 */

	if (fabs(offset) > PANICT)

		return (PANIC);



	/*

	 * Clock state machine transition function. This is where the

	 * action is and defines how the system reacts to large time

	 * and frequency errors. There are two main regimes: when the

	 * offset exceeds the step threshold and when it does not.

	 */

	rval = SLEW;

	mu = p->t - s.t;

	freq = 0;

	if (fabs(offset) > STEPT) {

		switch (c.state) {



		/*

		 * In S_SYNC state we ignore the first outlyer amd

		 * switch to S_SPIK state.

		 */

		case SYNC:

			state = SPIK;

			return (rval);



		/*

		 * In S_FREQ state we ignore outlyers and inlyers. At

		 * the first outlyer after the stepout threshold,

		 * compute the apparent frequency correction and step

		 * the time.

		 */

		case FREQ:

			if (mu < WATCH)

				return (IGNORE);



			freq = (offset - c.base - c.offset) / mu;

			/* fall through to S_SPIK */



		/*

		 * In S_SPIK state we ignore succeeding outlyers until

		 * either an inlyer is found or the stepout threshold is

		 * exceeded.

		 */

		case SPIK:

			if (mu < WATCH)

				return (IGNORE);



			/* fall through to default */



		/*

		 * We get here by default in S_NSET and S_FSET states

		 * and from above in S_FREQ state. Step the time and

		 * clamp down the poll interval.

		 *

		 * In S_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

		 * capture range, the clock is stepped. The frequency

		 * will be set directly following the stepout interval.

		 *

		 * In S_FSET state the initial frequency has been set

		 * from the frequency file. Since the time is outside

		 * the capture range, the clock is stepped immediately,

		 * rather than after the stepout interval. Guys get

		 * nervous if it takes 17 minutes to set the clock for

		 * the first time.

		 *

		 * In S_SPIK state 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 at the longer poll

		 * intervals.

		 */ 

		default:



			/*

			 * This is the kernel set time function, usually

			 * implemented by the Unix settimeofday() system

			 * call.

			 */

			step_time(offset);

			c.count = 0;

			rval = STEP;

			if (state == NSET) {

				rstclock(FREQ, p->t, 0);

				return (rval);

			}

			break;

		}

		rstclock(SYNC, p->t, 0);

	} else {



		/*

		 * Compute the clock jitter as the RMS of exponentially

		 * weighted offset differences. This is used by the

		 * poll-adjust code.

		 */

		etemp = SQUARE(c.jitter);

		dtemp = SQUARE(max(fabs(offset - c.last),

		    LOG2D(s.precision)));

		c.jitter = SQRT(etemp + (dtemp - etemp) / AVG);

		switch (c.state) {



		/*

		 * In S_NSET state this is the first update received and

		 * the frequency has not been initialized. The first

		 * thing to do is directly measure the oscillator

		 * frequency.

		 */

		case NSET:

			c.offset = offset;

			rstclock(FREQ, p->t, offset);

			return (IGNORE);



		/*

		 * In S_FSET state this is the first update and the

		 * frequency has been initialized. Adjust the phase, but

		 * don’t adjust the frequency until the next update.

		 */

		case FSET:

			c.offset = offset;

			break;



		/*

		 * In S_FREQ state ignore updates until the stepout

		 * threshold. After that, correct the phase and

		 * frequency and switch to S_SYNC state.

		 */

		case FREQ:

			if (c.t - s.t < WATCH)

				return (IGNORE);



			freq = (offset - c.base - c.offset) / mu;

			break;



		/*

		 * We get here by default in S_SYNC and S_SPIK states.

		 * Here we compute the frequency update due to PLL and

		 * FLL contributions.

		 */

		default:



			/*

			 * The FLL and PLL frequency gain constants

			 * depend on the poll interval and Allan

			 * intercept. The FLL is not used below one-half

			 * the Allan intercept. Above that the loop gain

			 * increases in steps to 1 / AVG.

			 */ 

			if (LOG2D(s.poll) > ALLAN / 2) {

				etemp = FLL - s.poll;

				if (etemp < AVG)

					etemp = AVG;

				freq += (offset - c.offset) / (max(mu,

				    ALLAN) * etemp);

			}



			/*

			 * For the PLL the integration interval

			 * (numerator) is the minimum of the update

			 * interval and poll interval. This allows

			 * oversampling, but not undersampling.

			 */ 

			etemp = min(mu, LOG2D(s.poll));

			dtemp = 4 * PLL * LOG2D(s.poll);

			freq += offset * etemp / (dtemp * dtemp);

			break;

		}

		rstclock(SYNC, p->t, offset);

	}



	/*

	 * Calculate the new frequency and frequency stability (wander).

	 * Compute the clock wander as the RMS of exponentially weighted

	 * frequency differences. This is not used directly, but can,

	 * along withthe jitter, be a highly useful monitoring and

	 * debugging tool

	 */

	freq += c.freq;

	c.freq = max(min(MAXFREQ, freq), -MAXFREQ);

	etemp = SQUARE(c.wander);

	dtemp = SQUARE(freq);

	c.wander = SQRT(etemp + (dtemp - etemp) / AVG);



	/*

	 * Here we adjust the poll interval 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.

	 */

	if (fabs(c.offset) < PGATE * c.jitter) {

		c.count += s.poll;

		if (c.count > LIMIT) {

			c.count = LIMIT;

			if (s.poll < MAXPOLL) {

				c.count = 0;

				s.poll++;

			}

		}

	} else {

		c.count -= s.poll << 1;

		if (c.count < -LIMIT) {

			c.count = -LIMIT;

			if (s.poll > MINPOLL) {

				c.count = 0;

				s.poll--;

			}

		}

	}

	return (rval);

}



B.6.7  rstclock()

/*

 * rstclock() - clock state machine

 */

void

rstclock(

	int	state,		/* new state */

	double	offset,		/* new offset */

	double	t		/* new update time */

	)

{

	/*

	 * Enter new state and set state variables. Note we use the time

	 * of the last clock filter sample, which must be earlier than

	 * the current time.

	 */

	c.state = state;

	c.base = offset - c.offset;

	c.last = c.offset = offset;

	s.t = t;

}



B.7 Clock Adjust Process

B.7.1  clock_adjust()

/*

 * clock_adjust() - runs at one-second intervals

 */

void

clock_adjust() {

	double	dtemp;



	/*

	 * Update the process time c.t. Also increase the dispersion

	 * since the last update. In contrast to NTPv3, NTPv4 does not

	 * declare unsynchronized after one day, since the dispersion

	 * threshold serves this function. When the dispersion exceeds

	 * MAXDIST (1 s), the server is considered unaccept for

	 * synchroniztion.

	 */

	c.t++;

	s.rootdisp += PHI;



	/*

	 * Implement the phase and frequency adjustments. The gain

	 * factor (denominator) is not allowed to increase beyond the

	 * Allan intercept. It doesn’t make sense to average phase noise

	 * beyond this point and it helps to damp residual offset at the

	 * longer poll intervals.

	 */

	dtemp = c.offset / (PLL * min(LOG2D(s.poll), ALLAN));

	c.offset -= dtemp;



	/*

	 * This is the kernel adjust time function, usually implemented

	 * by the Unix adjtime() system call.

	 */

	adjust_time(c.freq + dtemp);



	/*

	 * Peer timer. Call the poll() routine when the poll timer

	 * expires.

	 */

	while (/* all associations */ 0) {

		struct p *p;	/* dummy peer structure pointer */



		if (c.t >= p->next)

			poll(p);

	}



	/*

	 * Once per hour write the clock frequency to a file

	 */

	if (c.t % 3600 == 3599)

		/* write c.freq to file */ 0;

}



B.8 Poll Process

#include “ntp4.h”



/*

 * Constants

 */

#define	UNREACH		12	/* unreach counter threshold */

#define	BCOUNT		8	/* packets in a burst */

#define	BTIME		2	/* burst interval (s) */



B.8.1  poll()

/*

 * poll() - determine when to send a packet for association p->

 */

void

poll(

	struct p *p		/* peer structure pointer */

	)

{

	int	hpoll;

	int	oreach;



	/*

	 * This routine is called when the current time c.t catches up

	 * to the next poll time p->next. The value p->last is

	 * the last time this routine was executed. The poll_update()

	 * routine determines the next execution time p->next.

	 *

	 * If broadcasting, just do it, but only if we are synchronized.

	 */

	hpoll = p->hpoll;

	if (p->mode == M_BCST) {

		p->last = c.t;

		if (s.p != NULL)

			peer_xmit(p);

		poll_update(p, hpoll);

		return;

	}

	if (p->burst == 0) {



		/*

		 * We are not in a burst. Shift the reachability

		 * register to the left. Hopefully, some time before the

		 * next poll a packet will arrive and set the rightmost

		 * bit.

		 */

		p->last = c.t;

		oreach = p->reach;

		p->reach << 1;

		if (!p->reach) {



			/*

			 * The server is unreachable, so bump the

			 * unreach counter. If the unreach threshold has

			 * been reached, double the poll interval to

			 * minimize wasted network traffic.

			 */

			if (p->flags & P_IBURST && p->unreach == 0) {

				p->burst = BCOUNT;

			} else if (p->unreach < UNREACH)

				p->unreach++;

			else

				hpoll++;

			p->unreach++;

		} else {



			/*

			 * The server is reachable. However, if has not

			 * been heard for three consecutive poll

			 * intervals, stuff the clock register to

			 * increase the peer dispersion. This makes old

			 * servers less desirable and eventually boots

			 * them off the island.

			 */

			p->unreach = 0;

			if (!(p->reach & 0x7))

				clock_filter(p, 0, 0, MAXDISP);

			hpoll = s.poll;

			if (p->flags & P_BURST && accept(p))

				p->burst = BCOUNT;

		}

	} else {



		/*

		 * If in a burst, count it down. When the reply comes

		 * back the clock_filter() routine will call

		 * clock_select() to process the results of the burst.

		 */

		p->burst--;

	}



	/*

	 * Do not transmit if in broadcast client mode. 

	 */

	if (p->mode != M_BCLN)

		peer_xmit(p);

	poll_update(p, hpoll);

}



B.8.2  poll_update()

/*

 * poll_update() - update the poll interval for association p

 *

 * Note: This routine is called by both the packet() and poll() routine.

 * Since the packet() routine is executed when a network packet arrives

 * and the poll() routine is executed as the result of timeout, a

 * potential race can occur, possibly causing an incorrect interval for

 * the next poll. This is considered so unlikely as to be negligible.

 */

void

poll_update(

	struct p *p,		/* peer structure pointer */

	int	hpoll		/* poll interval (log2 s) */

	)

{

	int	poll;



	/*

	 * This routine is called by both the poll() and packet()

	 * routines to determine the next poll time. If within a burst

	 * the poll interval is two seconds. Otherwise, it is the

	 * minimum of the host poll interval and peer poll interval, but

	 * not greater than MAXPOLL and not less than MINPOLL. The

	 * design insures that a longer interval can be preempted by a

	 * shorter one if required for rapid response.

	 */

	p->hpoll = min(MAXPOLL, max(MINPOLL, hpoll));

	if (p->burst != 0) {

		if(c.t != p->next)

			return;



		p->next += BTIME;

	} else {

		poll = min(p->hpoll, max(MINPOLL, ppoll));

	}

		/*

		 * While not shown here, the reference implementation

		 * randonizes the poll interval by a small factor.

		 */

		p->next = p->last + (1 << poll);

	}



	/*

	 * It might happen that the due time has already passed. If so,

	 * make it one second in the future.

	 */

	if (p->next <= c.t)

		p->next = c.t + 1;

}



B.8.3  transmit()

/*

 * transmit() - transmit a packet for association p

 */

void

peer_xmit(

	struct p *p		/* peer structure pointer */

	)

{

	struct x x;		/* transmit packet */



	/*

	 * Initialize header and transmit timestamp

	 */

	x.srcaddr = p->dstaddr;

	x.dstaddr = p->srcaddr;

	x.leap = s.leap;

	x.version = VERSION;

	x.mode = p->mode;

	if (s.stratum == MAXSTRAT)

		x.stratum = 0;

	else

		x.stratum = s.stratum;

	x.poll = p->hpoll;

	x.precision = s.precision;

	x.rootdelay = D2FP(s.rootdelay);

	x.rootdisp = D2FP(s.rootdisp);

	x.refid = s.refid;

	x.reftime = s.reftime;

	x.org = p->org;

	x.rec = p->rec;

	x.xmt = get_time();

	p->xmt = x.xmt;



	/*

	 * If the key ID is nonzero, send a valid MAC using the key ID

	 * of the association and the key in the local key cache. If

	 * something breaks, like a missing trusted key, don’t send the

	 * packet; just reset the association and stop until the problem

	 * is fixed.

	 */

	if (p->keyid)

		if (/* p->keyid invalid */ 0) {

			clear(p, X_NKEY);

			return;

		}

		x.digest = md5(p->keyid);

	xmit_packet(&x);

}