/*
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/sysmacros.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <tzfile.h>
#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 *)(uintptr_t)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 *)(uintptr_t)ntp->li_vn_mode -
dlc_header, 1),
"Version = %lu", proto_version);
(void) sprintf(get_line((char *)(uintptr_t)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 *)(uintptr_t)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 *)(uintptr_t)ntp->ppoll -
dlc_header, 1), "Poll = %hu", ntp->ppoll);
(void) sprintf(get_line((char *)(uintptr_t)ntp->precision -
dlc_header, 1),
"Precision = %d seconds",
ntp->precision);
(void) sprintf(get_line(
(char *)(uintptr_t)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 *)(uintptr_t)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 *)(uintptr_t)ntp->refid -
dlc_header, 1), "Reference clock = %s",
show_ref(ntp->stratum, ntp->refid));
(void) sprintf(get_line(
(char *)(uintptr_t)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 *)(uintptr_t)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 *)(uintptr_t)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 *)(uintptr_t)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 *)(uintptr_t)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 *)(uintptr_t)ntp->li_vn_mode -
dlc_header, 1),
"Version = %lu", proto_version);
(void) sprintf(get_line((char *)(uintptr_t)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 *)(uintptr_t)ntpc->r_m_e_op -
dlc_header, 1),
"Flags and operation code = 0x%02x",
ntpc->r_m_e_op);
(void) sprintf(get_line((char *)(uintptr_t)ntpc->r_m_e_op -
dlc_header, 1),
" %s",
getflag(ntpc->r_m_e_op, CTL_RESPONSE, "response",
"request"));
(void) sprintf(get_line((char *)(uintptr_t)ntpc->r_m_e_op -
dlc_header, 1),
" %s",
getflag(ntpc->r_m_e_op, CTL_ERROR, "error",
"success"));
(void) sprintf(get_line((char *)(uintptr_t)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 *)(uintptr_t)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 *)(uintptr_t)ntpc->sequence -
dlc_header, 1),
"Sequence = %hu",
ntohs(ntpc->sequence));
(void) sprintf(get_line((char *)(uintptr_t)ntpc->status -
dlc_header, 1),
"Status = 0x%04hx",
ntohs(ntpc->status));
(void) sprintf(get_line((char *)(uintptr_t)ntpc->associd -
dlc_header, 1),
"Assoc ID = %hu",
ntohs(ntpc->associd));
(void) sprintf(get_line((char *)(uintptr_t)ntpc->offset -
dlc_header, 1),
"Data offset = %hu",
ntohs(ntpc->offset));
(void) sprintf(get_line((char *)(uintptr_t)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 *)(uintptr_t)ntpp->rm_vn_mode - dlc_header, 1),
"Version = %hu", INFO_VERSION(ntpp->rm_vn_mode));
(void) sprintf(get_line(
(char *)(uintptr_t)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 *)(uintptr_t)ntpp->rm_vn_mode - dlc_header, 1),
"Flags = 0x%02hx", ntpp->rm_vn_mode);
(void) sprintf(get_line(
(char *)(uintptr_t)ntpp->rm_vn_mode - dlc_header, 1),
" %s",
getflag(ntpp->rm_vn_mode, RESP_BIT, "response",
"request"));
(void) sprintf(get_line(
(char *)(uintptr_t)ntpp->rm_vn_mode - dlc_header, 1),
" %s",
getflag(ntpp->rm_vn_mode, MORE_BIT, "more", "no more"));
(void) sprintf(get_line((char *)(uintptr_t)ntpp->auth_seq -
dlc_header, 1),
"Authentication and sequence = 0x%02x", ntpp->auth_seq);
(void) sprintf(get_line((char *)(uintptr_t)ntpp->auth_seq -
dlc_header, 1),
" %s",
getflag(ntpp->auth_seq, AUTH_BIT, "authenticated",
"unauthenticated"));
(void) sprintf(get_line((char *)(uintptr_t)ntpp->auth_seq -
dlc_header, 1),
" .xxx xxxx = %hu (sequence number)",
INFO_SEQ(ntpp->auth_seq));
(void) sprintf(get_line(
(char *)(uintptr_t)ntpp->implementation - dlc_header,
1), "Implementation = %hu", ntpp->implementation);
(void) sprintf(get_line((char *)(uintptr_t)ntpp->request -
dlc_header, 1), "Request = %hu", ntpp->request);
(void) sprintf(get_line(
(char *)(uintptr_t)ntpp->err_nitems - dlc_header, 1),
"Error = %hu", INFO_ERR(ntpp->err_nitems));
(void) sprintf(get_line(
(char *)(uintptr_t)ntpp->err_nitems - dlc_header, 1),
"Items = %hu", INFO_NITEMS(ntpp->err_nitems));
(void) sprintf(get_line(
(char *)(uintptr_t)ntpp->mbz_itemsize - dlc_header, 1),
"Item size = %hu", INFO_ITEMSIZE(ntpp->mbz_itemsize));
break;
default:
/* Unknown mode */
(void) sprintf(get_line((char *)(uintptr_t)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");
}
}