xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ntp.c (revision 2e3b64671f0fdac42d7fb21a8fa7e3ce9fce3359)
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 
48 extern char *dlc_header;
49 
50 static	char	*show_leap(int);
51 static	char	*show_mode(int);
52 static	char	*show_ref(int, ulong_t);
53 static	char	*show_time(struct l_fixedpt);
54 static	double	s_fixed_to_double(struct s_fixedpt *);
55 static	char	*iso_date_time(time_t);
56 static	char	*show_operation(int);
57 
58 int
59 interpret_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 
376 char *
377 show_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 
388 char *
389 show_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 
404 char *
405 show_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  */
432 double
433 s_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  */
453 char *
454 iso_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  */
467 char *
468 show_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 
490 char *
491 show_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