1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <fcntl.h>
30#include <sys/socket.h>
31#include <sys/sysmacros.h>
32#include <netinet/in.h>
33#include <netdb.h>
34#include <stdio.h>
35#include <string.h>
36#include <tzfile.h>
37#include "snoop.h"
38#include "ntp.h"
39
40/*
41 * In verbose mode, how many octets of the control-mode data payload
42 * are displayed per line of output.  The value 64 fits well on an
43 * 80-column screen and, as a power of 2, is easily correlated to
44 * hexadecimal output.
45 */
46#define	OCTETS_PER_LINE	64
47
48extern char *dlc_header;
49
50static	char	*show_leap(int);
51static	char	*show_mode(int);
52static	char	*show_ref(int, ulong_t);
53static	char	*show_time(struct l_fixedpt);
54static	double	s_fixed_to_double(struct s_fixedpt *);
55static	char	*iso_date_time(time_t);
56static	char	*show_operation(int);
57
58int
59interpret_ntp(int flags, struct ntpdata *ntp_pkt, int fraglen)
60{
61	unsigned int	i, j, macbytes;
62	unsigned int	proto_version;
63	unsigned int	datalen;
64	unsigned int	linelen = OCTETS_PER_LINE;
65	unsigned int	sofar = 0;
66
67	char	*datap;
68	char	hbuf[2 * MAC_OCTETS_MAX + 1];
69	static	char *hexstr = "0123456789ABCDEF";
70
71	union	ntp_pkt_buf {
72		struct	ntpdata ntp_msg;
73		union ntpc_buf {
74			struct	ntp_control chdr;
75			uchar_t	data2[NTPC_DATA_MAXLEN - 1];
76		} ntpc_msg;
77		union ntpp_buf {
78			struct	ntp_private phdr;
79			uchar_t	data2[1];
80		} ntpp_msg;
81	} fragbuf;
82
83	struct	ntpdata		*ntp = &fragbuf.ntp_msg;
84	struct	ntp_control	*ntpc = (struct ntp_control *)&fragbuf.ntpc_msg;
85	struct	ntp_private	*ntpp = (struct ntp_private *)&fragbuf.ntpp_msg;
86
87	/*
88	 * Copying packet contents into a local buffer avoids
89	 * problems of interpretation if the packet is truncated.
90	 */
91	(void) memcpy(&fragbuf, ntp_pkt, MIN(sizeof (fragbuf), fraglen));
92
93	if (flags & F_SUM) {
94		switch (ntp->li_vn_mode & NTPMODEMASK) {
95		case MODE_SYM_ACT:
96		case MODE_SYM_PAS:
97		case MODE_CLIENT:
98		case MODE_SERVER:
99		case MODE_BROADCAST:
100		    (void) sprintf(get_sum_line(),
101			"NTP  %s [st=%hd] (%s)",
102			show_mode(ntp->li_vn_mode & NTPMODEMASK),
103			ntp->stratum,
104			show_time(ntp->xmt));
105		    break;
106		case MODE_CONTROL:
107		    (void) sprintf(get_sum_line(),
108			"NTP  %s "
109			"(Flags/op=0x%02x Seq=%hu Status=0x%04hx Assoc=%hu)",
110			show_mode(ntpc->li_vn_mode & NTPMODEMASK),
111			ntpc->r_m_e_op,
112			ntohs(ntpc->sequence),
113			ntohs(ntpc->status),
114			ntohs(ntpc->associd));
115		    break;
116		default:
117		    (void) sprintf(get_sum_line(),
118			"NTP  %s",
119			show_mode(ntpp->rm_vn_mode & NTPMODEMASK));
120		    break;
121		}
122	}
123
124	proto_version = (ntp->li_vn_mode & VERSIONMASK) >> 3;
125
126	if (flags & F_DTAIL) {
127		show_header("NTP:  ", "Network Time Protocol", fraglen);
128		show_space();
129		switch (ntp->li_vn_mode & NTPMODEMASK) {
130		case MODE_SYM_ACT:
131		case MODE_SYM_PAS:
132		case MODE_CLIENT:
133		case MODE_SERVER:
134		case MODE_BROADCAST:
135		    (void) sprintf(get_line((char *)(uintptr_t)ntp->li_vn_mode -
136			dlc_header, 1),
137			"Leap    = 0x%x (%s)",
138			(int)(ntp->li_vn_mode & LEAPMASK) >> 6,
139			show_leap(ntp->li_vn_mode & LEAPMASK));
140		    (void) sprintf(get_line((char *)(uintptr_t)ntp->li_vn_mode -
141			dlc_header, 1),
142			"Version = %lu", proto_version);
143		    (void) sprintf(get_line((char *)(uintptr_t)ntp->li_vn_mode -
144			dlc_header, 1),
145			"Mode    = %hu (%s)",
146			ntp->li_vn_mode & NTPMODEMASK,
147			show_mode(ntp->li_vn_mode & NTPMODEMASK));
148		    (void) sprintf(get_line((char *)(uintptr_t)ntp->stratum -
149			dlc_header, 1),
150			"Stratum = %d (%s)",
151			ntp->stratum,
152			ntp->stratum == 0 ? "unspecified" :
153			ntp->stratum == 1 ? "primary reference" :
154			"secondary reference");
155		    (void) sprintf(get_line((char *)(uintptr_t)ntp->ppoll -
156			dlc_header, 1),	"Poll    = %hu", ntp->ppoll);
157		    (void) sprintf(get_line((char *)(uintptr_t)ntp->precision -
158			dlc_header, 1),
159			"Precision = %d seconds",
160			ntp->precision);
161		    (void) sprintf(get_line(
162			(char *)(uintptr_t)ntp->distance.int_part -
163			dlc_header, 1),
164			"Synchronizing distance   = 0x%04x.%04x  (%f)",
165			ntohs(ntp->distance.int_part),
166			ntohs(ntp->distance.fraction),
167			s_fixed_to_double(&ntp->distance));
168		    (void) sprintf(get_line(
169			(char *)(uintptr_t)ntp->dispersion.int_part -
170			dlc_header, 1),
171			"Synchronizing dispersion = 0x%04x.%04x  (%f)",
172			ntohs(ntp->dispersion.int_part),
173			ntohs(ntp->dispersion.fraction),
174			s_fixed_to_double(&ntp->dispersion));
175		    (void) sprintf(get_line((char *)(uintptr_t)ntp->refid -
176			dlc_header, 1), "Reference clock = %s",
177			show_ref(ntp->stratum, ntp->refid));
178
179		    (void) sprintf(get_line(
180			(char *)(uintptr_t)ntp->reftime.int_part - dlc_header,
181			1), "Reference time = 0x%08lx.%08lx (%s)",
182			ntohl(ntp->reftime.int_part),
183			ntohl(ntp->reftime.fraction),
184			show_time(ntp->reftime));
185
186		    (void) sprintf(get_line(
187			(char *)(uintptr_t)ntp->org.int_part - dlc_header, 1),
188			"Originate time = 0x%08lx.%08lx (%s)",
189			ntohl(ntp->org.int_part),
190			ntohl(ntp->org.fraction),
191			show_time(ntp->org));
192
193		    (void) sprintf(get_line(
194			(char *)(uintptr_t)ntp->rec.int_part - dlc_header, 1),
195			"Receive   time = 0x%08lx.%08lx (%s)",
196			ntohl(ntp->rec.int_part),
197			ntohl(ntp->rec.fraction),
198			show_time(ntp->rec));
199
200		    (void) sprintf(get_line(
201			(char *)(uintptr_t)ntp->xmt.int_part - dlc_header, 1),
202			"Transmit  time = 0x%08lx.%08lx (%s)",
203			ntohl(ntp->xmt.int_part),
204			ntohl(ntp->xmt.fraction),
205			show_time(ntp->xmt));
206
207		    if (proto_version > 3 ||
208			fraglen < (LEN_PKT_NOMAC + MAC_OCTETS_MIN)) {
209				/*
210				 * A newer protocol version we can't parse,
211				 * or v3 packet with no valid authentication.
212				 */
213				break;
214		    }
215		    (void) sprintf(get_line((char *)ntp->keyid -
216			dlc_header, 1),
217			"Key ID  = %8lu", ntohl(ntp->keyid));
218
219		    macbytes = fraglen - (LEN_PKT_NOMAC + sizeof (uint32_t));
220
221		    for (i = 0, j = 0; i < macbytes; i++) {
222			    hbuf[j++] = hexstr[ntp->mac[i] >> 4 & 0x0f];
223			    hbuf[j++] = hexstr[ntp->mac[i] & 0x0f];
224		    }
225		    hbuf[j] = '\0';
226		    (void) sprintf(get_line((char *)ntp->mac -
227			dlc_header, 1),
228			"Authentication code = %s", hbuf);
229		    break;
230
231		case MODE_CONTROL:
232		    /* NTP Control Message, mode 6 */
233
234		    (void) sprintf(get_line((char *)(uintptr_t)ntp->li_vn_mode -
235			dlc_header, 1),
236			"Leap    = 0x%x (%s)",
237			(int)(ntp->li_vn_mode & LEAPMASK) >> 6,
238			show_leap(ntp->li_vn_mode & LEAPMASK));
239		    (void) sprintf(get_line((char *)(uintptr_t)ntp->li_vn_mode -
240			dlc_header, 1),
241			"Version = %lu", proto_version);
242		    (void) sprintf(get_line((char *)(uintptr_t)ntp->li_vn_mode -
243			dlc_header, 1),
244			"Mode    = %hu (%s)",
245			ntp->li_vn_mode & NTPMODEMASK,
246			show_mode(ntp->li_vn_mode & NTPMODEMASK));
247		    (void) sprintf(get_line((char *)(uintptr_t)ntpc->r_m_e_op -
248			dlc_header, 1),
249			"Flags and operation code = 0x%02x",
250			ntpc->r_m_e_op);
251		    (void) sprintf(get_line((char *)(uintptr_t)ntpc->r_m_e_op -
252			dlc_header, 1),
253			"      %s",
254			getflag(ntpc->r_m_e_op, CTL_RESPONSE, "response",
255			"request"));
256		    (void) sprintf(get_line((char *)(uintptr_t)ntpc->r_m_e_op -
257			dlc_header, 1),
258			"      %s",
259			getflag(ntpc->r_m_e_op, CTL_ERROR, "error",
260			"success"));
261		    (void) sprintf(get_line((char *)(uintptr_t)ntpc->r_m_e_op -
262			dlc_header, 1),
263			"      %s",
264			getflag(ntpc->r_m_e_op, CTL_MORE, "more",
265			"no more"));
266		    (void) sprintf(get_line((char *)(uintptr_t)ntpc->r_m_e_op -
267			dlc_header, 1),
268			"      ...x xxxx = %hd (%s)",
269			ntpc->r_m_e_op & CTL_OP_MASK,
270			show_operation(ntpc->r_m_e_op & CTL_OP_MASK));
271		    (void) sprintf(get_line((char *)(uintptr_t)ntpc->sequence -
272			dlc_header, 1),
273			"Sequence = %hu",
274			ntohs(ntpc->sequence));
275		    (void) sprintf(get_line((char *)(uintptr_t)ntpc->status -
276			dlc_header, 1),
277			"Status = 0x%04hx",
278			ntohs(ntpc->status));
279		    (void) sprintf(get_line((char *)(uintptr_t)ntpc->associd -
280			dlc_header, 1),
281			"Assoc ID = %hu",
282			ntohs(ntpc->associd));
283		    (void) sprintf(get_line((char *)(uintptr_t)ntpc->offset -
284			dlc_header, 1),
285			"Data offset = %hu",
286			ntohs(ntpc->offset));
287		    (void) sprintf(get_line((char *)(uintptr_t)ntpc->count -
288			dlc_header, 1),
289			"Data bytes = %hu",
290			ntohs(ntpc->count));
291		    datalen = ntohs(ntpc->count);
292		    if (datalen == 0) {
293			    break;
294		    } else if (datalen > NTPC_DATA_MAXLEN) {
295			    datalen = NTPC_DATA_MAXLEN;
296		    }
297		    show_space();
298		    datap = (char *)ntpc->data;
299		    do {
300			    (void) sprintf(get_line(datap -
301				dlc_header, 1),
302				"\"%s\"",
303				show_string(datap, linelen, datalen));
304			    sofar += linelen;
305			    datap += linelen;
306			    if ((sofar + linelen) > datalen) {
307				    linelen = datalen - sofar;
308			    }
309		    } while (sofar < datalen);
310		    show_trailer();
311		    break;
312
313		case MODE_PRIVATE:
314		    /* NTP Private Message, mode 7 */
315
316		    (void) sprintf(get_line(
317			(char *)(uintptr_t)ntpp->rm_vn_mode - dlc_header, 1),
318			"Version = %hu", INFO_VERSION(ntpp->rm_vn_mode));
319		    (void) sprintf(get_line(
320			(char *)(uintptr_t)ntpp->rm_vn_mode - dlc_header, 1),
321			"Mode    = %hu (%s)", INFO_MODE(ntpp->rm_vn_mode),
322			show_mode(INFO_MODE(ntpp->rm_vn_mode)));
323		    (void) sprintf(get_line(
324			(char *)(uintptr_t)ntpp->rm_vn_mode - dlc_header, 1),
325			"Flags = 0x%02hx", ntpp->rm_vn_mode);
326		    (void) sprintf(get_line(
327			(char *)(uintptr_t)ntpp->rm_vn_mode - dlc_header, 1),
328			"      %s",
329			getflag(ntpp->rm_vn_mode, RESP_BIT, "response",
330			"request"));
331		    (void) sprintf(get_line(
332			(char *)(uintptr_t)ntpp->rm_vn_mode - dlc_header, 1),
333			"      %s",
334			getflag(ntpp->rm_vn_mode, MORE_BIT, "more", "no more"));
335		    (void) sprintf(get_line((char *)(uintptr_t)ntpp->auth_seq -
336			dlc_header, 1),
337			"Authentication and sequence = 0x%02x", ntpp->auth_seq);
338		    (void) sprintf(get_line((char *)(uintptr_t)ntpp->auth_seq -
339			dlc_header, 1),
340			"      %s",
341			getflag(ntpp->auth_seq, AUTH_BIT, "authenticated",
342			"unauthenticated"));
343		    (void) sprintf(get_line((char *)(uintptr_t)ntpp->auth_seq -
344			dlc_header, 1),
345			"      .xxx xxxx = %hu (sequence number)",
346			INFO_SEQ(ntpp->auth_seq));
347		    (void) sprintf(get_line(
348			(char *)(uintptr_t)ntpp->implementation - dlc_header,
349			1), "Implementation = %hu", ntpp->implementation);
350		    (void) sprintf(get_line((char *)(uintptr_t)ntpp->request -
351			dlc_header, 1), "Request = %hu", ntpp->request);
352		    (void) sprintf(get_line(
353			(char *)(uintptr_t)ntpp->err_nitems - dlc_header, 1),
354			"Error = %hu", INFO_ERR(ntpp->err_nitems));
355		    (void) sprintf(get_line(
356			(char *)(uintptr_t)ntpp->err_nitems - dlc_header, 1),
357			"Items = %hu", INFO_NITEMS(ntpp->err_nitems));
358		    (void) sprintf(get_line(
359			(char *)(uintptr_t)ntpp->mbz_itemsize - dlc_header, 1),
360			"Item size = %hu", INFO_ITEMSIZE(ntpp->mbz_itemsize));
361		    break;
362
363		default:
364		    /* Unknown mode */
365		    (void) sprintf(get_line((char *)(uintptr_t)ntp->li_vn_mode -
366			dlc_header, 1),	"Mode    = %hu (%s)",
367			ntp->li_vn_mode & NTPMODEMASK,
368			show_mode(ntp->li_vn_mode & NTPMODEMASK));
369		    break;
370		}
371	}
372
373	return (fraglen);
374}
375
376char *
377show_leap(int leap)
378{
379	switch (leap) {
380	case NO_WARNING: return ("OK");
381	case PLUS_SEC:	return ("add a second (61 seconds)");
382	case MINUS_SEC: return ("minus a second (59 seconds)");
383	case ALARM:	return ("alarm condition (clock unsynchronized)");
384	default:	return ("unknown");
385	}
386}
387
388char *
389show_mode(int mode)
390{
391	switch (mode) {
392	case MODE_UNSPEC:	return ("unspecified");
393	case MODE_SYM_ACT:	return ("symmetric active");
394	case MODE_SYM_PAS:	return ("symmetric passive");
395	case MODE_CLIENT:	return ("client");
396	case MODE_SERVER:	return ("server");
397	case MODE_BROADCAST:	return ("broadcast");
398	case MODE_CONTROL:	return ("control");
399	case MODE_PRIVATE:	return ("private");
400	default:		return ("unknown");
401	}
402}
403
404char *
405show_ref(int mode, ulong_t refid)
406{
407	static char buff[MAXHOSTNAMELEN + 32];
408	struct in_addr host;
409	extern char *inet_ntoa();
410
411	switch (mode) {
412	case 0:
413	case 1:
414		(void) strncpy(buff, (char *)&refid, 4);
415		buff[4] = '\0';
416		break;
417
418	default:
419		host.s_addr = refid;
420		(void) sprintf(buff, "%s (%s)",
421		    inet_ntoa(host),
422		    addrtoname(AF_INET, &host));
423		break;
424	}
425
426	return (buff);
427}
428
429/*
430 *  Here we have to worry about the high order bit being signed
431 */
432double
433s_fixed_to_double(struct s_fixedpt *t)
434{
435	double a;
436
437	if (ntohs(t->int_part) & 0x8000) {
438		a = ntohs((int)(~t->fraction) & 0xFFFF);
439		a = a / 65536.0;	/* shift dec point over by 16 bits */
440		a +=  ntohs((int)(~t->int_part) & 0xFFFF);
441		a = -a;
442	} else {
443		a = ntohs(t->fraction);
444		a = a / 65536.0;	/* shift dec point over by 16 bits */
445		a += ntohs(t->int_part);
446	}
447	return (a);
448}
449
450/*
451 * Consistent with RFC-3339, ISO 8601.
452 */
453char *
454iso_date_time(time_t input_time)
455{
456	struct tm	*time_parts;
457	static char	tbuf[sizeof ("yyyy-mm-dd hh:mm:ss")];
458
459	time_parts = localtime(&input_time);
460	(void) strftime(tbuf, sizeof (tbuf), "%Y-%m-%d %H:%M:%S", time_parts);
461	return (tbuf);
462}
463
464/*
465 * The base of NTP timestamps is 1900-01-01 00:00:00.00000
466 */
467char *
468show_time(struct l_fixedpt pkt_time)
469{
470	struct l_fixedpt net_time;
471	unsigned long	fracsec;
472	static char	buff[32];
473
474	if (pkt_time.int_part == 0) {
475		buff[0] = '\0';
476		return (buff);
477	}
478
479	net_time.int_part = ntohl(pkt_time.int_part) - JAN_1970;
480	net_time.fraction = ntohl(pkt_time.fraction);
481
482	fracsec = net_time.fraction / 42949;	/* fract / (2**32/10**6) */
483
484	(void) strlcpy(buff, iso_date_time(net_time.int_part), sizeof (buff));
485	(void) snprintf(buff, sizeof (buff), "%s.%05lu", buff, fracsec);
486
487	return (buff);
488}
489
490char *
491show_operation(int op)
492{
493	switch (op) {
494	case CTL_OP_UNSPEC:	return ("unspecified");
495	case CTL_OP_READSTAT:	return ("read stats");
496	case CTL_OP_READVAR:	return ("read var");
497	case CTL_OP_WRITEVAR:	return ("write var");
498	case CTL_OP_READCLOCK:	return ("read clock");
499	case CTL_OP_WRITECLOCK: return ("write clock");
500	case CTL_OP_SETTRAP:	return ("set trap");
501	case CTL_OP_ASYNCMSG:	return ("async msg");
502	case CTL_OP_UNSETTRAP:	return ("unset trap");
503	default:		return ("unknown");
504	}
505}
506