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