/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1991, 2000, 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include "snoop.h" #include "ntp.h" /* * In verbose mode, how many octets of the control-mode data payload * are displayed per line of output. The value 64 fits well on an * 80-column screen and, as a power of 2, is easily correlated to * hexadecimal output. */ #define OCTETS_PER_LINE 64 extern char *dlc_header; static char *show_leap(int); static char *show_mode(int); static char *show_ref(int, ulong_t); static char *show_time(struct l_fixedpt); static double s_fixed_to_double(struct s_fixedpt *); static char *iso_date_time(time_t); static char *show_operation(int); int interpret_ntp(int flags, struct ntpdata *ntp_pkt, int fraglen) { unsigned int i, j, macbytes; unsigned int proto_version; unsigned int datalen; unsigned int linelen = OCTETS_PER_LINE; unsigned int sofar = 0; char *datap; char hbuf[2 * MAC_OCTETS_MAX + 1]; static char *hexstr = "0123456789ABCDEF"; union ntp_pkt_buf { struct ntpdata ntp_msg; union ntpc_buf { struct ntp_control chdr; uchar_t data2[NTPC_DATA_MAXLEN - 1]; } ntpc_msg; union ntpp_buf { struct ntp_private phdr; uchar_t data2[1]; } ntpp_msg; } fragbuf; struct ntpdata *ntp = &fragbuf.ntp_msg; struct ntp_control *ntpc = (struct ntp_control *)&fragbuf.ntpc_msg; struct ntp_private *ntpp = (struct ntp_private *)&fragbuf.ntpp_msg; /* * Copying packet contents into a local buffer avoids * problems of interpretation if the packet is truncated. */ (void) memcpy(&fragbuf, ntp_pkt, MIN(sizeof (fragbuf), fraglen)); if (flags & F_SUM) { switch (ntp->li_vn_mode & NTPMODEMASK) { case MODE_SYM_ACT: case MODE_SYM_PAS: case MODE_CLIENT: case MODE_SERVER: case MODE_BROADCAST: (void) sprintf(get_sum_line(), "NTP %s [st=%hd] (%s)", show_mode(ntp->li_vn_mode & NTPMODEMASK), ntp->stratum, show_time(ntp->xmt)); break; case MODE_CONTROL: (void) sprintf(get_sum_line(), "NTP %s " "(Flags/op=0x%02x Seq=%hu Status=0x%04hx Assoc=%hu)", show_mode(ntpc->li_vn_mode & NTPMODEMASK), ntpc->r_m_e_op, ntohs(ntpc->sequence), ntohs(ntpc->status), ntohs(ntpc->associd)); break; default: (void) sprintf(get_sum_line(), "NTP %s", show_mode(ntpp->rm_vn_mode & NTPMODEMASK)); break; } } proto_version = (ntp->li_vn_mode & VERSIONMASK) >> 3; if (flags & F_DTAIL) { show_header("NTP: ", "Network Time Protocol", fraglen); show_space(); switch (ntp->li_vn_mode & NTPMODEMASK) { case MODE_SYM_ACT: case MODE_SYM_PAS: case MODE_CLIENT: case MODE_SERVER: case MODE_BROADCAST: (void) sprintf(get_line((char *)ntp->li_vn_mode - dlc_header, 1), "Leap = 0x%x (%s)", (int)(ntp->li_vn_mode & LEAPMASK) >> 6, show_leap(ntp->li_vn_mode & LEAPMASK)); (void) sprintf(get_line((char *)ntp->li_vn_mode - dlc_header, 1), "Version = %lu", proto_version); (void) sprintf(get_line((char *)ntp->li_vn_mode - dlc_header, 1), "Mode = %hu (%s)", ntp->li_vn_mode & NTPMODEMASK, show_mode(ntp->li_vn_mode & NTPMODEMASK)); (void) sprintf(get_line((char *)ntp->stratum - dlc_header, 1), "Stratum = %d (%s)", ntp->stratum, ntp->stratum == 0 ? "unspecified" : ntp->stratum == 1 ? "primary reference" : "secondary reference"); (void) sprintf(get_line((char *)ntp->ppoll - dlc_header, 1), "Poll = %hu", ntp->ppoll); (void) sprintf(get_line((char *)ntp->precision - dlc_header, 1), "Precision = %d seconds", ntp->precision); (void) sprintf(get_line((char *)ntp->distance.int_part - dlc_header, 1), "Synchronizing distance = 0x%04x.%04x (%f)", ntohs(ntp->distance.int_part), ntohs(ntp->distance.fraction), s_fixed_to_double(&ntp->distance)); (void) sprintf(get_line((char *)ntp->dispersion.int_part - dlc_header, 1), "Synchronizing dispersion = 0x%04x.%04x (%f)", ntohs(ntp->dispersion.int_part), ntohs(ntp->dispersion.fraction), s_fixed_to_double(&ntp->dispersion)); (void) sprintf(get_line((char *)ntp->refid - dlc_header, 1), "Reference clock = %s", show_ref(ntp->stratum, ntp->refid)); (void) sprintf(get_line((char *)ntp->reftime.int_part - dlc_header, 1), "Reference time = 0x%08lx.%08lx (%s)", ntohl(ntp->reftime.int_part), ntohl(ntp->reftime.fraction), show_time(ntp->reftime)); (void) sprintf(get_line((char *)ntp->org.int_part - dlc_header, 1), "Originate time = 0x%08lx.%08lx (%s)", ntohl(ntp->org.int_part), ntohl(ntp->org.fraction), show_time(ntp->org)); (void) sprintf(get_line((char *)ntp->rec.int_part - dlc_header, 1), "Receive time = 0x%08lx.%08lx (%s)", ntohl(ntp->rec.int_part), ntohl(ntp->rec.fraction), show_time(ntp->rec)); (void) sprintf(get_line((char *)ntp->xmt.int_part - dlc_header, 1), "Transmit time = 0x%08lx.%08lx (%s)", ntohl(ntp->xmt.int_part), ntohl(ntp->xmt.fraction), show_time(ntp->xmt)); if (proto_version > 3 || fraglen < (LEN_PKT_NOMAC + MAC_OCTETS_MIN)) { /* * A newer protocol version we can't parse, * or v3 packet with no valid authentication. */ break; } (void) sprintf(get_line((char *)ntp->keyid - dlc_header, 1), "Key ID = %8lu", ntohl(ntp->keyid)); macbytes = fraglen - (LEN_PKT_NOMAC + sizeof (uint32_t)); for (i = 0, j = 0; i < macbytes; i++) { hbuf[j++] = hexstr[ntp->mac[i] >> 4 & 0x0f]; hbuf[j++] = hexstr[ntp->mac[i] & 0x0f]; } hbuf[j] = '\0'; (void) sprintf(get_line((char *)ntp->mac - dlc_header, 1), "Authentication code = %s", hbuf); break; case MODE_CONTROL: /* NTP Control Message, mode 6 */ (void) sprintf(get_line((char *)ntp->li_vn_mode - dlc_header, 1), "Leap = 0x%x (%s)", (int)(ntp->li_vn_mode & LEAPMASK) >> 6, show_leap(ntp->li_vn_mode & LEAPMASK)); (void) sprintf(get_line((char *)ntp->li_vn_mode - dlc_header, 1), "Version = %lu", proto_version); (void) sprintf(get_line((char *)ntp->li_vn_mode - dlc_header, 1), "Mode = %hu (%s)", ntp->li_vn_mode & NTPMODEMASK, show_mode(ntp->li_vn_mode & NTPMODEMASK)); (void) sprintf(get_line((char *)ntpc->r_m_e_op - dlc_header, 1), "Flags and operation code = 0x%02x", ntpc->r_m_e_op); (void) sprintf(get_line((char *)ntpc->r_m_e_op - dlc_header, 1), " %s", getflag(ntpc->r_m_e_op, CTL_RESPONSE, "response", "request")); (void) sprintf(get_line((char *)ntpc->r_m_e_op - dlc_header, 1), " %s", getflag(ntpc->r_m_e_op, CTL_ERROR, "error", "success")); (void) sprintf(get_line((char *)ntpc->r_m_e_op - dlc_header, 1), " %s", getflag(ntpc->r_m_e_op, CTL_MORE, "more", "no more")); (void) sprintf(get_line((char *)ntpc->r_m_e_op - dlc_header, 1), " ...x xxxx = %hd (%s)", ntpc->r_m_e_op & CTL_OP_MASK, show_operation(ntpc->r_m_e_op & CTL_OP_MASK)); (void) sprintf(get_line((char *)ntpc->sequence - dlc_header, 1), "Sequence = %hu", ntohs(ntpc->sequence)); (void) sprintf(get_line((char *)ntpc->status - dlc_header, 1), "Status = 0x%04hx", ntohs(ntpc->status)); (void) sprintf(get_line((char *)ntpc->associd - dlc_header, 1), "Assoc ID = %hu", ntohs(ntpc->associd)); (void) sprintf(get_line((char *)ntpc->offset - dlc_header, 1), "Data offset = %hu", ntohs(ntpc->offset)); (void) sprintf(get_line((char *)ntpc->count - dlc_header, 1), "Data bytes = %hu", ntohs(ntpc->count)); datalen = ntohs(ntpc->count); if (datalen == 0) { break; } else if (datalen > NTPC_DATA_MAXLEN) { datalen = NTPC_DATA_MAXLEN; } show_space(); datap = (char *)ntpc->data; do { (void) sprintf(get_line(datap - dlc_header, 1), "\"%s\"", show_string(datap, linelen, datalen)); sofar += linelen; datap += linelen; if ((sofar + linelen) > datalen) { linelen = datalen - sofar; } } while (sofar < datalen); show_trailer(); break; case MODE_PRIVATE: /* NTP Private Message, mode 7 */ (void) sprintf(get_line((char *)ntpp->rm_vn_mode - dlc_header, 1), "Version = %hu", INFO_VERSION(ntpp->rm_vn_mode)); (void) sprintf(get_line((char *)ntpp->rm_vn_mode - dlc_header, 1), "Mode = %hu (%s)", INFO_MODE(ntpp->rm_vn_mode), show_mode(INFO_MODE(ntpp->rm_vn_mode))); (void) sprintf(get_line((char *)ntpp->rm_vn_mode - dlc_header, 1), "Flags = 0x%02hx", ntpp->rm_vn_mode); (void) sprintf(get_line((char *)ntpp->rm_vn_mode - dlc_header, 1), " %s", getflag(ntpp->rm_vn_mode, RESP_BIT, "response", "request")); (void) sprintf(get_line((char *)ntpp->rm_vn_mode - dlc_header, 1), " %s", getflag(ntpp->rm_vn_mode, MORE_BIT, "more", "no more")); (void) sprintf(get_line((char *)ntpp->auth_seq - dlc_header, 1), "Authentication and sequence = 0x%02x", ntpp->auth_seq); (void) sprintf(get_line((char *)ntpp->auth_seq - dlc_header, 1), " %s", getflag(ntpp->auth_seq, AUTH_BIT, "authenticated", "unauthenticated")); (void) sprintf(get_line((char *)ntpp->auth_seq - dlc_header, 1), " .xxx xxxx = %hu (sequence number)", INFO_SEQ(ntpp->auth_seq)); (void) sprintf(get_line((char *)ntpp->implementation - dlc_header, 1), "Implementation = %hu", ntpp->implementation); (void) sprintf(get_line((char *)ntpp->request - dlc_header, 1), "Request = %hu", ntpp->request); (void) sprintf(get_line((char *)ntpp->err_nitems - dlc_header, 1), "Error = %hu", INFO_ERR(ntpp->err_nitems)); (void) sprintf(get_line((char *)ntpp->err_nitems - dlc_header, 1), "Items = %hu", INFO_NITEMS(ntpp->err_nitems)); (void) sprintf(get_line((char *)ntpp->mbz_itemsize - dlc_header, 1), "Item size = %hu", INFO_ITEMSIZE(ntpp->mbz_itemsize)); break; default: /* Unknown mode */ (void) sprintf(get_line((char *)ntp->li_vn_mode - dlc_header, 1), "Mode = %hu (%s)", ntp->li_vn_mode & NTPMODEMASK, show_mode(ntp->li_vn_mode & NTPMODEMASK)); break; } } return (fraglen); } char * show_leap(int leap) { switch (leap) { case NO_WARNING: return ("OK"); case PLUS_SEC: return ("add a second (61 seconds)"); case MINUS_SEC: return ("minus a second (59 seconds)"); case ALARM: return ("alarm condition (clock unsynchronized)"); default: return ("unknown"); } } char * show_mode(int mode) { switch (mode) { case MODE_UNSPEC: return ("unspecified"); case MODE_SYM_ACT: return ("symmetric active"); case MODE_SYM_PAS: return ("symmetric passive"); case MODE_CLIENT: return ("client"); case MODE_SERVER: return ("server"); case MODE_BROADCAST: return ("broadcast"); case MODE_CONTROL: return ("control"); case MODE_PRIVATE: return ("private"); default: return ("unknown"); } } char * show_ref(int mode, ulong_t refid) { static char buff[MAXHOSTNAMELEN + 32]; struct in_addr host; extern char *inet_ntoa(); switch (mode) { case 0: case 1: (void) strncpy(buff, (char *)&refid, 4); buff[4] = '\0'; break; default: host.s_addr = refid; (void) sprintf(buff, "%s (%s)", inet_ntoa(host), addrtoname(AF_INET, &host)); break; } return (buff); } /* * Here we have to worry about the high order bit being signed */ double s_fixed_to_double(struct s_fixedpt *t) { double a; if (ntohs(t->int_part) & 0x8000) { a = ntohs((int)(~t->fraction) & 0xFFFF); a = a / 65536.0; /* shift dec point over by 16 bits */ a += ntohs((int)(~t->int_part) & 0xFFFF); a = -a; } else { a = ntohs(t->fraction); a = a / 65536.0; /* shift dec point over by 16 bits */ a += ntohs(t->int_part); } return (a); } /* * Consistent with RFC-3339, ISO 8601. */ char * iso_date_time(time_t input_time) { struct tm *time_parts; static char tbuf[sizeof ("yyyy-mm-dd hh:mm:ss")]; time_parts = localtime(&input_time); (void) strftime(tbuf, sizeof (tbuf), "%Y-%m-%d %H:%M:%S", time_parts); return (tbuf); } /* * The base of NTP timestamps is 1900-01-01 00:00:00.00000 */ char * show_time(struct l_fixedpt pkt_time) { struct l_fixedpt net_time; unsigned long fracsec; static char buff[32]; if (pkt_time.int_part == 0) { buff[0] = '\0'; return (buff); } net_time.int_part = ntohl(pkt_time.int_part) - JAN_1970; net_time.fraction = ntohl(pkt_time.fraction); fracsec = net_time.fraction / 42949; /* fract / (2**32/10**6) */ (void) strlcpy(buff, iso_date_time(net_time.int_part), sizeof (buff)); (void) snprintf(buff, sizeof (buff), "%s.%05lu", buff, fracsec); return (buff); } char * show_operation(int op) { switch (op) { case CTL_OP_UNSPEC: return ("unspecified"); case CTL_OP_READSTAT: return ("read stats"); case CTL_OP_READVAR: return ("read var"); case CTL_OP_WRITEVAR: return ("write var"); case CTL_OP_READCLOCK: return ("read clock"); case CTL_OP_WRITECLOCK: return ("write clock"); case CTL_OP_SETTRAP: return ("set trap"); case CTL_OP_ASYNCMSG: return ("async msg"); case CTL_OP_UNSETTRAP: return ("unset trap"); default: return ("unknown"); } }