summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet/usr.sbin
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.sbin')
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile80
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/at.h206
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/fakewin.h71
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/fw.h147
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_lib.h96
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_rpc.h78
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/inc.flg30
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/nfs4_xdr.c2972
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/ntp.h533
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/slp.h146
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c1127
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h355
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aarp.c143
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_adsp.c142
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aecho.c72
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_apple.c210
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_arp.c263
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_atp.c128
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bparam.c220
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bpdu.c95
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c766
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcp.c746
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcpv6.c1051
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_display.c836
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c917
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c1802
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_filter.c2845
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_http.c141
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_icmp.c1003
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_igmp.c226
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ip.c1490
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipaddr.c425
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipsec.c243
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_isis.c99
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ldap.c1470
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.c869
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.h327
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mount.c563
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nbp.c145
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_netbios.c555
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.c691
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.h58
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs3.c1214
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c4900
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs_acl.c797
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nis.c676
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nlm.c1136
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ntp.c505
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.c772
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.h280
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.c815
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.h185
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pf.c1664
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pmap.c772
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.c1946
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.h192
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pppoe.c399
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip.c440
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip6.c140
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpc.c771
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcprint.c106
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcsec.c403
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c452
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rquota.c164
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rstat.c272
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rtmp.c181
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_sctp.c1208
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_slp.c1963
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_smb.c2136
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_socks.c472
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_solarnet.c299
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c435
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tftp.c178
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_trill.c117
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_udp.c124
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_vlan.h57
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_zip.c407
77 files changed, 50960 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile
new file mode 100644
index 0000000..1d408bc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile
@@ -0,0 +1,80 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG= snoop
+OBJS= nfs4_xdr.o snoop.o snoop_aarp.o snoop_adsp.o snoop_aecho.o \
+ snoop_apple.o snoop_arp.o snoop_atp.o snoop_bparam.o \
+ snoop_bpdu.o \
+ snoop_capture.o snoop_dhcp.o snoop_dhcpv6.o snoop_display.o \
+ snoop_dns.o snoop_ether.o \
+ snoop_filter.o snoop_http.o snoop_icmp.o snoop_igmp.o snoop_ip.o \
+ snoop_ipaddr.o snoop_ipsec.o snoop_isis.o \
+ snoop_ldap.o snoop_mip.o snoop_mount.o \
+ snoop_nbp.o snoop_netbios.o snoop_nfs.o snoop_nfs3.o snoop_nfs4.o \
+ snoop_nfs_acl.o snoop_nis.o snoop_nlm.o snoop_ntp.o \
+ snoop_pf.o snoop_ospf.o snoop_ospf6.o snoop_pmap.o snoop_ppp.o \
+ snoop_pppoe.o snoop_rip.o snoop_rip6.o snoop_rpc.o snoop_rpcprint.o \
+ snoop_rpcsec.o snoop_rport.o snoop_rquota.o snoop_rstat.o snoop_rtmp.o \
+ snoop_sctp.o snoop_slp.o snoop_smb.o snoop_socks.o snoop_solarnet.o \
+ snoop_tcp.o snoop_tftp.o snoop_trill.o snoop_udp.o snoop_zip.o
+
+SRCS= $(OBJS:.o=.c)
+HDRS= snoop.h snoop_mip.h at.h snoop_ospf.h snoop_ospf6.h
+
+include ../../../Makefile.cmd
+
+CPPFLAGS += -I. -I$(SRC)/common/net/dhcp \
+ -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+LDLIBS += -ldhcputil -ldlpi -lsocket -lnsl -ltsol
+LDFLAGS += $(MAPFILE.NGB:%=-M%)
+CERRWARN += -_gcc=-Wno-switch
+CERRWARN += -_gcc=-Wno-implicit-function-declaration
+CERRWARN += -_gcc=-Wno-uninitialized
+CERRWARN += -_gcc=-Wno-clobbered
+CERRWARN += -_gcc=-Wno-unused-value
+CERRWARN += -_gcc=-Wno-parentheses
+CERRWARN += -_gcc=-Wno-unused-variable
+CERRWARN += -_gcc=-Wno-unused-label
+CERRWARN += -_gcc=-Wno-unused-function
+
+.KEEP_STATE:
+
+.PARALLEL: $(OBJS)
+
+all: $(PROG)
+
+$(PROG): $(OBJS) $(MAPFILE.NGB)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTUSRSBINPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/at.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/at.h
new file mode 100644
index 0000000..e98072f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/at.h
@@ -0,0 +1,206 @@
+/*
+ * 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 (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ */
+
+#ifndef _AT_H
+#define _AT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * There is a lot of alignment problems in AppleTalk packets.
+ * This is the reason some of the headers use uint8_t arrays instead of the
+ * natural datatype.
+ */
+
+/* AARP */
+
+#define AARP_REQ 1
+#define AARP_RESP 2
+#define AARP_PROBE 3
+
+
+/* DDP */
+
+struct ddp_hdr {
+ uint8_t ddp_hop_len;
+ uint8_t ddp_len_lo;
+ uint16_t ddp_cksum;
+ uint16_t ddp_dest_net;
+ uint16_t ddp_src_net;
+ uint8_t ddp_dest_id;
+ uint8_t ddp_src_id;
+ uint8_t ddp_dest_sock;
+ uint8_t ddp_src_sock;
+ uint8_t ddp_type;
+};
+
+#define ddp_pad(x) ((x)->ddp_hop_len & 0xc0)
+#define ddp_hop(x) (((x)->ddp_hop_len >> 2) & 0xf)
+#define ddp_len(x) ((((x)->ddp_hop_len & 0x3) << 8) + (x)->ddp_len_lo)
+
+#define DDPHDR_SIZE 13
+
+#define DDP_TYPE_RTMPRQ 5
+#define DDP_TYPE_RTMPRESP 1
+#define DDP_TYPE_NBP 2
+#define DDP_TYPE_ATP 3
+#define DDP_TYPE_AEP 4
+#define DDP_TYPE_ZIP 6
+#define DDP_TYPE_ADSP 7
+
+
+/* AECHO */
+
+#define AEP_REQ 1
+#define AEP_REPLY 2
+
+/* NBP */
+
+struct nbp_hdr {
+ uint8_t ddphdr[DDPHDR_SIZE];
+ uint8_t nbp_fun_cnt;
+ uint8_t nbp_id;
+};
+
+#define NBP_BRRQ 1
+#define NBP_LKUP 2
+#define NBP_LKUP_REPLY 3
+#define NBP_FWDREQ 4
+
+
+/* ZIP */
+
+struct zip_hdr {
+ uint8_t ddphdr[DDPHDR_SIZE];
+ uint8_t zip_func;
+ uint8_t zip_netcnt;
+};
+
+#define ZIP_QUERY 1
+#define ZIP_REPLY 2
+#define ZIP_GET_NET_INFO 5
+#define ZIP_GET_NET_INFO_REPLY 6
+#define ZIP_NOTIFY 7
+#define ZIP_EXT_REPLY 8
+
+#define ZIP_ATP_GETMYZONE 7
+#define ZIP_ATP_GETZONELIST 8
+#define ZIP_ATP_GETLOCALZONES 9
+
+#define ZIP_FLG_ONEZ 0x20
+#define ZIP_FLG_USEBRC 0x40
+#define ZIP_FLG_ZINV 0x80
+
+
+/* ATP */
+
+struct atp_hdr {
+ uint8_t ddphdr[DDPHDR_SIZE];
+ uint8_t atp_ctrl;
+ uint8_t atp_seq;
+ uint8_t atp_tid[2];
+ uint8_t atp_user[4];
+};
+
+#define ATPHDR_SIZE 8
+
+#define atp_fun(x) (((x) >> 6) & 0x3)
+#define atp_tmo(x) ((x) & 0x7)
+
+#define ATP_TREQ 1
+#define ATP_TRESP 2
+#define ATP_TREL 3
+#define ATP_FLG_STS 0x08
+#define ATP_FLG_EOM 0x10
+#define ATP_FLG_XO 0x20
+
+
+#define NODE_ID_BROADCAST 0xff
+
+struct ddp_adsphdr {
+ uint8_t ddphdr[DDPHDR_SIZE];
+ uint8_t ad_connid[2]; /* short */
+ uint8_t ad_fbseq[4]; /* long */
+ uint8_t ad_nrseq[4]; /* long */
+ uint8_t ad_rcvwin[2]; /* short */
+ uint8_t ad_desc;
+};
+
+#define AD_CTRL 0x80
+#define AD_ACKREQ 0x40
+#define AD_EOM 0x20
+#define AD_ATT 0x10
+#define AD_CTRL_MASK 0x0f
+
+#define AD_CREQ 0x81 /* Open Conn Request */
+#define AD_CACK 0x82 /* Open Conn Ack */
+#define AD_CREQ_ACK 0x83 /* Open Conn Req+Ack */
+#define AD_CDENY 0x84 /* Open Conn Denial */
+
+struct ddp_adsp_att {
+ struct ddp_adsphdr ad;
+ uint8_t ad_att_code[2]; /* short */
+};
+
+struct ddp_adsp_open {
+ struct ddp_adsphdr ad;
+ uint8_t ad_version[2]; /* short */
+ uint8_t ad_dconnid[2]; /* short */
+ uint8_t ad_attseq[4]; /* long */
+};
+
+#define RTMP_REQ 1
+#define RTMP_RDR_SH 2 /* Route Data Request, split horizon */
+#define RTMP_RDR_NSH 3 /* Route Data Request, no split horizon */
+
+#define RTMP_DIST_MASK 0x1f
+#define RTMP_EXTEND 0x80
+#define RTMP_FILLER 0x82
+
+
+uint16_t get_short(uint8_t *);
+uint32_t get_long(uint8_t *);
+
+extern void interpret_aarp(int, char *, int);
+extern void interpret_at(int, struct ddp_hdr *, int);
+extern void interpret_nbp(int, struct nbp_hdr *, int);
+extern void interpret_rtmp(int, struct ddp_hdr *, int);
+extern void interpret_aecho(int, struct ddp_hdr *, int);
+extern void interpret_atp(int, struct ddp_hdr *, int);
+extern void interpret_adsp(int, struct ddp_adsphdr *, int);
+extern void interpret_ddp_zip(int, struct zip_hdr *, int);
+extern void interpret_atp_zip(int, struct atp_hdr *, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AT_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/fakewin.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fakewin.h
new file mode 100644
index 0000000..d075e0b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fakewin.h
@@ -0,0 +1,71 @@
+/*
+ * 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 (c) 1993 by Sun Microsystems, Inc.
+ */
+
+#ifndef _FAKEWIN_H
+#define _FAKEWIN_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file defines appropriate macros so that
+ * we can use the same codebase for Unix, DOS, and Windows.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WINDOWS
+#include <tklib.h>
+
+#define malloc _fmalloc
+#define calloc _fcalloc
+#define free _ffree
+#define strdup _fstrdup
+#define strcpy _fstrcpy
+#define strcmp _fstrcmp
+#define strchr _fstrchr
+#define sprintf wsprintf
+#define vsprintf wvsprintf
+#define memcpy _fmemcpy
+#define strlen _fstrlen
+#else
+#define LPSTR char *
+#endif
+
+#if !defined(_WINDOWS) && !defined(_MSDOS)
+#define _TKFAR
+#endif
+
+#ifndef _WINDOWS
+#define _TKPASCAL
+#define __export
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_FAKEWIN_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw.h
new file mode 100644
index 0000000..3274623
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw.h
@@ -0,0 +1,147 @@
+/*
+ * 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 (c) 1993 by Sun Microsystems, Inc.
+ */
+
+#ifndef _FW_H
+#define _FW_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <rpc/rpc.h>
+#include "fakewin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Header file for the framework.
+ */
+#define CTXTLEN 1024
+
+struct Op_arg_item {
+ char _TKFAR *name;
+ char _TKFAR *value;
+ struct Op_arg_item _TKFAR *next;
+};
+typedef struct Op_arg_item Op_arg_item;
+
+struct Op_row_link {
+ Op_arg_item _TKFAR *first;
+ Op_arg_item _TKFAR *last;
+ struct Op_row_link _TKFAR *next;
+};
+typedef struct Op_row_link Op_row_link;
+
+struct Op_arg {
+ Op_row_link _TKFAR *first;
+ Op_row_link _TKFAR *last;
+ Op_row_link _TKFAR *curr;
+ Op_arg_item _TKFAR *cura;
+ u_long rows;
+ bool_t xdr_flag;
+};
+typedef struct Op_arg Op_arg;
+
+enum Fw_err {
+ FW_ERR_NONE = 0,
+ FW_ERR_FW = 1,
+ FW_ERR_OP = 2
+};
+typedef enum Fw_err Fw_err;
+
+struct Op_err {
+ Fw_err type;
+ u_long code;
+ bool_t xdr_flag;
+ char _TKFAR *message;
+};
+typedef struct Op_err Op_err;
+
+typedef char invk_context[CTXTLEN];
+
+struct invk_result {
+ Op_err _TKFAR *err;
+ Op_arg _TKFAR *arg;
+ bool_t eof;
+};
+typedef struct invk_result invk_result;
+
+struct invk_request {
+ char _TKFAR *category;
+ char _TKFAR *op;
+ char _TKFAR *vers;
+ char _TKFAR *locale;
+ u_long threshold;
+ invk_context context;
+ Op_arg _TKFAR *arg;
+};
+typedef struct invk_request invk_request;
+
+struct more_request {
+ invk_context context;
+ u_long threshold;
+};
+typedef struct more_request more_request;
+
+struct kill_request {
+ invk_context context;
+};
+typedef struct kill_request kill_request;
+
+#define FW_KV_DELIM "="
+#define FW_KV_DELIM_CH '='
+#define FW_VK_DELIM "\n"
+#define FW_VK_DELIM_CH '\n';
+#define FW_INPUT_VERS_VAL 1
+#define FW_INPUT_VERS_STR "1"
+#define FW_OUTPUT_VERS_VAL 1
+#define FW_OUTPUT_VERS_STR "1"
+#define FW_INPUT_VERS_KEY "_SUNW_AO_INPUT_VERS"
+#define FW_OUTPUT_VERS_KEY "_SUNW_AO_OUTPUT_VERS"
+#define FW_ROW_MARKER_KEY "_SUNW_AO_BEGIN_ROW"
+#define FW_ROW_MARKER FW_ROW_MARKER_KEY FW_KV_DELIM FW_OUTPUT_VERS_STR \
+ FW_VK_DELIM
+#define FW_INPUT_VERS FW_INPUT_VERS_KEY FW_KV_DELIM FW_INPUT_VERS_STR \
+ FW_VK_DELIM
+#define FW_OUTPUT_VERS FW_OUTPUT_VERS_KEY FW_KV_DELIM FW_OUTPUT_VERS_STR \
+ FW_VK_DELIM
+#define FW_ERR_MSG_MAX 2047
+#define FW_UNIX_USER "UU"
+
+#define FW_SUCCESS 0
+#define FW_ERROR -1
+#define FW_TIMEOUT -2
+
+#define SN_LOCALE_PATH_VAR "_SN_LOCALE_PATH"
+#define SN_UNAME_VAR "_SN_UNAME"
+#define SN_UID_VAR "_SN_UID"
+
+#include "fw_lib.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_FW_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_lib.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_lib.h
new file mode 100644
index 0000000..36e7e40
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_lib.h
@@ -0,0 +1,96 @@
+/*
+ * 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 (c) 1993 by Sun Microsystems, Inc.
+ */
+
+#ifndef _FW_LIB_H
+#define _FW_LIB_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __STDC__
+
+extern short net_invoke(char _TKFAR *, char _TKFAR *, char _TKFAR *,
+ char _TKFAR *, u_long, u_long, char _TKFAR *,
+ invk_context, Op_arg _TKFAR * _TKFAR *, Op_err _TKFAR * _TKFAR *, ...);
+extern short net_more(u_long, u_long, invk_context, Op_arg _TKFAR * _TKFAR *,
+ Op_err _TKFAR * _TKFAR *);
+extern short net_end(u_long, invk_context, Op_err _TKFAR * _TKFAR *);
+extern Op_arg _TKFAR *net_arg_init(void);
+extern short net_arg_set(char _TKFAR *, ...);
+extern short net_arg_markrow(void);
+extern short net_arg_get(Op_arg _TKFAR *, char _TKFAR *,
+ char _TKFAR * _TKFAR *);
+extern short net_arg_getnext(Op_arg _TKFAR *, char _TKFAR * _TKFAR *,
+ char _TKFAR * _TKFAR *);
+extern short net_arg_nextrow(Op_arg _TKFAR *);
+extern short net_arg_rowstart(Op_arg _TKFAR *);
+extern short net_arg_reset(Op_arg _TKFAR *);
+extern void net_arg_free(Op_arg _TKFAR *);
+extern void net_err_free(Op_err _TKFAR *);
+extern Op_arg _TKFAR *new_Op_arg(void);
+extern void free_Op_arg(Op_arg _TKFAR *);
+extern void free_Op_err(Op_err _TKFAR *);
+extern short append_Op_arg(Op_arg _TKFAR *, char _TKFAR *, char _TKFAR *);
+extern void fw_err_set(Op_err _TKFAR * _TKFAR *, Fw_err, u_long, ...);
+
+#ifdef _WINDOWS
+extern void net_cleanup(void);
+#endif
+
+#else
+
+extern short net_invoke();
+extern short net_more();
+extern short net_end();
+extern Op_arg _TKFAR *net_arg_init();
+extern short net_arg_set();
+extern short net_arg_markrow();
+extern short net_arg_get();
+extern short net_arg_getnext();
+extern short net_arg_nextrow();
+extern short net_arg_rowstart();
+extern short net_arg_reset();
+extern void net_arg_free();
+extern void net_err_free();
+extern Op_arg _TKFAR *new_Op_arg();
+extern void free_Op_arg();
+extern void free_Op_err();
+extern short append_Op_arg();
+extern void fw_err_set();
+
+#ifdef _WINDOWS
+extern void net_cleanup();
+#endif
+
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_FW_LIB_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_rpc.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_rpc.h
new file mode 100644
index 0000000..b44f3cd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_rpc.h
@@ -0,0 +1,78 @@
+/*
+ * 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 (c) 1993 by Sun Microsystems, Inc.
+ */
+
+/*
+ * This file contains definitions which are only of interest to the actual
+ * service daemon and client stubs. Normal framework users will not include
+ * this file.
+ */
+
+#ifndef _FW_RPC_H
+#define _FW_RPC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FW_NO_CONTEXT "Plead no context"
+
+#define FW_PROG ((unsigned long)(150006))
+#define FW_VERSION ((unsigned long)(1))
+
+#define FW_INVOKE ((unsigned long)(1))
+#define FW_MORE ((unsigned long)(2))
+#define FW_KILL ((unsigned long)(3))
+
+
+/* the xdr functions */
+
+extern bool_t _TKFAR _TKPASCAL xdr_Op_arg(XDR _TKFAR *, Op_arg _TKFAR *);
+extern bool_t _TKFAR _TKPASCAL xdr_Fw_err(XDR _TKFAR *, Fw_err _TKFAR *);
+extern bool_t _TKFAR _TKPASCAL xdr_Op_err(XDR _TKFAR *, Op_err _TKFAR *);
+extern bool_t _TKFAR _TKPASCAL xdr_invk_context(XDR _TKFAR *, invk_context);
+extern bool_t _TKFAR _TKPASCAL xdr_invk_result(XDR _TKFAR *,
+ invk_result _TKFAR *);
+extern bool_t _TKFAR _TKPASCAL xdr_invk_request(XDR _TKFAR *,
+ invk_request _TKFAR *);
+extern bool_t _TKFAR _TKPASCAL xdr_more_request(XDR _TKFAR *,
+ more_request _TKFAR *);
+extern bool_t _TKFAR _TKPASCAL xdr_kill_request(XDR _TKFAR *,
+ kill_request _TKFAR *);
+
+#ifdef _WINDOWS
+extern void thunk_xdrs(void);
+extern void unthunk_xdrs(void);
+extern FARPROC lp_xdr_invk_request, lp_xdr_invk_result;
+extern FARPROC lp_xdr_more_request, lp_xdr_kill_request;
+extern FARPROC lp_xdr_Op_err, lp_xdr_Op_arg;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_FW_RPC_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/inc.flg b/usr/src/cmd/cmd-inet/usr.sbin/snoop/inc.flg
new file mode 100644
index 0000000..d7246ea
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/inc.flg
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# 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 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+find_files "s.*" usr/src/common/net/dhcp
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/nfs4_xdr.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/nfs4_xdr.c
new file mode 100644
index 0000000..0ee7a6c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/nfs4_xdr.c
@@ -0,0 +1,2972 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file was initially generated using rpcgen. The rpcgen-erated
+ * code used tail recursion to implement linked lists which resulted
+ * in various crashes due to blown stacks. If the NFS4 protocol changes
+ * be sure to either use the NFS4-friendly rpcgen (doesn't use tail
+ * recursion) or do the xdr by hand.
+ *
+ * CAUTION: This file is kept in sync with it's uts counterpart:
+ *
+ * usr/src/uts/common/fs/nfs/nfs4_xdr.c
+ *
+ * However, it is not an exact copy. NEVER copy uts's nfs4_xdr.c
+ * directly over this file. Changes from the uts version must be
+ * integrated by hand into this file.
+ */
+
+#include <rpcsvc/nfs4_prot.h>
+#include <nfs/nfs4.h>
+#include <malloc.h>
+
+#define IGNORE_RDWR_DATA
+
+extern int nfs4_skip_bytes;
+
+bool_t
+xdr_nfs_ftype4(register XDR *xdrs, nfs_ftype4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfsstat4(register XDR *xdrs, nfsstat4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_bitmap4(register XDR *xdrs, bitmap4 *objp)
+{
+
+ if (!xdr_array(xdrs, (char **)&objp->bitmap4_val,
+ (uint_t *)&objp->bitmap4_len, ~0,
+ sizeof (uint32_t), (xdrproc_t)xdr_uint32_t))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_offset4(register XDR *xdrs, offset4 *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_count4(register XDR *xdrs, count4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_length4(register XDR *xdrs, length4 *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_clientid4(register XDR *xdrs, clientid4 *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_seqid4(register XDR *xdrs, seqid4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_utf8string(register XDR *xdrs, utf8string *objp)
+{
+
+ if (!xdr_bytes(xdrs, (char **)&objp->utf8string_val,
+ (uint_t *)&objp->utf8string_len, NFS4_MAX_UTF8STRING))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_component4(register XDR *xdrs, component4 *objp)
+{
+
+ if (!xdr_utf8string(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_pathname4(register XDR *xdrs, pathname4 *objp)
+{
+
+ if (!xdr_array(xdrs, (char **)&objp->pathname4_val,
+ (uint_t *)&objp->pathname4_len, NFS4_MAX_PATHNAME4,
+ sizeof (component4), (xdrproc_t)xdr_component4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_lockid4(register XDR *xdrs, nfs_lockid4 *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_cookie4(register XDR *xdrs, nfs_cookie4 *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_linktext4(register XDR *xdrs, linktext4 *objp)
+{
+
+ if (!xdr_utf8string(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_sec_oid4(register XDR *xdrs, sec_oid4 *objp)
+{
+
+ if (!xdr_bytes(xdrs, (char **)&objp->sec_oid4_val,
+ (uint_t *)&objp->sec_oid4_len, NFS4_MAX_SECOID4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_qop4(register XDR *xdrs, qop4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_mode4(register XDR *xdrs, mode4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_changeid4(register XDR *xdrs, changeid4 *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_verifier4(register XDR *xdrs, verifier4 objp)
+{
+
+ if (!xdr_opaque(xdrs, objp, NFS4_VERIFIER_SIZE))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfstime4(register XDR *xdrs, nfstime4 *objp)
+{
+
+ if (!xdr_int64_t(xdrs, &objp->seconds))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->nseconds))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_time_how4(register XDR *xdrs, time_how4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_settime4(register XDR *xdrs, settime4 *objp)
+{
+
+ if (!xdr_time_how4(xdrs, &objp->set_it))
+ return (FALSE);
+ switch (objp->set_it) {
+ case SET_TO_CLIENT_TIME4:
+ if (!xdr_nfstime4(xdrs, &objp->settime4_u.time))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_fh4(register XDR *xdrs, nfs_fh4 *objp)
+{
+
+ if (!xdr_bytes(xdrs, (char **)&objp->nfs_fh4_val,
+ (uint_t *)&objp->nfs_fh4_len, NFS4_FHSIZE))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fsid4(register XDR *xdrs, fsid4 *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, &objp->major))
+ return (FALSE);
+ if (!xdr_uint64_t(xdrs, &objp->minor))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fs_location4(register XDR *xdrs, fs_location4 *objp)
+{
+
+ if (!xdr_array(xdrs, (char **)&objp->server.server_val,
+ (uint_t *)&objp->server.server_len, ~0,
+ sizeof (utf8string), (xdrproc_t)xdr_utf8string))
+ return (FALSE);
+ if (!xdr_pathname4(xdrs, &objp->rootpath))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fs_locations4(register XDR *xdrs, fs_locations4 *objp)
+{
+
+ if (!xdr_pathname4(xdrs, &objp->fs_root))
+ return (FALSE);
+ if (!xdr_array(xdrs, (char **)&objp->locations.locations_val,
+ (uint_t *)&objp->locations.locations_len, ~0,
+ sizeof (fs_location4), (xdrproc_t)xdr_fs_location4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_acetype4(register XDR *xdrs, acetype4 *objp)
+{
+
+ if (!xdr_u_int(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_aceflag4(register XDR *xdrs, aceflag4 *objp)
+{
+
+ if (!xdr_u_int(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_acemask4(register XDR *xdrs, acemask4 *objp)
+{
+
+ if (!xdr_u_int(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfsace4(register XDR *xdrs, nfsace4 *objp)
+{
+ if (!xdr_acetype4(xdrs, &objp->type))
+ return (FALSE);
+ if (!xdr_aceflag4(xdrs, &objp->flag))
+ return (FALSE);
+ if (!xdr_acemask4(xdrs, &objp->access_mask))
+ return (FALSE);
+ if (xdrs->x_op == XDR_DECODE) {
+ objp->who.utf8string_val = NULL;
+ objp->who.utf8string_len = 0;
+ }
+ return (xdr_bytes(xdrs, (char **)&objp->who.utf8string_val,
+ (uint_t *)&objp->who.utf8string_len,
+ NFS4_MAX_UTF8STRING));
+}
+
+bool_t
+xdr_specdata4(register XDR *xdrs, specdata4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->specdata1))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->specdata2))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_supported_attrs(register XDR *xdrs, fattr4_supported_attrs *objp)
+{
+
+ if (!xdr_bitmap4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_type(register XDR *xdrs, fattr4_type *objp)
+{
+
+ if (!xdr_nfs_ftype4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_fh_expire_type(register XDR *xdrs, fattr4_fh_expire_type *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_change(register XDR *xdrs, fattr4_change *objp)
+{
+
+ if (!xdr_changeid4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_size(register XDR *xdrs, fattr4_size *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_link_support(register XDR *xdrs, fattr4_link_support *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_symlink_support(register XDR *xdrs, fattr4_symlink_support *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_named_attr(register XDR *xdrs, fattr4_named_attr *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_fsid(register XDR *xdrs, fattr4_fsid *objp)
+{
+
+ if (!xdr_fsid4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_unique_handles(register XDR *xdrs, fattr4_unique_handles *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_lease_time(register XDR *xdrs, fattr4_lease_time *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_rdattr_error(register XDR *xdrs, fattr4_rdattr_error *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_acl(register XDR *xdrs, fattr4_acl *objp)
+{
+
+ if (!xdr_array(xdrs, (char **)&objp->fattr4_acl_val,
+ (uint_t *)&objp->fattr4_acl_len, ~0,
+ sizeof (nfsace4), (xdrproc_t)xdr_nfsace4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_aclsupport(register XDR *xdrs, fattr4_aclsupport *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_archive(register XDR *xdrs, fattr4_archive *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_cansettime(register XDR *xdrs, fattr4_cansettime *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_case_insensitive(register XDR *xdrs, fattr4_case_insensitive *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_case_preserving(register XDR *xdrs, fattr4_case_preserving *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_chown_restricted(register XDR *xdrs, fattr4_chown_restricted *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_fileid(register XDR *xdrs, fattr4_fileid *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_files_avail(register XDR *xdrs, fattr4_files_avail *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_filehandle(register XDR *xdrs, fattr4_filehandle *objp)
+{
+
+ if (!xdr_nfs_fh4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_files_free(register XDR *xdrs, fattr4_files_free *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_files_total(register XDR *xdrs, fattr4_files_total *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_fs_locations(register XDR *xdrs, fattr4_fs_locations *objp)
+{
+
+ if (!xdr_fs_locations4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_hidden(register XDR *xdrs, fattr4_hidden *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_homogeneous(register XDR *xdrs, fattr4_homogeneous *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_maxfilesize(register XDR *xdrs, fattr4_maxfilesize *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_maxlink(register XDR *xdrs, fattr4_maxlink *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_maxname(register XDR *xdrs, fattr4_maxname *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_maxread(register XDR *xdrs, fattr4_maxread *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_maxwrite(register XDR *xdrs, fattr4_maxwrite *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_mimetype(register XDR *xdrs, fattr4_mimetype *objp)
+{
+
+ if (!xdr_utf8string(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_mode(register XDR *xdrs, fattr4_mode *objp)
+{
+
+ if (!xdr_mode4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_mounted_on_fileid(register XDR *xdrs, fattr4_mounted_on_fileid *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_no_trunc(register XDR *xdrs, fattr4_no_trunc *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_numlinks(register XDR *xdrs, fattr4_numlinks *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_owner(register XDR *xdrs, fattr4_owner *objp)
+{
+
+ if (!xdr_utf8string(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_owner_group(register XDR *xdrs, fattr4_owner_group *objp)
+{
+
+ if (!xdr_utf8string(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_quota_avail_hard(register XDR *xdrs, fattr4_quota_avail_hard *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_quota_avail_soft(register XDR *xdrs, fattr4_quota_avail_soft *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_quota_used(register XDR *xdrs, fattr4_quota_used *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_rawdev(register XDR *xdrs, fattr4_rawdev *objp)
+{
+
+ if (!xdr_specdata4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_space_avail(register XDR *xdrs, fattr4_space_avail *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_space_free(register XDR *xdrs, fattr4_space_free *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_space_total(register XDR *xdrs, fattr4_space_total *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_space_used(register XDR *xdrs, fattr4_space_used *objp)
+{
+
+ if (!xdr_uint64_t(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_system(register XDR *xdrs, fattr4_system *objp)
+{
+
+ if (!xdr_bool(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_access(register XDR *xdrs, fattr4_time_access *objp)
+{
+
+ if (!xdr_nfstime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_access_set(register XDR *xdrs, fattr4_time_access_set *objp)
+{
+
+ if (!xdr_settime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_backup(register XDR *xdrs, fattr4_time_backup *objp)
+{
+
+ if (!xdr_nfstime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_create(register XDR *xdrs, fattr4_time_create *objp)
+{
+
+ if (!xdr_nfstime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_delta(register XDR *xdrs, fattr4_time_delta *objp)
+{
+
+ if (!xdr_nfstime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_metadata(register XDR *xdrs, fattr4_time_metadata *objp)
+{
+
+ if (!xdr_nfstime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_modify(register XDR *xdrs, fattr4_time_modify *objp)
+{
+
+ if (!xdr_nfstime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4_time_modify_set(register XDR *xdrs, fattr4_time_modify_set *objp)
+{
+
+ if (!xdr_settime4(xdrs, objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_attrlist4(register XDR *xdrs, attrlist4 *objp)
+{
+
+ if (!xdr_bytes(xdrs, (char **)&objp->attrlist4_val,
+ (uint_t *)&objp->attrlist4_len, ~0))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_fattr4(register XDR *xdrs, fattr4 *objp)
+{
+
+ if (!xdr_bitmap4(xdrs, &objp->attrmask))
+ return (FALSE);
+ if (!xdr_attrlist4(xdrs, &objp->attr_vals))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_change_info4(register XDR *xdrs, change_info4 *objp)
+{
+
+ if (!xdr_bool(xdrs, &objp->atomic))
+ return (FALSE);
+ if (!xdr_changeid4(xdrs, &objp->before))
+ return (FALSE);
+ if (!xdr_changeid4(xdrs, &objp->after))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_clientaddr4(register XDR *xdrs, clientaddr4 *objp)
+{
+
+ if (!xdr_string(xdrs, &objp->r_netid, ~0))
+ return (FALSE);
+ if (!xdr_string(xdrs, &objp->r_addr, ~0))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_cb_client4(register XDR *xdrs, cb_client4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->cb_program))
+ return (FALSE);
+ if (!xdr_clientaddr4(xdrs, &objp->cb_location))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_stateid4(register XDR *xdrs, stateid4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->seqid))
+ return (FALSE);
+ if (!xdr_opaque(xdrs, objp->other, 12))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_client_id4(register XDR *xdrs, nfs_client_id4 *objp)
+{
+
+ if (!xdr_verifier4(xdrs, objp->verifier))
+ return (FALSE);
+ if (!xdr_bytes(xdrs, (char **)&objp->id.id_val,
+ (uint_t *)&objp->id.id_len, NFS4_OPAQUE_LIMIT))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_open_owner4(register XDR *xdrs, open_owner4 *objp)
+{
+
+ if (!xdr_clientid4(xdrs, &objp->clientid))
+ return (FALSE);
+ if (!xdr_bytes(xdrs, (char **)&objp->owner.owner_val,
+ (uint_t *)&objp->owner.owner_len, NFS4_OPAQUE_LIMIT))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_lock_owner4(register XDR *xdrs, lock_owner4 *objp)
+{
+
+ if (!xdr_clientid4(xdrs, &objp->clientid))
+ return (FALSE);
+ if (!xdr_bytes(xdrs, (char **)&objp->owner.owner_val,
+ (uint_t *)&objp->owner.owner_len, NFS4_OPAQUE_LIMIT))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_lock_type4(register XDR *xdrs, nfs_lock_type4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_ACCESS4args(register XDR *xdrs, ACCESS4args *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->access))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_ACCESS4resok(register XDR *xdrs, ACCESS4resok *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->supported))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->access))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_ACCESS4res(register XDR *xdrs, ACCESS4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_ACCESS4resok(xdrs, &objp->ACCESS4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_CLOSE4args(register XDR *xdrs, CLOSE4args *objp)
+{
+
+ if (!xdr_seqid4(xdrs, &objp->seqid))
+ return (FALSE);
+ if (!xdr_stateid4(xdrs, &objp->open_stateid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CLOSE4res(register XDR *xdrs, CLOSE4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_stateid4(xdrs, &objp->CLOSE4res_u.open_stateid))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_COMMIT4args(register XDR *xdrs, COMMIT4args *objp)
+{
+
+ if (!xdr_offset4(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_count4(xdrs, &objp->count))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_COMMIT4resok(register XDR *xdrs, COMMIT4resok *objp)
+{
+
+ if (!xdr_verifier4(xdrs, objp->writeverf))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_COMMIT4res(register XDR *xdrs, COMMIT4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_COMMIT4resok(xdrs, &objp->COMMIT4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_createtype4(register XDR *xdrs, createtype4 *objp)
+{
+
+ if (!xdr_nfs_ftype4(xdrs, &objp->type))
+ return (FALSE);
+ switch (objp->type) {
+ case NF4LNK:
+ if (!xdr_linktext4(xdrs, &objp->createtype4_u.linkdata))
+ return (FALSE);
+ break;
+ case NF4BLK:
+ case NF4CHR:
+ if (!xdr_specdata4(xdrs, &objp->createtype4_u.devdata))
+ return (FALSE);
+ break;
+ case NF4SOCK:
+ case NF4FIFO:
+ case NF4DIR:
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_CREATE4args(register XDR *xdrs, CREATE4args *objp)
+{
+
+ if (!xdr_createtype4(xdrs, &objp->objtype))
+ return (FALSE);
+ if (!xdr_component4(xdrs, &objp->objname))
+ return (FALSE);
+ if (!xdr_fattr4(xdrs, &objp->createattrs))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CREATE4resok(register XDR *xdrs, CREATE4resok *objp)
+{
+
+ if (!xdr_change_info4(xdrs, &objp->cinfo))
+ return (FALSE);
+ if (!xdr_bitmap4(xdrs, &objp->attrset))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CREATE4res(register XDR *xdrs, CREATE4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_CREATE4resok(xdrs, &objp->CREATE4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_DELEGPURGE4args(register XDR *xdrs, DELEGPURGE4args *objp)
+{
+
+ if (!xdr_clientid4(xdrs, &objp->clientid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_DELEGPURGE4res(register XDR *xdrs, DELEGPURGE4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_DELEGRETURN4args(register XDR *xdrs, DELEGRETURN4args *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->deleg_stateid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_DELEGRETURN4res(register XDR *xdrs, DELEGRETURN4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_GETATTR4args(register XDR *xdrs, GETATTR4args *objp)
+{
+
+ if (!xdr_bitmap4(xdrs, &objp->attr_request))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_GETATTR4resok(register XDR *xdrs, GETATTR4resok *objp)
+{
+
+ if (!xdr_fattr4(xdrs, &objp->obj_attributes))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_GETATTR4res(register XDR *xdrs, GETATTR4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_GETATTR4resok(xdrs, &objp->GETATTR4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_GETFH4resok(register XDR *xdrs, GETFH4resok *objp)
+{
+
+ if (!xdr_nfs_fh4(xdrs, &objp->object))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_GETFH4res(register XDR *xdrs, GETFH4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_GETFH4resok(xdrs, &objp->GETFH4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_LINK4args(register XDR *xdrs, LINK4args *objp)
+{
+
+ if (!xdr_component4(xdrs, &objp->newname))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LINK4resok(register XDR *xdrs, LINK4resok *objp)
+{
+
+ if (!xdr_change_info4(xdrs, &objp->cinfo))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LINK4res(register XDR *xdrs, LINK4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_LINK4resok(xdrs, &objp->LINK4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_open_to_lock_owner4(register XDR *xdrs, open_to_lock_owner4 *objp)
+{
+
+ if (!xdr_seqid4(xdrs, &objp->open_seqid))
+ return (FALSE);
+ if (!xdr_stateid4(xdrs, &objp->open_stateid))
+ return (FALSE);
+ if (!xdr_seqid4(xdrs, &objp->lock_seqid))
+ return (FALSE);
+ if (!xdr_lock_owner4(xdrs, &objp->lock_owner))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_exist_lock_owner4(register XDR *xdrs, exist_lock_owner4 *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->lock_stateid))
+ return (FALSE);
+ if (!xdr_seqid4(xdrs, &objp->lock_seqid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_locker4(register XDR *xdrs, locker4 *objp)
+{
+
+ if (!xdr_bool(xdrs, &objp->new_lock_owner))
+ return (FALSE);
+ switch (objp->new_lock_owner) {
+ case TRUE:
+ if (!xdr_open_to_lock_owner4(xdrs, &objp->locker4_u.open_owner))
+ return (FALSE);
+ break;
+ case FALSE:
+ if (!xdr_exist_lock_owner4(xdrs, &objp->locker4_u.lock_owner))
+ return (FALSE);
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCK4args(register XDR *xdrs, LOCK4args *objp)
+{
+
+ if (!xdr_nfs_lock_type4(xdrs, &objp->locktype))
+ return (FALSE);
+ if (!xdr_bool(xdrs, &objp->reclaim))
+ return (FALSE);
+ if (!xdr_offset4(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_length4(xdrs, &objp->length))
+ return (FALSE);
+ if (!xdr_locker4(xdrs, &objp->locker))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCK4denied(register XDR *xdrs, LOCK4denied *objp)
+{
+
+ if (!xdr_offset4(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_length4(xdrs, &objp->length))
+ return (FALSE);
+ if (!xdr_nfs_lock_type4(xdrs, &objp->locktype))
+ return (FALSE);
+ if (!xdr_lock_owner4(xdrs, &objp->owner))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCK4resok(register XDR *xdrs, LOCK4resok *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->lock_stateid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCK4res(register XDR *xdrs, LOCK4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_LOCK4resok(xdrs, &objp->LOCK4res_u.resok4))
+ return (FALSE);
+ break;
+ case NFS4ERR_DENIED:
+ if (!xdr_LOCK4denied(xdrs, &objp->LOCK4res_u.denied))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCKT4args(register XDR *xdrs, LOCKT4args *objp)
+{
+
+ if (!xdr_nfs_lock_type4(xdrs, &objp->locktype))
+ return (FALSE);
+ if (!xdr_offset4(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_length4(xdrs, &objp->length))
+ return (FALSE);
+ if (!xdr_lock_owner4(xdrs, &objp->owner))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCKT4res(register XDR *xdrs, LOCKT4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4ERR_DENIED:
+ if (!xdr_LOCK4denied(xdrs, &objp->LOCKT4res_u.denied))
+ return (FALSE);
+ break;
+ case NFS4_OK:
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCKU4args(register XDR *xdrs, LOCKU4args *objp)
+{
+
+ if (!xdr_nfs_lock_type4(xdrs, &objp->locktype))
+ return (FALSE);
+ if (!xdr_seqid4(xdrs, &objp->seqid))
+ return (FALSE);
+ if (!xdr_stateid4(xdrs, &objp->lock_stateid))
+ return (FALSE);
+ if (!xdr_offset4(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_length4(xdrs, &objp->length))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LOCKU4res(register XDR *xdrs, LOCKU4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_stateid4(xdrs, &objp->LOCKU4res_u.lock_stateid))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_LOOKUP4args(register XDR *xdrs, LOOKUP4args *objp)
+{
+
+ if (!xdr_component4(xdrs, &objp->objname))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LOOKUP4res(register XDR *xdrs, LOOKUP4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_LOOKUPP4res(register XDR *xdrs, LOOKUPP4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_NVERIFY4args(register XDR *xdrs, NVERIFY4args *objp)
+{
+
+ if (!xdr_fattr4(xdrs, &objp->obj_attributes))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_NVERIFY4res(register XDR *xdrs, NVERIFY4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_createmode4(register XDR *xdrs, createmode4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_createhow4(register XDR *xdrs, createhow4 *objp)
+{
+
+ if (!xdr_createmode4(xdrs, &objp->mode))
+ return (FALSE);
+ switch (objp->mode) {
+ case UNCHECKED4:
+ case GUARDED4:
+ if (!xdr_fattr4(xdrs, &objp->createhow4_u.createattrs))
+ return (FALSE);
+ break;
+ case EXCLUSIVE4:
+ if (!xdr_verifier4(xdrs, objp->createhow4_u.createverf))
+ return (FALSE);
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_opentype4(register XDR *xdrs, opentype4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_openflag4(register XDR *xdrs, openflag4 *objp)
+{
+
+ if (!xdr_opentype4(xdrs, &objp->opentype))
+ return (FALSE);
+ switch (objp->opentype) {
+ case OPEN4_CREATE:
+ if (!xdr_createhow4(xdrs, &objp->openflag4_u.how))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_limit_by4(register XDR *xdrs, limit_by4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_modified_limit4(register XDR *xdrs, nfs_modified_limit4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->num_blocks))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->bytes_per_block))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_space_limit4(register XDR *xdrs, nfs_space_limit4 *objp)
+{
+
+ if (!xdr_limit_by4(xdrs, &objp->limitby))
+ return (FALSE);
+ switch (objp->limitby) {
+ case NFS_LIMIT_SIZE:
+ if (!xdr_uint64_t(xdrs, &objp->nfs_space_limit4_u.filesize))
+ return (FALSE);
+ break;
+ case NFS_LIMIT_BLOCKS:
+ if (!xdr_nfs_modified_limit4(xdrs, &objp->nfs_space_limit4_u.
+ mod_blocks))
+ return (FALSE);
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_open_delegation_type4(register XDR *xdrs, open_delegation_type4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_open_claim_type4(register XDR *xdrs, open_claim_type4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_open_claim_delegate_cur4(register XDR *xdrs, open_claim_delegate_cur4 *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->delegate_stateid))
+ return (FALSE);
+ if (!xdr_component4(xdrs, &objp->file))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_open_claim4(register XDR *xdrs, open_claim4 *objp)
+{
+
+ if (!xdr_open_claim_type4(xdrs, &objp->claim))
+ return (FALSE);
+ switch (objp->claim) {
+ case CLAIM_NULL:
+ if (!xdr_component4(xdrs, &objp->open_claim4_u.file))
+ return (FALSE);
+ break;
+ case CLAIM_PREVIOUS:
+ if (!xdr_open_delegation_type4(xdrs, &objp->open_claim4_u.
+ delegate_type))
+ return (FALSE);
+ break;
+ case CLAIM_DELEGATE_CUR:
+ if (!xdr_open_claim_delegate_cur4(xdrs, &objp->open_claim4_u.
+ delegate_cur_info))
+ return (FALSE);
+ break;
+ case CLAIM_DELEGATE_PREV:
+ if (!xdr_component4(xdrs, &objp->open_claim4_u.
+ file_delegate_prev))
+ return (FALSE);
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN4args(register XDR *xdrs, OPEN4args *objp)
+{
+
+ if (!xdr_seqid4(xdrs, &objp->seqid))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->share_access))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->share_deny))
+ return (FALSE);
+ if (!xdr_open_owner4(xdrs, &objp->owner))
+ return (FALSE);
+ if (!xdr_openflag4(xdrs, &objp->openhow))
+ return (FALSE);
+ if (!xdr_open_claim4(xdrs, &objp->claim))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_open_read_delegation4(register XDR *xdrs, open_read_delegation4 *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->stateid))
+ return (FALSE);
+ if (!xdr_bool(xdrs, &objp->recall))
+ return (FALSE);
+ if (!xdr_nfsace4(xdrs, &objp->permissions))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_open_write_delegation4(register XDR *xdrs, open_write_delegation4 *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->stateid))
+ return (FALSE);
+ if (!xdr_bool(xdrs, &objp->recall))
+ return (FALSE);
+ if (!xdr_nfs_space_limit4(xdrs, &objp->space_limit))
+ return (FALSE);
+ if (!xdr_nfsace4(xdrs, &objp->permissions))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_open_delegation4(register XDR *xdrs, open_delegation4 *objp)
+{
+
+ if (!xdr_open_delegation_type4(xdrs, &objp->delegation_type))
+ return (FALSE);
+ switch (objp->delegation_type) {
+ case OPEN_DELEGATE_NONE:
+ break;
+ case OPEN_DELEGATE_READ:
+ if (!xdr_open_read_delegation4(xdrs, &objp->open_delegation4_u.
+ read))
+ return (FALSE);
+ break;
+ case OPEN_DELEGATE_WRITE:
+ if (!xdr_open_write_delegation4(xdrs, &objp->open_delegation4_u.
+ write))
+ return (FALSE);
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN4resok(register XDR *xdrs, OPEN4resok *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->stateid))
+ return (FALSE);
+ if (!xdr_change_info4(xdrs, &objp->cinfo))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->rflags))
+ return (FALSE);
+ if (!xdr_bitmap4(xdrs, &objp->attrset))
+ return (FALSE);
+ if (!xdr_open_delegation4(xdrs, &objp->delegation))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN4res(register XDR *xdrs, OPEN4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_OPEN4resok(xdrs, &objp->OPEN4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_OPENATTR4args(register XDR *xdrs, OPENATTR4args *objp)
+{
+
+ if (!xdr_bool(xdrs, &objp->createdir))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_OPENATTR4res(register XDR *xdrs, OPENATTR4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN_CONFIRM4args(register XDR *xdrs, OPEN_CONFIRM4args *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->open_stateid))
+ return (FALSE);
+ if (!xdr_seqid4(xdrs, &objp->seqid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN_CONFIRM4resok(register XDR *xdrs, OPEN_CONFIRM4resok *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->open_stateid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN_CONFIRM4res(register XDR *xdrs, OPEN_CONFIRM4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_OPEN_CONFIRM4resok(xdrs, &objp->OPEN_CONFIRM4res_u.
+ resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN_DOWNGRADE4args(register XDR *xdrs, OPEN_DOWNGRADE4args *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->open_stateid))
+ return (FALSE);
+ if (!xdr_seqid4(xdrs, &objp->seqid))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->share_access))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->share_deny))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN_DOWNGRADE4resok(register XDR *xdrs, OPEN_DOWNGRADE4resok *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->open_stateid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_OPEN_DOWNGRADE4res(register XDR *xdrs, OPEN_DOWNGRADE4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_OPEN_DOWNGRADE4resok(xdrs, &objp->OPEN_DOWNGRADE4res_u.
+ resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_PUTFH4args(register XDR *xdrs, PUTFH4args *objp)
+{
+
+ if (!xdr_nfs_fh4(xdrs, &objp->object))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_PUTFH4res(register XDR *xdrs, PUTFH4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_PUTPUBFH4res(register XDR *xdrs, PUTPUBFH4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_PUTROOTFH4res(register XDR *xdrs, PUTROOTFH4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_READ4args(register XDR *xdrs, READ4args *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->stateid))
+ return (FALSE);
+ if (!xdr_offset4(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_count4(xdrs, &objp->count))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_READ4resok(register XDR *xdrs, READ4resok *objp)
+{
+
+ if (!xdr_bool(xdrs, &objp->eof))
+ return (FALSE);
+
+#ifdef IGNORE_RDWR_DATA
+ /*
+ * Try to get length of read, and if that
+ * fails, default to 0. Don't return FALSE
+ * because the other read info will not be
+ * displayed.
+ */
+ objp->data.data_val = NULL;
+ if (!xdr_u_int(xdrs, &objp->data.data_len))
+ objp->data.data_len = 0;
+ nfs4_skip_bytes = objp->data.data_len;
+#else
+ if (!xdr_bytes(xdrs, (char **)&objp->data.data_val,
+ (uint_t *)&objp->data.data_len, ~0))
+ return (FALSE);
+#endif
+ return (TRUE);
+}
+
+bool_t
+xdr_READ4res(register XDR *xdrs, READ4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_READ4resok(xdrs, &objp->READ4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_READDIR4args(register XDR *xdrs, READDIR4args *objp)
+{
+
+ if (!xdr_nfs_cookie4(xdrs, &objp->cookie))
+ return (FALSE);
+ if (!xdr_verifier4(xdrs, objp->cookieverf))
+ return (FALSE);
+ if (!xdr_count4(xdrs, &objp->dircount))
+ return (FALSE);
+ if (!xdr_count4(xdrs, &objp->maxcount))
+ return (FALSE);
+ if (!xdr_bitmap4(xdrs, &objp->attr_request))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_entry4(register XDR *xdrs, entry4 *objp)
+{
+
+ entry4 *tmp_entry4;
+ bool_t more_data = TRUE;
+ bool_t first_objp = TRUE;
+
+ while (more_data) {
+
+ if (!xdr_nfs_cookie4(xdrs, &objp->cookie))
+ return (FALSE);
+ if (!xdr_component4(xdrs, &objp->name))
+ return (FALSE);
+ if (!xdr_fattr4(xdrs, &objp->attrs))
+ return (FALSE);
+
+ if (xdrs->x_op == XDR_DECODE) {
+
+ void bzero();
+
+ if (!xdr_bool(xdrs, &more_data))
+ return (FALSE);
+
+ if (!more_data) {
+ objp->nextentry = NULL;
+ break;
+ }
+
+ objp->nextentry = (entry4 *)
+ mem_alloc(sizeof (entry4));
+ if (objp->nextentry == NULL)
+ return (NULL);
+ bzero(objp->nextentry, sizeof (entry4));
+ objp = objp->nextentry;
+
+ } else if (xdrs->x_op == XDR_ENCODE) {
+ objp = objp->nextentry;
+ if (!objp)
+ more_data = FALSE;
+
+ if (!xdr_bool(xdrs, &more_data))
+ return (FALSE);
+ } else {
+ tmp_entry4 = objp;
+ objp = objp->nextentry;
+ if (!objp)
+ more_data = FALSE;
+ if (!xdr_bool(xdrs, &more_data))
+ return (FALSE);
+ if (!first_objp)
+ mem_free(tmp_entry4, sizeof (entry4));
+ else
+ first_objp = FALSE;
+ }
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_dirlist4(register XDR *xdrs, dirlist4 *objp)
+{
+
+ if (!xdr_pointer(xdrs, (char **)&objp->entries, sizeof (entry4),
+ (xdrproc_t)xdr_entry4))
+ return (FALSE);
+ if (!xdr_bool(xdrs, &objp->eof))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_READDIR4resok(register XDR *xdrs, READDIR4resok *objp)
+{
+
+ if (!xdr_verifier4(xdrs, objp->cookieverf))
+ return (FALSE);
+ if (!xdr_dirlist4(xdrs, &objp->reply))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_READDIR4res(register XDR *xdrs, READDIR4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_READDIR4resok(xdrs, &objp->READDIR4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_READLINK4resok(register XDR *xdrs, READLINK4resok *objp)
+{
+
+ if (!xdr_linktext4(xdrs, &objp->link))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_READLINK4res(register XDR *xdrs, READLINK4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_READLINK4resok(xdrs, &objp->READLINK4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_REMOVE4args(register XDR *xdrs, REMOVE4args *objp)
+{
+
+ if (!xdr_component4(xdrs, &objp->target))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_REMOVE4resok(register XDR *xdrs, REMOVE4resok *objp)
+{
+
+ if (!xdr_change_info4(xdrs, &objp->cinfo))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_REMOVE4res(register XDR *xdrs, REMOVE4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_REMOVE4resok(xdrs, &objp->REMOVE4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_RENAME4args(register XDR *xdrs, RENAME4args *objp)
+{
+
+ if (!xdr_component4(xdrs, &objp->oldname))
+ return (FALSE);
+ if (!xdr_component4(xdrs, &objp->newname))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_RENAME4resok(register XDR *xdrs, RENAME4resok *objp)
+{
+
+ if (!xdr_change_info4(xdrs, &objp->source_cinfo))
+ return (FALSE);
+ if (!xdr_change_info4(xdrs, &objp->target_cinfo))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_RENAME4res(register XDR *xdrs, RENAME4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_RENAME4resok(xdrs, &objp->RENAME4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_RENEW4args(register XDR *xdrs, RENEW4args *objp)
+{
+
+ if (!xdr_clientid4(xdrs, &objp->clientid))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_RENEW4res(register XDR *xdrs, RENEW4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_RESTOREFH4res(register XDR *xdrs, RESTOREFH4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SAVEFH4res(register XDR *xdrs, SAVEFH4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SECINFO4args(register XDR *xdrs, SECINFO4args *objp)
+{
+
+ if (!xdr_component4(xdrs, &objp->name))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_rpc_gss_svc_t(register XDR *xdrs, rpc_gss_svc_t *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_rpcsec_gss_info(register XDR *xdrs, rpcsec_gss_info *objp)
+{
+
+ if (!xdr_sec_oid4(xdrs, &objp->oid))
+ return (FALSE);
+ if (!xdr_qop4(xdrs, &objp->qop))
+ return (FALSE);
+ if (!xdr_rpc_gss_svc_t(xdrs, &objp->service))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_secinfo4(register XDR *xdrs, secinfo4 *objp)
+{
+
+ if (!xdr_uint32_t(xdrs, &objp->flavor))
+ return (FALSE);
+ switch (objp->flavor) {
+ case RPCSEC_GSS:
+ if (!xdr_rpcsec_gss_info(xdrs, &objp->secinfo4_u.flavor_info))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_SECINFO4resok(register XDR *xdrs, SECINFO4resok *objp)
+{
+
+ if (!xdr_array(xdrs, (char **)&objp->SECINFO4resok_val,
+ (uint_t *)&objp->SECINFO4resok_len, ~0,
+ sizeof (secinfo4), (xdrproc_t)xdr_secinfo4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SECINFO4res(register XDR *xdrs, SECINFO4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_SECINFO4resok(xdrs, &objp->SECINFO4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_SETATTR4args(register XDR *xdrs, SETATTR4args *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->stateid))
+ return (FALSE);
+ if (!xdr_fattr4(xdrs, &objp->obj_attributes))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SETATTR4res(register XDR *xdrs, SETATTR4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ if (!xdr_bitmap4(xdrs, &objp->attrsset))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SETCLIENTID4args(register XDR *xdrs, SETCLIENTID4args *objp)
+{
+
+ if (!xdr_nfs_client_id4(xdrs, &objp->client))
+ return (FALSE);
+ if (!xdr_cb_client4(xdrs, &objp->callback))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->callback_ident))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SETCLIENTID4resok(register XDR *xdrs, SETCLIENTID4resok *objp)
+{
+
+ if (!xdr_clientid4(xdrs, &objp->clientid))
+ return (FALSE);
+ if (!xdr_verifier4(xdrs, objp->setclientid_confirm))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SETCLIENTID4res(register XDR *xdrs, SETCLIENTID4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_SETCLIENTID4resok(xdrs, &objp->SETCLIENTID4res_u.
+ resok4))
+ return (FALSE);
+ break;
+ case NFS4ERR_CLID_INUSE:
+ if (!xdr_clientaddr4(xdrs, &objp->SETCLIENTID4res_u.
+ client_using))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_SETCLIENTID_CONFIRM4args(register XDR *xdrs, SETCLIENTID_CONFIRM4args *objp)
+{
+
+ if (!xdr_clientid4(xdrs, &objp->clientid))
+ return (FALSE);
+ if (!xdr_verifier4(xdrs, objp->setclientid_confirm))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_SETCLIENTID_CONFIRM4res(register XDR *xdrs, SETCLIENTID_CONFIRM4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_VERIFY4args(register XDR *xdrs, VERIFY4args *objp)
+{
+
+ if (!xdr_fattr4(xdrs, &objp->obj_attributes))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_VERIFY4res(register XDR *xdrs, VERIFY4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_stable_how4(register XDR *xdrs, stable_how4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_WRITE4args(register XDR *xdrs, WRITE4args *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->stateid))
+ return (FALSE);
+ if (!xdr_offset4(xdrs, &objp->offset))
+ return (FALSE);
+ if (!xdr_stable_how4(xdrs, &objp->stable))
+ return (FALSE);
+
+#ifdef IGNORE_RDWR_DATA
+ /*
+ * try to get length of write, and if that
+ * fails, default to 0. Don't return FALSE
+ * because the other write info will not be
+ * displayed (write stateid).
+ */
+ objp->data.data_val = NULL;
+ if (!xdr_u_int(xdrs, &objp->data.data_len))
+ objp->data.data_len = 0;
+ nfs4_skip_bytes = objp->data.data_len;
+#else
+ if (!xdr_bytes(xdrs, (char **)&objp->data.data_val,
+ (uint_t *)&objp->data.data_len, ~0))
+ return (FALSE);
+#endif
+ return (TRUE);
+}
+
+bool_t
+xdr_WRITE4resok(register XDR *xdrs, WRITE4resok *objp)
+{
+
+ if (!xdr_count4(xdrs, &objp->count))
+ return (FALSE);
+ if (!xdr_stable_how4(xdrs, &objp->committed))
+ return (FALSE);
+ if (!xdr_verifier4(xdrs, objp->writeverf))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_WRITE4res(register XDR *xdrs, WRITE4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_WRITE4resok(xdrs, &objp->WRITE4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_RELEASE_LOCKOWNER4args(register XDR *xdrs, RELEASE_LOCKOWNER4args *objp)
+{
+
+ if (!xdr_lock_owner4(xdrs, &objp->lock_owner))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_RELEASE_LOCKOWNER4res(register XDR *xdrs, RELEASE_LOCKOWNER4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_ILLEGAL4res(register XDR *xdrs, ILLEGAL4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_opnum4(register XDR *xdrs, nfs_opnum4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_argop4(register XDR *xdrs, nfs_argop4 *objp)
+{
+ nfs4_skip_bytes = 0;
+ if (!xdr_nfs_opnum4(xdrs, &objp->argop))
+ return (FALSE);
+ switch (objp->argop) {
+ case OP_ACCESS:
+ if (!xdr_ACCESS4args(xdrs, &objp->nfs_argop4_u.opaccess))
+ return (FALSE);
+ break;
+ case OP_CLOSE:
+ if (!xdr_CLOSE4args(xdrs, &objp->nfs_argop4_u.opclose))
+ return (FALSE);
+ break;
+ case OP_COMMIT:
+ if (!xdr_COMMIT4args(xdrs, &objp->nfs_argop4_u.opcommit))
+ return (FALSE);
+ break;
+ case OP_CREATE:
+ if (!xdr_CREATE4args(xdrs, &objp->nfs_argop4_u.opcreate))
+ return (FALSE);
+ break;
+ case OP_DELEGPURGE:
+ if (!xdr_DELEGPURGE4args(xdrs, &objp->nfs_argop4_u.
+ opdelegpurge))
+ return (FALSE);
+ break;
+ case OP_DELEGRETURN:
+ if (!xdr_DELEGRETURN4args(xdrs, &objp->nfs_argop4_u.
+ opdelegreturn))
+ return (FALSE);
+ break;
+ case OP_GETATTR:
+ if (!xdr_GETATTR4args(xdrs, &objp->nfs_argop4_u.
+ opgetattr))
+ return (FALSE);
+ break;
+ case OP_GETFH:
+ break;
+ case OP_LINK:
+ if (!xdr_LINK4args(xdrs, &objp->nfs_argop4_u.oplink))
+ return (FALSE);
+ break;
+ case OP_LOCK:
+ if (!xdr_LOCK4args(xdrs, &objp->nfs_argop4_u.oplock))
+ return (FALSE);
+ break;
+ case OP_LOCKT:
+ if (!xdr_LOCKT4args(xdrs, &objp->nfs_argop4_u.oplockt))
+ return (FALSE);
+ break;
+ case OP_LOCKU:
+ if (!xdr_LOCKU4args(xdrs, &objp->nfs_argop4_u.oplocku))
+ return (FALSE);
+ break;
+ case OP_LOOKUP:
+ if (!xdr_LOOKUP4args(xdrs, &objp->nfs_argop4_u.oplookup))
+ return (FALSE);
+ break;
+ case OP_LOOKUPP:
+ break;
+ case OP_NVERIFY:
+ if (!xdr_NVERIFY4args(xdrs, &objp->nfs_argop4_u.opnverify))
+ return (FALSE);
+ break;
+ case OP_OPEN:
+ if (!xdr_OPEN4args(xdrs, &objp->nfs_argop4_u.opopen))
+ return (FALSE);
+ break;
+ case OP_OPENATTR:
+ if (!xdr_OPENATTR4args(xdrs, &objp->nfs_argop4_u.opopenattr))
+ return (FALSE);
+ break;
+ case OP_OPEN_CONFIRM:
+ if (!xdr_OPEN_CONFIRM4args(xdrs, &objp->nfs_argop4_u.
+ opopen_confirm))
+ return (FALSE);
+ break;
+ case OP_OPEN_DOWNGRADE:
+ if (!xdr_OPEN_DOWNGRADE4args(xdrs, &objp->nfs_argop4_u.
+ opopen_downgrade))
+ return (FALSE);
+ break;
+ case OP_PUTFH:
+ if (!xdr_PUTFH4args(xdrs, &objp->nfs_argop4_u.opputfh))
+ return (FALSE);
+ break;
+ case OP_PUTPUBFH:
+ break;
+ case OP_PUTROOTFH:
+ break;
+ case OP_READ:
+ if (!xdr_READ4args(xdrs, &objp->nfs_argop4_u.opread))
+ return (FALSE);
+ break;
+ case OP_READDIR:
+ if (!xdr_READDIR4args(xdrs, &objp->nfs_argop4_u.opreaddir))
+ return (FALSE);
+ break;
+ case OP_READLINK:
+ break;
+ case OP_REMOVE:
+ if (!xdr_REMOVE4args(xdrs, &objp->nfs_argop4_u.opremove))
+ return (FALSE);
+ break;
+ case OP_RENAME:
+ if (!xdr_RENAME4args(xdrs, &objp->nfs_argop4_u.oprename))
+ return (FALSE);
+ break;
+ case OP_RENEW:
+ if (!xdr_RENEW4args(xdrs, &objp->nfs_argop4_u.oprenew))
+ return (FALSE);
+ break;
+ case OP_RESTOREFH:
+ break;
+ case OP_SAVEFH:
+ break;
+ case OP_SECINFO:
+ if (!xdr_SECINFO4args(xdrs, &objp->nfs_argop4_u.opsecinfo))
+ return (FALSE);
+ break;
+ case OP_SETATTR:
+ if (!xdr_SETATTR4args(xdrs, &objp->nfs_argop4_u.opsetattr))
+ return (FALSE);
+ break;
+ case OP_SETCLIENTID:
+ if (!xdr_SETCLIENTID4args(xdrs, &objp->nfs_argop4_u.
+ opsetclientid))
+ return (FALSE);
+ break;
+ case OP_SETCLIENTID_CONFIRM:
+ if (!xdr_SETCLIENTID_CONFIRM4args(xdrs, &objp->nfs_argop4_u.
+ opsetclientid_confirm))
+ return (FALSE);
+ break;
+ case OP_VERIFY:
+ if (!xdr_VERIFY4args(xdrs, &objp->nfs_argop4_u.opverify))
+ return (FALSE);
+ break;
+ case OP_WRITE:
+ if (!xdr_WRITE4args(xdrs, &objp->nfs_argop4_u.opwrite))
+ return (FALSE);
+ break;
+ case OP_RELEASE_LOCKOWNER:
+ if (!xdr_RELEASE_LOCKOWNER4args(xdrs,
+ &objp->nfs_argop4_u.oprelease_lockowner))
+ return (FALSE);
+ break;
+ case OP_ILLEGAL:
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_resop4(register XDR *xdrs, nfs_resop4 *objp)
+{
+ nfs4_skip_bytes = 0;
+ if (!xdr_nfs_opnum4(xdrs, &objp->resop))
+ return (FALSE);
+ switch (objp->resop) {
+ case OP_ACCESS:
+ if (!xdr_ACCESS4res(xdrs, &objp->nfs_resop4_u.opaccess))
+ return (FALSE);
+ break;
+ case OP_CLOSE:
+ if (!xdr_CLOSE4res(xdrs, &objp->nfs_resop4_u.opclose))
+ return (FALSE);
+ break;
+ case OP_COMMIT:
+ if (!xdr_COMMIT4res(xdrs, &objp->nfs_resop4_u.opcommit))
+ return (FALSE);
+ break;
+ case OP_CREATE:
+ if (!xdr_CREATE4res(xdrs, &objp->nfs_resop4_u.opcreate))
+ return (FALSE);
+ break;
+ case OP_DELEGPURGE:
+ if (!xdr_DELEGPURGE4res(xdrs, &objp->nfs_resop4_u.opdelegpurge))
+ return (FALSE);
+ break;
+ case OP_DELEGRETURN:
+ if (!xdr_DELEGRETURN4res(xdrs, &objp->nfs_resop4_u.
+ opdelegreturn))
+ return (FALSE);
+ break;
+ case OP_GETATTR:
+ if (!xdr_GETATTR4res(xdrs, &objp->nfs_resop4_u.opgetattr))
+ return (FALSE);
+ break;
+ case OP_GETFH:
+ if (!xdr_GETFH4res(xdrs, &objp->nfs_resop4_u.opgetfh))
+ return (FALSE);
+ break;
+ case OP_LINK:
+ if (!xdr_LINK4res(xdrs, &objp->nfs_resop4_u.oplink))
+ return (FALSE);
+ break;
+ case OP_LOCK:
+ if (!xdr_LOCK4res(xdrs, &objp->nfs_resop4_u.oplock))
+ return (FALSE);
+ break;
+ case OP_LOCKT:
+ if (!xdr_LOCKT4res(xdrs, &objp->nfs_resop4_u.oplockt))
+ return (FALSE);
+ break;
+ case OP_LOCKU:
+ if (!xdr_LOCKU4res(xdrs, &objp->nfs_resop4_u.oplocku))
+ return (FALSE);
+ break;
+ case OP_LOOKUP:
+ if (!xdr_LOOKUP4res(xdrs, &objp->nfs_resop4_u.oplookup))
+ return (FALSE);
+ break;
+ case OP_LOOKUPP:
+ if (!xdr_LOOKUPP4res(xdrs, &objp->nfs_resop4_u.oplookupp))
+ return (FALSE);
+ break;
+ case OP_NVERIFY:
+ if (!xdr_NVERIFY4res(xdrs, &objp->nfs_resop4_u.opnverify))
+ return (FALSE);
+ break;
+ case OP_OPEN:
+ if (!xdr_OPEN4res(xdrs, &objp->nfs_resop4_u.opopen))
+ return (FALSE);
+ break;
+ case OP_OPENATTR:
+ if (!xdr_OPENATTR4res(xdrs, &objp->nfs_resop4_u.opopenattr))
+ return (FALSE);
+ break;
+ case OP_OPEN_CONFIRM:
+ if (!xdr_OPEN_CONFIRM4res(xdrs, &objp->nfs_resop4_u.
+ opopen_confirm))
+ return (FALSE);
+ break;
+ case OP_OPEN_DOWNGRADE:
+ if (!xdr_OPEN_DOWNGRADE4res(xdrs, &objp->nfs_resop4_u.
+ opopen_downgrade))
+ return (FALSE);
+ break;
+ case OP_PUTFH:
+ if (!xdr_PUTFH4res(xdrs, &objp->nfs_resop4_u.opputfh))
+ return (FALSE);
+ break;
+ case OP_PUTPUBFH:
+ if (!xdr_PUTPUBFH4res(xdrs, &objp->nfs_resop4_u.opputpubfh))
+ return (FALSE);
+ break;
+ case OP_PUTROOTFH:
+ if (!xdr_PUTROOTFH4res(xdrs, &objp->nfs_resop4_u.opputrootfh))
+ return (FALSE);
+ break;
+ case OP_READ:
+ if (!xdr_READ4res(xdrs, &objp->nfs_resop4_u.opread))
+ return (FALSE);
+ break;
+ case OP_READDIR:
+ if (!xdr_READDIR4res(xdrs, &objp->nfs_resop4_u.opreaddir))
+ return (FALSE);
+ break;
+ case OP_READLINK:
+ if (!xdr_READLINK4res(xdrs, &objp->nfs_resop4_u.opreadlink))
+ return (FALSE);
+ break;
+ case OP_REMOVE:
+ if (!xdr_REMOVE4res(xdrs, &objp->nfs_resop4_u.opremove))
+ return (FALSE);
+ break;
+ case OP_RENAME:
+ if (!xdr_RENAME4res(xdrs, &objp->nfs_resop4_u.oprename))
+ return (FALSE);
+ break;
+ case OP_RENEW:
+ if (!xdr_RENEW4res(xdrs, &objp->nfs_resop4_u.oprenew))
+ return (FALSE);
+ break;
+ case OP_RESTOREFH:
+ if (!xdr_RESTOREFH4res(xdrs, &objp->nfs_resop4_u.oprestorefh))
+ return (FALSE);
+ break;
+ case OP_SAVEFH:
+ if (!xdr_SAVEFH4res(xdrs, &objp->nfs_resop4_u.opsavefh))
+ return (FALSE);
+ break;
+ case OP_SECINFO:
+ if (!xdr_SECINFO4res(xdrs, &objp->nfs_resop4_u.opsecinfo))
+ return (FALSE);
+ break;
+ case OP_SETATTR:
+ if (!xdr_SETATTR4res(xdrs, &objp->nfs_resop4_u.opsetattr))
+ return (FALSE);
+ break;
+ case OP_SETCLIENTID:
+ if (!xdr_SETCLIENTID4res(xdrs, &objp->nfs_resop4_u.
+ opsetclientid))
+ return (FALSE);
+ break;
+ case OP_SETCLIENTID_CONFIRM:
+ if (!xdr_SETCLIENTID_CONFIRM4res(xdrs, &objp->nfs_resop4_u.
+ opsetclientid_confirm))
+ return (FALSE);
+ break;
+ case OP_VERIFY:
+ if (!xdr_VERIFY4res(xdrs, &objp->nfs_resop4_u.opverify))
+ return (FALSE);
+ break;
+ case OP_WRITE:
+ if (!xdr_WRITE4res(xdrs, &objp->nfs_resop4_u.opwrite))
+ return (FALSE);
+ break;
+ case OP_RELEASE_LOCKOWNER:
+ if (!xdr_RELEASE_LOCKOWNER4res(xdrs,
+ &objp->nfs_resop4_u.oprelease_lockowner))
+ return (FALSE);
+ break;
+ case OP_ILLEGAL:
+ if (!xdr_ILLEGAL4res(xdrs, &objp->nfs_resop4_u.opillegal))
+ return (FALSE);
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_COMPOUND4args(register XDR *xdrs, COMPOUND4args *objp)
+{
+
+ if (!xdr_utf8string(xdrs, &objp->tag))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->minorversion))
+ return (FALSE);
+ if (!xdr_array(xdrs, (char **)&objp->argarray.argarray_val,
+ (uint_t *)&objp->argarray.argarray_len, ~0,
+ sizeof (nfs_argop4), (xdrproc_t)xdr_nfs_argop4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_COMPOUND4res(register XDR *xdrs, COMPOUND4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ if (!xdr_utf8string(xdrs, &objp->tag))
+ return (FALSE);
+ if (!xdr_array(xdrs, (char **)&objp->resarray.resarray_val,
+ (uint_t *)&objp->resarray.resarray_len, ~0,
+ sizeof (nfs_resop4), (xdrproc_t)xdr_nfs_resop4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_GETATTR4args(register XDR *xdrs, CB_GETATTR4args *objp)
+{
+
+ if (!xdr_nfs_fh4(xdrs, &objp->fh))
+ return (FALSE);
+ if (!xdr_bitmap4(xdrs, &objp->attr_request))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_GETATTR4resok(register XDR *xdrs, CB_GETATTR4resok *objp)
+{
+
+ if (!xdr_fattr4(xdrs, &objp->obj_attributes))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_GETATTR4res(register XDR *xdrs, CB_GETATTR4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ switch (objp->status) {
+ case NFS4_OK:
+ if (!xdr_CB_GETATTR4resok(xdrs, &objp->CB_GETATTR4res_u.resok4))
+ return (FALSE);
+ break;
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_RECALL4args(register XDR *xdrs, CB_RECALL4args *objp)
+{
+
+ if (!xdr_stateid4(xdrs, &objp->stateid))
+ return (FALSE);
+ if (!xdr_bool(xdrs, &objp->truncate))
+ return (FALSE);
+ if (!xdr_nfs_fh4(xdrs, &objp->fh))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_RECALL4res(register XDR *xdrs, CB_RECALL4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_ILLEGAL4res(register XDR *xdrs, CB_ILLEGAL4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_cb_opnum4(register XDR *xdrs, nfs_cb_opnum4 *objp)
+{
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_cb_argop4(register XDR *xdrs, nfs_cb_argop4 *objp)
+{
+
+ if (!xdr_u_int(xdrs, &objp->argop))
+ return (FALSE);
+ switch (objp->argop) {
+ case OP_CB_GETATTR:
+ if (!xdr_CB_GETATTR4args(xdrs, &objp->nfs_cb_argop4_u.
+ opcbgetattr))
+ return (FALSE);
+ break;
+ case OP_CB_RECALL:
+ if (!xdr_CB_RECALL4args(xdrs, &objp->nfs_cb_argop4_u.
+ opcbrecall))
+ return (FALSE);
+ break;
+ case OP_CB_ILLEGAL:
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_nfs_cb_resop4(register XDR *xdrs, nfs_cb_resop4 *objp)
+{
+
+ if (!xdr_u_int(xdrs, &objp->resop))
+ return (FALSE);
+ switch (objp->resop) {
+ case OP_CB_GETATTR:
+ if (!xdr_CB_GETATTR4res(xdrs, &objp->nfs_cb_resop4_u.
+ opcbgetattr))
+ return (FALSE);
+ break;
+ case OP_CB_RECALL:
+ if (!xdr_CB_RECALL4res(xdrs, &objp->nfs_cb_resop4_u.opcbrecall))
+ return (FALSE);
+ break;
+ case OP_CB_ILLEGAL:
+ if (!xdr_CB_ILLEGAL4res(xdrs,
+ &objp->nfs_cb_resop4_u.opcbillegal))
+ return (FALSE);
+ break;
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_COMPOUND4args(register XDR *xdrs, CB_COMPOUND4args *objp)
+{
+
+ if (!xdr_utf8string(xdrs, &objp->tag))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->minorversion))
+ return (FALSE);
+ if (!xdr_uint32_t(xdrs, &objp->callback_ident))
+ return (FALSE);
+ if (!xdr_array(xdrs, (char **)&objp->argarray.argarray_val,
+ (uint_t *)&objp->argarray.argarray_len, ~0,
+ sizeof (nfs_cb_argop4), (xdrproc_t)xdr_nfs_cb_argop4))
+ return (FALSE);
+ return (TRUE);
+}
+
+bool_t
+xdr_CB_COMPOUND4res(register XDR *xdrs, CB_COMPOUND4res *objp)
+{
+
+ if (!xdr_nfsstat4(xdrs, &objp->status))
+ return (FALSE);
+ if (!xdr_utf8string(xdrs, &objp->tag))
+ return (FALSE);
+ if (!xdr_array(xdrs, (char **)&objp->resarray.resarray_val,
+ (uint_t *)&objp->resarray.resarray_len, ~0,
+ sizeof (nfs_cb_resop4), (xdrproc_t)xdr_nfs_cb_resop4))
+ return (FALSE);
+ return (TRUE);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/ntp.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/ntp.h
new file mode 100644
index 0000000..71809d4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/ntp.h
@@ -0,0 +1,533 @@
+/*
+ * 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.
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#ifndef _NTP_H
+#define _NTP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* $Source: /usr/users/louie/ntp/RCS/ntp.h,v $ */
+/* $Revision: 3.4.1.5 $ $Date: 89/04/10 15:55:42 $ */
+
+/*
+ * $Log: ntp.h,v $
+ * Revision 3.4.1.5 89/04/10 15:55:42 louie
+ * Provide default value for number of bits/byte if not defined. Compute the
+ * Window shift mask inside of conditional code on XTAL so we get the correct
+ * value if configured without a crystal controled clock (!!)
+ *
+ * Revision 3.4.1.4 89/03/31 16:34:50 louie
+ * Add bit in flags which allow a peer to be synced to. Changed a char to a bit
+ * field so that it is always signed.
+ *
+ * Revision 3.4.1.3 89/03/29 12:26:18 louie
+ * Removed some unused #defines. Replaced MAXSTRATUM with NTP_INFIN per new
+ * spec. The variable 'mode' in the peer structure has been renamed 'hmode'
+ * per the new spec.
+ *
+ * Revision 3.4.1.2 89/03/22 18:28:18 louie
+ * patch3: Use new RCS headers.
+ *
+ * Revision 3.4.1.1 89/03/20 00:02:53 louie
+ * 1
+ *
+ * Revision 3.4 89/03/17 18:37:00 louie
+ * Latest test release.
+ *
+ * Revision 3.3.1.1 89/03/17 18:23:49 louie
+ * Change CLOCK_FACTOR to be a power of 2.
+ *
+ * Revision 3.3 89/03/15 14:19:36 louie
+ * New baseline for next release.
+ *
+ * Revision 3.2.1.2 89/03/15 13:46:52 louie
+ * The version number for that particular flavor of ntpd <--> ntpdc interaction
+ * is now defined by NTPDC_VERSION. The packet format for the ntpdc program
+ * has changed slightly to improve robustness when dealing with multiple packets
+ * of status data.
+ *
+ * Revision 3.2.1.1 89/03/09 17:11:24 louie
+ * patch1: Updated constants, which were previously in incorrect units.
+ *
+ * Revision 3.2 89/03/07 18:21:45 louie
+ * New version of UNIX NTP daemon and software based on the 6 March 1989
+ * draft of the new NTP protocol specification. This version doesn't
+ * implement authentication, and accepts and send only NTP Version 1
+ * packets.
+ *
+ * Revision 3.1.1.1 89/02/15 08:54:42 louie
+ * *** empty log message ***
+ *
+ *
+ * Revision 3.1 89/01/30 14:43:07 louie
+ * Second UNIX NTP test release.
+ *
+ * Revision 3.0 88/12/12 16:01:07 louie
+ * Test release of new UNIX NTP software. This version should conform to the
+ * revised NTP protocol specification.
+ *
+ */
+
+#ifndef FD_SET
+#define NFDBITS 32
+#define FD_SETSIZE 32
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) bzero((char *)(p), sizeof (*(p)))
+#endif
+
+#ifndef NBBY
+#define NBBY 8 /* number of bits per byte */
+#endif
+
+#define MAXNETIF 10
+
+struct intf {
+ int fd;
+ char *name;
+ struct sockaddr_in sin;
+ struct sockaddr_in bcast;
+ struct sockaddr_in mask;
+ int uses;
+ int if_flags;
+};
+extern struct intf addrs[];
+extern int nintf;
+
+/*
+ * Definitions for the masses
+ */
+#define JAN_1970 2208988800U /* 1970 - 1900 in seconds */
+
+/*
+ * Daemon specific (ntpd.c)
+ */
+#define SHIFT_MASK 0xff /* number of intervals to wait */
+
+#ifndef WAYTOOBIG
+#define WAYTOOBIG 1000.0 /* Too many seconds to correct, something is */
+ /* really wrong */
+#endif
+
+#ifndef XTAL
+#define XTAL 1 /* crystal controlled clock by default */
+#endif
+
+#ifndef NTPINITFILE
+#define NTPINITFILE "/etc/ntp.conf"
+#endif
+
+struct list {
+ struct ntp_peer *head;
+ struct ntp_peer *tail;
+ int members;
+};
+
+#define STRMCMP(a, cond, b) \
+ (((a) == UNSPECIFIED ? NTP_INFIN+1 : a) cond \
+ ((b) == UNSPECIFIED ? NTP_INFIN+1 : (b)))
+
+
+/*
+ * Definitions outlined in the NTP spec
+ */
+#define NTP_VERSION 1
+#define NTP_PORT 123 /* for ref only (see /etc/services) */
+#define NTP_INFIN 15
+#define NTP_MAXAGE 86400
+#define NTP_MAXSKW 0.01 /* seconds */
+#define NTP_MINDIST 0.02 /* seconds */
+#define NTP_MINPOLL 6 /* (64) seconds between messages */
+#define NTP_MAXPOLL 10 /* (1024) secs to poll */
+#define NTP_WINDOW 8 /* size of shift register */
+#define NTP_MAXWGT 8 /* maximum allowable dispersion */
+#define NTP_MAXLIST 5 /* max size of selection list */
+#define NTP_MAXSTRA 2 /* max number of strata in selection list */
+#define X_NTP_CANDIDATES 64 /* number of peers to consider when doing */
+ /* clock selection */
+#define NTP_SELECT 0.75 /* weight used to compute dispersion */
+
+#define PEER_MAXDISP 64.0 /* Maximum dispersion */
+#define PEER_THRESHOLD 0.5 /* dispersion threshold */
+#define PEER_FILTER 0.5 /* filter weight */
+
+#if XTAL == 0
+#define PEER_SHIFT 4
+#define NTP_WINDOW_SHIFT_MASK 0x0f
+#else
+#define PEER_SHIFT 8
+#define NTP_WINDOW_SHIFT_MASK 0xff
+#endif
+
+
+/*
+ * 5.1 Uniform Phase Adjustments
+ * Clock parameters
+ */
+#define CLOCK_UPDATE 8 /* update interval (1<<CLOCK_UPDATE secs) */
+#if XTAL
+#define CLOCK_ADJ 2 /* adjustment interval (1<<CLOCK_ADJ secs) */
+#define CLOCK_PHASE 8 /* phase shift */
+#define CLOCK_MAX 0.128 /* maximum aperture (milliseconds) */
+#else
+#define CLOCK_ADJ 0
+#define CLOCK_PHASE 6 /* phase shift */
+#define CLOCK_MAX 0.512 /* maximum aperture (milliseconds) */
+#endif
+#define CLOCK_FREQ 10 /* frequency shift */
+#define CLOCK_TRACK 8
+#define CLOCK_COMP 4
+#define CLOCK_FACTOR 18
+
+/*
+ * Structure definitions for NTP fixed point values
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Integer Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Fraction Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Integer Part | Fraction Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+struct l_fixedpt {
+ ulong_t int_part;
+ ulong_t fraction;
+};
+
+struct s_fixedpt {
+ ushort_t int_part;
+ ushort_t fraction;
+};
+
+/*
+ * ================= Table 3.3. Packet Variables =================
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |LI | VN | Mode| Stratum | Poll | Precision |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Synchronizing Distance |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Synchronizing Dispersion |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reference Clock Identifier |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Reference Timestamp (64 bits) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Originate Timestamp (64 bits) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Receive Timestamp (64 bits) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Transmit Timestamp (64 bits) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Encryption Keyid (32 bits, when A bit set) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Message Authentication Code/MAC (when A bit set) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+#define MAC_OCTETS_DES 8
+#define MAC_OCTETS_MD5 16
+#define MAC_OCTETS_MIN MAC_OCTETS_DES
+#define MAC_OCTETS_MAX MAC_OCTETS_MD5
+#define AUTH_OCTETS_V3 (MAC_OCTETS_MAX + sizeof (uint32_t))
+
+struct ntpdata {
+ uchar_t li_vn_mode; /* contains leap indicator, version and mode */
+ uchar_t stratum; /* Stratum level */
+ uchar_t ppoll; /* poll value */
+ int precision:8;
+ struct s_fixedpt distance;
+ struct s_fixedpt dispersion;
+ ulong_t refid;
+ struct l_fixedpt reftime;
+ struct l_fixedpt org;
+ struct l_fixedpt rec;
+ struct l_fixedpt xmt;
+ uint32_t keyid;
+ uchar_t mac[MAC_OCTETS_MAX];
+};
+
+#define LEN_PKT_NOMAC (sizeof (struct ntpdata) - AUTH_OCTETS_V3)
+
+/*
+ * Leap Second Codes (high order two bits)
+ */
+#define NO_WARNING 0x00 /* no warning */
+#define PLUS_SEC 0x40 /* add a second (61 seconds) */
+#define MINUS_SEC 0x80 /* minus a second (59 seconds) */
+#define ALARM 0xc0 /* alarm condition (clock unsynchronized) */
+
+/*
+ * Clock Status Bits that Encode Version
+ */
+#define NTPVERSION_1 0x08
+#define VERSIONMASK 0x38
+#define LEAPMASK 0xc0
+#define NTPMODEMASK 0x07
+
+/*
+ * Code values
+ */
+#define MODE_UNSPEC 0 /* unspecified */
+#define MODE_SYM_ACT 1 /* symmetric active */
+#define MODE_SYM_PAS 2 /* symmetric passive */
+#define MODE_CLIENT 3 /* client */
+#define MODE_SERVER 4 /* server */
+#define MODE_BROADCAST 5 /* broadcast */
+#define MODE_CONTROL 6 /* control */
+#define MODE_PRIVATE 7 /* private */
+
+/*
+ * Stratum Definitions
+ */
+#define UNSPECIFIED 0
+#define PRIM_REF 1 /* radio clock */
+#define INFO_QUERY 62 /* **** THIS implementation dependent **** */
+#define INFO_REPLY 63 /* **** THIS implementation dependent **** */
+
+
+/* ================= table 3.2 Peer Variables ================= */
+struct ntp_peer {
+ struct ntp_peer *next, *prev;
+ struct sockaddr_in src; /* both peer.srcadr and peer.srcport */
+ int flags; /* local flags */
+#define PEER_FL_CONFIG 1
+#define PEER_FL_AUTHENABLE 2
+#define PEER_FL_SYNC 0x1000 /* peer can bet sync'd to */
+#define PEER_FL_BCAST 0x2000 /* broadcast peer */
+#define PEER_FL_SELECTED 0x8000 /* actually used by query routine */
+
+ int sock; /* index into sockets to derive */
+ /* peer.dstadr and peer.dstport */
+ uchar_t leap; /* receive */
+ uchar_t hmode; /* receive */
+ uchar_t stratum; /* receive */
+ uchar_t ppoll; /* receive */
+ uchar_t hpoll; /* poll update */
+ short precision; /* receive */
+ struct s_fixedpt distance; /* receive */
+ struct s_fixedpt dispersion; /* receive */
+ ulong_t refid; /* receive */
+ struct l_fixedpt reftime; /* receive */
+ struct l_fixedpt org; /* receive, clear */
+ struct l_fixedpt rec; /* receive, clear */
+ struct l_fixedpt xmt; /* transmit, clear */
+ ulong_t reach; /* receive, transmit, clear */
+ ulong_t valid; /* packet, transmit, clear */
+ ulong_t timer; /* receive, transmit, poll update */
+ long stopwatch; /* <<local>> for timing */
+ /*
+ * first order offsets
+ */
+ struct filter {
+ short samples; /* <<local>> */
+ double offset[PEER_SHIFT];
+ double delay[PEER_SHIFT];
+ } filter; /* filter, clear */
+
+ double estdelay; /* filter */
+ double estoffset; /* filter */
+ double estdisp; /* filter */
+
+ ulong_t pkt_sent; /* <<local>> */
+ ulong_t pkt_rcvd; /* <<local>> */
+ ulong_t pkt_dropped; /* <<local>> */
+};
+
+/* ================= table 3.1: System Variables ================= */
+
+struct sysdata { /* procedure */
+ uchar_t leap; /* clock update */
+ uchar_t stratum; /* clock update */
+ short precision; /* system */
+ struct s_fixedpt distance; /* clock update */
+ struct s_fixedpt dispersion; /* clock update */
+ ulong_t refid; /* clock update */
+ struct l_fixedpt reftime; /* clock update */
+ int hold; /* clock update */
+ struct ntp_peer *peer; /* selection */
+ int maxpeers; /* <<local>> */
+ uchar_t filler; /* put here for %&*%$$ SUNs */
+};
+
+#define NTPDC_VERSION 2
+
+/*
+ * These structures are used to pass information to the ntpdc (control)
+ * program. They are unique to this implementation and not part of the
+ * NTP specification.
+ */
+struct clockinfo {
+ ulong_t net_address;
+ ulong_t my_address;
+ ushort_t port;
+ ushort_t flags;
+ ulong_t pkt_sent;
+ ulong_t pkt_rcvd;
+ ulong_t pkt_dropped;
+ ulong_t timer;
+ uchar_t leap;
+ uchar_t stratum;
+ uchar_t ppoll;
+ int precision:8;
+
+ uchar_t hpoll;
+ uchar_t filler1;
+ ushort_t reach;
+
+ long estdisp; /* scaled by 1000 */
+ long estdelay; /* in milliseconds */
+ long estoffset; /* in milliseconds */
+ ulong_t refid;
+ struct l_fixedpt reftime;
+ struct info_filter {
+ short index;
+ short filler;
+ long offset[PEER_SHIFT]; /* in milliseconds */
+ long delay[PEER_SHIFT]; /* in milliseconds */
+ } info_filter;
+};
+
+struct ntpinfo {
+ uchar_t version;
+ uchar_t type; /* request type (stratum in ntp packets) */
+ uchar_t count; /* number of entries in this packet */
+ uchar_t seq; /* sequence number of this packet */
+
+ uchar_t npkts; /* total number of packets */
+ uchar_t peers;
+ uchar_t fill3;
+ uchar_t fill4;
+};
+
+/*
+ * From usr/src/cmd/xntpd/include/ntp_control.h:
+ * Definition of a mode 6 packet.
+ */
+struct ntp_control {
+ uchar_t li_vn_mode; /* leap, version, mode */
+ uchar_t r_m_e_op; /* response, more, error, opcode */
+ ushort_t sequence; /* sequence number of request */
+ ushort_t status; /* status word for association */
+ ushort_t associd; /* association ID */
+ ushort_t offset; /* offset of this batch of data */
+ ushort_t count; /* count of data in this packet */
+ uchar_t data[1]; /* data + auth */
+};
+
+#define NTPC_DATA_MAXLEN (480 + AUTH_OCTETS_V3)
+
+/*
+ * Decoding for the r_m_e_op field
+ */
+#define CTL_RESPONSE 0x80
+#define CTL_ERROR 0x40
+#define CTL_MORE 0x20
+#define CTL_OP_MASK 0x1f
+
+/*
+ * Opcodes
+ */
+#define CTL_OP_UNSPEC 0
+#define CTL_OP_READSTAT 1
+#define CTL_OP_READVAR 2
+#define CTL_OP_WRITEVAR 3
+#define CTL_OP_READCLOCK 4
+#define CTL_OP_WRITECLOCK 5
+#define CTL_OP_SETTRAP 6
+#define CTL_OP_ASYNCMSG 7
+#define CTL_OP_UNSETTRAP 31
+
+/*
+ * From usr/src/cmd/xntpd/include/ntp_request.h:
+ * A mode 7 packet is used exchanging data between an NTP server
+ * and a client for purposes other than time synchronization, e.g.
+ * monitoring, statistics gathering and configuration. A mode 7
+ * packet has the following format:
+ */
+
+struct ntp_private {
+ uchar_t rm_vn_mode; /* response, more, version, mode */
+ uchar_t auth_seq; /* key, sequence number */
+ uchar_t implementation; /* implementation number */
+ uchar_t request; /* request number */
+ ushort_t err_nitems; /* error code/number of data items */
+ ushort_t mbz_itemsize; /* item size */
+ char data[1]; /* data area */
+};
+
+#define RESP_BIT 0x80
+#define MORE_BIT 0x40
+#define INFO_VERSION(rm_vn_mode) ((uchar_t)(((rm_vn_mode)>>3) & 0x7))
+#define INFO_MODE(rm_vn_mode) ((rm_vn_mode) & 0x7)
+
+#define AUTH_BIT 0x80
+#define INFO_SEQ(auth_seq) ((auth_seq) & 0x7f)
+
+#define INFO_ERR(err_nitems) ((ushort_t)((ntohs(err_nitems) >> 12) & 0xf))
+#define INFO_NITEMS(err_nitems) ((ushort_t)(ntohs(err_nitems) & 0xfff))
+
+#define INFO_ITEMSIZE(mbz_itemsize) (ntohs(mbz_itemsize) & 0xfff)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NTP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/slp.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/slp.h
new file mode 100644
index 0000000..b01c5ea
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/slp.h
@@ -0,0 +1,146 @@
+/*
+ * 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 (c) 1998 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SNOOP_SLP_H
+#define _SNOOP_SLP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Structs and definitions for the snoop SLP interpreter only
+ * (This code is not used by the SLP library).
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct slpv1_hdr {
+ unsigned char vers;
+ unsigned char function;
+ unsigned short length;
+ unsigned char flags;
+ unsigned char dialect;
+ unsigned char language[2];
+ unsigned short charset;
+ unsigned short xid;
+};
+
+struct slpv2_hdr {
+ unsigned char vers;
+ unsigned char function;
+ unsigned char l1, l2, l3;
+ unsigned char flags;
+ unsigned char reserved;
+ unsigned char o1, o2, o3;
+ unsigned short xid;
+};
+
+/*
+ * flags
+ */
+#define V1_OVERFLOW 0x80
+#define V1_MONOLINGUAL 0x40
+#define V1_URL_AUTH 0x20
+#define V1_ATTR_AUTH 0x10
+#define V1_FRESH_REG 0x08
+
+#define V2_OVERFLOW 0x80
+#define V2_FRESH 0x40
+#define V2_MCAST 0x20
+
+/*
+ * packet types
+ */
+
+#define V1_SRVREQ 1
+#define V1_SRVRPLY 2
+#define V1_SRVREG 3
+#define V1_SRVDEREG 4
+#define V1_SRVACK 5
+#define V1_ATTRRQST 6
+#define V1_ATTRRPLY 7
+#define V1_DAADVERT 8
+#define V1_SRVTYPERQST 9
+#define V1_SRVTYPERPLY 10
+
+#define V2_SRVRQST 1
+#define V2_SRVRPLY 2
+#define V2_SRVREG 3
+#define V2_SRVDEREG 4
+#define V2_SRVACK 5
+#define V2_ATTRRQST 6
+#define V2_ATTRRPLY 7
+#define V2_DAADVERT 8
+#define V2_SRVTYPERQST 9
+#define V2_SRVTYPERPLY 10
+#define V2_SAADVERT 11
+
+/*
+ * extended packet types
+ */
+#define SCOPERQST 65
+#define SCOPERPLY 66
+#define DARQST 67
+#define DARPLY 68
+#define DASTRIKE 69
+
+
+/*
+ * error codes
+ */
+
+#define OK 0x0000
+#define LANG_NOT_SUPPORTED 0x0001
+#define PROTOCOL_PARSE_ERR 0x0002
+#define INVALID_REGISTRATION 0x0003
+#define SCOPE_NOT_SUPPORTED 0x0004
+#define CHARSET_NOT_UNDERSTOOD 0x0005
+#define AUTHENTICATION_UNKNOWN 0x0005
+#define AUTHENTICATION_INVALID 0x0006
+#define V2_AUTHENTICATION_ABSENT 0x0006
+#define V2_AUTHENTICATION_FAILED 0x0007
+#define V2_VER_NOT_SUPPORTED 0x0009
+#define NOT_SUPPORTED_YET 0x000a
+#define V2_INTERNAL_ERROR 0x000a
+#define REQUEST_TIMED_OUT 0x000b
+#define V2_DA_BUSY_NOW 0x000b
+#define COULD_NOT_INIT_NET_RESOURCES 0x000c
+#define V2_OPTION_NOT_UNDERSTOOD 0x000c
+#define COULD_NOT_ALLOCATE_MEMORY 0x000d
+#define V2_INVALID_UPDATE 0x000d
+#define PARAMETER_BAD 0x000e
+#define V2_RQST_NOT_SUPPORTED 0x000e
+#define INVALID_LIFETIME 0x000f
+
+#define INTERNAL_NET_ERROR 0x000f
+#define INTERNAL_SYSTEM_ERROR 0x0010
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SNOOP_SLP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c
new file mode 100644
index 0000000..097dd6e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c
@@ -0,0 +1,1127 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <sys/sysmacros.h>
+
+#include <sys/socket.h>
+#include <sys/pfmod.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netdb.h>
+
+#include "snoop.h"
+
+static int snaplen;
+
+/* Global error recovery variables */
+sigjmp_buf jmp_env, ojmp_env; /* error recovery jmp buf */
+int snoop_nrecover; /* number of recoveries on curr pkt */
+int quitting; /* user termination flag */
+
+static struct snoop_handler *snoop_hp; /* global alarm handler head */
+static struct snoop_handler *snoop_tp; /* global alarm handler tail */
+static time_t snoop_nalarm; /* time of next alarm */
+
+/* protected interpreter output areas */
+#define MAXSUM 8
+#define REDZONE 64
+static char *sumline[MAXSUM];
+static char *detail_line;
+static char *line;
+static char *encap;
+
+static int audio;
+int maxcount; /* maximum no of packets to capture */
+int count; /* count of packets captured */
+static int sumcount;
+int x_offset = -1;
+int x_length = 0x7fffffff;
+FILE *namefile;
+boolean_t Pflg;
+boolean_t Iflg;
+boolean_t qflg;
+boolean_t rflg;
+#ifdef DEBUG
+boolean_t zflg;
+#endif
+struct Pf_ext_packetfilt pf;
+
+static int vlanid = 0;
+
+static void usage(void);
+static void snoop_sigrecover(int sig, siginfo_t *info, void *p);
+static char *protmalloc(size_t);
+static void resetperm(void);
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ int filter = 0;
+ int flags = F_SUM;
+ struct Pf_ext_packetfilt *fp = NULL;
+ char *icapfile = NULL;
+ char *ocapfile = NULL;
+ boolean_t nflg = B_FALSE;
+ boolean_t Nflg = B_FALSE;
+ int Cflg = 0;
+ boolean_t Uflg = B_FALSE;
+ int first = 1;
+ int last = 0x7fffffff;
+ boolean_t use_kern_pf;
+ char *p, *p2;
+ char names[MAXPATHLEN + 1];
+ char self[MAXHOSTNAMELEN + 1];
+ char *argstr = NULL;
+ void (*proc)();
+ char *audiodev;
+ int ret;
+ struct sigaction sigact;
+ stack_t sigstk;
+ char *output_area;
+ int nbytes;
+ char *datalink = NULL;
+ dlpi_handle_t dh;
+
+ names[0] = '\0';
+ /*
+ * Global error recovery: Prepare for interpreter failures
+ * with corrupted packets or confused interpreters.
+ * Allocate protected output and stack areas, with generous
+ * red-zones.
+ */
+ nbytes = (MAXSUM + 3) * (MAXLINE + REDZONE);
+ output_area = protmalloc(nbytes);
+ if (output_area == NULL) {
+ perror("Warning: mmap");
+ exit(1);
+ }
+
+ /* Allocate protected output areas */
+ for (ret = 0; ret < MAXSUM; ret++) {
+ sumline[ret] = (char *)output_area;
+ output_area += (MAXLINE + REDZONE);
+ }
+ detail_line = output_area;
+ output_area += MAXLINE + REDZONE;
+ line = output_area;
+ output_area += MAXLINE + REDZONE;
+ encap = output_area;
+ output_area += MAXLINE + REDZONE;
+
+ /* Initialize an alternate signal stack to increase robustness */
+ if ((sigstk.ss_sp = (char *)malloc(SIGSTKSZ+REDZONE)) == NULL) {
+ perror("Warning: malloc");
+ exit(1);
+ }
+ sigstk.ss_size = SIGSTKSZ;
+ sigstk.ss_flags = 0;
+ if (sigaltstack(&sigstk, (stack_t *)NULL) < 0) {
+ perror("Warning: sigaltstack");
+ exit(1);
+ }
+
+ /* Initialize a master signal handler */
+ sigact.sa_handler = NULL;
+ sigact.sa_sigaction = snoop_sigrecover;
+ (void) sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = SA_ONSTACK|SA_SIGINFO;
+
+ /* Register master signal handler */
+ if (sigaction(SIGHUP, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGINT, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGQUIT, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGILL, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGTRAP, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGIOT, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGEMT, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGFPE, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGBUS, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGSYS, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGALRM, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+ if (sigaction(SIGTERM, &sigact, (struct sigaction *)NULL) < 0) {
+ perror("Warning: sigaction");
+ exit(1);
+ }
+
+ /* Prepare for failure during program initialization/exit */
+ if (sigsetjmp(jmp_env, 1)) {
+ exit(1);
+ }
+ (void) setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
+
+ while ((c = getopt(argc, argv, "at:CPDSi:o:Nn:s:d:I:vVp:f:c:x:U?rqz"))
+ != EOF) {
+ switch (c) {
+ case 'a':
+ audiodev = getenv("AUDIODEV");
+ if (audiodev == NULL)
+ audiodev = "/dev/audio";
+ audio = open(audiodev, O_WRONLY);
+ if (audio < 0) {
+ pr_err("Audio device %s: %m",
+ audiodev);
+ exit(1);
+ }
+ break;
+ case 't':
+ flags |= F_TIME;
+ switch (*optarg) {
+ case 'r': flags |= F_RTIME; break;
+ case 'a': flags |= F_ATIME; break;
+ case 'd': break;
+ default: usage();
+ }
+ break;
+ case 'I':
+ if (datalink != NULL)
+ usage();
+ Iflg = B_TRUE;
+ datalink = optarg;
+ break;
+ case 'P':
+ Pflg = B_TRUE;
+ break;
+ case 'D':
+ flags |= F_DROPS;
+ break;
+ case 'S':
+ flags |= F_LEN;
+ break;
+ case 'i':
+ icapfile = optarg;
+ break;
+ case 'o':
+ ocapfile = optarg;
+ break;
+ case 'N':
+ Nflg = B_TRUE;
+ break;
+ case 'n':
+ nflg = B_TRUE;
+ (void) strlcpy(names, optarg, MAXPATHLEN);
+ break;
+ case 's':
+ snaplen = atoi(optarg);
+ break;
+ case 'd':
+ if (Iflg)
+ usage();
+ datalink = optarg;
+ break;
+ case 'v':
+ flags &= ~(F_SUM);
+ flags |= F_DTAIL;
+ break;
+ case 'V':
+ flags |= F_ALLSUM;
+ break;
+ case 'p':
+ p = optarg;
+ p2 = strpbrk(p, ",:-");
+ if (p2 == NULL) {
+ first = last = atoi(p);
+ } else {
+ *p2++ = '\0';
+ first = atoi(p);
+ last = atoi(p2);
+ }
+ break;
+ case 'f':
+ (void) gethostname(self, MAXHOSTNAMELEN);
+ p = strchr(optarg, ':');
+ if (p) {
+ *p = '\0';
+ if (strcmp(optarg, self) == 0 ||
+ strcmp(p+1, self) == 0)
+ (void) fprintf(stderr,
+ "Warning: cannot capture packets from %s\n",
+ self);
+ *p = ' ';
+ } else if (strcmp(optarg, self) == 0)
+ (void) fprintf(stderr,
+ "Warning: cannot capture packets from %s\n",
+ self);
+ argstr = optarg;
+ break;
+ case 'x':
+ p = optarg;
+ p2 = strpbrk(p, ",:-");
+ if (p2 == NULL) {
+ x_offset = atoi(p);
+ x_length = -1;
+ } else {
+ *p2++ = '\0';
+ x_offset = atoi(p);
+ x_length = atoi(p2);
+ }
+ break;
+ case 'c':
+ maxcount = atoi(optarg);
+ break;
+ case 'C':
+ Cflg = B_TRUE;
+ break;
+ case 'q':
+ qflg = B_TRUE;
+ break;
+ case 'r':
+ rflg = B_TRUE;
+ break;
+ case 'U':
+ Uflg = B_TRUE;
+ break;
+#ifdef DEBUG
+ case 'z':
+ zflg = B_TRUE;
+ break;
+#endif /* DEBUG */
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ if (argc > optind)
+ argstr = (char *)concat_args(&argv[optind], argc - optind);
+
+ /*
+ * Need to know before we decide on filtering method some things
+ * about the interface. So, go ahead and do part of the initialization
+ * now so we have that data. Note that if no datalink is specified,
+ * open_datalink() selects one and returns it. In an ideal world,
+ * it might be nice if the "correct" interface for the filter
+ * requested was chosen, but that's too hard.
+ */
+ if (!icapfile) {
+ use_kern_pf = open_datalink(&dh, datalink);
+ } else {
+ use_kern_pf = B_FALSE;
+ cap_open_read(icapfile);
+
+ if (!nflg) {
+ names[0] = '\0';
+ (void) strlcpy(names, icapfile, MAXPATHLEN);
+ (void) strlcat(names, ".names", MAXPATHLEN);
+ }
+ }
+
+ if (Uflg)
+ use_kern_pf = B_FALSE;
+
+ /* attempt to read .names file if it exists before filtering */
+ if ((!Nflg) && names[0] != '\0') {
+ if (access(names, F_OK) == 0) {
+ load_names(names);
+ } else if (nflg) {
+ (void) fprintf(stderr, "%s not found\n", names);
+ exit(1);
+ }
+ }
+
+ if (argstr) {
+ if (use_kern_pf) {
+ ret = pf_compile(argstr, Cflg);
+ switch (ret) {
+ case 0:
+ filter++;
+ compile(argstr, Cflg);
+ break;
+ case 1:
+ fp = &pf;
+ break;
+ case 2:
+ fp = &pf;
+ filter++;
+ break;
+ }
+ } else {
+ filter++;
+ compile(argstr, Cflg);
+ }
+
+ if (Cflg)
+ exit(0);
+ }
+
+ if (flags & F_SUM)
+ flags |= F_WHO;
+
+ /*
+ * If the -o flag is set then capture packets
+ * directly to a file. Don't attempt to
+ * interpret them on the fly (F_NOW).
+ * Note: capture to file is much less likely
+ * to drop packets since we don't spend cpu
+ * cycles running through the interpreters
+ * and possibly hanging in address-to-name
+ * mappings through the name service.
+ */
+ if (ocapfile) {
+ cap_open_write(ocapfile);
+ proc = cap_write;
+ } else {
+ flags |= F_NOW;
+ proc = process_pkt;
+ }
+
+
+ /*
+ * If the -i flag is set then get packets from
+ * the log file which has been previously captured
+ * with the -o option.
+ */
+ if (icapfile) {
+ names[0] = '\0';
+ (void) strlcpy(names, icapfile, MAXPATHLEN);
+ (void) strlcat(names, ".names", MAXPATHLEN);
+
+ if (Nflg) {
+ namefile = fopen(names, "w");
+ if (namefile == NULL) {
+ perror(names);
+ exit(1);
+ }
+ flags = 0;
+ (void) fprintf(stderr,
+ "Creating name file %s\n", names);
+ }
+
+ if (flags & F_DTAIL)
+ flags = F_DTAIL;
+ else
+ flags |= F_NUM | F_TIME;
+
+ resetperm();
+ cap_read(first, last, filter, proc, flags);
+
+ if (Nflg)
+ (void) fclose(namefile);
+
+ } else {
+ const int chunksize = 8 * 8192;
+ struct timeval timeout;
+
+ /*
+ * If listening to packets on audio
+ * then set the buffer timeout down
+ * to 1/10 sec. A higher value
+ * makes the audio "bursty".
+ */
+ if (audio) {
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 100000;
+ } else {
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ }
+
+ init_datalink(dh, snaplen, chunksize, &timeout, fp);
+ if (! qflg && ocapfile)
+ show_count();
+ resetperm();
+ net_read(dh, chunksize, filter, proc, flags);
+ dlpi_close(dh);
+
+ if (!(flags & F_NOW))
+ (void) printf("\n");
+ }
+
+ if (ocapfile)
+ cap_close();
+
+ return (0);
+}
+
+static int tone[] = {
+0x076113, 0x153333, 0x147317, 0x144311, 0x147315, 0x050353, 0x037103, 0x051106,
+0x157155, 0x142723, 0x133273, 0x134664, 0x051712, 0x024465, 0x026447, 0x072473,
+0x136715, 0x126257, 0x135256, 0x047344, 0x034476, 0x027464, 0x036062, 0x133334,
+0x127256, 0x130660, 0x136262, 0x040724, 0x016446, 0x025437, 0x137171, 0x127672,
+0x124655, 0x134654, 0x032741, 0x021447, 0x037450, 0x125675, 0x127650, 0x077277,
+0x046514, 0x036077, 0x035471, 0x147131, 0x136272, 0x162720, 0x166151, 0x037527,
+};
+
+/*
+ * Make a sound on /dev/audio according to the length of the packet. The
+ * tone data was ripped from /usr/share/audio/samples/au/bark.au. The
+ * amount of waveform used is a function of packet length e.g. a series
+ * of small packets is heard as clicks, whereas a series of NFS packets in
+ * an 8k read sounds like a "WHAAAARP".
+ */
+void
+click(len)
+ int len;
+{
+ len /= 8;
+ len = len ? len : 4;
+
+ if (audio) {
+ (void) write(audio, tone, len);
+ }
+}
+
+/* Display a count of packets */
+void
+show_count()
+{
+ static int prev = -1;
+
+ if (count == prev)
+ return;
+
+ prev = count;
+ (void) fprintf(stderr, "\r%d ", count);
+}
+
+#define ENCAP_LEN 16 /* Hold "(NN encap)" */
+
+/*
+ * Display data that's external to the packet.
+ * This constitutes the first half of the summary
+ * line display.
+ */
+void
+show_pktinfo(flags, num, src, dst, ptvp, tvp, drops, len)
+ int flags, num, drops, len;
+ char *src, *dst;
+ struct timeval *ptvp, *tvp;
+{
+ struct tm *tm;
+ static struct timeval tvp0;
+ int sec, usec;
+ char *lp = line;
+ int i, start;
+
+ if (flags & F_NUM) {
+ (void) sprintf(lp, "%3d ", num);
+ lp += strlen(lp);
+ }
+ tm = localtime(&tvp->tv_sec);
+
+ if (flags & F_TIME) {
+ if (flags & F_ATIME) {
+ (void) sprintf(lp, "%d:%02d:%d.%05d ",
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
+ (int)tvp->tv_usec / 10);
+ lp += strlen(lp);
+ } else {
+ if (flags & F_RTIME) {
+ if (tvp0.tv_sec == 0) {
+ tvp0.tv_sec = tvp->tv_sec;
+ tvp0.tv_usec = tvp->tv_usec;
+ }
+ ptvp = &tvp0;
+ }
+ sec = tvp->tv_sec - ptvp->tv_sec;
+ usec = tvp->tv_usec - ptvp->tv_usec;
+ if (usec < 0) {
+ usec += 1000000;
+ sec -= 1;
+ }
+ (void) sprintf(lp, "%3d.%05d ", sec, usec / 10);
+ lp += strlen(lp);
+ }
+ }
+
+ if ((flags & F_SUM) && !(flags & F_ALLSUM) && (vlanid != 0)) {
+ (void) snprintf(lp, MAXLINE, "VLAN#%i: ", vlanid);
+ lp += strlen(lp);
+ }
+
+ if (flags & F_WHO) {
+ (void) sprintf(lp, "%12s -> %-12s ", src, dst);
+ lp += strlen(lp);
+ }
+
+ if (flags & F_DROPS) {
+ (void) sprintf(lp, "drops: %d ", drops);
+ lp += strlen(lp);
+ }
+
+ if (flags & F_LEN) {
+ (void) sprintf(lp, "length: %4d ", len);
+ lp += strlen(lp);
+ }
+
+ if (flags & F_SUM) {
+ if (flags & F_ALLSUM)
+ (void) printf("________________________________\n");
+
+ start = flags & F_ALLSUM ? 0 : sumcount - 1;
+ (void) sprintf(encap, " (%d encap)", total_encap_levels - 1);
+ (void) printf("%s%s%s\n", line, sumline[start],
+ ((flags & F_ALLSUM) || (total_encap_levels == 1)) ? "" :
+ encap);
+
+ for (i = start + 1; i < sumcount; i++)
+ (void) printf("%s%s\n", line, sumline[i]);
+
+ sumcount = 0;
+ }
+
+ if (flags & F_DTAIL) {
+ (void) printf("%s\n\n", detail_line);
+ detail_line[0] = '\0';
+ }
+}
+
+/*
+ * The following three routines are called back
+ * from the interpreters to display their stuff.
+ * The theory is that when snoop becomes a window
+ * based tool we can just supply a new version of
+ * get_sum_line and get_detail_line and not have
+ * to touch the interpreters at all.
+ */
+char *
+get_sum_line()
+{
+ int tsumcount = sumcount;
+
+ if (sumcount >= MAXSUM) {
+ sumcount = 0; /* error recovery */
+ pr_err(
+ "get_sum_line: sumline overflow (sumcount=%d, MAXSUM=%d)\n",
+ tsumcount, MAXSUM);
+ }
+
+ sumline[sumcount][0] = '\0';
+ return (sumline[sumcount++]);
+}
+
+/*ARGSUSED*/
+char *
+get_detail_line(off, len)
+ int off, len;
+{
+ if (detail_line[0]) {
+ (void) printf("%s\n", detail_line);
+ detail_line[0] = '\0';
+ }
+ return (detail_line);
+}
+
+/*
+ * This function exists to make sure that VLAN information is
+ * prepended to summary lines displayed. The problem this function
+ * solves is how to display VLAN information while in summary mode.
+ * Each interpretor uses the get_sum_line and get_detail_line functions
+ * to get a character buffer to display information to the user.
+ * get_sum_line is the important one here. Each call to get_sum_line
+ * gets a buffer which stores one line of information. In summary mode,
+ * the last line generated is the line printed. Instead of changing each
+ * interpreter to add VLAN information to the summary line, the ethernet
+ * interpreter changes to call this function and set an ID. If the ID is not
+ * zero and snoop is in default summary mode, snoop displays the
+ * VLAN information at the beginning of the output line. Otherwise,
+ * no VLAN information is displayed.
+ */
+void
+set_vlan_id(int id)
+{
+ vlanid = id;
+}
+
+/*
+ * Print an error.
+ * Works like printf (fmt string and variable args)
+ * except that it will substitute an error message
+ * for a "%m" string (like syslog) and it calls
+ * long_jump - it doesn't return to where it was
+ * called from - it goes to the last setjmp().
+ */
+/* VARARGS1 */
+void
+pr_err(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[1024], *p2;
+ const char *p1;
+
+ (void) strcpy(buf, "snoop: ");
+ p2 = buf + strlen(buf);
+
+ /*
+ * Note that we terminate the buffer with '\n' and '\0'.
+ */
+ for (p1 = fmt; *p1 != '\0' && p2 < buf + sizeof (buf) - 2; p1++) {
+ if (*p1 == '%' && *(p1+1) == 'm') {
+ const char *errstr;
+
+ if ((errstr = strerror(errno)) != NULL) {
+ *p2 = '\0';
+ (void) strlcat(buf, errstr, sizeof (buf));
+ p2 += strlen(p2);
+ }
+ p1++;
+ } else {
+ *p2++ = *p1;
+ }
+ }
+ if (p2 > buf && *(p2-1) != '\n')
+ *p2++ = '\n';
+ *p2 = '\0';
+
+ va_start(ap, fmt);
+ /* LINTED: E_SEC_PRINTF_VAR_FMT */
+ (void) vfprintf(stderr, buf, ap);
+ va_end(ap);
+ snoop_sigrecover(-1, NULL, NULL); /* global error recovery */
+}
+
+/*
+ * Store a copy of linkname associated with the DLPI handle.
+ * Save errno before closing the dlpi handle so that the
+ * correct error value is used if 'err' is a system error.
+ */
+void
+pr_errdlpi(dlpi_handle_t dh, const char *cmd, int err)
+{
+ int save_errno = errno;
+ char linkname[DLPI_LINKNAME_MAX];
+
+ (void) strlcpy(linkname, dlpi_linkname(dh), sizeof (linkname));
+
+ dlpi_close(dh);
+ errno = save_errno;
+
+ pr_err("%s on \"%s\": %s", cmd, linkname, dlpi_strerror(err));
+}
+
+/*
+ * Ye olde usage proc
+ * PLEASE keep this up to date!
+ * Naive users *love* this stuff.
+ */
+static void
+usage(void)
+{
+ (void) fprintf(stderr, "\nUsage: snoop\n");
+ (void) fprintf(stderr,
+ "\t[ -a ] # Listen to packets on audio\n");
+ (void) fprintf(stderr,
+ "\t[ -d link ] # Listen on named link\n");
+ (void) fprintf(stderr,
+ "\t[ -s snaplen ] # Truncate packets\n");
+ (void) fprintf(stderr,
+ "\t[ -I IP interface ] # Listen on named IP interface\n");
+ (void) fprintf(stderr,
+ "\t[ -c count ] # Quit after count packets\n");
+ (void) fprintf(stderr,
+ "\t[ -P ] # Turn OFF promiscuous mode\n");
+ (void) fprintf(stderr,
+ "\t[ -D ] # Report dropped packets\n");
+ (void) fprintf(stderr,
+ "\t[ -S ] # Report packet size\n");
+ (void) fprintf(stderr,
+ "\t[ -i file ] # Read previously captured packets\n");
+ (void) fprintf(stderr,
+ "\t[ -o file ] # Capture packets in file\n");
+ (void) fprintf(stderr,
+ "\t[ -n file ] # Load addr-to-name table from file\n");
+ (void) fprintf(stderr,
+ "\t[ -N ] # Create addr-to-name table\n");
+ (void) fprintf(stderr,
+ "\t[ -t r|a|d ] # Time: Relative, Absolute or Delta\n");
+ (void) fprintf(stderr,
+ "\t[ -v ] # Verbose packet display\n");
+ (void) fprintf(stderr,
+ "\t[ -V ] # Show all summary lines\n");
+ (void) fprintf(stderr,
+ "\t[ -p first[,last] ] # Select packet(s) to display\n");
+ (void) fprintf(stderr,
+ "\t[ -x offset[,length] ] # Hex dump from offset for length\n");
+ (void) fprintf(stderr,
+ "\t[ -C ] # Print packet filter code\n");
+ (void) fprintf(stderr,
+ "\t[ -q ] # Suppress printing packet count\n");
+ (void) fprintf(stderr,
+ "\t[ -r ] # Do not resolve address to name\n");
+ (void) fprintf(stderr,
+ "\n\t[ filter expression ]\n");
+ (void) fprintf(stderr, "\nExample:\n");
+ (void) fprintf(stderr, "\tsnoop -o saved host fred\n\n");
+ (void) fprintf(stderr, "\tsnoop -i saved -tr -v -p19\n");
+ exit(1);
+}
+
+/*
+ * sdefault: default global alarm handler. Causes the current packet
+ * to be skipped.
+ */
+static void
+sdefault(void)
+{
+ snoop_nrecover = SNOOP_MAXRECOVER;
+}
+
+/*
+ * snoop_alarm: register or unregister an alarm handler to be called after
+ * s_sec seconds. Because snoop wasn't written to tolerate random signal
+ * delivery, periodic SIGALRM delivery (or SA_RESTART) cannot be used.
+ *
+ * s_sec argument of 0 seconds unregisters the handler.
+ * s_handler argument of NULL registers default handler sdefault(), or
+ * unregisters all signal handlers (for error recovery).
+ *
+ * Variables must be volatile to force the compiler to not optimize
+ * out the signal blocking.
+ */
+/*ARGSUSED*/
+int
+snoop_alarm(int s_sec, void (*s_handler)())
+{
+ volatile time_t now;
+ volatile time_t nalarm = 0;
+ volatile struct snoop_handler *sh = NULL;
+ volatile struct snoop_handler *hp, *tp, *next;
+ volatile sigset_t s_mask;
+ volatile int ret = -1;
+
+ (void) sigemptyset((sigset_t *)&s_mask);
+ (void) sigaddset((sigset_t *)&s_mask, SIGALRM);
+ if (s_sec < 0)
+ return (-1);
+
+ /* register an alarm handler */
+ now = time(NULL);
+ if (s_sec) {
+ sh = malloc(sizeof (struct snoop_handler));
+ sh->s_time = now + s_sec;
+ if (s_handler == NULL)
+ s_handler = sdefault;
+ sh->s_handler = s_handler;
+ sh->s_next = NULL;
+ (void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL);
+ if (snoop_hp == NULL) {
+ snoop_hp = snoop_tp = (struct snoop_handler *)sh;
+
+ snoop_nalarm = sh->s_time;
+ (void) alarm(sh->s_time - now);
+ } else {
+ snoop_tp->s_next = (struct snoop_handler *)sh;
+ snoop_tp = (struct snoop_handler *)sh;
+
+ if (sh->s_time < snoop_nalarm) {
+ snoop_nalarm = sh->s_time;
+ (void) alarm(sh->s_time - now);
+ }
+ }
+ (void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL);
+
+ return (0);
+ }
+
+ /* unregister an alarm handler */
+ (void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL);
+ tp = (struct snoop_handler *)&snoop_hp;
+ for (hp = snoop_hp; hp; hp = next) {
+ next = hp->s_next;
+ if (s_handler == NULL || hp->s_handler == s_handler) {
+ ret = 0;
+ tp->s_next = hp->s_next;
+ if (snoop_tp == hp) {
+ if (tp == (struct snoop_handler *)&snoop_hp)
+ snoop_tp = NULL;
+ else
+ snoop_tp = (struct snoop_handler *)tp;
+ }
+ free((void *)hp);
+ } else {
+ if (nalarm == 0 || nalarm > hp->s_time)
+ nalarm = now < hp->s_time ? hp->s_time :
+ now + 1;
+ tp = hp;
+ }
+ }
+ /*
+ * Stop or adjust timer
+ */
+ if (snoop_hp == NULL) {
+ snoop_nalarm = 0;
+ (void) alarm(0);
+ } else if (nalarm > 0 && nalarm < snoop_nalarm) {
+ snoop_nalarm = nalarm;
+ (void) alarm(nalarm - now);
+ }
+
+ (void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL);
+ return (ret);
+}
+
+/*
+ * snoop_recover: reset snoop's output area, and any internal variables,
+ * to allow continuation.
+ * XXX: make this an interface such that each interpreter can
+ * register a reset routine.
+ */
+void
+snoop_recover(void)
+{
+ int i;
+
+ /* Error recovery: reset output_area and associated variables */
+ for (i = 0; i < MAXSUM; i++)
+ sumline[i][0] = '\0';
+ detail_line[0] = '\0';
+ line[0] = '\0';
+ encap[0] = '\0';
+ sumcount = 0;
+
+ /* stacking/unstacking cannot be relied upon */
+ encap_levels = 0;
+ total_encap_levels = 1;
+
+ /* remove any pending timeouts */
+ (void) snoop_alarm(0, NULL);
+}
+
+/*
+ * snoop_sigrecover: global sigaction routine to manage recovery
+ * from catastrophic interpreter failures while interpreting
+ * corrupt trace files/packets. SIGALRM timeouts, program errors,
+ * and user termination are all handled. In the case of a corrupt
+ * packet or confused interpreter, the packet will be skipped, and
+ * execution will continue in scan().
+ *
+ * Global alarm handling (see snoop_alarm()) is managed here.
+ *
+ * Variables must be volatile to force the compiler to not optimize
+ * out the signal blocking.
+ */
+/*ARGSUSED*/
+static void
+snoop_sigrecover(int sig, siginfo_t *info, void *p)
+{
+ volatile time_t now;
+ volatile time_t nalarm = 0;
+ volatile struct snoop_handler *hp;
+
+ /*
+ * Invoke any registered alarms. This involves first calculating
+ * the time for the next alarm, setting it up, then progressing
+ * through handler invocations. Note that since handlers may
+ * use siglongjmp(), in the worst case handlers may be serviced
+ * at a later time.
+ */
+ if (sig == SIGALRM) {
+ now = time(NULL);
+ /* Calculate next alarm time */
+ for (hp = snoop_hp; hp; hp = hp->s_next) {
+ if (hp->s_time) {
+ if ((hp->s_time - now) > 0) {
+ if (nalarm == 0 || nalarm > hp->s_time)
+ nalarm = now < hp->s_time ?
+ hp->s_time : now + 1;
+ }
+ }
+ }
+ /* Setup next alarm */
+ if (nalarm) {
+ snoop_nalarm = nalarm;
+ (void) alarm(nalarm - now);
+ } else {
+ snoop_nalarm = 0;
+ }
+
+ /* Invoke alarm handlers (may not return) */
+ for (hp = snoop_hp; hp; hp = hp->s_next) {
+ if (hp->s_time) {
+ if ((now - hp->s_time) >= 0) {
+ hp->s_time = 0; /* only invoke once */
+ if (hp->s_handler)
+ hp->s_handler();
+ }
+ }
+ }
+ } else {
+ snoop_nrecover++;
+ }
+
+ /*
+ * Exit if a signal has occurred after snoop has begun the process
+ * of quitting.
+ */
+ if (quitting)
+ exit(1);
+
+ /*
+ * If an alarm handler has timed out, and snoop_nrecover has
+ * reached SNOOP_MAXRECOVER, skip to the next packet.
+ *
+ * If any other signal has occurred, and snoop_nrecover has
+ * reached SNOOP_MAXRECOVER, give up.
+ */
+ if (sig == SIGALRM) {
+ if (ioctl(STDOUT_FILENO, I_CANPUT, 0) == 0) {
+ /*
+ * We've stalled on output, which is not a critical
+ * failure. Reset the recovery counter so we do not
+ * consider this a persistent failure, and return so
+ * we do not skip this packet.
+ */
+ snoop_nrecover = 0;
+ return;
+ }
+ if (snoop_nrecover >= SNOOP_MAXRECOVER) {
+ (void) fprintf(stderr,
+ "snoop: WARNING: skipping from packet %d\n",
+ count);
+ snoop_nrecover = 0;
+ } else {
+ /* continue trying */
+ return;
+ }
+ } else if (snoop_nrecover >= SNOOP_MAXRECOVER) {
+ (void) fprintf(stderr,
+ "snoop: ERROR: cannot recover from packet %d\n", count);
+ exit(1);
+ }
+
+#ifdef DEBUG
+ (void) fprintf(stderr, "snoop_sigrecover(%d, %p, %p)\n", sig, info, p);
+#endif /* DEBUG */
+
+ /*
+ * Prepare to quit. This allows final processing to occur
+ * after first terminal interruption.
+ */
+ if (sig == SIGTERM || sig == SIGHUP || sig == SIGINT) {
+ quitting = 1;
+ return;
+ } else if (sig != -1 && sig != SIGALRM) {
+ /* Inform user that snoop has taken a fault */
+ (void) fprintf(stderr,
+ "WARNING: received signal %d from packet %d\n",
+ sig, count);
+ }
+
+ /* Reset interpreter variables */
+ snoop_recover();
+
+ /* Continue in scan() with the next packet */
+ siglongjmp(jmp_env, 1);
+ /*NOTREACHED*/
+}
+
+/*
+ * Protected malloc for global error recovery: prepare for interpreter
+ * failures with corrupted packets or confused interpreters. Dynamically
+ * allocate `nbytes' bytes, and sandwich it between two PROT_NONE pages to
+ * catch writes outside of the allocated region.
+ */
+static char *
+protmalloc(size_t nbytes)
+{
+ caddr_t start;
+ int psz = sysconf(_SC_PAGESIZE);
+
+ nbytes = P2ROUNDUP(nbytes, psz);
+ start = mmap(NULL, nbytes + psz * 2, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (start == MAP_FAILED) {
+ perror("Error: protmalloc: mmap");
+ return (NULL);
+ }
+ assert(IS_P2ALIGNED(start, psz));
+ if (mprotect(start, 1, PROT_NONE) == -1)
+ perror("Warning: mprotect");
+
+ start += psz;
+ if (mprotect(start + nbytes, 1, PROT_NONE) == -1)
+ perror("Warning: mprotect");
+
+ return (start);
+}
+
+/*
+ * resetperm - reduce security vulnerabilities by resetting
+ * owner/group/permissions. Always attempt setuid() - if we have
+ * permission to drop our privilege level, do so.
+ */
+void
+resetperm(void)
+{
+ if (geteuid() == 0) {
+ (void) setgid(GID_NOBODY);
+ (void) setuid(UID_NOBODY);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h
new file mode 100644
index 0000000..e4f1825
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h
@@ -0,0 +1,355 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#ifndef _SNOOP_H
+#define _SNOOP_H
+
+#include <rpc/types.h>
+#include <sys/pfmod.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/bufmod.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+#include <net/pppoe.h>
+#include <libdlpi.h>
+#include <note.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Flags to control packet info display
+ */
+#define F_NOW 0x00000001 /* display in realtime */
+#define F_SUM 0x00000002 /* display summary line */
+#define F_ALLSUM 0x00000004 /* display all summary lines */
+#define F_DTAIL 0x00000008 /* display detail lines */
+#define F_TIME 0x00000010 /* display time */
+#define F_ATIME 0x00000020 /* display absolute time */
+#define F_RTIME 0x00000040 /* display relative time */
+#define F_DROPS 0x00000080 /* display drops */
+#define F_LEN 0x00000100 /* display pkt length */
+#define F_NUM 0x00000200 /* display pkt number */
+#define F_WHO 0x00000400 /* display src/dst */
+
+#define MAXLINE (1088) /* max len of detail line */
+
+/*
+ * The RPC XID cache structure.
+ * When analyzing RPC protocols we
+ * have to cache the xid of the RPC
+ * request together with the program
+ * number, proc, version etc since this
+ * information is missing in the reply
+ * packet. Using the xid in the reply
+ * we can lookup this previously stashed
+ * information in the cache.
+ *
+ * For RPCSEC_GSS flavor, some special processing is
+ * needed for the argument interpretation based on its
+ * control procedure and service type. This information
+ * is stored in the cache table during interpretation of
+ * the rpc header and will be referenced later when the rpc
+ * argument is interpreted.
+ */
+#define XID_CACHE_SIZE 256
+struct cache_struct {
+ int xid_num; /* RPC transaction id */
+ int xid_frame; /* Packet number */
+ int xid_prog; /* RPC program number */
+ int xid_vers; /* RPC version number */
+ int xid_proc; /* RPC procedure number */
+ unsigned int xid_gss_proc; /* control procedure */
+ int xid_gss_service; /* none, integ, priv */
+} xid_cache[XID_CACHE_SIZE];
+
+
+/*
+ * The following macros advance the pointer passed to them. They
+ * assume they are given a char *.
+ */
+#define GETINT8(v, ptr) { \
+ (v) = (*(ptr)++); \
+}
+
+#define GETINT16(v, ptr) { \
+ (v) = *(ptr)++ << 8; \
+ (v) |= *(ptr)++; \
+}
+
+#define GETINT32(v, ptr) { \
+ (v) = *(ptr)++ << 8; \
+ (v) |= *(ptr)++; (v) <<= 8; \
+ (v) |= *(ptr)++; (v) <<= 8; \
+ (v) |= *(ptr)++; \
+}
+
+/*
+ * Used to print nested protocol layers. For example, an ip datagram included
+ * in an icmp error, or a PPP packet included in an LCP protocol reject..
+ */
+extern char *prot_nest_prefix;
+
+extern char *get_sum_line(void);
+extern char *get_detail_line(int, int);
+extern int want_packet(uchar_t *, int, int);
+extern void set_vlan_id(int);
+extern struct timeval prev_time;
+extern void process_pkt(struct sb_hdr *, char *, int, int);
+extern char *getflag(int, int, char *, char *);
+extern void show_header(char *, char *, int);
+extern void show_count(void);
+extern void xdr_init(char *, int);
+extern char *get_line(int, int);
+extern int get_line_remain(void);
+extern char getxdr_char(void);
+extern char showxdr_char(char *);
+extern uchar_t getxdr_u_char(void);
+extern uchar_t showxdr_u_char(char *);
+extern short getxdr_short(void);
+extern short showxdr_short(char *);
+extern ushort_t getxdr_u_short(void);
+extern ushort_t showxdr_u_short(char *);
+extern long getxdr_long(void);
+extern long showxdr_long(char *);
+extern ulong_t getxdr_u_long(void);
+extern ulong_t showxdr_u_long(char *);
+extern longlong_t getxdr_longlong(void);
+extern longlong_t showxdr_longlong(char *);
+extern u_longlong_t getxdr_u_longlong(void);
+extern u_longlong_t showxdr_u_longlong(char *);
+extern char *getxdr_opaque(char *, int);
+extern char *getxdr_string(char *, int);
+extern char *showxdr_string(int, char *);
+extern char *getxdr_bytes(uint_t *);
+extern void xdr_skip(int);
+extern int getxdr_pos(void);
+extern void setxdr_pos(int);
+extern char *getxdr_context(char *, int);
+extern char *showxdr_context(char *);
+extern enum_t getxdr_enum(void);
+extern void show_space(void);
+extern void show_trailer(void);
+extern char *getxdr_date(void);
+extern char *showxdr_date(char *);
+extern char *getxdr_date_ns(void);
+char *format_time(int64_t sec, uint32_t nsec);
+extern char *showxdr_date_ns(char *);
+extern char *getxdr_hex(int);
+extern char *showxdr_hex(int, char *);
+extern bool_t getxdr_bool(void);
+extern bool_t showxdr_bool(char *);
+extern char *concat_args(char **, int);
+extern int pf_compile(char *, int);
+extern void compile(char *, int);
+extern void load_names(char *);
+extern void cap_write(struct sb_hdr *, char *, int, int);
+extern void cap_open_read(const char *);
+extern void cap_open_write(const char *);
+extern void cap_read(int, int, int, void (*)(), int);
+extern void cap_close(void);
+extern boolean_t open_datalink(dlpi_handle_t *, const char *);
+extern void init_datalink(dlpi_handle_t, ulong_t, ulong_t, struct timeval *,
+ struct Pf_ext_packetfilt *);
+extern void net_read(dlpi_handle_t, size_t, int, void (*)(), int);
+extern void click(int);
+extern void show_pktinfo(int, int, char *, char *, struct timeval *,
+ struct timeval *, int, int);
+extern void show_line(char *);
+/*PRINTFLIKE1*/
+extern void show_printf(char *fmt, ...)
+ __PRINTFLIKE(1);
+extern char *getxdr_time(void);
+extern char *showxdr_time(char *);
+extern char *addrtoname(int, const void *);
+extern char *show_string(const char *, int, int);
+extern void pr_err(const char *, ...);
+extern void pr_errdlpi(dlpi_handle_t, const char *, int);
+extern void check_retransmit(char *, ulong_t);
+extern char *nameof_prog(int);
+extern char *getproto(int);
+extern uint8_t print_ipv6_extensions(int, uint8_t **, uint8_t *, int *, int *);
+extern void protoprint(int, int, ulong_t, int, int, int, char *, int);
+extern char *getportname(int, in_port_t);
+
+extern void interpret_arp(int, struct arphdr *, int);
+extern void interpret_bparam(int, int, int, int, int, char *, int);
+extern void interpret_dns(int, int, const uchar_t *, int, int);
+extern void interpret_mount(int, int, int, int, int, char *, int);
+extern void interpret_nfs(int, int, int, int, int, char *, int);
+extern void interpret_nfs3(int, int, int, int, int, char *, int);
+extern void interpret_nfs4(int, int, int, int, int, char *, int);
+extern void interpret_nfs4_cb(int, int, int, int, int, char *, int);
+extern void interpret_nfs_acl(int, int, int, int, int, char *, int);
+extern void interpret_nis(int, int, int, int, int, char *, int);
+extern void interpret_nisbind(int, int, int, int, int, char *, int);
+extern void interpret_nlm(int, int, int, int, int, char *, int);
+extern void interpret_pmap(int, int, int, int, int, char *, int);
+extern int interpret_reserved(int, int, in_port_t, in_port_t, char *, int);
+extern void interpret_rquota(int, int, int, int, int, char *, int);
+extern void interpret_rstat(int, int, int, int, int, char *, int);
+extern void interpret_solarnet_fw(int, int, int, int, int, char *, int);
+extern void interpret_ldap(int, char *, int, int, int);
+extern void interpret_icmp(int, struct icmp *, int, int);
+extern void interpret_icmpv6(int, icmp6_t *, int, int);
+extern int interpret_ip(int, const struct ip *, int);
+extern int interpret_ipv6(int, const ip6_t *, int);
+extern int interpret_ppp(int, uchar_t *, int);
+extern int interpret_pppoe(int, poep_t *, int);
+struct tcphdr;
+extern int interpret_tcp(int, struct tcphdr *, int, int);
+struct udphdr;
+extern int interpret_udp(int, struct udphdr *, int, int);
+extern int interpret_esp(int, uint8_t *, int, int);
+extern int interpret_ah(int, uint8_t *, int, int);
+struct sctp_hdr;
+extern void interpret_sctp(int, struct sctp_hdr *, int, int);
+extern void interpret_mip_cntrlmsg(int, uchar_t *, int);
+struct dhcp;
+extern int interpret_dhcp(int, struct dhcp *, int);
+extern int interpret_dhcpv6(int, const uint8_t *, int);
+struct tftphdr;
+extern int interpret_tftp(int, struct tftphdr *, int);
+extern int interpret_http(int, char *, int);
+struct ntpdata;
+extern int interpret_ntp(int, struct ntpdata *, int);
+extern void interpret_netbios_ns(int, uchar_t *, int);
+extern void interpret_netbios_datagram(int, uchar_t *, int);
+extern void interpret_netbios_ses(int, uchar_t *, int);
+extern void interpret_slp(int, char *, int);
+struct rip;
+extern int interpret_rip(int, struct rip *, int);
+struct rip6;
+extern int interpret_rip6(int, struct rip6 *, int);
+extern int interpret_socks_call(int, char *, int);
+extern int interpret_socks_reply(int, char *, int);
+extern int interpret_trill(int, struct ether_header **, char *, int *);
+extern int interpret_isis(int, char *, int, boolean_t);
+extern int interpret_bpdu(int, char *, int);
+extern void init_ldap(void);
+extern boolean_t arp_for_ether(char *, struct ether_addr *);
+extern char *ether_ouiname(uint32_t);
+extern char *tohex(char *p, int len);
+extern char *printether(struct ether_addr *);
+extern char *print_ethertype(int);
+extern const char *arp_htype(int);
+extern int valid_rpc(char *, int);
+
+/*
+ * Describes characteristics of the Media Access Layer.
+ * The mac_type is one of the supported DLPI media
+ * types (see <sys/dlpi.h>).
+ * The mtu_size is the size of the largest frame.
+ * network_type_offset is where the network type
+ * is located in the link layer header.
+ * The header length is returned by a function to
+ * allow for variable header size - for ethernet it's
+ * just a constant 14 octets.
+ * The interpreter is the function that "knows" how
+ * to interpret the frame.
+ * try_kernel_filter tells snoop to first try a kernel
+ * filter (because the header size is fixed, or if it could
+ * be of variable size where the variable size is easy for a kernel
+ * filter to handle, for example, Ethernet and VLAN tags)
+ * and only use a user space filter if the filter expression
+ * cannot be expressed in kernel space.
+ */
+typedef uint_t (interpreter_fn_t)(int, char *, int, int);
+typedef uint_t (headerlen_fn_t)(char *, size_t);
+typedef struct interface {
+ uint_t mac_type;
+ uint_t mtu_size;
+ uint_t network_type_offset;
+ size_t network_type_len;
+ uint_t network_type_ip;
+ uint_t network_type_ipv6;
+ headerlen_fn_t *header_len;
+ interpreter_fn_t *interpreter;
+ boolean_t try_kernel_filter;
+} interface_t;
+
+extern interface_t INTERFACES[], *interface;
+extern char *dlc_header;
+extern char *src_name, *dst_name;
+extern char *prot_prefix;
+extern char *prot_nest_prefix;
+extern char *prot_title;
+
+/* Keep track of how many nested IP headers we have. */
+extern unsigned int encap_levels, total_encap_levels;
+
+extern int quitting;
+extern boolean_t Iflg, Pflg, rflg;
+
+/*
+ * Global error recovery routine: used to reset snoop variables after
+ * catastrophic failure.
+ */
+void snoop_recover(void);
+
+/*
+ * Global alarm handler structure for managing multiple alarms within
+ * snoop.
+ */
+typedef struct snoop_handler {
+ struct snoop_handler *s_next; /* next alarm handler */
+ time_t s_time; /* time to fire */
+ void (*s_handler)(); /* alarm handler */
+} snoop_handler_t;
+
+#define SNOOP_MAXRECOVER 20 /* maxium number of recoveries */
+#define SNOOP_ALARM_GRAN 3 /* alarm() timeout multiplier */
+
+/*
+ * Global alarm handler management routine.
+ */
+extern int snoop_alarm(int s_sec, void (*s_handler)());
+
+/*
+ * The next two definitions do not take into account the length
+ * of the underlying link header. In order to use them, you must
+ * add link_header_len to them. The reason it is not done here is
+ * that later these macros are used to initialize a table.
+ */
+#define IPV4_TYPE_HEADER_OFFSET 9
+#define IPV6_TYPE_HEADER_OFFSET 6
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SNOOP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aarp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aarp.c
new file mode 100644
index 0000000..32133ee
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aarp.c
@@ -0,0 +1,143 @@
+/*
+ * 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 (c) 1991-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <at.h>
+#include <snoop.h>
+
+static char *printat(uint8_t *);
+
+static char *aarp_opname[] = {
+ "",
+ "AARP Request",
+ "AARP Reply",
+ "AARP Probe",
+};
+
+void
+interpret_aarp(int flags, char *data, int alen)
+{
+ /* LINTED */
+ struct ether_arp *ap = (struct ether_arp *)data;
+
+ extern char *dst_name;
+
+ if (flags & F_SUM) {
+ if (alen < sizeof (struct ether_arp)) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "AARP (short packet)");
+ return;
+ }
+
+ switch (ntohs(ap->arp_op)) {
+ case AARP_REQ:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "AARP C Who is %s ?",
+ printat(ap->arp_tpa));
+ break;
+ case AARP_RESP:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "AARP R %s is %s",
+ printat(ap->arp_spa),
+ printether((struct ether_addr *)&ap->arp_sha));
+ dst_name = printat(ap->arp_tpa);
+ break;
+ case AARP_PROBE:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "AARP Probe %s ?",
+ printat(ap->arp_tpa));
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("AARP: ", "AARP Frame", alen);
+ show_space();
+
+ if (alen < sizeof (struct ether_arp)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "AARP (short packet)");
+ return;
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Hardware type = %d",
+ ntohs(ap->arp_hrd));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Protocol type = %04X (%s)",
+ ntohs(ap->arp_pro),
+ print_ethertype(ntohs(ap->arp_pro)));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length of hardware address = %d bytes",
+ ap->arp_hln);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length of protocol address = %d bytes",
+ ap->arp_pln);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Opcode %d (%s)",
+ ntohs(ap->arp_op),
+ aarp_opname[ntohs(ap->arp_op)]);
+
+ if (ntohs(ap->arp_hrd) == ARPHRD_ETHER &&
+ ntohs(ap->arp_pro) == ETHERTYPE_AT) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Sender's hardware address = %s",
+ printether((struct ether_addr *)&ap->arp_sha));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Sender's protocol address = %s",
+ printat(ap->arp_spa));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Target hardware address = %s",
+ (ntohs(ap->arp_op) == AARP_REQ ||
+ ntohs(ap->arp_op) == AARP_PROBE) ? "?" :
+ printether((struct ether_addr *)&ap->arp_tha));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Target protocol address = %s",
+ ntohs(ap->arp_op) == REVARP_REQUEST ? "?" :
+ printat(ap->arp_tpa));
+ }
+ show_trailer();
+ }
+}
+
+static char *
+printat(uint8_t *p)
+{
+ static char buf[16];
+
+ (void) snprintf(buf, sizeof (buf), "%d.%d", get_short(&p[1]), p[3]);
+ return (buf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_adsp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_adsp.c
new file mode 100644
index 0000000..e771837
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_adsp.c
@@ -0,0 +1,142 @@
+/*
+ * 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 (c) 1991-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <at.h>
+#include <snoop.h>
+
+static char *adsp_ctrl(uint8_t);
+
+void
+interpret_adsp(int flags, struct ddp_adsphdr *adp, int len)
+{
+ struct ddp_adsp_open *apo;
+
+ if (flags & F_SUM) {
+ if (len < sizeof (struct ddp_adsphdr)) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ADSP (short packet)");
+ return;
+ }
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ADSP ConnID=%u (%s)",
+ get_short(adp->ad_connid),
+ adsp_ctrl(adp->ad_desc));
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ADSP: ", "ADSP Header",
+ len - sizeof (struct ddp_adsphdr));
+ show_space();
+
+ if (len < sizeof (struct ddp_adsphdr)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "(short packet)");
+ return;
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "ConnID = %u, ByteSeq = %u, RecvSeq = %u",
+ get_short(adp->ad_connid),
+ get_long(adp->ad_fbseq),
+ get_long(adp->ad_nrseq));
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "RcvWin = %u, Ctrl = 0x%x (%s)",
+ get_short(adp->ad_rcvwin),
+ adp->ad_desc,
+ adsp_ctrl(adp->ad_desc));
+
+ switch (adp->ad_desc) {
+ case AD_CREQ: /* open requests */
+ case AD_CACK:
+ case AD_CREQ_ACK:
+ case AD_CDENY:
+ apo = (struct ddp_adsp_open *)adp;
+ if (len < sizeof (struct ddp_adsp_open)) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "(short packet)");
+ return;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Dest ConnID = %u, AttRcvSeq = %u",
+ get_short(apo->ad_dconnid),
+ get_long(apo->ad_attseq));
+ break;
+ }
+
+ if (adp->ad_desc & AD_ATT) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "AttCode = 0x%x",
+ get_short(((struct ddp_adsp_att *)adp)->
+ ad_att_code));
+ }
+ }
+}
+
+static char *adsp_ctrl_msg[] = {
+ "Probe/Ack",
+ "OpenConnReq",
+ "OpenConnAck",
+ "OpenConnReq+Ack",
+ "OpenConnDeny",
+ "CloseConnAdv",
+ "ForwReset",
+ "ForwReset Ack",
+ "RetransAdv",
+ "9", "10", "11", "12", "13", "14", "15",
+};
+
+static char *
+adsp_ctrl(uint8_t ctrl)
+{
+ static char buf[50];
+ char *p = buf;
+ char *tail = &buf[sizeof (buf)];
+
+ if (ctrl & AD_ACKREQ)
+ p += snprintf(p, tail-p, "AckReq");
+
+ if (ctrl & AD_EOM) {
+ p += snprintf(p, tail-p, p == buf ? "EOM" : " EOM");
+ }
+
+ if (ctrl & AD_ATT) {
+ p += snprintf(p, tail-p, p == buf ? "Att" : " Att");
+ }
+
+ if (ctrl & AD_CTRL) {
+ (void) snprintf(p, tail-p, "%s%s", p == buf ? "" : " ",
+ adsp_ctrl_msg[ctrl & AD_CTRL_MASK]);
+ }
+
+ return (buf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aecho.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aecho.c
new file mode 100644
index 0000000..348f6a9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aecho.c
@@ -0,0 +1,72 @@
+/*
+ * 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 (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <at.h>
+#include <snoop.h>
+
+void
+interpret_aecho(int flags, struct ddp_hdr *ddp, int len)
+{
+ char *data;
+
+ data = (char *)ddp + DDPHDR_SIZE;
+
+ if (flags & F_SUM) {
+ if (len < DDPHDR_SIZE + 1) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "AECHO (short packet)");
+ return;
+ }
+
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "AECHO F=%s LEN=%d",
+ *data == AEP_REQ ? "Request" : "Reply",
+ len);
+ }
+
+ if (flags & F_DTAIL) {
+ if (len < DDPHDR_SIZE + 1) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "AECHO (short packet)");
+ return;
+ }
+
+ show_header("AECHO: ", "AECHO Header", len);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Func = %d (%s)",
+ data[0],
+ data[0] == AEP_REQ ? "Request" : "Reply");
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length = %d", len);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_apple.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_apple.c
new file mode 100644
index 0000000..bf6a6b4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_apple.c
@@ -0,0 +1,210 @@
+/*
+ * 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 <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <at.h>
+#include <snoop.h>
+
+extern char *src_name, *dst_name;
+
+struct socktable {
+ int pt_num;
+ char *pt_short;
+};
+
+static struct socktable pt_ddp[] = {
+ {1, "RTMP"},
+ {2, "NIS"},
+ {4, "Echoer"},
+ {6, "ZIS"},
+ {0, NULL},
+};
+
+static struct socktable pt_ddp_types[] = {
+ {1, "RTMP Resp"},
+ {2, "NBP"},
+ {3, "ATP"},
+ {4, "AEP"},
+ {5, "RTMP Req"},
+ {6, "ZIP"},
+ {7, "ADSP"},
+ {0, NULL},
+};
+
+static char *
+apple_ddp_type(struct socktable *p, uint16_t port)
+{
+ for (; p->pt_num != 0; p++) {
+ if (port == p->pt_num)
+ return (p->pt_short);
+ }
+ return (NULL);
+}
+
+/*
+ * return the short at p, regardless of alignment
+ */
+
+uint16_t
+get_short(uint8_t *p)
+{
+ return (p[0] << 8 | p[1]);
+}
+
+/*
+ * return the long at p, regardless of alignment
+ */
+uint32_t
+get_long(uint8_t *p)
+{
+ return (p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]);
+}
+
+/*
+ * format a MAC address
+ */
+
+char *
+print_macaddr(uint8_t *ha, int len)
+{
+ static char buf[128];
+ char *p = buf;
+
+ while (len-- != 0) {
+ p += snprintf(p, sizeof (buf) - (p - buf),
+ len > 0 ? "%x:" : "%x", *ha++);
+ }
+ return (buf);
+}
+
+/* ARGSUSED */
+void
+interpret_at(int flags, struct ddp_hdr *ddp, int len)
+{
+ int ddplen;
+ char *pname;
+ char buff [32];
+ static char src_buf[16];
+ static char dst_buf[16];
+
+ if (ddp_pad(ddp) != 0)
+ return; /* unknown AppleTalk proto */
+
+ ddplen = ddp_len(ddp);
+
+ (void) snprintf(src_buf, sizeof (src_buf),
+ "%u.%u", ntohs(ddp->ddp_src_net), ddp->ddp_src_id);
+ src_name = src_buf;
+
+ (void) snprintf(dst_buf, sizeof (dst_buf),
+ "%u.%u", ntohs(ddp->ddp_dest_net), ddp->ddp_dest_id);
+ if (ddp->ddp_dest_id == NODE_ID_BROADCAST)
+ dst_name = "(broadcast)";
+ else
+ dst_name = dst_buf;
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "DDP S=%u.%u:%u D=%u.%u:%u LEN=%d",
+ ntohs(ddp->ddp_src_net),
+ ddp->ddp_src_id,
+ ddp->ddp_src_sock,
+ ntohs(ddp->ddp_dest_net),
+ ddp->ddp_dest_id,
+ ddp->ddp_dest_sock,
+ ddp_len(ddp));
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("DDP: ", "DDP Header", ddplen - DDPHDR_SIZE);
+ show_space();
+ pname = apple_ddp_type(pt_ddp, ddp->ddp_src_sock);
+ if (pname == NULL) {
+ pname = "";
+ } else {
+ (void) snprintf(buff, sizeof (buff), "(%s)", pname);
+ pname = buff;
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Source = %s, Socket = %u %s",
+ src_name, ddp->ddp_src_sock, pname);
+ pname = apple_ddp_type(pt_ddp, ddp->ddp_dest_sock);
+ if (pname == NULL) {
+ pname = "";
+ } else {
+ (void) snprintf(buff, sizeof (buff), "(%s)", pname);
+ pname = buff;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Destination = %s, Socket = %u %s",
+ dst_name, ddp->ddp_dest_sock, pname);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Hop count = %d",
+ ddp_hop(ddp));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length = %d",
+ ddp_len(ddp));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Checksum = %04x %s",
+ ntohs(ddp->ddp_cksum),
+ ddp->ddp_cksum == 0 ? "(no checksum)" : "");
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "DDP type = %d (%s)",
+ ddp->ddp_type,
+ apple_ddp_type(pt_ddp_types, ddp->ddp_type));
+ show_space();
+ }
+
+
+ /* go to the next protocol layer */
+
+ switch (ddp->ddp_type) {
+ case DDP_TYPE_NBP:
+ interpret_nbp(flags, (struct nbp_hdr *)ddp, ddplen);
+ break;
+ case DDP_TYPE_AEP:
+ interpret_aecho(flags, ddp, ddplen);
+ break;
+ case DDP_TYPE_ATP:
+ interpret_atp(flags, ddp, ddplen);
+ break;
+ case DDP_TYPE_ZIP:
+ interpret_ddp_zip(flags, (struct zip_hdr *)ddp, ddplen);
+ break;
+ case DDP_TYPE_ADSP:
+ interpret_adsp(flags, (struct ddp_adsphdr *)ddp, ddplen);
+ break;
+ case DDP_TYPE_RTMPRQ:
+ case DDP_TYPE_RTMPRESP:
+ interpret_rtmp(flags, ddp, ddplen);
+ break;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_arp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_arp.c
new file mode 100644
index 0000000..c41507c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_arp.c
@@ -0,0 +1,263 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <netdb.h>
+#include <net/if_types.h>
+
+#include "snoop.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+static char *printip(unsigned char *);
+static char *addrtoname_align(unsigned char *);
+
+static char unarp_addr[] = "Unknown";
+char *opname[] = {
+ "",
+ "ARP Request",
+ "ARP Reply",
+ "REVARP Request",
+ "REVARP Reply",
+};
+
+void
+interpret_arp(int flags, struct arphdr *ap, int alen)
+{
+ char *line;
+ extern char *src_name, *dst_name;
+ unsigned char *sip, *tip, *sha, *tha;
+ char *smacbuf = NULL, *dmacbuf = NULL;
+ int maclen;
+ ushort_t arpop;
+ boolean_t is_ip = B_FALSE;
+
+ /*
+ * Check that at least the generic ARP header was received.
+ */
+ if (sizeof (struct arphdr) > alen)
+ goto short_packet;
+
+ arpop = ntohs(ap->ar_op);
+ maclen = ap->ar_hln;
+ if (ntohs(ap->ar_pro) == ETHERTYPE_IP)
+ is_ip = B_TRUE;
+
+ sha = (unsigned char *)(ap + 1);
+ sip = sha + maclen;
+ tha = sip + ap->ar_pln;
+ tip = tha + maclen;
+
+ /*
+ * Check that the protocol/hardware addresses were received.
+ */
+ if ((tip + ap->ar_pln) > ((unsigned char *)ap + alen))
+ goto short_packet;
+
+ if (maclen == 0) {
+ smacbuf = dmacbuf = unarp_addr;
+ } else {
+ if (((flags & F_DTAIL) && is_ip) || (arpop == ARPOP_REPLY)) {
+ smacbuf = _link_ntoa(sha, NULL, maclen, IFT_OTHER);
+ if (smacbuf == NULL)
+ pr_err("Warning: malloc failure");
+ }
+
+ if (((flags & F_DTAIL) && is_ip) || (arpop ==
+ REVARP_REQUEST) || (arpop == REVARP_REPLY)) {
+ dmacbuf = _link_ntoa(tha, NULL, maclen, IFT_OTHER);
+ if (dmacbuf == NULL)
+ pr_err("Warning: malloc failure");
+ }
+ }
+
+ src_name = addrtoname_align(sip);
+
+ if (flags & F_SUM) {
+
+ line = get_sum_line();
+
+ switch (arpop) {
+ case ARPOP_REQUEST:
+ (void) snprintf(line, MAXLINE, "ARP C Who is %s ?",
+ printip(tip));
+ break;
+ case ARPOP_REPLY:
+ (void) snprintf(line, MAXLINE, "ARP R %s is %s",
+ printip(sip), smacbuf);
+ dst_name = addrtoname_align(tip);
+ break;
+ case REVARP_REQUEST:
+ (void) snprintf(line, MAXLINE, "RARP C Who is %s ?",
+ dmacbuf);
+ break;
+ case REVARP_REPLY:
+ (void) snprintf(line, MAXLINE, "RARP R %s is %s",
+ dmacbuf, printip(tip));
+ dst_name = addrtoname_align(tip);
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ARP: ", "ARP/RARP Frame", alen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Hardware type = %d (%s)", ntohs(ap->ar_hrd),
+ arp_htype(ntohs(ap->ar_hrd)));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Protocol type = %04x (%s)", ntohs(ap->ar_pro),
+ print_ethertype(ntohs(ap->ar_pro)));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length of hardware address = %d bytes", ap->ar_hln);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length of protocol address = %d bytes", ap->ar_pln);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Opcode %d (%s)", arpop,
+ (arpop > REVARP_REPLY) ? opname[0] : opname[arpop]);
+
+ if (is_ip) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Sender's hardware address = %s", smacbuf);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Sender's protocol address = %s",
+ printip(sip));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Target hardware address = %s",
+ arpop == ARPOP_REQUEST ? "?" : dmacbuf);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Target protocol address = %s",
+ arpop == REVARP_REQUEST ? "?" :
+ printip(tip));
+ }
+ show_trailer();
+ }
+
+ if (maclen != 0) {
+ free(smacbuf);
+ free(dmacbuf);
+ }
+ return;
+
+short_packet:
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ARP (short packet)");
+ } else if (flags & F_DTAIL) {
+ show_header("ARP: ", "ARP/RARP Frame", alen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "ARP (short packet)");
+ }
+}
+
+char *
+printip(unsigned char *p)
+{
+ static char buff[MAXHOSTNAMELEN + 32];
+ char *ap, *np;
+ struct in_addr a;
+
+ memcpy(&a, p, 4);
+ ap = (char *)inet_ntoa(a);
+ np = (char *)addrtoname(AF_INET, &a);
+ (void) snprintf(buff, MAXHOSTNAMELEN, "%s, %s", ap, np);
+ return (buff);
+}
+
+char *
+addrtoname_align(unsigned char *p)
+{
+ struct in_addr a;
+
+ memcpy(&a, p, 4);
+ return ((char *)addrtoname(AF_INET, &a));
+}
+
+/*
+ * These numbers are assigned by the IANA. See the arp-parameters registry.
+ * Only those values that are used within Solaris have #defines.
+ */
+const char *
+arp_htype(int t)
+{
+ switch (t) {
+ case ARPHRD_ETHER:
+ return ("Ethernet (10Mb)");
+ case 2:
+ return ("Experimental Ethernet (3MB)");
+ case 3:
+ return ("Amateur Radio AX.25");
+ case 4:
+ return ("Proteon ProNET Token Ring");
+ case 5:
+ return ("Chaos");
+ case ARPHRD_IEEE802:
+ return ("IEEE 802");
+ case 7:
+ return ("ARCNET");
+ case 8:
+ return ("Hyperchannel");
+ case 9:
+ return ("Lanstar");
+ case 10:
+ return ("Autonet");
+ case 11:
+ return ("LocalTalk");
+ case 12:
+ return ("LocalNet");
+ case 13:
+ return ("Ultra Link");
+ case 14:
+ return ("SMDS");
+ case ARPHRD_FRAME:
+ return ("Frame Relay");
+ case ARPHRD_ATM:
+ return ("ATM");
+ case ARPHRD_HDLC:
+ return ("HDLC");
+ case ARPHRD_FC:
+ return ("Fibre Channel");
+ case ARPHRD_IPATM:
+ return ("IP-ATM");
+ case ARPHRD_TUNNEL:
+ return ("Tunnel");
+ case ARPHRD_IB:
+ return ("IPIB");
+ };
+ return ("UNKNOWN");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_atp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_atp.c
new file mode 100644
index 0000000..31808ec
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_atp.c
@@ -0,0 +1,128 @@
+/*
+ * 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 (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <at.h>
+#include <snoop.h>
+
+static char *atp_ci(uint8_t);
+
+static char *atp_trel[8] = {
+ "30s",
+ "1m",
+ "2m",
+ "4m",
+ "8m",
+ "(undef 5)",
+ "(undef 6)",
+ "(undef 7)"
+};
+
+void
+interpret_atp(int flags, struct ddp_hdr *ddp, int len)
+{
+ struct atp_hdr *atp = (struct atp_hdr *)ddp;
+ int atplen = len - (DDPHDR_SIZE + ATPHDR_SIZE);
+
+ if (flags & F_SUM) {
+ if (atplen < 0) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ATP (short packet)");
+ return;
+ }
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ATP (%s), TID=%d, L=%d",
+ atp_ci(atp->atp_ctrl),
+ get_short((uint8_t *)&atp->atp_tid),
+ len);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ATP: ", "ATP Header", 8);
+ show_space();
+
+ if (atplen < 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "ATP (short packet)");
+ return;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length = %d", len);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Ctrl = 0x%x (%s), bitmap/seq = 0x%x",
+ atp->atp_ctrl,
+ atp_ci(atp->atp_ctrl),
+ atp->atp_seq);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "TID = %d, user bytes 0x%x 0x%x 0x%x 0x%x",
+ get_short((uint8_t *)&atp->atp_tid),
+ atp->atp_user[0], atp->atp_user[1],
+ atp->atp_user[2], atp->atp_user[3]);
+ show_space();
+ }
+
+ if (ddp->ddp_dest_sock == DDP_TYPE_ZIP ||
+ ddp->ddp_src_sock == DDP_TYPE_ZIP)
+ interpret_atp_zip(flags, atp, atplen);
+}
+
+static char *
+atp_ci(uint8_t ci)
+{
+ static char buf[50];
+ char *p = buf;
+ char *to = NULL;
+ char *tail = &buf[sizeof (buf)];
+
+ switch (atp_fun(ci)) {
+ case ATP_TREQ:
+ p += snprintf(p, tail-p, "TReq");
+ to = atp_trel[atp_tmo(ci)];
+ break;
+ case ATP_TRESP:
+ p += snprintf(p, tail-p, "TResp");
+ break;
+ case ATP_TREL:
+ p += snprintf(p, tail-p, "TRel");
+ break;
+ }
+
+ p += snprintf(p, tail-p, ci & ATP_FLG_XO ? " XO" : " ALO");
+
+ if (ci & ATP_FLG_EOM)
+ p += snprintf(p, tail-p, " EOM");
+
+ if (ci & ATP_FLG_STS)
+ p += snprintf(p, tail-p, " STS");
+
+ if (to != NULL)
+ (void) snprintf(p, tail-p, " %s", to);
+ return (buf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bparam.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bparam.c
new file mode 100644
index 0000000..5d1be71
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bparam.c
@@ -0,0 +1,220 @@
+/*
+ * 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 (c) 1991, 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include <netinet/in.h>
+#include <rpc/types.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <rpcsvc/bootparam_prot.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+static void show_address(char *);
+static char *sum_address(void);
+
+static char *procnames_short[] = {
+ "Null", /* 0 */
+ "WHOAMI?", /* 1 */
+ "GETFILE", /* 2 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Who am I?", /* 1 */
+ "Get file name", /* 2 */
+};
+
+#define MAXPROC 2
+
+void
+interpret_bparam(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[MAX_PATH_LEN + 1];
+ char buff2[MAX_MACHINE_NAME + 1];
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "BPARAM C %s",
+ procnames_short[proc]);
+ line += strlen(line);
+
+ switch (proc) {
+ case BOOTPARAMPROC_WHOAMI:
+ (void) sprintf(line, " %s",
+ sum_address());
+ break;
+ case BOOTPARAMPROC_GETFILE:
+ (void) getxdr_string(buff,
+ MAX_MACHINE_NAME);
+ (void) sprintf(line, " %s",
+ getxdr_string(buff,
+ MAX_FILEID));
+ break;
+ }
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "BPARAM R %s ",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case BOOTPARAMPROC_WHOAMI:
+ (void) getxdr_string(buff,
+ MAX_MACHINE_NAME);
+ (void) getxdr_string(buff2,
+ MAX_MACHINE_NAME);
+ (void) sprintf(line, "%s in %s",
+ buff, buff2);
+ break;
+ case BOOTPARAMPROC_GETFILE:
+ (void) getxdr_string(buff,
+ MAX_MACHINE_NAME);
+ (void) sum_address();
+ (void) sprintf(line, "File=%s",
+ getxdr_string(buff,
+ MAX_PATH_LEN));
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("BPARAM: ", "Boot Parameters", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+
+ if (type == CALL) {
+ switch (proc) {
+ case BOOTPARAMPROC_WHOAMI:
+ show_address("Client address");
+ break;
+
+ case BOOTPARAMPROC_GETFILE:
+ (void) showxdr_string(MAX_MACHINE_NAME,
+ "Hostname = %s");
+ (void) showxdr_string(MAX_FILEID,
+ "File = %s");
+ break;
+ }
+ } else {
+ switch (proc) {
+ case BOOTPARAMPROC_WHOAMI:
+ (void) showxdr_string(MAX_MACHINE_NAME,
+ "Client name = %s");
+ (void) showxdr_string(MAX_MACHINE_NAME,
+ "Domain name = %s");
+ show_address("Router addr");
+ break;
+
+ case BOOTPARAMPROC_GETFILE:
+ (void) showxdr_string(MAX_MACHINE_NAME,
+ "Server name = %s");
+ show_address("Server addr");
+ (void) showxdr_string(MAX_PATH_LEN,
+ "Server file = %s");
+ break;
+ }
+ }
+
+ show_trailer();
+ }
+}
+
+static char *
+sum_address()
+{
+ struct in_addr host;
+ extern char *inet_ntoa();
+ int atype;
+
+ atype = getxdr_u_long();
+ if (atype != IP_ADDR_TYPE)
+ return ("?");
+
+ host.S_un.S_un_b.s_b1 = getxdr_char();
+ host.S_un.S_un_b.s_b2 = getxdr_char();
+ host.S_un.S_un_b.s_b3 = getxdr_char();
+ host.S_un.S_un_b.s_b4 = getxdr_char();
+
+ return (inet_ntoa(host));
+}
+
+static void
+show_address(label)
+ char *label;
+{
+ struct in_addr host;
+ extern char *inet_ntoa();
+ int atype;
+
+ atype = getxdr_u_long();
+ if (atype == IP_ADDR_TYPE) {
+ host.S_un.S_un_b.s_b1 = getxdr_char();
+ host.S_un.S_un_b.s_b2 = getxdr_char();
+ host.S_un.S_un_b.s_b3 = getxdr_char();
+ host.S_un.S_un_b.s_b4 = getxdr_char();
+
+ (void) sprintf(get_line(0, 0),
+ "%s = %s (%s)",
+ label,
+ inet_ntoa(host),
+ addrtoname(AF_INET, &host));
+ } else {
+ (void) sprintf(get_line(0, 0),
+ "Router addr = ? (type not known)");
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bpdu.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bpdu.c
new file mode 100644
index 0000000..89aa775
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bpdu.c
@@ -0,0 +1,95 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ethernet.h>
+
+#include <snoop.h>
+
+struct conf_bpdu {
+ uchar_t cb_protid[2]; /* Protocol Identifier */
+ uchar_t cb_protvers; /* Protocol Version Identifier */
+ uchar_t cb_type; /* BPDU Type */
+ uchar_t cb_flags; /* BPDU Flags */
+ uchar_t cb_rootid[8]; /* Root Identifier */
+ uchar_t cb_rootcost[4]; /* Root Path Cost */
+ uchar_t cb_bridgeid[8]; /* Bridge Identifier */
+ uchar_t cb_portid[2]; /* Port Identifier */
+ uchar_t cb_messageage[2]; /* Message Age */
+ uchar_t cb_maxage[2]; /* Max Age */
+ uchar_t cb_hello[2]; /* Hello Time */
+ uchar_t cb_fwddelay[2]; /* Forward Delay */
+};
+
+#define BPDU_TYPE_CONF 0
+#define BPDU_TYPE_RCONF 2
+#define BPDU_TYPE_TCNOTIF 0x80
+
+int
+interpret_bpdu(int flags, char *data, int dlen)
+{
+ struct conf_bpdu *cb;
+ const char *pdutype;
+
+ if (dlen < 4) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "BPDU (short packet)");
+ return (0);
+ }
+
+ cb = (struct conf_bpdu *)data;
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "Bridge PDU T:%d L:%d", cb->cb_type, dlen);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("Bridge-PDU: ",
+ "Bridge PDU Frame", dlen);
+ show_space();
+ switch (cb->cb_type) {
+ case BPDU_TYPE_CONF:
+ pdutype = "Configuration";
+ break;
+ case BPDU_TYPE_RCONF:
+ pdutype = "Rapid Configuration";
+ break;
+ case BPDU_TYPE_TCNOTIF:
+ pdutype = "TC Notification";
+ break;
+ default:
+ pdutype = "?";
+ break;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "PDU type = %d (%s)", cb->cb_type, pdutype);
+ show_trailer();
+ }
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c
new file mode 100644
index 0000000..ab6bc29
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c
@@ -0,0 +1,766 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2012 Milan Jurik. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/pfmod.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/bufmod.h>
+
+#include <unistd.h>
+#include <stropts.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <values.h>
+#include <libdlpi.h>
+
+#include "snoop.h"
+
+/*
+ * Old header format.
+ * Actually two concatenated structs: nit_bufhdr + nit_head
+ */
+struct ohdr {
+ /* nit_bufhdr */
+ int o_msglen;
+ int o_totlen;
+ /* nit_head */
+ struct timeval o_time;
+ int o_drops;
+ int o_len;
+};
+
+static void scan(char *, int, int, int, int, void (*)(), int, int, int);
+void convert_to_network();
+void convert_from_network();
+static void convert_old(struct ohdr *);
+extern sigjmp_buf jmp_env, ojmp_env;
+static char *bufp; /* pointer to read buffer */
+
+static int strioctl(int, int, int, int, void *);
+
+enum { DWA_NONE, DWA_EXISTS, DWA_PLUMBED };
+
+typedef struct dlpi_walk_arg {
+ char dwa_linkname[MAXLINKNAMELEN];
+ int dwa_type; /* preference type above */
+ int dwa_s4; /* IPv4 socket */
+ int dwa_s6; /* IPv6 socket */
+} dlpi_walk_arg_t;
+
+static boolean_t
+select_datalink(const char *linkname, void *arg)
+{
+ struct lifreq lifr;
+ dlpi_walk_arg_t *dwap = arg;
+ int s4 = dwap->dwa_s4;
+ int s6 = dwap->dwa_s6;
+
+ (void) strlcpy(dwap->dwa_linkname, linkname, MAXLINKNAMELEN);
+ dwap->dwa_type = DWA_EXISTS;
+
+ /*
+ * See if it's plumbed by IP. We prefer such links because they're
+ * more likely to have interesting traffic.
+ */
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, linkname, LIFNAMSIZ);
+ if ((s4 != -1 && ioctl(s4, SIOCGLIFFLAGS, &lifr) != -1) ||
+ (s6 != -1 && ioctl(s6, SIOCGLIFFLAGS, &lifr) != -1)) {
+ dwap->dwa_type = DWA_PLUMBED;
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Open `linkname' in raw/passive mode (see dlpi_open(3DLPI)). If `linkname'
+ * is NULL, pick a datalink as per snoop(1M). Also gather some information
+ * about the datalink useful for building the proper packet filters.
+ */
+boolean_t
+open_datalink(dlpi_handle_t *dhp, const char *linkname)
+{
+ int retval;
+ int flags = DLPI_PASSIVE | DLPI_RAW;
+ dlpi_walk_arg_t dwa;
+ dlpi_info_t dlinfo;
+
+ if (linkname == NULL) {
+ /*
+ * Select a datalink to use by default. Prefer datalinks that
+ * are plumbed by IP.
+ */
+ bzero(&dwa, sizeof (dwa));
+ dwa.dwa_s4 = socket(AF_INET, SOCK_DGRAM, 0);
+ dwa.dwa_s6 = socket(AF_INET6, SOCK_DGRAM, 0);
+ dlpi_walk(select_datalink, &dwa, 0);
+ (void) close(dwa.dwa_s4);
+ (void) close(dwa.dwa_s6);
+
+ if (dwa.dwa_type == DWA_NONE)
+ pr_err("no datalinks found");
+ if (dwa.dwa_type == DWA_EXISTS) {
+ (void) fprintf(stderr, "snoop: WARNING: "
+ "no datalinks plumbed for IP traffic\n");
+ }
+ linkname = dwa.dwa_linkname;
+ }
+ if (Iflg)
+ flags |= DLPI_DEVIPNET;
+ if (Iflg || strcmp(linkname, "lo0") == 0)
+ flags |= DLPI_IPNETINFO;
+ if ((retval = dlpi_open(linkname, dhp, flags)) != DLPI_SUCCESS) {
+ pr_err("cannot open \"%s\": %s", linkname,
+ dlpi_strerror(retval));
+ }
+
+ if ((retval = dlpi_info(*dhp, &dlinfo, 0)) != DLPI_SUCCESS)
+ pr_errdlpi(*dhp, "dlpi_info failed", retval);
+
+ for (interface = &INTERFACES[0]; interface->mac_type != -1; interface++)
+ if (interface->mac_type == dlinfo.di_mactype)
+ break;
+
+ /* allow limited functionality even if interface isn't known */
+ if (interface->mac_type == -1) {
+ (void) fprintf(stderr, "snoop: WARNING: Mac Type = %x "
+ "not supported\n", dlinfo.di_mactype);
+ }
+
+ return (interface->try_kernel_filter);
+}
+
+/*
+ * Initialize `dh' for packet capture using the provided arguments.
+ */
+void
+init_datalink(dlpi_handle_t dh, ulong_t snaplen, ulong_t chunksize,
+ struct timeval *timeout, struct Pf_ext_packetfilt *fp)
+{
+ int retv;
+ int netfd;
+
+ retv = dlpi_bind(dh, DLPI_ANY_SAP, NULL);
+ if (retv != DLPI_SUCCESS)
+ pr_errdlpi(dh, "cannot bind on", retv);
+
+ if (Iflg) {
+ (void) fprintf(stderr, "Using device ipnet/%s ",
+ dlpi_linkname(dh));
+ } else {
+ (void) fprintf(stderr, "Using device %s ", dlpi_linkname(dh));
+ }
+
+ /*
+ * If Pflg not set - use physical level
+ * promiscuous mode. Otherwise - just SAP level.
+ */
+ if (!Pflg) {
+ (void) fprintf(stderr, "(promiscuous mode)\n");
+ retv = dlpi_promiscon(dh, DL_PROMISC_PHYS);
+ if (retv != DLPI_SUCCESS) {
+ pr_errdlpi(dh, "promiscuous mode(physical) failed",
+ retv);
+ }
+ } else {
+ (void) fprintf(stderr, "(non promiscuous)\n");
+ retv = dlpi_promiscon(dh, DL_PROMISC_MULTI);
+ if (retv != DLPI_SUCCESS) {
+ pr_errdlpi(dh, "promiscuous mode(multicast) failed",
+ retv);
+ }
+ }
+
+ retv = dlpi_promiscon(dh, DL_PROMISC_SAP);
+ if (retv != DLPI_SUCCESS)
+ pr_errdlpi(dh, "promiscuous mode(SAP) failed", retv);
+
+ netfd = dlpi_fd(dh);
+
+ if (fp) {
+ /*
+ * push and configure the packet filtering module
+ */
+ if (ioctl(netfd, I_PUSH, "pfmod") < 0)
+ pr_errdlpi(dh, "cannot push \"pfmod\"", DL_SYSERR);
+
+ if (strioctl(netfd, PFIOCSETF, -1, sizeof (*fp),
+ (char *)fp) < 0)
+ pr_errdlpi(dh, "PFIOCSETF", DL_SYSERR);
+ }
+
+ if (ioctl(netfd, I_PUSH, "bufmod") < 0)
+ pr_errdlpi(dh, "cannot push \"bufmod\"", DL_SYSERR);
+
+ if (strioctl(netfd, SBIOCSTIME, -1, sizeof (struct timeval),
+ (char *)timeout) < 0)
+ pr_errdlpi(dh, "SBIOCSTIME", DL_SYSERR);
+
+ if (strioctl(netfd, SBIOCSCHUNK, -1, sizeof (uint_t),
+ (char *)&chunksize) < 0)
+ pr_errdlpi(dh, "SBIOCGCHUNK", DL_SYSERR);
+
+ if (strioctl(netfd, SBIOCSSNAP, -1, sizeof (uint_t),
+ (char *)&snaplen) < 0)
+ pr_errdlpi(dh, "SBIOCSSNAP", DL_SYSERR);
+
+ /*
+ * Flush the read queue, to get rid of anything that
+ * accumulated before the device reached its final configuration.
+ */
+ if (ioctl(netfd, I_FLUSH, FLUSHR) < 0)
+ pr_errdlpi(dh, "cannot flush \"I_FLUSH\"", DL_SYSERR);
+}
+
+/*
+ * Read packets from the network. init_datalink() is called in
+ * here to set up the network interface for reading of
+ * raw ethernet packets in promiscuous mode into a buffer.
+ * Packets are read and either written directly to a file
+ * or interpreted for display on the fly.
+ */
+void
+net_read(dlpi_handle_t dh, size_t chunksize, int filter, void (*proc)(),
+ int flags)
+{
+ int retval;
+ extern int count;
+ size_t msglen;
+
+ count = 0;
+
+ /* allocate a read buffer */
+ bufp = malloc(chunksize);
+ if (bufp == NULL)
+ pr_err("no memory for %d buffer", chunksize);
+
+ /*
+ * read frames
+ */
+ for (;;) {
+ msglen = chunksize;
+ retval = dlpi_recv(dh, NULL, NULL, bufp, &msglen, -1, NULL);
+
+ if (retval != DLPI_SUCCESS || quitting)
+ break;
+
+ if (msglen != 0)
+ scan(bufp, msglen, filter, 0, 0, proc, 0, 0, flags);
+ }
+
+ free(bufp);
+
+ if (!quitting)
+ pr_errdlpi(dh, "network read failed", retval);
+}
+
+#ifdef DEBUG
+/*
+ * corrupt: simulate packet corruption for debugging interpreters
+ */
+void
+corrupt(volatile char *pktp, volatile char *pstop, char *buf,
+ volatile char *bufstop)
+{
+ int c;
+ int i;
+ int p;
+ int li = rand() % (pstop - pktp - 1) + 1;
+ volatile char *pp = pktp;
+ volatile char *pe = bufstop < pstop ? bufstop : pstop;
+
+ if (pktp < buf || pktp > bufstop)
+ return;
+
+ for (pp = pktp; pp < pe; pp += li) {
+ c = ((pe - pp) < li ? pe - pp : li);
+ i = (rand() % c)>>1;
+ while (--i > 0) {
+ p = (rand() % c);
+ pp[p] = (unsigned char)(rand() & 0xFF);
+ }
+ }
+}
+#endif /* DEBUG */
+
+static void
+scan(char *buf, int len, int filter, int cap, int old, void (*proc)(),
+ int first, int last, int flags)
+{
+ volatile char *bp, *bufstop;
+ volatile struct sb_hdr *hdrp;
+ volatile struct sb_hdr nhdr, *nhdrp;
+ volatile char *pktp;
+ volatile struct timeval last_timestamp;
+ volatile int header_okay;
+ extern int count, maxcount;
+ extern int snoop_nrecover;
+#ifdef DEBUG
+ extern int zflg;
+#endif /* DEBUG */
+
+ proc(0, 0, 0);
+ bufstop = buf + len;
+
+ /*
+ *
+ * Loop through each packet in the buffer
+ */
+ last_timestamp.tv_sec = 0;
+ (void) memcpy((char *)ojmp_env, (char *)jmp_env, sizeof (jmp_env));
+ for (bp = buf; bp < bufstop; bp += nhdrp->sbh_totlen) {
+ /*
+ * Gracefully exit if user terminates
+ */
+ if (quitting)
+ break;
+ /*
+ * Global error recocery: Prepare to continue when a corrupt
+ * packet or header is encountered.
+ */
+ if (sigsetjmp(jmp_env, 1)) {
+ goto err;
+ }
+
+ header_okay = 0;
+ hdrp = (struct sb_hdr *)bp;
+ nhdrp = hdrp;
+ pktp = (char *)hdrp + sizeof (*hdrp);
+
+ /*
+ * If reading a capture file
+ * convert the headers from network
+ * byte order (for little-endians like X86)
+ */
+ if (cap) {
+ /*
+ * If the packets come from an old
+ * capture file, convert the header.
+ */
+ if (old) {
+ convert_old((struct ohdr *)hdrp);
+ }
+
+ nhdrp = &nhdr;
+
+ nhdrp->sbh_origlen = ntohl(hdrp->sbh_origlen);
+ nhdrp->sbh_msglen = ntohl(hdrp->sbh_msglen);
+ nhdrp->sbh_totlen = ntohl(hdrp->sbh_totlen);
+ nhdrp->sbh_drops = ntohl(hdrp->sbh_drops);
+ nhdrp->sbh_timestamp.tv_sec =
+ ntohl(hdrp->sbh_timestamp.tv_sec);
+ nhdrp->sbh_timestamp.tv_usec =
+ ntohl(hdrp->sbh_timestamp.tv_usec);
+ }
+
+ /* Enhanced check for valid header */
+
+ if ((nhdrp->sbh_totlen == 0) ||
+ (bp + nhdrp->sbh_totlen) < bp ||
+ (bp + nhdrp->sbh_totlen) > bufstop ||
+ (nhdrp->sbh_origlen == 0) ||
+ (bp + nhdrp->sbh_origlen) < bp ||
+ (nhdrp->sbh_msglen == 0) ||
+ (bp + nhdrp->sbh_msglen) < bp ||
+ (bp + nhdrp->sbh_msglen) > bufstop ||
+ (nhdrp->sbh_msglen > nhdrp->sbh_origlen) ||
+ (nhdrp->sbh_totlen < nhdrp->sbh_msglen) ||
+ (nhdrp->sbh_timestamp.tv_sec == 0)) {
+ if (cap) {
+ (void) fprintf(stderr, "(warning) bad packet "
+ "header in capture file");
+ } else {
+ (void) fprintf(stderr, "(warning) bad packet "
+ "header in buffer");
+ }
+ (void) fprintf(stderr, " offset %d: length=%d\n",
+ bp - buf, nhdrp->sbh_totlen);
+ goto err;
+ }
+
+ /*
+ * Check for incomplete packet. We are conservative here,
+ * since we don't know how good the checking is in other
+ * parts of the code. We pass a partial packet, with
+ * a warning.
+ */
+ if (pktp + nhdrp->sbh_msglen > bufstop) {
+ (void) fprintf(stderr, "truncated packet buffer\n");
+ nhdrp->sbh_msglen = bufstop - pktp;
+ }
+
+#ifdef DEBUG
+ if (zflg)
+ corrupt(pktp, pktp + nhdrp->sbh_msglen, buf, bufstop);
+#endif /* DEBUG */
+
+ header_okay = 1;
+ if (!filter ||
+ want_packet((uchar_t *)pktp,
+ nhdrp->sbh_msglen,
+ nhdrp->sbh_origlen)) {
+ count++;
+
+ /*
+ * Start deadman timer for interpreter processing
+ */
+ (void) snoop_alarm(SNOOP_ALARM_GRAN*SNOOP_MAXRECOVER,
+ NULL);
+
+ encap_levels = 0;
+ if (!cap || count >= first)
+ proc(nhdrp, pktp, count, flags);
+
+ if (cap && count >= last) {
+ (void) snoop_alarm(0, NULL);
+ break;
+ }
+
+ if (maxcount && count >= maxcount) {
+ (void) fprintf(stderr, "%d packets captured\n",
+ count);
+ exit(0);
+ }
+
+ snoop_nrecover = 0; /* success */
+ (void) snoop_alarm(0, NULL);
+ last_timestamp = hdrp->sbh_timestamp; /* save stamp */
+ }
+ continue;
+err:
+ /*
+ * Corruption has been detected. Reset errors.
+ */
+ snoop_recover();
+
+ /*
+ * packet header was apparently okay. Continue.
+ */
+ if (header_okay)
+ continue;
+
+ /*
+ * Otherwise try to scan forward to the next packet, using
+ * the last known timestamp if it is available.
+ */
+ nhdrp = &nhdr;
+ nhdrp->sbh_totlen = 0;
+ if (last_timestamp.tv_sec == 0) {
+ bp += sizeof (int);
+ } else {
+ for (bp += sizeof (int); bp <= bufstop;
+ bp += sizeof (int)) {
+ hdrp = (struct sb_hdr *)bp;
+ /* An approximate timestamp located */
+ if ((hdrp->sbh_timestamp.tv_sec >> 8) ==
+ (last_timestamp.tv_sec >> 8))
+ break;
+ }
+ }
+ }
+ /* reset jmp_env for program exit */
+ (void) memcpy((char *)jmp_env, (char *)ojmp_env, sizeof (jmp_env));
+ proc(0, -1, 0);
+}
+
+/*
+ * Called if nwrite() encounters write problems.
+ */
+static void
+cap_write_error(const char *msgtype)
+{
+ (void) fprintf(stderr,
+ "snoop: cannot write %s to capture file: %s\n",
+ msgtype, strerror(errno));
+ exit(1);
+}
+
+/*
+ * Writes target buffer to the open file descriptor. Upon detection of a short
+ * write, an attempt to process the remaining bytes occurs until all anticipated
+ * bytes are written. An error status is returned to indicate any serious write
+ * failures.
+ */
+static int
+nwrite(int fd, const void *buffer, size_t buflen)
+{
+ size_t nwritten;
+ ssize_t nbytes = 0;
+ const char *buf = buffer;
+
+ for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
+ nbytes = write(fd, &buf[nwritten], buflen - nwritten);
+ if (nbytes == -1)
+ return (-1);
+ if (nbytes == 0) {
+ errno = EIO;
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Routines for opening, closing, reading and writing
+ * a capture file of packets saved with the -o option.
+ */
+static int capfile_out;
+
+/*
+ * The snoop capture file has a header to identify
+ * it as a capture file and record its version.
+ * A file without this header is assumed to be an
+ * old format snoop file.
+ *
+ * A version 1 header looks like this:
+ *
+ * 0 1 2 3 4 5 6 7 8 9 10 11
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | s | n | o | o | p | \0| \0| \0| version | data
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | word 0 | word 1 | word 2 |
+ *
+ *
+ * A version 2 header adds a word that identifies the MAC type.
+ * This allows for capture files from FDDI etc.
+ *
+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | s | n | o | o | p | \0| \0| \0| version | MAC type | data
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | word 0 | word 1 | word 2 | word 3
+ *
+ */
+static const char *snoop_id = "snoop\0\0\0";
+static const int snoop_idlen = 8;
+static const int snoop_version = 2;
+
+void
+cap_open_write(const char *name)
+{
+ int vers;
+
+ capfile_out = open(name, O_CREAT | O_TRUNC | O_RDWR, 0666);
+ if (capfile_out < 0)
+ pr_err("%s: %m", name);
+
+ vers = htonl(snoop_version);
+ if (nwrite(capfile_out, snoop_id, snoop_idlen) == -1)
+ cap_write_error("snoop_id");
+
+ if (nwrite(capfile_out, &vers, sizeof (int)) == -1)
+ cap_write_error("version");
+}
+
+
+void
+cap_close(void)
+{
+ (void) close(capfile_out);
+}
+
+static char *cap_buffp = NULL;
+static int cap_len = 0;
+static int cap_new;
+
+void
+cap_open_read(const char *name)
+{
+ struct stat st;
+ int cap_vers;
+ int *word;
+ int device_mac_type = -1;
+ int capfile_in;
+
+ capfile_in = open(name, O_RDONLY);
+ if (capfile_in < 0)
+ pr_err("couldn't open %s: %m", name);
+
+ if (fstat(capfile_in, &st) < 0)
+ pr_err("couldn't stat %s: %m", name);
+ cap_len = st.st_size;
+
+ cap_buffp = mmap(0, cap_len, PROT_READ, MAP_PRIVATE, capfile_in, 0);
+ (void) close(capfile_in);
+ if ((int)cap_buffp == -1)
+ pr_err("couldn't mmap %s: %m", name);
+
+ /* Check if new snoop capture file format */
+
+ cap_new = bcmp(cap_buffp, snoop_id, snoop_idlen) == 0;
+
+ /*
+ * If new file - check version and
+ * set buffer pointer to point at first packet
+ */
+ if (cap_new) {
+ cap_vers = ntohl(*(int *)(cap_buffp + snoop_idlen));
+ cap_buffp += snoop_idlen + sizeof (int);
+ cap_len -= snoop_idlen + sizeof (int);
+
+ switch (cap_vers) {
+ case 1:
+ device_mac_type = DL_ETHER;
+ break;
+
+ case 2:
+ device_mac_type = ntohl(*((int *)cap_buffp));
+ cap_buffp += sizeof (int);
+ cap_len -= sizeof (int);
+ break;
+
+ default:
+ pr_err("capture file: %s: Version %d unrecognized\n",
+ name, cap_vers);
+ }
+
+ for (interface = &INTERFACES[0]; interface->mac_type != -1;
+ interface++)
+ if (interface->mac_type == device_mac_type)
+ break;
+
+ if (interface->mac_type == -1)
+ pr_err("Mac Type = %x is not supported\n",
+ device_mac_type);
+ } else {
+ /* Use heuristic to check if it's an old-style file */
+
+ device_mac_type = DL_ETHER;
+ word = (int *)cap_buffp;
+
+ if (!((word[0] < 1600 && word[1] < 1600) &&
+ (word[0] < word[1]) &&
+ (word[2] > 610000000 && word[2] < 770000000)))
+ pr_err("not a capture file: %s", name);
+
+ /* Change protection so's we can fix the headers */
+
+ if (mprotect(cap_buffp, cap_len, PROT_READ | PROT_WRITE) < 0)
+ pr_err("mprotect: %s: %m", name);
+ }
+}
+
+void
+cap_read(int first, int last, int filter, void (*proc)(), int flags)
+{
+ extern int count;
+
+ count = 0;
+
+ scan(cap_buffp, cap_len, filter, 1, !cap_new, proc, first, last, flags);
+
+ (void) munmap(cap_buffp, cap_len);
+}
+
+/* ARGSUSED */
+void
+cap_write(struct sb_hdr *hdrp, char *pktp, int num, int flags)
+{
+ int pktlen, mac;
+ static int first = 1;
+ struct sb_hdr nhdr;
+ extern boolean_t qflg;
+
+ if (hdrp == NULL)
+ return;
+
+ if (first) {
+ first = 0;
+ mac = htonl(interface->mac_type);
+ if (nwrite(capfile_out, &mac, sizeof (int)) == -1)
+ cap_write_error("mac_type");
+ }
+
+ pktlen = hdrp->sbh_totlen - sizeof (*hdrp);
+
+ /*
+ * Convert sb_hdr to network byte order
+ */
+ nhdr.sbh_origlen = htonl(hdrp->sbh_origlen);
+ nhdr.sbh_msglen = htonl(hdrp->sbh_msglen);
+ nhdr.sbh_totlen = htonl(hdrp->sbh_totlen);
+ nhdr.sbh_drops = htonl(hdrp->sbh_drops);
+ nhdr.sbh_timestamp.tv_sec = htonl(hdrp->sbh_timestamp.tv_sec);
+ nhdr.sbh_timestamp.tv_usec = htonl(hdrp->sbh_timestamp.tv_usec);
+
+ if (nwrite(capfile_out, &nhdr, sizeof (nhdr)) == -1)
+ cap_write_error("packet header");
+
+ if (nwrite(capfile_out, pktp, pktlen) == -1)
+ cap_write_error("packet");
+
+ if (! qflg)
+ show_count();
+}
+
+/*
+ * Convert a packet header from
+ * old to new format.
+ */
+static void
+convert_old(struct ohdr *ohdrp)
+{
+ struct sb_hdr nhdr;
+
+ nhdr.sbh_origlen = ohdrp->o_len;
+ nhdr.sbh_msglen = ohdrp->o_msglen;
+ nhdr.sbh_totlen = ohdrp->o_totlen;
+ nhdr.sbh_drops = ohdrp->o_drops;
+ nhdr.sbh_timestamp = ohdrp->o_time;
+
+ *(struct sb_hdr *)ohdrp = nhdr;
+}
+
+static int
+strioctl(int fd, int cmd, int timout, int len, void *dp)
+{
+ struct strioctl sioc;
+ int rc;
+
+ sioc.ic_cmd = cmd;
+ sioc.ic_timout = timout;
+ sioc.ic_len = len;
+ sioc.ic_dp = dp;
+ rc = ioctl(fd, I_STR, &sioc);
+
+ if (rc < 0)
+ return (rc);
+ else
+ return (sioc.ic_len);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcp.c
new file mode 100644
index 0000000..7857da7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcp.c
@@ -0,0 +1,746 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <netinet/in.h>
+#include <netinet/dhcp.h>
+#include <arpa/inet.h>
+#include <dhcp_inittab.h>
+#include <dhcp_symbol.h>
+#include "snoop.h"
+
+static const char *show_msgtype(unsigned char);
+static int show_options(unsigned char *, int);
+static void display_ip(int, char *, char *, unsigned char **);
+static void display_ascii(char *, char *, unsigned char **);
+static void display_number(char *, char *, unsigned char **);
+static void display_ascii_hex(char *, unsigned char **);
+static unsigned char bootmagic[] = BOOTMAGIC; /* rfc 1048 */
+
+static char *option_types[] = {
+"", /* 0 */
+"Subnet Mask", /* 1 */
+"UTC Time Offset", /* 2 */
+"Router", /* 3 */
+"RFC868 Time Servers", /* 4 */
+"IEN 116 Name Servers", /* 5 */
+"DNS Servers", /* 6 */
+"UDP LOG Servers", /* 7 */
+"RFC 865 Cookie Servers", /* 8 */
+"RFC 1179 Line Printer Servers (LPR)", /* 9 */
+"Impress Servers", /* 10 */
+"RFC 887 Resource Location Servers", /* 11 */
+"Client Hostname", /* 12 */
+"Boot File size in 512 byte Blocks", /* 13 */
+"Merit Dump File", /* 14 */
+"DNS Domain Name", /* 15 */
+"SWAP Server", /* 16 */
+"Client Root Path", /* 17 */
+"BOOTP options extensions path", /* 18 */
+"IP Forwarding Flag", /* 19 */
+"NonLocal Source Routing Flag", /* 20 */
+"Policy Filters for NonLocal Routing", /* 21 */
+"Maximum Datagram Reassembly Size", /* 22 */
+"Default IP Time To Live", /* 23 */
+"Path MTU Aging Timeout", /* 24 */
+"Path MTU Size Plateau Table", /* 25 */
+"Interface MTU Size", /* 26 */
+"All Subnets are Local Flag", /* 27 */
+"Broadcast Address", /* 28 */
+"Perform Mask Discovery Flag", /* 29 */
+"Mask Supplier Flag", /* 30 */
+"Perform Router Discovery Flag", /* 31 */
+"Router Solicitation Address", /* 32 */
+"Static Routes", /* 33 */
+"Trailer Encapsulation Flag", /* 34 */
+"ARP Cache Timeout Seconds", /* 35 */
+"Ethernet Encapsulation Flag", /* 36 */
+"TCP Default Time To Live", /* 37 */
+"TCP Keepalive Interval Seconds", /* 38 */
+"TCP Keepalive Garbage Flag", /* 39 */
+"NIS Domainname", /* 40 */
+"NIS Servers", /* 41 */
+"Network Time Protocol Servers", /* 42 */
+"Vendor Specific Options", /* 43 */
+"NetBIOS RFC 1001/1002 Name Servers", /* 44 */
+"NetBIOS Datagram Dist. Servers", /* 45 */
+"NetBIOS Node Type", /* 46 */
+"NetBIOS Scope", /* 47 */
+"X Window Font Servers", /* 48 */
+"X Window Display Manager Servers", /* 49 */
+"Requested IP Address", /* 50 */
+"IP Address Lease Time", /* 51 */
+"Option Field Overload Flag", /* 52 */
+"DHCP Message Type", /* 53 */
+"DHCP Server Identifier", /* 54 */
+"Option Request List", /* 55 */
+"Error Message", /* 56 */
+"Maximum DHCP Message Size", /* 57 */
+"Renewal (T1) Time Value", /* 58 */
+"Rebinding (T2) Time Value", /* 59 */
+"Client Class Identifier =", /* 60 */
+"Client Identifier =", /* 61 */
+"Netware IP Domain =", /* 62 */
+"Netware IP Options =", /* 63 */
+"TFTP Server Name", /* 66 */
+"Option BootFile Name", /* 67 */
+"Mobile IP Agents", /* 68 */
+"Simple Mail (SMTP) Servers", /* 69 */
+"Post Office (POP3) Servers", /* 70 */
+"Net News (NNTP) Servers", /* 71 */
+"WorldWideWeb Servers", /* 72 */
+"Finger Servers", /* 73 */
+"Internet Relay Chat (IRC) Servers", /* 74 */
+"StreetTalk Servers", /* 75 */
+"StreetTalk Directory Assist. Servers", /* 76 */
+"User Class Identifier", /* 77 */
+};
+
+#define OPTIONS_ARRAY_SIZE 78
+
+int
+interpret_dhcp(int flags, struct dhcp *dp, int len)
+{
+ if (flags & F_SUM) {
+ if ((memcmp(dp->cookie, bootmagic, sizeof (bootmagic)) == 0) &&
+ (len >= BASE_PKT_SIZE + 3) &&
+ dp->options[0] == CD_DHCP_TYPE) {
+ (void) sprintf(get_sum_line(),
+ "DHCP/BOOTP %s", show_msgtype(dp->options[2]));
+ } else {
+ switch (ntohs(dp->op)) {
+ case BOOTREQUEST:
+ (void) sprintf(get_sum_line(),
+ "DHCP/BOOTP BOOTREQUEST");
+ break;
+ case BOOTREPLY:
+ (void) sprintf(get_sum_line(),
+ "DHCP/BOOTP BOOTREPLY");
+ break;
+ }
+ }
+ }
+ if (flags & F_DTAIL) {
+ show_header("DHCP: ", "Dynamic Host Configuration Protocol",
+ len);
+ show_space();
+ (void) sprintf(get_line((char *)(uintptr_t)dp->htype -
+ dlc_header, 1),
+ "Hardware address type (htype) = %d (%s)", dp->htype,
+ arp_htype(dp->htype));
+ (void) sprintf(get_line((char *)(uintptr_t)dp->hlen -
+ dlc_header, 1),
+ "Hardware address length (hlen) = %d octets", dp->hlen);
+ (void) sprintf(get_line((char *)(uintptr_t)dp->hops -
+ dlc_header, 1),
+ "Relay agent hops = %d", dp->hops);
+ (void) sprintf(get_line((char *)(uintptr_t)dp->xid -
+ dlc_header, 4),
+ "Transaction ID = 0x%x", ntohl(dp->xid));
+ (void) sprintf(get_line((char *)(uintptr_t)dp->secs -
+ dlc_header, 2),
+ "Time since boot = %d seconds", ntohs(dp->secs));
+ (void) sprintf(get_line((char *)(uintptr_t)dp->flags -
+ dlc_header, 2),
+ "Flags = 0x%.4x", ntohs(dp->flags));
+ (void) sprintf(get_line((char *)&dp->ciaddr - dlc_header, 4),
+ "Client address (ciaddr) = %s", inet_ntoa(dp->ciaddr));
+ (void) sprintf(get_line((char *)&dp->yiaddr - dlc_header, 4),
+ "Your client address (yiaddr) = %s",
+ inet_ntoa(dp->yiaddr));
+ (void) sprintf(get_line((char *)&dp->siaddr - dlc_header, 4),
+ "Next server address (siaddr) = %s",
+ inet_ntoa(dp->siaddr));
+ (void) sprintf(get_line((char *)&dp->giaddr - dlc_header, 4),
+ "Relay agent address (giaddr) = %s",
+ inet_ntoa(dp->giaddr));
+ if (dp->htype == 1) {
+ (void) sprintf(get_line((char *)dp->chaddr -
+ dlc_header, dp->hlen),
+ "Client hardware address (chaddr) = %.2X:%.2X:%.2X:%.2X:%.2X:%.2X",
+ dp->chaddr[0],
+ dp->chaddr[1],
+ dp->chaddr[2],
+ dp->chaddr[3],
+ dp->chaddr[4],
+ dp->chaddr[5]);
+ }
+ /*
+ * Check cookie, process options
+ */
+ if (memcmp(dp->cookie, bootmagic, sizeof (bootmagic)) != 0) {
+ (void) sprintf(get_line(0, 0),
+ "Unrecognized cookie: 0x%.2X%.2X%.2X%.2X\n",
+ dp->cookie[0],
+ dp->cookie[1],
+ dp->cookie[2],
+ dp->cookie[3]);
+ return (0);
+ }
+ show_space();
+ show_header("DHCP: ", "(Options) field options", len);
+ show_space();
+ switch (show_options(dp->options, (len - BASE_PKT_SIZE))) {
+ case 0:
+ /* No option overloading */
+ if (*(unsigned char *)(dp->sname) != '\0') {
+ (void) sprintf(get_line(0, 0),
+ "Server Name = %s", dp->sname);
+ }
+ if (*(unsigned char *)(dp->file) != '\0') {
+ (void) sprintf(get_line(0, 0),
+ "Boot File Name = %s", dp->file);
+ }
+ break;
+ case 1:
+ /* file field used */
+ if (*(unsigned char *)(dp->sname) != '\0') {
+ (void) sprintf(get_line(0, 0),
+ "Server Name = %s", dp->sname);
+ }
+ show_space();
+ show_header("DHCP: ", "(File) field options", len);
+ show_space();
+ (void) show_options(dp->file, 128);
+ break;
+ case 2:
+ /* sname field used for options */
+ if (*(unsigned char *)(dp->file) != '\0') {
+ (void) sprintf(get_line(0, 0),
+ "Boot File Name = %s", dp->file);
+ }
+ show_space();
+ show_header("DHCP: ", "(Sname) field options", len);
+ show_space();
+ (void) show_options(dp->sname, 64);
+ break;
+ case 3:
+ show_space();
+ show_header("DHCP: ", "(File) field options", len);
+ show_space();
+ (void) show_options(dp->file, 128);
+ show_space();
+ show_header("DHCP: ", "(Sname) field options", len);
+ show_space();
+ (void) show_options(dp->sname, 64);
+ break;
+ };
+ }
+ return (len);
+}
+
+static int
+show_options(unsigned char *cp, int len)
+{
+ char *prmpt;
+ unsigned char *end, *vend;
+ unsigned char *start, save;
+ int items, i;
+ int nooverload = 0;
+ ushort_t s_buf;
+ struct in_addr tmp;
+ char scratch[128];
+ dhcp_symbol_t *entry;
+ char *decoded_opt;
+ int opt_len;
+
+ start = cp;
+ end = (unsigned char *)cp + len;
+
+ while (start < end) {
+ if (*start == CD_PAD) {
+ start++;
+ continue;
+ }
+ if (*start == CD_END)
+ break; /* done */
+
+ save = *start++;
+ switch (save) {
+ /* Network order IP address(es) */
+ case CD_SUBNETMASK:
+ case CD_ROUTER_SOLICIT_SERV:
+ case CD_BROADCASTADDR:
+ case CD_REQUESTED_IP_ADDR:
+ case CD_SERVER_ID:
+ /* Single IP address */
+ if (*start != 4) {
+ (void) sprintf(get_line(0, 0),
+ "Error: Bad %s", option_types[save]);
+ } else {
+ start++;
+ display_ip(1, "%s = %s", option_types[save],
+ &start);
+ }
+ break;
+ case CD_ROUTER:
+ case CD_TIMESERV:
+ case CD_IEN116_NAME_SERV:
+ case CD_DNSSERV:
+ case CD_LOG_SERV:
+ case CD_COOKIE_SERV:
+ case CD_LPR_SERV:
+ case CD_IMPRESS_SERV:
+ case CD_RESOURCE_SERV:
+ case CD_SWAP_SERV:
+ case CD_NIS_SERV:
+ case CD_NTP_SERV:
+ case CD_NETBIOS_NAME_SERV:
+ case CD_NETBIOS_DIST_SERV:
+ case CD_XWIN_FONT_SERV:
+ case CD_XWIN_DISP_SERV:
+ case CD_MOBILE_IP_AGENT:
+ case CD_SMTP_SERVS:
+ case CD_POP3_SERVS:
+ case CD_NNTP_SERVS:
+ case CD_WWW_SERVS:
+ case CD_FINGER_SERVS:
+ case CD_IRC_SERVS:
+ case CD_STREETTALK_SERVS:
+ case CD_STREETTALK_DA_SERVS:
+ /* Multiple IP addresses */
+ if ((*start % 4) != 0) {
+ (void) sprintf(get_line(0, 0),
+ "Error: Bad %s address",
+ option_types[save]);
+ } else {
+ items = *start++ / 4;
+ display_ip(items, "%s at = %s",
+ option_types[save], &start);
+ }
+ break;
+ case CD_TFTP_SERV_NAME:
+ case CD_HOSTNAME:
+ case CD_DUMP_FILE:
+ case CD_DNSDOMAIN:
+ case CD_ROOT_PATH:
+ case CD_NIS_DOMAIN:
+ case CD_NETBIOS_SCOPE:
+ case CD_MESSAGE:
+ case CD_OPT_BOOTFILE_NAME:
+ case CD_USER_CLASS_ID:
+ /* Ascii strings */
+ display_ascii("%s = %s", option_types[save], &start);
+ break;
+ case CD_TIMEOFFSET:
+ case CD_IPTTL:
+ case CD_PATH_MTU_TIMEOUT:
+ case CD_ARP_TIMEOUT:
+ case CD_TCP_TTL:
+ case CD_TCP_KALIVE_INTVL:
+ case CD_T1_TIME:
+ case CD_T2_TIME:
+ case CD_LEASE_TIME:
+ /* Number: seconds */
+ display_number("%s = %d seconds", option_types[save],
+ &start);
+ break;
+ case CD_IP_FORWARDING_ON:
+ case CD_NON_LCL_ROUTE_ON:
+ case CD_ALL_SUBNETS_LCL_ON:
+ case CD_MASK_DISCVRY_ON:
+ case CD_MASK_SUPPLIER_ON:
+ case CD_ROUTER_DISCVRY_ON:
+ case CD_TRAILER_ENCAPS_ON:
+ case CD_ETHERNET_ENCAPS_ON:
+ case CD_TCP_KALIVE_GRBG_ON:
+ /* Number: hex flag */
+ display_number("%s flag = 0x%x", option_types[save],
+ &start);
+ break;
+ case CD_MAXIPSIZE:
+ case CD_MTU:
+ case CD_MAX_DHCP_SIZE:
+ /* Number: bytes */
+ display_number("%s = %d bytes", option_types[save],
+ &start);
+ break;
+ case CD_CLASS_ID:
+ case CD_CLIENT_ID:
+ case CD_NW_IP_DOMAIN:
+ case CD_NW_IP_OPTIONS:
+ /* Hex ascii strings */
+ display_ascii_hex(option_types[save], &start);
+ break;
+ case CD_BOOT_SIZE:
+ display_number("%s = %d 512 byte blocks",
+ "Boot file size", &start);
+ break;
+ case CD_POLICY_FILTER:
+ if ((*start % 8) != 0) {
+ (void) sprintf(get_line(0, 0),
+ "Error: Bad Policy Filter option");
+ } else {
+ items = *start++ / 8;
+ for (i = 0; i < items; i++) {
+ display_ip(1,
+ "%s = %s",
+ "Policy Destination",
+ &start);
+ display_ip(1, "%s = %s", "Mask",
+ &start);
+ }
+ }
+ break;
+ case CD_PATH_MTU_TABLE_SZ:
+ if (*start % 2 != 0) {
+ (void) sprintf(get_line(0, 0),
+ "Error: Bad Path MTU Table");
+ } else {
+ (void) sprintf(get_line(0, 0),
+ "\tPath MTU Plateau Table:");
+ (void) sprintf(get_line(0, 0),
+ "\t=======================");
+ items = *start / sizeof (ushort_t);
+ ++start;
+ for (i = 0; i < items; i++) {
+ if (IS_P2ALIGNED(start,
+ sizeof (ushort_t))) {
+ /* LINTED: improper alignment */
+ s_buf = *(ushort_t *)start;
+ } else {
+ memcpy((char *)&s_buf,
+ start, sizeof (short));
+ }
+ (void) sprintf(get_line(0, 0),
+ "\t\tEntry %d:\t\t%d", i,
+ ntohs(s_buf));
+ start += sizeof (ushort_t);
+ }
+ }
+ break;
+ case CD_STATIC_ROUTE:
+ if ((*start % 8) != 0) {
+ (void) sprintf(get_line(0, 0),
+ "Error: Bad Static Route option: %d",
+ *start);
+ } else {
+ items = *start++ / 8;
+ for (i = 0; i < items; i++) {
+ memcpy((char *)&tmp, start,
+ sizeof (struct in_addr));
+ (void) strcpy(scratch, inet_ntoa(tmp));
+ start += sizeof (ulong_t);
+ memcpy((char *)&tmp, start,
+ sizeof (struct in_addr));
+ (void) sprintf(get_line(0, 0),
+ "Static route from %s to %s",
+ scratch, inet_ntoa(tmp));
+ start += sizeof (ulong_t);
+ }
+ }
+ break;
+ case CD_VENDOR_SPEC:
+ i = *start++;
+ (void) sprintf(get_line(0, 0),
+ "Vendor-specific Options (%d total octets):", i);
+ /*
+ * We don't know what these things are, so just
+ * display the option number, length, and value
+ * (hex).
+ */
+ vend = (uchar_t *)((uchar_t *)start + i);
+ while (start < vend && *start != CD_END) {
+ if (*start == CD_PAD) {
+ start++;
+ continue;
+ }
+ (void) sprintf(scratch,
+ "\t(%.2d) %.2d octets", *start,
+ *(uchar_t *)((uchar_t *)start + 1));
+ start++;
+ display_ascii_hex(scratch, &start);
+ }
+ start = vend; /* in case CD_END found */
+ break;
+ case CD_NETBIOS_NODE_TYPE:
+ if (*start != 1) {
+ (void) sprintf(get_line(0, 0),
+ "Error: Bad '%s' parameter",
+ option_types[CD_NETBIOS_NODE_TYPE]);
+ } else {
+ char *type;
+ start++;
+ switch (*start) {
+ case 0x1:
+ type = "Broadcast Node";
+ break;
+ case 0x2:
+ type = "Point To Point Node";
+ break;
+ case 0x4:
+ type = "Mixed Mode Node";
+ break;
+ case 0x8:
+ type = "Hybrid Node";
+ break;
+ default:
+ type = "??? Node";
+ break;
+ };
+ (void) sprintf(get_line(0, 0),
+ "%s = %s (%d)",
+ option_types[CD_NETBIOS_NODE_TYPE],
+ type, *start);
+ start++;
+ }
+ break;
+ case CD_OPTION_OVERLOAD:
+ if (*start != 1) {
+ (void) sprintf(get_line(0, 0),
+ "Bad Option Overload value.");
+ } else {
+ start++;
+ nooverload = *start++;
+ }
+ break;
+ case CD_DHCP_TYPE:
+ if (*start < 1 || *start > 7) {
+ (void) sprintf(get_line(0, 0),
+ "Bad DHCP Message Type.");
+ } else {
+ start++;
+ (void) sprintf(get_line(0, 0),
+ "Message type = %s",
+ show_msgtype(*start));
+ start++;
+ }
+ break;
+ case CD_REQUEST_LIST:
+ opt_len = *start++;
+ (void) sprintf(get_line(0, 0),
+ "Requested Options:");
+ for (i = 0; i < opt_len; i++) {
+ entry = NULL;
+ if (*start < OPTIONS_ARRAY_SIZE) {
+ prmpt = option_types[*start];
+ } else {
+ entry = inittab_getbycode(
+ ITAB_CAT_STANDARD|ITAB_CAT_SITE,
+ ITAB_CONS_SNOOP, *start);
+ if (entry == NULL) {
+ if (*start >= DHCP_SITE_OPT &&
+ *start <= DHCP_END_SITE) {
+ prmpt = "Site Option";
+ } else {
+ prmpt = "Unrecognized "
+ "Option";
+ }
+ } else {
+ prmpt = entry->ds_name;
+ }
+ }
+ (void) sprintf(get_line(0, 0),
+ "\t%2d (%s)", *start, prmpt);
+ start++;
+ free(entry);
+ }
+ break;
+ default:
+ opt_len = *start++;
+ entry = inittab_getbycode(
+ ITAB_CAT_STANDARD|ITAB_CAT_SITE,
+ ITAB_CONS_SNOOP, save);
+ if (entry == NULL) {
+ if (save >= DHCP_SITE_OPT &&
+ save <= DHCP_END_SITE)
+ prmpt = "Site";
+ else
+ prmpt = "Unrecognized";
+ decoded_opt = NULL;
+ } else {
+ if (save < OPTIONS_ARRAY_SIZE) {
+ prmpt = option_types[save];
+ } else {
+ prmpt = entry->ds_name;
+ }
+ decoded_opt = inittab_decode(entry, start,
+ opt_len, B_TRUE);
+ }
+ if (decoded_opt == NULL) {
+ (void) sprintf(get_line(0, 0),
+ "%s Option = %d, length = %d octets",
+ prmpt, save, opt_len);
+ start--;
+ display_ascii_hex("\tValue =", &start);
+ } else {
+ (void) sprintf(get_line(0, 0), "%s = %s", prmpt,
+ decoded_opt);
+ start += opt_len;
+ free(decoded_opt);
+ }
+ free(entry);
+ break;
+ };
+ }
+ return (nooverload);
+}
+
+static const char *
+show_msgtype(unsigned char type)
+{
+ /*
+ * note: the ordering here allows direct indexing of the table
+ * based on the RFC2131 packet type value passed in.
+ */
+
+ static const char *types[] = {
+ "BOOTP",
+ "DHCPDISCOVER", "DHCPOFFER", "DHCPREQUEST", "DHCPDECLINE",
+ "DHCPACK", "DHCPNAK", "DHCPRELEASE", "DHCPINFORM"
+ };
+
+ if (type >= (sizeof (types) / sizeof (*types)) || types[type] == NULL)
+ return ("UNKNOWN");
+
+ return (types[type]);
+}
+
+static void
+display_ip(int items, char *fmt, char *msg, unsigned char **opt)
+{
+ struct in_addr tmp;
+ int i;
+
+ for (i = 0; i < items; i++) {
+ memcpy((char *)&tmp, *opt, sizeof (struct in_addr));
+ (void) sprintf(get_line(0, 0), fmt, msg, inet_ntoa(tmp));
+ *opt += 4;
+ }
+}
+
+static void
+display_ascii(char *fmt, char *msg, unsigned char **opt)
+{
+ static unsigned char buf[256];
+ int len = **opt;
+ unsigned char slen = len;
+
+ if (len >= sizeof (buf))
+ len = sizeof (buf) - 1;
+ (*opt)++;
+ memcpy(buf, *opt, len);
+ *(unsigned char *)(buf + len) = '\0';
+ (void) sprintf(get_line(0, 0), fmt, msg, buf);
+ (*opt) += slen;
+}
+
+static void
+display_number(char *fmt, char *msg, unsigned char **opt)
+{
+ int len = **opt;
+ unsigned long l_buf = 0;
+ unsigned short s_buf = 0;
+
+ if (len > 4) {
+ (*opt)++;
+ (void) sprintf(get_line(0, 0), fmt, msg, 0xdeadbeef);
+ return;
+ }
+ switch (len) {
+ case sizeof (uchar_t):
+ (*opt)++;
+ (void) sprintf(get_line(0, 0), fmt, msg, **opt);
+ break;
+ case sizeof (ushort_t):
+ (*opt)++;
+ if (IS_P2ALIGNED(*opt, sizeof (ushort_t)))
+ /* LINTED: improper alignment */
+ s_buf = *(unsigned short *)*opt;
+ else
+ memcpy((char *)&s_buf, *opt, len);
+ (void) sprintf(get_line(0, 0), fmt, msg, ntohs(s_buf));
+ break;
+ case sizeof (ulong_t):
+ (*opt)++;
+ if (IS_P2ALIGNED(*opt, sizeof (ulong_t)))
+ /* LINTED: improper alignment */
+ l_buf = *(unsigned long *)*opt;
+ else
+ memcpy((char *)&l_buf, *opt, len);
+ (void) sprintf(get_line(0, 0), fmt, msg, ntohl(l_buf));
+ break;
+ }
+ (*opt) += len;
+}
+
+static void
+display_ascii_hex(char *msg, unsigned char **opt)
+{
+ int printable;
+ char buffer[512];
+ char *line, *tmp, *ap, *fmt;
+ int i, len = **opt;
+
+ line = get_line(0, 0);
+
+ (*opt)++;
+
+ if (len >= 255) {
+ (void) sprintf(line, "\t%s <TOO LONG>", msg);
+ return;
+ }
+
+ for (printable = 1, tmp = (char *)(*opt), ap = buffer;
+ tmp < (char *)&((*opt)[len]); tmp++) {
+ if (isprint(*tmp))
+ *ap++ = *tmp;
+ else {
+ *ap++ = '.';
+ printable = 0;
+ }
+ }
+ *ap = '\0';
+
+ if (!printable) {
+ for (tmp = (char *)(*opt), ap = buffer;
+ (tmp < (char *)&((*opt)[len])) && ((ap + 5) < &buffer[512]);
+ tmp++) {
+ ap += sprintf(ap, "0x%02X ", *(uchar_t *)(tmp));
+ }
+ /* Truncate the trailing space */
+ *(--ap) = '\0';
+ /* More bytes to print in hex but no space in buffer */
+ if (tmp < (char *)&((*opt)[len])) {
+ i = ap - buffer;
+ buffer[i - 1] = '.';
+ buffer[i - 2] = '.';
+ buffer[i - 3] = '.';
+ }
+ fmt = "%s\t%s (unprintable)";
+ } else {
+ fmt = "%s\t\"%s\"";
+ }
+ (*opt) += len;
+ (void) sprintf(line, fmt, msg, buffer);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcpv6.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcpv6.c
new file mode 100644
index 0000000..0e7c1be
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcpv6.c
@@ -0,0 +1,1051 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Dynamic Host Configuration Protocol version 6, for IPv6. Supports
+ * RFCs 3315, 3319, 3646, 3898, 4075, 4242, 4280, 4580, 4649, and 4704.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/dhcp6.h>
+#include <arpa/inet.h>
+#include <dhcp_impl.h>
+#include <dhcp_inittab.h>
+
+#include "snoop.h"
+
+static const char *mtype_to_str(uint8_t);
+static const char *option_to_str(uint8_t);
+static const char *duidtype_to_str(uint16_t);
+static const char *status_to_str(uint16_t);
+static const char *entr_to_str(uint32_t);
+static const char *reconf_to_str(uint8_t);
+static const char *authproto_to_str(uint8_t);
+static const char *authalg_to_str(uint8_t, uint8_t);
+static const char *authrdm_to_str(uint8_t);
+static const char *cwhat_to_str(uint8_t);
+static const char *catype_to_str(uint8_t);
+static void show_hex(const uint8_t *, int, const char *);
+static void show_ascii(const uint8_t *, int, const char *);
+static void show_address(const char *, const void *);
+static void show_options(const uint8_t *, int);
+
+int
+interpret_dhcpv6(int flags, const uint8_t *data, int len)
+{
+ int olen = len;
+ char *line, *lstart;
+ dhcpv6_relay_t d6r;
+ dhcpv6_message_t d6m;
+ uint_t optlen;
+ uint16_t statuscode;
+
+ if (len <= 0) {
+ (void) strlcpy(get_sum_line(), "DHCPv6?", MAXLINE);
+ return (0);
+ }
+ if (flags & F_SUM) {
+ uint_t ias;
+ dhcpv6_option_t *d6o;
+ in6_addr_t link, peer;
+ char linkstr[INET6_ADDRSTRLEN];
+ char peerstr[INET6_ADDRSTRLEN];
+
+ line = lstart = get_sum_line();
+ line += snprintf(line, MAXLINE, "DHCPv6 %s",
+ mtype_to_str(data[0]));
+ if (data[0] == DHCPV6_MSG_RELAY_FORW ||
+ data[0] == DHCPV6_MSG_RELAY_REPL) {
+ if (len < sizeof (d6r)) {
+ (void) strlcpy(line, "?",
+ MAXLINE - (line - lstart));
+ return (olen);
+ }
+ /* Not much in DHCPv6 is aligned. */
+ (void) memcpy(&d6r, data, sizeof (d6r));
+ (void) memcpy(&link, d6r.d6r_linkaddr, sizeof (link));
+ (void) memcpy(&peer, d6r.d6r_peeraddr, sizeof (peer));
+ line += snprintf(line, MAXLINE - (line - lstart),
+ " HC=%d link=%s peer=%s", d6r.d6r_hop_count,
+ inet_ntop(AF_INET6, &link, linkstr,
+ sizeof (linkstr)),
+ inet_ntop(AF_INET6, &peer, peerstr,
+ sizeof (peerstr)));
+ data += sizeof (d6r);
+ len -= sizeof (d6r);
+ } else {
+ if (len < sizeof (d6m)) {
+ (void) strlcpy(line, "?",
+ MAXLINE - (line - lstart));
+ return (olen);
+ }
+ (void) memcpy(&d6m, data, sizeof (d6m));
+ line += snprintf(line, MAXLINE - (line - lstart),
+ " xid=%x", DHCPV6_GET_TRANSID(&d6m));
+ data += sizeof (d6m);
+ len -= sizeof (d6m);
+ }
+ ias = 0;
+ d6o = NULL;
+ while ((d6o = dhcpv6_find_option(data, len, d6o,
+ DHCPV6_OPT_IA_NA, NULL)) != NULL)
+ ias++;
+ if (ias > 0)
+ line += snprintf(line, MAXLINE - (line - lstart),
+ " IAs=%u", ias);
+ d6o = dhcpv6_find_option(data, len, NULL,
+ DHCPV6_OPT_STATUS_CODE, &optlen);
+ optlen -= sizeof (*d6o);
+ if (d6o != NULL && optlen >= sizeof (statuscode)) {
+ (void) memcpy(&statuscode, d6o + 1,
+ sizeof (statuscode));
+ line += snprintf(line, MAXLINE - (line - lstart),
+ " status=%u", ntohs(statuscode));
+ optlen -= sizeof (statuscode);
+ if (optlen > 0) {
+ line += snprintf(line,
+ MAXLINE - (line - lstart), " \"%.*s\"",
+ optlen, (char *)(d6o + 1) + 2);
+ }
+ }
+ d6o = dhcpv6_find_option(data, len, NULL,
+ DHCPV6_OPT_RELAY_MSG, &optlen);
+ optlen -= sizeof (*d6o);
+ if (d6o != NULL && optlen >= 1) {
+ line += snprintf(line, MAXLINE - (line - lstart),
+ " relay=%s", mtype_to_str(*(uint8_t *)(d6o + 1)));
+ }
+ } else if (flags & F_DTAIL) {
+ show_header("DHCPv6: ",
+ "Dynamic Host Configuration Protocol Version 6", len);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Message type (msg-type) = %u (%s)", data[0],
+ mtype_to_str(data[0]));
+ if (data[0] == DHCPV6_MSG_RELAY_FORW ||
+ data[0] == DHCPV6_MSG_RELAY_REPL) {
+ if (len < sizeof (d6r)) {
+ (void) strlcpy(get_line(0, 0), "Truncated",
+ get_line_remain());
+ return (olen);
+ }
+ (void) memcpy(&d6r, data, sizeof (d6r));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Hop count = %u", d6r.d6r_hop_count);
+ show_address("Link address", d6r.d6r_linkaddr);
+ show_address("Peer address", d6r.d6r_peeraddr);
+ data += sizeof (d6r);
+ len -= sizeof (d6r);
+ } else {
+ if (len < sizeof (d6m)) {
+ (void) strlcpy(get_line(0, 0), "Truncated",
+ get_line_remain());
+ return (olen);
+ }
+ (void) memcpy(&d6m, data, sizeof (d6m));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Transaction ID = %x", DHCPV6_GET_TRANSID(&d6m));
+ data += sizeof (d6m);
+ len -= sizeof (d6m);
+ }
+ show_space();
+ show_options(data, len);
+ show_space();
+ }
+ return (olen);
+}
+
+static const char *
+mtype_to_str(uint8_t mtype)
+{
+ switch (mtype) {
+ case DHCPV6_MSG_SOLICIT:
+ return ("Solicit");
+ case DHCPV6_MSG_ADVERTISE:
+ return ("Advertise");
+ case DHCPV6_MSG_REQUEST:
+ return ("Request");
+ case DHCPV6_MSG_CONFIRM:
+ return ("Confirm");
+ case DHCPV6_MSG_RENEW:
+ return ("Renew");
+ case DHCPV6_MSG_REBIND:
+ return ("Rebind");
+ case DHCPV6_MSG_REPLY:
+ return ("Reply");
+ case DHCPV6_MSG_RELEASE:
+ return ("Release");
+ case DHCPV6_MSG_DECLINE:
+ return ("Decline");
+ case DHCPV6_MSG_RECONFIGURE:
+ return ("Reconfigure");
+ case DHCPV6_MSG_INFO_REQ:
+ return ("Information-Request");
+ case DHCPV6_MSG_RELAY_FORW:
+ return ("Relay-Forward");
+ case DHCPV6_MSG_RELAY_REPL:
+ return ("Relay-Reply");
+ default:
+ return ("Unknown");
+ }
+}
+
+static const char *
+option_to_str(uint8_t mtype)
+{
+ switch (mtype) {
+ case DHCPV6_OPT_CLIENTID:
+ return ("Client Identifier");
+ case DHCPV6_OPT_SERVERID:
+ return ("Server Identifier");
+ case DHCPV6_OPT_IA_NA:
+ return ("Identity Association for Non-temporary Addresses");
+ case DHCPV6_OPT_IA_TA:
+ return ("Identity Association for Temporary Addresses");
+ case DHCPV6_OPT_IAADDR:
+ return ("IA Address");
+ case DHCPV6_OPT_ORO:
+ return ("Option Request");
+ case DHCPV6_OPT_PREFERENCE:
+ return ("Preference");
+ case DHCPV6_OPT_ELAPSED_TIME:
+ return ("Elapsed Time");
+ case DHCPV6_OPT_RELAY_MSG:
+ return ("Relay Message");
+ case DHCPV6_OPT_AUTH:
+ return ("Authentication");
+ case DHCPV6_OPT_UNICAST:
+ return ("Server Unicast");
+ case DHCPV6_OPT_STATUS_CODE:
+ return ("Status Code");
+ case DHCPV6_OPT_RAPID_COMMIT:
+ return ("Rapid Commit");
+ case DHCPV6_OPT_USER_CLASS:
+ return ("User Class");
+ case DHCPV6_OPT_VENDOR_CLASS:
+ return ("Vendor Class");
+ case DHCPV6_OPT_VENDOR_OPT:
+ return ("Vendor-specific Information");
+ case DHCPV6_OPT_INTERFACE_ID:
+ return ("Interface-Id");
+ case DHCPV6_OPT_RECONF_MSG:
+ return ("Reconfigure Message");
+ case DHCPV6_OPT_RECONF_ACC:
+ return ("Reconfigure Accept");
+ case DHCPV6_OPT_SIP_NAMES:
+ return ("SIP Servers Domain Name List");
+ case DHCPV6_OPT_SIP_ADDR:
+ return ("SIP Servers IPv6 Address List");
+ case DHCPV6_OPT_DNS_ADDR:
+ return ("DNS Recursive Name Server");
+ case DHCPV6_OPT_DNS_SEARCH:
+ return ("Domain Search List");
+ case DHCPV6_OPT_IA_PD:
+ return ("Identity Association for Prefix Delegation");
+ case DHCPV6_OPT_IAPREFIX:
+ return ("IA_PD Prefix");
+ case DHCPV6_OPT_NIS_SERVERS:
+ return ("Network Information Service Servers");
+ case DHCPV6_OPT_NIS_DOMAIN:
+ return ("Network Information Service Domain Name");
+ case DHCPV6_OPT_SNTP_SERVERS:
+ return ("Simple Network Time Protocol Servers");
+ case DHCPV6_OPT_INFO_REFTIME:
+ return ("Information Refresh Time");
+ case DHCPV6_OPT_BCMCS_SRV_D:
+ return ("BCMCS Controller Domain Name List");
+ case DHCPV6_OPT_BCMCS_SRV_A:
+ return ("BCMCS Controller IPv6 Address");
+ case DHCPV6_OPT_GEOCONF_CVC:
+ return ("Civic Location");
+ case DHCPV6_OPT_REMOTE_ID:
+ return ("Relay Agent Remote-ID");
+ case DHCPV6_OPT_SUBSCRIBER:
+ return ("Relay Agent Subscriber-ID");
+ case DHCPV6_OPT_CLIENT_FQDN:
+ return ("Client FQDN");
+ default:
+ return ("Unknown");
+ }
+}
+
+static const char *
+duidtype_to_str(uint16_t dtype)
+{
+ switch (dtype) {
+ case DHCPV6_DUID_LLT:
+ return ("Link-layer Address Plus Time");
+ case DHCPV6_DUID_EN:
+ return ("Enterprise Number");
+ case DHCPV6_DUID_LL:
+ return ("Link-layer Address");
+ default:
+ return ("Unknown");
+ }
+}
+
+static const char *
+status_to_str(uint16_t status)
+{
+ switch (status) {
+ case DHCPV6_STAT_SUCCESS:
+ return ("Success");
+ case DHCPV6_STAT_UNSPECFAIL:
+ return ("Failure, reason unspecified");
+ case DHCPV6_STAT_NOADDRS:
+ return ("No addresses for IAs");
+ case DHCPV6_STAT_NOBINDING:
+ return ("Client binding unavailable");
+ case DHCPV6_STAT_NOTONLINK:
+ return ("Prefix not on link");
+ case DHCPV6_STAT_USEMCAST:
+ return ("Use multicast");
+ case DHCPV6_STAT_NOPREFIX:
+ return ("No prefix available");
+ default:
+ return ("Unknown");
+ }
+}
+
+static const char *
+entr_to_str(uint32_t entr)
+{
+ switch (entr) {
+ case DHCPV6_SUN_ENT:
+ return ("Sun Microsystems");
+ default:
+ return ("Unknown");
+ }
+}
+
+static const char *
+reconf_to_str(uint8_t msgtype)
+{
+ switch (msgtype) {
+ case DHCPV6_RECONF_RENEW:
+ return ("Renew");
+ case DHCPV6_RECONF_INFO:
+ return ("Information-request");
+ default:
+ return ("Unknown");
+ }
+}
+
+static const char *
+authproto_to_str(uint8_t aproto)
+{
+ switch (aproto) {
+ case DHCPV6_PROTO_DELAYED:
+ return ("Delayed");
+ case DHCPV6_PROTO_RECONFIG:
+ return ("Reconfigure Key");
+ default:
+ return ("Unknown");
+ }
+}
+
+static const char *
+authalg_to_str(uint8_t aproto, uint8_t aalg)
+{
+ switch (aproto) {
+ case DHCPV6_PROTO_DELAYED:
+ case DHCPV6_PROTO_RECONFIG:
+ switch (aalg) {
+ case DHCPV6_ALG_HMAC_MD5:
+ return ("HMAC-MD5 Signature");
+ default:
+ return ("Unknown");
+ }
+ break;
+ default:
+ return ("Unknown");
+ }
+}
+
+static const char *
+authrdm_to_str(uint8_t ardm)
+{
+ switch (ardm) {
+ case DHCPV6_RDM_MONOCNT:
+ return ("Monotonic Counter");
+ default:
+ return ("Unknown");
+ }
+}
+
+static const char *
+cwhat_to_str(uint8_t what)
+{
+ switch (what) {
+ case DHCPV6_CWHAT_SERVER:
+ return ("Server");
+ case DHCPV6_CWHAT_NETWORK:
+ return ("Network");
+ case DHCPV6_CWHAT_CLIENT:
+ return ("Client");
+ default:
+ return ("Unknown");
+ }
+}
+
+static const char *
+catype_to_str(uint8_t catype)
+{
+ switch (catype) {
+ case CIVICADDR_LANG:
+ return ("Language; RFC 2277");
+ case CIVICADDR_A1:
+ return ("National division (state)");
+ case CIVICADDR_A2:
+ return ("County");
+ case CIVICADDR_A3:
+ return ("City");
+ case CIVICADDR_A4:
+ return ("City division");
+ case CIVICADDR_A5:
+ return ("Neighborhood");
+ case CIVICADDR_A6:
+ return ("Street group");
+ case CIVICADDR_PRD:
+ return ("Leading street direction");
+ case CIVICADDR_POD:
+ return ("Trailing street suffix");
+ case CIVICADDR_STS:
+ return ("Street suffix or type");
+ case CIVICADDR_HNO:
+ return ("House number");
+ case CIVICADDR_HNS:
+ return ("House number suffix");
+ case CIVICADDR_LMK:
+ return ("Landmark");
+ case CIVICADDR_LOC:
+ return ("Additional location information");
+ case CIVICADDR_NAM:
+ return ("Name/occupant");
+ case CIVICADDR_PC:
+ return ("Postal Code/ZIP");
+ case CIVICADDR_BLD:
+ return ("Building");
+ case CIVICADDR_UNIT:
+ return ("Unit/apt/suite");
+ case CIVICADDR_FLR:
+ return ("Floor");
+ case CIVICADDR_ROOM:
+ return ("Room number");
+ case CIVICADDR_TYPE:
+ return ("Place type");
+ case CIVICADDR_PCN:
+ return ("Postal community name");
+ case CIVICADDR_POBOX:
+ return ("Post office box");
+ case CIVICADDR_ADDL:
+ return ("Additional code");
+ case CIVICADDR_SEAT:
+ return ("Seat/desk");
+ case CIVICADDR_ROAD:
+ return ("Primary road or street");
+ case CIVICADDR_RSEC:
+ return ("Road section");
+ case CIVICADDR_RBRA:
+ return ("Road branch");
+ case CIVICADDR_RSBR:
+ return ("Road sub-branch");
+ case CIVICADDR_SPRE:
+ return ("Street name pre-modifier");
+ case CIVICADDR_SPOST:
+ return ("Street name post-modifier");
+ case CIVICADDR_SCRIPT:
+ return ("Script");
+ default:
+ return ("Unknown");
+ }
+}
+
+static void
+show_hex(const uint8_t *data, int len, const char *name)
+{
+ char buffer[16 * 3 + 1];
+ int nlen;
+ int i;
+ char sep;
+
+ nlen = strlen(name);
+ sep = '=';
+ while (len > 0) {
+ for (i = 0; i < 16 && i < len; i++)
+ (void) snprintf(buffer + 3 * i, 4, " %02x", *data++);
+ (void) snprintf(get_line(0, 0), get_line_remain(), "%*s %c%s",
+ nlen, name, sep, buffer);
+ name = "";
+ sep = ' ';
+ len -= i;
+ }
+}
+
+static void
+show_ascii(const uint8_t *data, int len, const char *name)
+{
+ char buffer[64], *bp;
+ int nlen;
+ int i;
+ char sep;
+
+ nlen = strlen(name);
+ sep = '=';
+ while (len > 0) {
+ bp = buffer;
+ for (i = 0; i < sizeof (buffer) - 4 && len > 0; len--) {
+ if (!isascii(*data) || !isprint(*data))
+ bp += snprintf(bp, 5, "\\%03o", *data++);
+ else
+ *bp++;
+ }
+ *bp = '\0';
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%*s %c \"%s\"", nlen, name, sep, buffer);
+ sep = ' ';
+ name = "";
+ }
+}
+
+static void
+show_address(const char *addrname, const void *aptr)
+{
+ char *hname;
+ char addrstr[INET6_ADDRSTRLEN];
+ in6_addr_t addr;
+
+ (void) memcpy(&addr, aptr, sizeof (in6_addr_t));
+ (void) inet_ntop(AF_INET6, &addr, addrstr, sizeof (addrstr));
+ hname = addrtoname(AF_INET6, &addr);
+ if (strcmp(hname, addrstr) == 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(), "%s = %s",
+ addrname, addrstr);
+ } else {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s = %s (%s)", addrname, addrstr, hname);
+ }
+}
+
+static void
+nest_options(const uint8_t *data, uint_t olen, char *prefix, char *title)
+{
+ char *str, *oldnest, *oldprefix;
+
+ if (olen <= 0)
+ return;
+ oldprefix = prot_prefix;
+ oldnest = prot_nest_prefix;
+ str = malloc(strlen(prot_nest_prefix) + strlen(prot_prefix) + 1);
+ if (str == NULL) {
+ prot_nest_prefix = prot_prefix;
+ } else {
+ (void) sprintf(str, "%s%s", prot_nest_prefix, prot_prefix);
+ prot_nest_prefix = str;
+ }
+ show_header(prefix, title, 0);
+ show_options(data, olen);
+ free(str);
+ prot_prefix = oldprefix;
+ prot_nest_prefix = oldnest;
+}
+
+static void
+show_options(const uint8_t *data, int len)
+{
+ dhcpv6_option_t d6o;
+ uint_t olen, retlen;
+ uint16_t val16;
+ uint16_t type;
+ uint32_t val32;
+ const uint8_t *ostart;
+ char *str, *sp;
+ char *oldnest;
+
+ /*
+ * Be very careful with negative numbers; ANSI signed/unsigned
+ * comparison doesn't work as expected.
+ */
+ while (len >= (signed)sizeof (d6o)) {
+ (void) memcpy(&d6o, data, sizeof (d6o));
+ d6o.d6o_code = ntohs(d6o.d6o_code);
+ d6o.d6o_len = olen = ntohs(d6o.d6o_len);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Option Code = %u (%s)", d6o.d6o_code,
+ option_to_str(d6o.d6o_code));
+ ostart = data += sizeof (d6o);
+ len -= sizeof (d6o);
+ if (olen > len) {
+ (void) strlcpy(get_line(0, 0), "Option truncated",
+ get_line_remain());
+ olen = len;
+ }
+ switch (d6o.d6o_code) {
+ case DHCPV6_OPT_CLIENTID:
+ case DHCPV6_OPT_SERVERID:
+ if (olen < sizeof (val16))
+ break;
+ (void) memcpy(&val16, data, sizeof (val16));
+ data += sizeof (val16);
+ olen -= sizeof (val16);
+ type = ntohs(val16);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " DUID Type = %u (%s)", type,
+ duidtype_to_str(type));
+ if (type == DHCPV6_DUID_LLT || type == DHCPV6_DUID_LL) {
+ if (olen < sizeof (val16))
+ break;
+ (void) memcpy(&val16, data, sizeof (val16));
+ data += sizeof (val16);
+ olen -= sizeof (val16);
+ val16 = ntohs(val16);
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ " Hardware Type = %u (%s)", val16,
+ arp_htype(val16));
+ }
+ if (type == DHCPV6_DUID_LLT) {
+ time_t timevalue;
+
+ if (olen < sizeof (val32))
+ break;
+ (void) memcpy(&val32, data, sizeof (val32));
+ data += sizeof (val32);
+ olen -= sizeof (val32);
+ timevalue = ntohl(val32) + DUID_TIME_BASE;
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ " Time = %lu (%.24s)", ntohl(val32),
+ ctime(&timevalue));
+ }
+ if (type == DHCPV6_DUID_EN) {
+ if (olen < sizeof (val32))
+ break;
+ (void) memcpy(&val32, data, sizeof (val32));
+ data += sizeof (val32);
+ olen -= sizeof (val32);
+ val32 = ntohl(val32);
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ " Enterprise Number = %lu (%s)", val32,
+ entr_to_str(val32));
+ }
+ if (olen == 0)
+ break;
+ if ((str = malloc(olen * 3)) == NULL)
+ pr_err("interpret_dhcpv6: no mem");
+ sp = str + snprintf(str, 3, "%02x", *data++);
+ while (--olen > 0) {
+ *sp++ = (type == DHCPV6_DUID_LLT ||
+ type == DHCPV6_DUID_LL) ? ':' : ' ';
+ sp = sp + snprintf(sp, 3, "%02x", *data++);
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ (type == DHCPV6_DUID_LLT ||
+ type == DHCPV6_DUID_LL) ?
+ " Link Layer Address = %s" :
+ " Identifier = %s", str);
+ free(str);
+ break;
+ case DHCPV6_OPT_IA_NA:
+ case DHCPV6_OPT_IA_PD: {
+ dhcpv6_ia_na_t d6in;
+
+ if (olen < sizeof (d6in) - sizeof (d6o))
+ break;
+ (void) memcpy(&d6in, data - sizeof (d6o),
+ sizeof (d6in));
+ data += sizeof (d6in) - sizeof (d6o);
+ olen -= sizeof (d6in) - sizeof (d6o);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " IAID = %u", ntohl(d6in.d6in_iaid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " T1 (renew) = %u seconds", ntohl(d6in.d6in_t1));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " T2 (rebind) = %u seconds", ntohl(d6in.d6in_t2));
+ nest_options(data, olen, "IA: ",
+ "Identity Association");
+ break;
+ }
+ case DHCPV6_OPT_IA_TA: {
+ dhcpv6_ia_ta_t d6it;
+
+ if (olen < sizeof (d6it) - sizeof (d6o))
+ break;
+ (void) memcpy(&d6it, data - sizeof (d6o),
+ sizeof (d6it));
+ data += sizeof (d6it) - sizeof (d6o);
+ olen -= sizeof (d6it) - sizeof (d6o);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " IAID = %u", ntohl(d6it.d6it_iaid));
+ nest_options(data, olen, "IA: ",
+ "Identity Association");
+ break;
+ }
+ case DHCPV6_OPT_IAADDR: {
+ dhcpv6_iaaddr_t d6ia;
+
+ if (olen < sizeof (d6ia) - sizeof (d6o))
+ break;
+ (void) memcpy(&d6ia, data - sizeof (d6o),
+ sizeof (d6ia));
+ data += sizeof (d6ia) - sizeof (d6o);
+ olen -= sizeof (d6ia) - sizeof (d6o);
+ show_address(" Address", &d6ia.d6ia_addr);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Preferred lifetime = %u seconds",
+ ntohl(d6ia.d6ia_preflife));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Valid lifetime = %u seconds",
+ ntohl(d6ia.d6ia_vallife));
+ nest_options(data, olen, "ADDR: ", "Address");
+ break;
+ }
+ case DHCPV6_OPT_ORO:
+ while (olen >= sizeof (val16)) {
+ (void) memcpy(&val16, data, sizeof (val16));
+ val16 = ntohs(val16);
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ " Requested Option Code = %u (%s)", val16,
+ option_to_str(val16));
+ data += sizeof (val16);
+ olen -= sizeof (val16);
+ }
+ break;
+ case DHCPV6_OPT_PREFERENCE:
+ if (olen > 0) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ *data == 255 ?
+ " Preference = %u (immediate)" :
+ " Preference = %u", *data);
+ }
+ break;
+ case DHCPV6_OPT_ELAPSED_TIME:
+ if (olen == sizeof (val16)) {
+ (void) memcpy(&val16, data, sizeof (val16));
+ val16 = ntohs(val16);
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ " Elapsed Time = %u.%02u seconds",
+ val16 / 100, val16 % 100);
+ }
+ break;
+ case DHCPV6_OPT_RELAY_MSG:
+ if (olen > 0) {
+ oldnest = prot_nest_prefix;
+ prot_nest_prefix = prot_prefix;
+ retlen = interpret_dhcpv6(F_DTAIL, data, olen);
+ prot_prefix = prot_nest_prefix;
+ prot_nest_prefix = oldnest;
+ }
+ break;
+ case DHCPV6_OPT_AUTH: {
+ dhcpv6_auth_t d6a;
+
+ if (olen < DHCPV6_AUTH_SIZE - sizeof (d6o))
+ break;
+ (void) memcpy(&d6a, data - sizeof (d6o),
+ DHCPV6_AUTH_SIZE);
+ data += DHCPV6_AUTH_SIZE - sizeof (d6o);
+ olen += DHCPV6_AUTH_SIZE - sizeof (d6o);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Protocol = %u (%s)", d6a.d6a_proto,
+ authproto_to_str(d6a.d6a_proto));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Algorithm = %u (%s)", d6a.d6a_alg,
+ authalg_to_str(d6a.d6a_proto, d6a.d6a_alg));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Replay Detection Method = %u (%s)", d6a.d6a_rdm,
+ authrdm_to_str(d6a.d6a_rdm));
+ show_hex(d6a.d6a_replay, sizeof (d6a.d6a_replay),
+ " RDM Data");
+ if (olen > 0)
+ show_hex(data, olen, " Auth Info");
+ break;
+ }
+ case DHCPV6_OPT_UNICAST:
+ if (olen >= sizeof (in6_addr_t))
+ show_address(" Server Address", data);
+ break;
+ case DHCPV6_OPT_STATUS_CODE:
+ if (olen < sizeof (val16))
+ break;
+ (void) memcpy(&val16, data, sizeof (val16));
+ val16 = ntohs(val16);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Status Code = %u (%s)", val16,
+ status_to_str(val16));
+ data += sizeof (val16);
+ olen -= sizeof (val16);
+ if (olen > 0)
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), " Text = \"%.*s\"",
+ olen, data);
+ break;
+ case DHCPV6_OPT_VENDOR_CLASS:
+ if (olen < sizeof (val32))
+ break;
+ (void) memcpy(&val32, data, sizeof (val32));
+ data += sizeof (val32);
+ olen -= sizeof (val32);
+ val32 = ntohl(val32);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Enterprise Number = %lu (%s)", val32,
+ entr_to_str(val32));
+ /* FALLTHROUGH */
+ case DHCPV6_OPT_USER_CLASS:
+ while (olen >= sizeof (val16)) {
+ (void) memcpy(&val16, data, sizeof (val16));
+ data += sizeof (val16);
+ olen -= sizeof (val16);
+ val16 = ntohs(val16);
+ if (val16 > olen) {
+ (void) strlcpy(get_line(0, 0),
+ " Truncated class",
+ get_line_remain());
+ val16 = olen;
+ }
+ show_hex(data, olen, " Class");
+ data += val16;
+ olen -= val16;
+ }
+ break;
+ case DHCPV6_OPT_VENDOR_OPT: {
+ dhcpv6_option_t sd6o;
+
+ if (olen < sizeof (val32))
+ break;
+ (void) memcpy(&val32, data, sizeof (val32));
+ data += sizeof (val32);
+ olen -= sizeof (val32);
+ val32 = ntohl(val32);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Enterprise Number = %lu (%s)", val32,
+ entr_to_str(val32));
+ while (olen >= sizeof (sd6o)) {
+ (void) memcpy(&sd6o, data, sizeof (sd6o));
+ sd6o.d6o_code = ntohs(sd6o.d6o_code);
+ sd6o.d6o_len = ntohs(sd6o.d6o_len);
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ " Vendor Option Code = %u", d6o.d6o_code);
+ data += sizeof (d6o);
+ olen -= sizeof (d6o);
+ if (sd6o.d6o_len > olen) {
+ (void) strlcpy(get_line(0, 0),
+ " Vendor Option truncated",
+ get_line_remain());
+ sd6o.d6o_len = olen;
+ }
+ if (sd6o.d6o_len > 0) {
+ show_hex(data, sd6o.d6o_len,
+ " Data");
+ data += sd6o.d6o_len;
+ olen -= sd6o.d6o_len;
+ }
+ }
+ break;
+ }
+ case DHCPV6_OPT_REMOTE_ID:
+ if (olen < sizeof (val32))
+ break;
+ (void) memcpy(&val32, data, sizeof (val32));
+ data += sizeof (val32);
+ olen -= sizeof (val32);
+ val32 = ntohl(val32);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Enterprise Number = %lu (%s)", val32,
+ entr_to_str(val32));
+ /* FALLTHROUGH */
+ case DHCPV6_OPT_INTERFACE_ID:
+ case DHCPV6_OPT_SUBSCRIBER:
+ if (olen > 0)
+ show_hex(data, olen, " ID");
+ break;
+ case DHCPV6_OPT_RECONF_MSG:
+ if (olen > 0) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ " Message Type = %u (%s)", *data,
+ reconf_to_str(*data));
+ }
+ break;
+ case DHCPV6_OPT_SIP_NAMES:
+ case DHCPV6_OPT_DNS_SEARCH:
+ case DHCPV6_OPT_NIS_DOMAIN:
+ case DHCPV6_OPT_BCMCS_SRV_D: {
+ dhcp_symbol_t *symp;
+ char *sp2;
+
+ symp = inittab_getbycode(
+ ITAB_CAT_STANDARD | ITAB_CAT_V6, ITAB_CONS_SNOOP,
+ d6o.d6o_code);
+ if (symp != NULL) {
+ str = inittab_decode(symp, data, olen, B_TRUE);
+ if (str != NULL) {
+ sp = str;
+ do {
+ sp2 = strchr(sp, ' ');
+ if (sp2 != NULL)
+ *sp2++ = '\0';
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ " Name = %s", sp);
+ } while ((sp = sp2) != NULL);
+ free(str);
+ }
+ free(symp);
+ }
+ break;
+ }
+ case DHCPV6_OPT_SIP_ADDR:
+ case DHCPV6_OPT_DNS_ADDR:
+ case DHCPV6_OPT_NIS_SERVERS:
+ case DHCPV6_OPT_SNTP_SERVERS:
+ case DHCPV6_OPT_BCMCS_SRV_A:
+ while (olen >= sizeof (in6_addr_t)) {
+ show_address(" Address", data);
+ data += sizeof (in6_addr_t);
+ olen -= sizeof (in6_addr_t);
+ }
+ break;
+ case DHCPV6_OPT_IAPREFIX: {
+ dhcpv6_iaprefix_t d6ip;
+
+ if (olen < DHCPV6_IAPREFIX_SIZE - sizeof (d6o))
+ break;
+ (void) memcpy(&d6ip, data - sizeof (d6o),
+ DHCPV6_IAPREFIX_SIZE);
+ data += DHCPV6_IAPREFIX_SIZE - sizeof (d6o);
+ olen -= DHCPV6_IAPREFIX_SIZE - sizeof (d6o);
+ show_address(" Prefix", d6ip.d6ip_addr);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Preferred lifetime = %u seconds",
+ ntohl(d6ip.d6ip_preflife));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Valid lifetime = %u seconds",
+ ntohl(d6ip.d6ip_vallife));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Prefix length = %u", d6ip.d6ip_preflen);
+ nest_options(data, olen, "ADDR: ", "Address");
+ break;
+ }
+ case DHCPV6_OPT_INFO_REFTIME:
+ if (olen < sizeof (val32))
+ break;
+ (void) memcpy(&val32, data, sizeof (val32));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Refresh Time = %lu seconds", ntohl(val32));
+ break;
+ case DHCPV6_OPT_GEOCONF_CVC: {
+ dhcpv6_civic_t d6c;
+ int solen;
+
+ if (olen < DHCPV6_CIVIC_SIZE - sizeof (d6o))
+ break;
+ (void) memcpy(&d6c, data - sizeof (d6o),
+ DHCPV6_CIVIC_SIZE);
+ data += DHCPV6_CIVIC_SIZE - sizeof (d6o);
+ olen -= DHCPV6_CIVIC_SIZE - sizeof (d6o);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " What Location = %u (%s)", d6c.d6c_what,
+ cwhat_to_str(d6c.d6c_what));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Country Code = %.*s", sizeof (d6c.d6c_cc),
+ d6c.d6c_cc);
+ while (olen >= 2) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ " CA Element = %u (%s)", *data,
+ catype_to_str(*data));
+ solen = data[1];
+ data += 2;
+ olen -= 2;
+ if (solen > olen) {
+ (void) strlcpy(get_line(0, 0),
+ " CA Element truncated",
+ get_line_remain());
+ solen = olen;
+ }
+ if (solen > 0) {
+ show_ascii(data, solen, " CA Data");
+ data += solen;
+ olen -= solen;
+ }
+ }
+ break;
+ }
+ case DHCPV6_OPT_CLIENT_FQDN: {
+ dhcp_symbol_t *symp;
+
+ if (olen == 0)
+ break;
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Flags = %02x", *data);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " %s", getflag(*data, DHCPV6_FQDNF_S,
+ "Perform AAAA RR updates", "No AAAA RR updates"));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " %s", getflag(*data, DHCPV6_FQDNF_O,
+ "Server override updates",
+ "No server override updates"));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " %s", getflag(*data, DHCPV6_FQDNF_N,
+ "Server performs no updates",
+ "Server performs updates"));
+ symp = inittab_getbycode(
+ ITAB_CAT_STANDARD | ITAB_CAT_V6, ITAB_CONS_SNOOP,
+ d6o.d6o_code);
+ if (symp != NULL) {
+ str = inittab_decode(symp, data, olen, B_TRUE);
+ if (str != NULL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ " FQDN = %s", str);
+ free(str);
+ }
+ free(symp);
+ }
+ break;
+ }
+ }
+ data = ostart + d6o.d6o_len;
+ len -= d6o.d6o_len;
+ }
+ if (len != 0) {
+ (void) strlcpy(get_line(0, 0), "Option entry truncated",
+ get_line_remain());
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_display.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_display.c
new file mode 100644
index 0000000..9bc4eb5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_display.c
@@ -0,0 +1,836 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/bufmod.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <inttypes.h>
+
+#include "snoop.h"
+
+char *dlc_header;
+char *src_name, *dst_name;
+int pi_frame;
+int pi_time_hour;
+int pi_time_min;
+int pi_time_sec;
+int pi_time_usec;
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+static void hexdump(char *, int);
+
+/*
+ * This routine invokes the packet interpreters
+ * on a packet. There's some messing around
+ * setting up a few packet-externals before
+ * starting with the ethernet interpreter.
+ * Yes, we assume here that all packets will
+ * be ethernet packets.
+ */
+void
+process_pkt(struct sb_hdr *hdrp, char *pktp, int num, int flags)
+{
+ int drops, pktlen;
+ struct timeval *tvp;
+ struct tm *tm;
+ extern int x_offset;
+ extern int x_length;
+ int offset, length;
+ static struct timeval ptv;
+
+ if (hdrp == NULL)
+ return;
+
+ tvp = &hdrp->sbh_timestamp;
+ if (ptv.tv_sec == 0)
+ ptv = *tvp;
+ drops = hdrp->sbh_drops;
+ pktlen = hdrp->sbh_msglen;
+ if (pktlen <= 0)
+ return;
+
+ /* set up externals */
+ dlc_header = pktp;
+ pi_frame = num;
+ tm = localtime(&tvp->tv_sec);
+ pi_time_hour = tm->tm_hour;
+ pi_time_min = tm->tm_min;
+ pi_time_sec = tm->tm_sec;
+ pi_time_usec = tvp->tv_usec;
+
+ src_name = "?";
+ dst_name = "*";
+
+ click(hdrp->sbh_origlen);
+
+ (*interface->interpreter)(flags, dlc_header, hdrp->sbh_msglen,
+ hdrp->sbh_origlen);
+
+ show_pktinfo(flags, num, src_name, dst_name, &ptv, tvp, drops,
+ hdrp->sbh_origlen);
+
+ if (x_offset >= 0) {
+ offset = MIN(x_offset, hdrp->sbh_msglen);
+ offset -= (offset % 2); /* round down */
+ length = MIN(hdrp->sbh_msglen - offset, x_length);
+
+ hexdump(dlc_header + offset, length);
+ }
+
+ ptv = *tvp;
+}
+
+
+/*
+ * *************************************************************
+ * The following routines constitute a library
+ * used by the packet interpreters to facilitate
+ * the display of packet data. This library
+ * of routines helps provide a consistent
+ * "look and feel".
+ */
+
+
+/*
+ * Display the value of a flag bit in
+ * a byte together with some text that
+ * corresponds to its value - whether
+ * true or false.
+ */
+char *
+getflag(int val, int mask, char *s_true, char *s_false)
+{
+ static char buff[80];
+ char *p;
+ int set;
+
+ (void) strcpy(buff, ".... .... = ");
+ if (s_false == NULL)
+ s_false = s_true;
+
+ for (p = &buff[8]; p >= buff; p--) {
+ if (*p == ' ')
+ p--;
+ if (mask & 0x1) {
+ set = val & mask & 0x1;
+ *p = set ? '1':'0';
+ (void) strcat(buff, set ? s_true: s_false);
+ break;
+ }
+ mask >>= 1;
+ val >>= 1;
+ }
+ return (buff);
+}
+
+XDR xdrm;
+jmp_buf xdr_err;
+int xdr_totlen;
+char *prot_prefix;
+char *prot_nest_prefix = "";
+char *prot_title;
+
+void
+show_header(char *pref, char *str, int len)
+{
+ prot_prefix = pref;
+ prot_title = str;
+ (void) sprintf(get_detail_line(0, len), "%s%s----- %s -----",
+ prot_nest_prefix, pref, str);
+}
+
+void
+xdr_init(char *addr, int len)
+{
+ xdr_totlen = len;
+ xdrmem_create(&xdrm, addr, len, XDR_DECODE);
+}
+
+/* Note: begin+end are ignored in get_detail_line */
+char *
+get_line(int begin, int end)
+{
+ char *line;
+
+ line = get_detail_line(begin, end);
+ (void) strcpy(line, prot_nest_prefix);
+ (void) strcat(line, prot_prefix);
+ return (line + strlen(line));
+}
+
+int
+get_line_remain(void)
+{
+ return (MAXLINE - strlen(prot_nest_prefix) - strlen(prot_prefix));
+}
+
+void
+show_line(char *str)
+{
+ (void) strlcpy(get_line(0, 0), str, get_line_remain());
+}
+
+void
+show_printf(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void) vsnprintf(get_line(0, 0), get_line_remain(), fmt, ap);
+ va_end(ap);
+}
+
+char
+getxdr_char()
+{
+ char s;
+
+ if (xdr_char(&xdrm, &s))
+ return (s);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+char
+showxdr_char(char *fmt)
+{
+ int pos; char val;
+
+ pos = getxdr_pos();
+ val = getxdr_char();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+uchar_t
+getxdr_u_char()
+{
+ uchar_t s;
+
+ if (xdr_u_char(&xdrm, &s))
+ return (s);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+uchar_t
+showxdr_u_char(char *fmt)
+{
+ int pos;
+ uchar_t val;
+
+ pos = getxdr_pos();
+ val = getxdr_u_char();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+short
+getxdr_short()
+{
+ short s;
+
+ if (xdr_short(&xdrm, &s))
+ return (s);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+short
+showxdr_short(char *fmt)
+{
+ int pos; short val;
+
+ pos = getxdr_pos();
+ val = getxdr_short();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+ushort_t
+getxdr_u_short()
+{
+ ushort_t s;
+
+ if (xdr_u_short(&xdrm, &s))
+ return (s);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+ushort_t
+showxdr_u_short(char *fmt)
+{
+ int pos;
+ ushort_t val;
+
+ pos = getxdr_pos();
+ val = getxdr_u_short();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+long
+getxdr_long()
+{
+ long l;
+
+ if (xdr_long(&xdrm, &l))
+ return (l);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+long
+showxdr_long(char *fmt)
+{
+ int pos; long val;
+
+ pos = getxdr_pos();
+ val = getxdr_long();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+ulong_t
+getxdr_u_long()
+{
+ ulong_t l;
+
+ if (xdr_u_long(&xdrm, &l))
+ return (l);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+ulong_t
+showxdr_u_long(char *fmt)
+{
+ int pos;
+ ulong_t val;
+
+ pos = getxdr_pos();
+ val = getxdr_u_long();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+longlong_t
+getxdr_longlong()
+{
+ longlong_t l;
+
+ if (xdr_longlong_t(&xdrm, &l))
+ return (l);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+longlong_t
+showxdr_longlong(char *fmt)
+{
+ int pos; longlong_t val;
+
+ pos = getxdr_pos();
+ val = getxdr_longlong();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+u_longlong_t
+getxdr_u_longlong()
+{
+ u_longlong_t l;
+
+ if (xdr_u_longlong_t(&xdrm, &l))
+ return (l);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+u_longlong_t
+showxdr_u_longlong(char *fmt)
+{
+ int pos; u_longlong_t val;
+
+ pos = getxdr_pos();
+ val = getxdr_u_longlong();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
+ return (val);
+}
+
+bool_t
+getxdr_bool()
+{
+ bool_t b;
+
+ if (xdr_bool(&xdrm, &b))
+ return (b);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+bool_t
+showxdr_bool(char *fmt)
+{
+ int pos; bool_t val;
+
+ pos = getxdr_pos();
+ val = getxdr_bool();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt,
+ val ? "True" : "False");
+ return (val);
+}
+
+char *
+getxdr_opaque(char *p, int len)
+{
+ if (xdr_opaque(&xdrm, p, len))
+ return (p);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+char *
+getxdr_string(char *p, /* len+1 bytes or longer */
+ int len)
+{
+ if (xdr_string(&xdrm, &p, len))
+ return (p);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+char *
+showxdr_string(int len, /* XDR length */
+ char *fmt)
+{
+ static int buff_len = 0;
+ static char *buff = NULL;
+ int pos;
+
+ /*
+ * XDR strings don't necessarily have a trailing null over the
+ * wire. However, the XDR code will put one in for us. Make sure
+ * we have allocated room for it.
+ */
+ len++;
+
+ if ((len > buff_len) || (buff_len == 0)) {
+ if (buff)
+ free(buff);
+ if ((buff = (char *)malloc(len)) == NULL)
+ pr_err("showxdr_string: no mem");
+ buff_len = len;
+ }
+ pos = getxdr_pos();
+ getxdr_string(buff, len);
+ (void) strcpy(buff+60, "...");
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, buff);
+ return (buff);
+}
+
+char *
+getxdr_bytes(uint_t *lenp)
+{
+ static char buff[1024];
+ char *p = buff;
+
+ if (xdr_bytes(&xdrm, &p, lenp, 1024))
+ return (buff);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+char *
+getxdr_context(char *p, int len)
+{
+ ushort_t size;
+
+ size = getxdr_u_short();
+ if (((int)size > 0) && ((int)size < len) && getxdr_opaque(p, size))
+ return (p);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+char *
+showxdr_context(char *fmt)
+{
+ ushort_t size;
+ static char buff[1024];
+ int pos;
+
+ pos = getxdr_pos();
+ size = getxdr_u_short();
+ if (((int)size > 0) && ((int)size < 1024) &&
+ getxdr_opaque(buff, size)) {
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, buff);
+ return (buff);
+ }
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+enum_t
+getxdr_enum()
+{
+ enum_t e;
+
+ if (xdr_enum(&xdrm, &e))
+ return (e);
+ longjmp(xdr_err, 1);
+ /* NOTREACHED */
+}
+
+void
+xdr_skip(int delta)
+{
+ uint_t pos;
+ if (delta % 4 != 0 || delta < 0)
+ longjmp(xdr_err, 1);
+ /* Check for overflow */
+ pos = xdr_getpos(&xdrm);
+ if ((pos + delta) < pos)
+ longjmp(xdr_err, 1);
+ /* xdr_setpos() checks for buffer overrun */
+ if (xdr_setpos(&xdrm, pos + delta) == FALSE)
+ longjmp(xdr_err, 1);
+}
+
+int
+getxdr_pos()
+{
+ return (xdr_getpos(&xdrm));
+}
+
+void
+setxdr_pos(int pos)
+{
+ xdr_setpos(&xdrm, pos);
+}
+
+void
+show_space()
+{
+ (void) get_line(0, 0);
+}
+
+void
+show_trailer()
+{
+ show_space();
+}
+
+char *
+getxdr_date()
+{
+ time_t sec;
+ int usec;
+ static char buff[64];
+ char *p;
+ struct tm my_time; /* private buffer to avoid collision */
+ /* between gmtime and strftime */
+ struct tm *tmp;
+
+ sec = getxdr_long();
+ usec = getxdr_long();
+ if (sec == -1)
+ return ("-1 ");
+
+ if (sec < 3600 * 24 * 365) { /* assume not a date */
+ (void) sprintf(buff, "%d.%06d", sec, usec);
+ } else {
+ tmp = gmtime(&sec);
+ (void) memcpy(&my_time, tmp, sizeof (struct tm));
+ strftime(buff, sizeof (buff), "%d-%h-%y %T.", &my_time);
+ p = buff + strlen(buff);
+ (void) sprintf(p, "%06d GMT", usec);
+ }
+ return (buff);
+}
+
+char *
+showxdr_date(char *fmt)
+{
+ int pos;
+ char *p;
+
+ pos = getxdr_pos();
+ p = getxdr_date();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, p);
+ return (p);
+}
+
+char *
+getxdr_date_ns(void)
+{
+ time_t sec, nsec;
+
+ sec = getxdr_long();
+ nsec = getxdr_long();
+ if (sec == -1)
+ return ("-1 ");
+ else
+ return (format_time(sec, nsec));
+}
+
+/*
+ * Format the given time.
+ */
+char *
+format_time(int64_t sec, uint32_t nsec)
+{
+ static char buff[64];
+ char *p;
+ struct tm my_time; /* private buffer to avoid collision */
+ /* between gmtime and strftime */
+ struct tm *tmp;
+
+ if (sec < 3600 * 24 * 365) {
+ /* assume not a date; includes negative times */
+ (void) sprintf(buff, "%lld.%06d", sec, nsec);
+ } else if (sec > INT32_MAX) {
+ /*
+ * XXX No routines are available yet for formatting 64-bit
+ * times.
+ */
+ (void) sprintf(buff, "%lld.%06d", sec, nsec);
+ } else {
+ time_t sec32 = (time_t)sec;
+
+ tmp = gmtime(&sec32);
+ memcpy(&my_time, tmp, sizeof (struct tm));
+ strftime(buff, sizeof (buff), "%d-%h-%y %T.", &my_time);
+ p = buff + strlen(buff);
+ (void) sprintf(p, "%09d GMT", nsec);
+ }
+ return (buff);
+}
+
+char *
+showxdr_date_ns(char *fmt)
+{
+ int pos;
+ char *p;
+
+ pos = getxdr_pos();
+ p = getxdr_date_ns();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, p);
+ return (p);
+}
+
+char *
+getxdr_time()
+{
+ time_t sec;
+ static char buff[64];
+ struct tm my_time; /* private buffer to avoid collision */
+ /* between gmtime and strftime */
+ struct tm *tmp;
+
+ sec = getxdr_long();
+ if (sec == -1)
+ return ("-1 ");
+
+ if (sec < 3600 * 24 * 365) { /* assume not a date */
+ (void) sprintf(buff, "%d", sec);
+ } else {
+ tmp = gmtime(&sec);
+ memcpy(&my_time, tmp, sizeof (struct tm));
+ strftime(buff, sizeof (buff), "%d-%h-%y %T", &my_time);
+ }
+ return (buff);
+}
+
+char *
+showxdr_time(char *fmt)
+{
+ int pos;
+ char *p;
+
+ pos = getxdr_pos();
+ p = getxdr_time();
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, p);
+ return (p);
+}
+
+char *
+getxdr_hex(int len)
+{
+ int i, j;
+ static char hbuff[1024];
+ char rbuff[1024];
+ static char *hexstr = "0123456789ABCDEF";
+ char toobig = 0;
+
+ if (len == 0) {
+ hbuff[0] = '\0';
+ return (hbuff);
+ }
+ if (len > 1024)
+ len = 1024;
+ if (len < 0 || xdr_opaque(&xdrm, rbuff, len) == FALSE) {
+ longjmp(xdr_err, 1);
+ }
+
+ if (len * 2 > sizeof (hbuff)) {
+ toobig++;
+ len = sizeof (hbuff) / 2;
+ }
+
+ j = 0;
+ for (i = 0; i < len; i++) {
+ hbuff[j++] = hexstr[rbuff[i] >> 4 & 0x0f];
+ hbuff[j++] = hexstr[rbuff[i] & 0x0f];
+ }
+
+ if (toobig) {
+ hbuff[len * 2 - strlen("<Too Long>")] = '\0';
+ strcat(hbuff, "<Too Long>");
+ } else
+ hbuff[j] = '\0';
+
+ return (hbuff);
+}
+
+char *
+showxdr_hex(int len, char *fmt)
+{
+ int pos;
+ char *p;
+
+ pos = getxdr_pos();
+ p = getxdr_hex(len);
+ (void) sprintf(get_line(pos, getxdr_pos()), fmt, p);
+ return (p);
+}
+
+static void
+hexdump(char *data, int datalen)
+{
+ char *p;
+ ushort_t *p16 = (ushort_t *)data;
+ char *p8 = data;
+ int i, left, len;
+ int chunk = 16; /* 16 bytes per line */
+
+ printf("\n");
+
+ for (p = data; p < data + datalen; p += chunk) {
+ printf("\t%4d: ", p - data);
+ left = (data + datalen) - p;
+ len = MIN(chunk, left);
+ for (i = 0; i < (len / 2); i++)
+ printf("%04x ", ntohs(*p16++) & 0xffff);
+ if (len % 2) {
+ printf("%02x ", *((unsigned char *)p16));
+ }
+ for (i = 0; i < (chunk - left) / 2; i++)
+ printf(" ");
+
+ printf(" ");
+ for (i = 0; i < len; i++, p8++)
+ printf("%c", isprint(*p8) ? *p8 : '.');
+ printf("\n");
+ }
+
+ printf("\n");
+}
+
+char *
+show_string(const char *str, int dlen, int maxlen)
+/*
+ * Prints len bytes from str enclosed in quotes.
+ * If len is negative, length is taken from strlen(str).
+ * No more than maxlen bytes will be printed. Longer
+ * strings are flagged with ".." after the closing quote.
+ * Non-printing characters are converted to C-style escape
+ * codes or octal digits.
+ */
+{
+#define TBSIZE 256
+ static char tbuff[TBSIZE];
+ const char *p;
+ char *pp;
+ int printable = 0;
+ int c, len;
+
+ len = dlen > maxlen ? maxlen : dlen;
+ dlen = len;
+
+ for (p = str, pp = tbuff; len; p++, len--) {
+ switch (c = *p & 0xFF) {
+ case '\n': (void) strcpy(pp, "\\n"); pp += 2; break;
+ case '\b': (void) strcpy(pp, "\\b"); pp += 2; break;
+ case '\t': (void) strcpy(pp, "\\t"); pp += 2; break;
+ case '\r': (void) strcpy(pp, "\\r"); pp += 2; break;
+ case '\f': (void) strcpy(pp, "\\f"); pp += 2; break;
+ default:
+ if (isascii(c) && isprint(c)) {
+ *pp++ = c;
+ printable++;
+ } else {
+ (void) snprintf(pp, TBSIZE - (pp - tbuff),
+ isdigit(*(p + 1)) ?
+ "\\%03o" : "\\%o", c);
+ pp += strlen(pp);
+ }
+ break;
+ }
+ *pp = '\0';
+ /*
+ * Check for overflow of temporary buffer. Allow for
+ * the next character to be a \nnn followed by a trailing
+ * null. If not, then just bail with what we have.
+ */
+ if (pp + 5 >= &tbuff[TBSIZE]) {
+ break;
+ }
+ }
+ return (printable > dlen / 2 ? tbuff : "");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c
new file mode 100644
index 0000000..309d24a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c
@@ -0,0 +1,917 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/tiuser.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include "snoop.h"
+
+/* The string used to indent detail lines */
+#define DNS_INDENT " "
+/*
+ * From RFC1035, the maximum size of a character-string is limited by the
+ * one octet length field. We add one character to that to make sure the
+ * result is terminated.
+ */
+#define MAX_CHAR_STRING_SIZE UCHAR_MAX + 1
+
+/* private functions */
+static char *dns_opcode_string(uint_t opcode);
+static char *dns_rcode_string(uint_t rcode);
+static char *dns_type_string(uint_t type, int detail);
+static char *dns_class_string(uint_t cls, int detail);
+static size_t skip_question(const uchar_t *header, const uchar_t *data,
+ const uchar_t *data_end);
+static size_t print_question(char *line, const uchar_t *header,
+ const uchar_t *data, const uchar_t *data_end, int detail);
+static size_t print_answer(char *line, const uchar_t *header,
+ const uchar_t *data, const uchar_t *data_end, int detail);
+static char *binary_string(char data);
+static void print_ip(int af, char *line, const uchar_t *data, uint16_t len);
+static const uchar_t *get_char_string(const uchar_t *data, char *charbuf,
+ uint16_t datalen);
+static size_t print_char_string(char *line, const uchar_t *data, uint16_t len);
+static const uchar_t *get_domain_name(const uchar_t *header,
+ const uchar_t *data, const uchar_t *data_end, char *namebuf, char *namend);
+static size_t print_domain_name(char *line, const uchar_t *header,
+ const uchar_t *data, const uchar_t *data_end);
+
+void
+interpret_dns(int flags, int proto, const uchar_t *data, int len, int port)
+{
+ typedef HEADER dns_header;
+ dns_header header;
+ char *line;
+ ushort_t id, qdcount, ancount, nscount, arcount;
+ ushort_t count;
+ const uchar_t *rrp; /* Resource Record Pointer. */
+ const uchar_t *data_end;
+ const char *protostr;
+ char *protopfxstr;
+ char *protohdrstr;
+
+ if (proto == IPPROTO_TCP) {
+ /* not supported now */
+ return;
+ }
+
+ if (port == IPPORT_DOMAIN) {
+ protostr = "DNS";
+ protopfxstr = "DNS: ";
+ protohdrstr = "DNS Header";
+ } else {
+ protostr = "MDNS";
+ protopfxstr = "MDNS: ";
+ protohdrstr = "MDNS Header";
+ }
+
+ /* We need at least the header in order to parse a packet. */
+ if (sizeof (dns_header) > len) {
+ return;
+ }
+ data_end = data + len;
+ /*
+ * Copy the header into a local structure for aligned access to
+ * each field.
+ */
+ (void) memcpy(&header, data, sizeof (header));
+ id = ntohs(header.id);
+ qdcount = ntohs(header.qdcount);
+ ancount = ntohs(header.ancount);
+ nscount = ntohs(header.nscount);
+ arcount = ntohs(header.arcount);
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+ line += sprintf(line, "%s %c ",
+ protostr, header.qr ? 'R' : 'C');
+
+ if (header.qr) {
+ /* answer */
+ if (header.rcode == 0) {
+ /* reply is OK */
+ rrp = data + sizeof (dns_header);
+ while (qdcount--) {
+ if (rrp >= data_end) {
+ return;
+ }
+ rrp += skip_question(data,
+ rrp, data_end);
+ }
+ /* the answers follow the questions */
+ if (ancount > 0) {
+ (void) print_answer(line,
+ data, rrp, data_end, FALSE);
+ }
+ } else {
+ (void) sprintf(line, " Error: %d(%s)",
+ header.rcode,
+ dns_rcode_string(header.rcode));
+ }
+ } else {
+ /* question */
+ rrp = data + sizeof (dns_header);
+ if (rrp >= data_end) {
+ return;
+ }
+ (void) print_question(line, data, rrp, data_end,
+ FALSE);
+ }
+ }
+ if (flags & F_DTAIL) {
+ show_header(protopfxstr, protohdrstr, sizeof (dns_header));
+ show_space();
+ if (header.qr) {
+ /* answer */
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Response ID = %d", id);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s%s%s",
+ header.aa ? "AA (Authoritative Answer) " : "",
+ header.tc ? "TC (TrunCation) " : "",
+ header.ra ? "RA (Recursion Available) ": "");
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Response Code: %d (%s)",
+ header.rcode, dns_rcode_string(header.rcode));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Reply to %d question(s)", qdcount);
+ } else {
+ /* question */
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Query ID = %d", id);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Opcode: %s", dns_opcode_string(header.opcode));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s%s",
+ header.tc ? "TC (TrunCation) " : "",
+ header.rd ? "RD (Recursion Desired) " : "");
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d question(s)", qdcount);
+ }
+ rrp = data + sizeof (dns_header);
+ count = 0;
+ while (qdcount--) {
+ if (rrp >= data_end) {
+ return;
+ }
+ count++;
+ rrp += print_question(get_line(0, 0),
+ data, rrp, data_end, TRUE);
+ show_space();
+ }
+ /* Only answers should hold answers, but just in case */
+ if (header.qr || ancount > 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d answer(s)", ancount);
+ count = 0;
+ while (ancount--) {
+ if (rrp >= data_end) {
+ return;
+ }
+ count++;
+ rrp += print_answer(get_line(0, 0),
+ data, rrp, data_end, TRUE);
+ show_space();
+ }
+ }
+ /* Likewise only answers should hold NS records */
+ if (header.qr || nscount > 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d name server resource(s)", nscount);
+ count = 0;
+ while (nscount--) {
+ if (rrp >= data_end) {
+ return;
+ }
+ count++;
+ rrp += print_answer(get_line(0, 0), data,
+ rrp, data_end, TRUE);
+ show_space();
+ }
+ }
+ /* Additional section may hold an EDNS0 record. */
+ if (header.qr || arcount > 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d additional record(s)", arcount);
+ count = 0;
+ while (arcount-- && rrp < data_end) {
+ count++;
+ rrp += print_answer(get_line(0, 0), data,
+ rrp, data_end, TRUE);
+ show_space();
+ }
+ }
+ }
+}
+
+
+static char *
+dns_opcode_string(uint_t opcode)
+{
+ static char buffer[64];
+ switch (opcode) {
+ case ns_o_query: return ("Query");
+ case ns_o_iquery: return ("Inverse Query");
+ case ns_o_status: return ("Status");
+ default:
+ (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)",
+ opcode);
+ return (buffer);
+ }
+}
+
+static char *
+dns_rcode_string(uint_t rcode)
+{
+ static char buffer[64];
+ switch (rcode) {
+ case ns_r_noerror: return ("OK");
+ case ns_r_formerr: return ("Format Error");
+ case ns_r_servfail: return ("Server Fail");
+ case ns_r_nxdomain: return ("Name Error");
+ case ns_r_notimpl: return ("Unimplemented");
+ case ns_r_refused: return ("Refused");
+ case ns_r_badvers: return ("Bad Version"); /* EDNS rcode */
+ default:
+ (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", rcode);
+ return (buffer);
+ }
+}
+
+static char *
+dns_type_string(uint_t type, int detail)
+{
+ static char buffer[64];
+ switch (type) {
+ case ns_t_a: return (detail ? "Address" : "Addr");
+ case ns_t_ns: return (detail ? "Authoritative Name Server" : "NS");
+ case ns_t_cname: return (detail ? "Canonical Name" : "CNAME");
+ case ns_t_soa: return (detail ? "Start Of a zone Authority" : "SOA");
+ case ns_t_mb: return (detail ? "Mailbox domain name" : "MB");
+ case ns_t_mg: return (detail ? "Mailbox Group member" : "MG");
+ case ns_t_mr: return (detail ? "Mail Rename domain name" : "MR");
+ case ns_t_null: return ("NULL");
+ case ns_t_wks: return (detail ? "Well Known Service" : "WKS");
+ case ns_t_ptr: return (detail ? "Domain Name Pointer" : "PTR");
+ case ns_t_hinfo: return (detail ? "Host Information": "HINFO");
+ case ns_t_minfo:
+ return (detail ? "Mailbox or maillist Info" : "MINFO");
+ case ns_t_mx: return (detail ? "Mail Exchange" : "MX");
+ case ns_t_txt: return (detail ? "Text strings" : "TXT");
+ case ns_t_aaaa: return (detail ? "IPv6 Address" : "AAAA");
+ case ns_t_opt: return (detail ? "EDNS0 option" : "OPT");
+ case ns_t_axfr: return (detail ? "Transfer of entire zone" : "AXFR");
+ case ns_t_mailb:
+ return (detail ? "Mailbox related records" : "MAILB");
+ case ns_t_maila: return (detail ? "Mail agent RRs" : "MAILA");
+ case ns_t_any: return (detail ? "All records" : "*");
+ default:
+ (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", type);
+ return (buffer);
+ }
+}
+
+static char *
+dns_class_string(uint_t cls, int detail)
+{
+ static char buffer[64];
+ switch (cls) {
+ case ns_c_in: return (detail ? "Internet" : "Internet");
+ case ns_c_chaos: return (detail ? "CHAOS" : "CH");
+ case ns_c_hs: return (detail ? "Hesiod" : "HS");
+ case ns_c_any: return (detail ? "* (Any class)" : "*");
+ default:
+ (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", cls);
+ return (buffer);
+ }
+}
+
+static size_t
+skip_question(const uchar_t *header, const uchar_t *data,
+ const uchar_t *data_end)
+{
+ const uchar_t *data_bak = data;
+ char dummy_buffer[NS_MAXDNAME];
+
+ data = get_domain_name(header, data, data_end, dummy_buffer,
+ dummy_buffer + sizeof (dummy_buffer));
+ /* Skip the 32 bits of class and type that follow the domain name */
+ data += sizeof (uint32_t);
+ return (data - data_bak);
+}
+
+static size_t
+print_question(char *line, const uchar_t *header, const uchar_t *data,
+ const uchar_t *data_end, int detail)
+{
+ const uchar_t *data_bak = data;
+ uint16_t type;
+ uint16_t cls;
+
+ if (detail) {
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT "Domain Name: ");
+ }
+ data += print_domain_name(line, header, data, data_end);
+
+ /*
+ * Make sure we don't run off the end of the packet by reading the
+ * type and class.
+ *
+ * The pointer subtraction on the left side of the following
+ * expression has a signed result of type ptrdiff_t, and the right
+ * side has an unsigned result of type size_t. We therefore need
+ * to cast the right side of the expression to be of the same
+ * signed type to keep the result of the pointer arithmetic to be
+ * automatically cast to an unsigned value. We do a similar cast
+ * in other similar expressions throughout this file.
+ */
+ if ((data_end - data) < (ptrdiff_t)(2 * sizeof (uint16_t)))
+ return (data_end - data_bak);
+
+ GETINT16(type, data);
+ GETINT16(cls, data);
+
+ /*
+ * Multicast DNS re-uses the top bit of the class field
+ * in the question and answer sections. Unicast DNS only
+ * uses 1 (Internet), 3 and 4. Hence it is safe. The top
+ * order bit is always cleared here to display the rrclass in case
+ * of Multicast DNS packets.
+ */
+ cls = cls & 0x7fff;
+
+ if (detail) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Class: %u (%s)",
+ cls, dns_class_string(cls, detail));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Type: %u (%s)", type,
+ dns_type_string(type, detail));
+ } else {
+ (void) sprintf(line + strlen(line), " %s %s \?",
+ dns_class_string(cls, detail),
+ dns_type_string(type, detail));
+ }
+ return (data - data_bak);
+}
+
+/*
+ * print_answer() is used to display the contents of a single resource
+ * record (RR) from either the answer, name server or additional
+ * section of the DNS packet.
+ *
+ * Input:
+ * *line: snoops output buffer.
+ * *header: start of the DNS packet, required for names and rcode.
+ * *data: location within header from where the RR starts.
+ * *data_end: where DNS data ends.
+ * detail: simple or verbose output.
+ *
+ * Returns:
+ * Pointer to next RR or data_end.
+ *
+ * Most RRs have the same top level format as defined in RFC 1035:
+ *
+ * 1 1 1 1 1 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | |
+ * / NAME /
+ * | |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | TYPE |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | CLASS |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | TTL |
+ * | |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | RDLENGTH |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+ * / RDATA /
+ * / /
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *
+ * However RFC 2671 introduced an exception to this rule
+ * with the "Extension Mechanisms for DNS" (EDNS0).
+ * When the type is 41 the remaining resource record format
+ * is:
+ *
+ * 1 1 1 1 1 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | TYPE = 41 |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | Sender's UDP payload size |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | Extended-rcode | Version |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | Zero |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | RDLENGTH |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+ * / RDATA /
+ * / /
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *
+ */
+static size_t
+print_answer(char *line, const uchar_t *header, const uchar_t *data,
+ const uchar_t *data_end, int detail)
+{
+ const uchar_t *data_bak = data;
+ const uchar_t *data_next;
+ uint16_t type;
+ uint16_t cls;
+ int32_t ttl;
+ uint16_t rdlen;
+ uint32_t serial, refresh, retry, expire, minimum;
+ uint8_t protocol;
+ int linepos;
+ uint16_t preference;
+ /* declarations for EDNS follow */
+ uint16_t size; /* Sender's UDP payload size */
+ uint8_t xrcode; /* Extended-rcode */
+ uint8_t ver; /* Version */
+ uint16_t rcode; /* Extracted from the DNS header */
+ union { /* DNS header overlay used for extraction */
+ HEADER *head;
+ const uchar_t *raw;
+ } headptr;
+
+ if (detail) {
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT "Domain Name: ");
+ }
+ data += print_domain_name(line, header, data, data_end);
+
+ /*
+ * Next, get the record type, being careful to make sure we
+ * don't run off the end of the packet.
+ */
+ if ((data_end - data) < (ptrdiff_t)(sizeof (type))) {
+ return (data_end - data_bak);
+ }
+
+ GETINT16(type, data);
+
+ if (type == ns_t_opt) {
+ /*
+ * Make sure we won't run off the end reading size,
+ * xrcode, version, zero and rdlen.
+ */
+ if ((data_end - data) <
+ ((ptrdiff_t)(sizeof (size)
+ + sizeof (xrcode)
+ + sizeof (ver)
+ + sizeof (cls) /* zero */
+ + sizeof (rdlen)))) {
+ return (data_end - data_bak);
+ }
+
+ GETINT16(size, data);
+ GETINT8(xrcode, data);
+ /*
+ * The extended rcode represents the top half of the
+ * rcode which must be added to the rcode in the header.
+ */
+ rcode = 0xff & (xrcode << 4);
+ headptr.raw = header; /* Overlay the header... */
+ rcode += headptr.head->rcode; /* And pluck out the rcode. */
+
+ GETINT8(ver, data);
+ GETINT16(cls, data); /* zero */
+ GETINT16(rdlen, data);
+
+ if (detail) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Type: %u (%s)", type,
+ dns_type_string(type, detail));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "UDP payload size: %u (0x%.4x)",
+ size, size);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Extended rcode: %u "
+ "(translates to %u (%s))",
+ xrcode, rcode, dns_rcode_string(rcode));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "EDNS0 Version: %u", ver);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "zero: %u", cls);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Data length: %u", rdlen);
+ } else {
+ line += strlen(line);
+ line += sprintf(line, " %s UDP %u rc %d ver %u len %u",
+ dns_type_string(type, detail), size, rcode, ver,
+ rdlen);
+ }
+
+ /*
+ * Make sure that rdlen is within data boundary.
+ */
+ if (rdlen > data_end - data)
+ return (data_end - data_bak);
+
+ /* Future OPT decode code goes here. */
+
+ data += rdlen;
+ return (data - data_bak);
+ }
+
+ /*
+ * Make sure we don't run off the end of the packet by reading the
+ * class, ttl, and length.
+ */
+ if ((data_end - data) <
+ ((ptrdiff_t)(sizeof (cls)
+ + sizeof (ttl)
+ + sizeof (rdlen)))) {
+ return (data_end - data_bak);
+ }
+
+ GETINT16(cls, data);
+
+ /*
+ * Multicast DNS re-uses the top bit of the class field
+ * in the question and answer sections. Unicast DNS only
+ * uses 1 (Internet), 3 and 4. Hence it is safe. The top
+ * order bit is always cleared here to display the rrclass in case
+ * of Multicast DNS packets.
+ */
+ cls = cls & 0x7fff;
+
+ if (detail) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Class: %d (%s)", cls,
+ dns_class_string(cls, detail));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Type: %d (%s)", type,
+ dns_type_string(type, detail));
+ } else {
+ line += strlen(line);
+ line += sprintf(line, " %s %s ",
+ dns_class_string(cls, detail),
+ dns_type_string(type, detail));
+ }
+
+ GETINT32(ttl, data);
+ if (detail) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "TTL (Time To Live): %d", ttl);
+ }
+
+ GETINT16(rdlen, data);
+ if (detail) {
+ line = get_line(0, 0);
+ line += snprintf(line, get_line_remain(), DNS_INDENT "%s: ",
+ dns_type_string(type, detail));
+ }
+
+ if (rdlen > data_end - data)
+ return (data_end - data_bak);
+
+ switch (type) {
+ case ns_t_a:
+ print_ip(AF_INET, line, data, rdlen);
+ break;
+ case ns_t_aaaa:
+ print_ip(AF_INET6, line, data, rdlen);
+ break;
+ case ns_t_hinfo:
+ line += sprintf(line, "CPU: ");
+ data_next = data + print_char_string(line, data, rdlen);
+ if (data_next >= data_end)
+ break;
+ line += strlen(line);
+ line += sprintf(line, "OS: ");
+ (void) print_char_string(line, data_next,
+ rdlen - (data_next - data));
+ break;
+ case ns_t_ns:
+ case ns_t_cname:
+ case ns_t_mb:
+ case ns_t_mg:
+ case ns_t_mr:
+ case ns_t_ptr:
+ (void) print_domain_name(line, header, data, data_end);
+ break;
+ case ns_t_mx:
+ data_next = data;
+ if (rdlen < sizeof (uint16_t))
+ break;
+ GETINT16(preference, data_next);
+ if (detail) {
+ (void) print_domain_name(line, header, data_next,
+ data_end);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Preference: %u", preference);
+ } else {
+ (void) print_domain_name(line, header, data_next,
+ data_end);
+ }
+ break;
+ case ns_t_soa:
+ if (!detail)
+ break;
+ line = get_line(0, 0);
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT "MNAME (Server name): ");
+ data_next = data + print_domain_name(line, header, data,
+ data_end);
+ if (data_next >= data_end)
+ break;
+ line = get_line(0, 0);
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT "RNAME (Resposible mailbox): ");
+ data_next = data_next +
+ print_domain_name(line, header, data_next, data_end);
+ if ((data_end - data_next) < (ptrdiff_t)(5 * sizeof (uint32_t)))
+ break;
+ GETINT32(serial, data_next);
+ GETINT32(refresh, data_next);
+ GETINT32(retry, data_next);
+ GETINT32(expire, data_next);
+ GETINT32(minimum, data_next);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Serial: %u", serial);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Refresh: %u Retry: %u "
+ "Expire: %u Minimum: %u",
+ refresh, retry, expire, minimum);
+ break;
+ case ns_t_wks:
+ print_ip(AF_INET, line, data, rdlen);
+ if (!detail)
+ break;
+ data_next = data + sizeof (in_addr_t);
+ if (data_next >= data_end)
+ break;
+ GETINT8(protocol, data_next);
+ line = get_line(0, 0);
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT "Protocol: %u ", protocol);
+ switch (protocol) {
+ case IPPROTO_UDP:
+ (void) snprintf(line, get_line_remain(), "(UDP)");
+ break;
+ case IPPROTO_TCP:
+ (void) snprintf(line, get_line_remain(), "(TCP)");
+ break;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ DNS_INDENT "Service bitmap:");
+ (void) snprintf(line, get_line_remain(),
+ DNS_INDENT "0 8 16 24");
+ linepos = 4;
+ while (data_next < data + rdlen) {
+ if (linepos == 4) {
+ line = get_line(0, 0);
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT);
+ linepos = 0;
+ }
+ line += snprintf(line, get_line_remain(), "%s",
+ binary_string(*data_next));
+ linepos++;
+ data_next++;
+ }
+ break;
+ case ns_t_minfo:
+ if (!detail)
+ break;
+ line = get_line(0, 0);
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT "RMAILBX (Resposible mailbox): ");
+ data_next = data + print_domain_name(line, header, data,
+ data_end);
+ line = get_line(0, 0);
+ line += snprintf(line, get_line_remain(),
+ DNS_INDENT "EMAILBX (mailbox to receive err message): ");
+ data_next = data_next + print_domain_name(line, header,
+ data_next, data_end);
+ break;
+ }
+ data += rdlen;
+ return (data - data_bak);
+}
+
+static char *
+binary_string(char data)
+{
+ static char bstring[8 + 1];
+ char *ptr;
+ int i;
+ ptr = bstring;
+ for (i = 0; i < 8; i++) {
+ *ptr++ = (data & 0x80) ? '1' : '0';
+ data = data << 1;
+ }
+ *ptr = (char)0;
+ return (bstring);
+}
+
+static void
+print_ip(int af, char *line, const uchar_t *data, uint16_t len)
+{
+ in6_addr_t addr6;
+ in_addr_t addr4;
+ void *addr;
+
+ switch (af) {
+ case AF_INET:
+ if (len != sizeof (in_addr_t))
+ return;
+ addr = memcpy(&addr4, data, sizeof (addr4));
+ break;
+ case AF_INET6:
+ if (len != sizeof (in6_addr_t))
+ return;
+ addr = memcpy(&addr6, data, sizeof (addr6));
+ break;
+ }
+
+ (void) inet_ntop(af, addr, line, INET6_ADDRSTRLEN);
+}
+
+/*
+ * charbuf is assumed to be of size MAX_CHAR_STRING_SIZE.
+ */
+static const uchar_t *
+get_char_string(const uchar_t *data, char *charbuf, uint16_t datalen)
+{
+ int len;
+ char *name = charbuf;
+ int i = 0;
+
+ /*
+ * From RFC1035, a character-string is a single length octet followed
+ * by that number of characters.
+ */
+ if (datalen > 1) {
+ len = *data;
+ data++;
+ if (len > 0 && len < MAX_CHAR_STRING_SIZE) {
+ for (i = 0; i < len; i++, data++)
+ name[i] = *data;
+ }
+ }
+ name[i] = '\0';
+ return (data);
+}
+
+static size_t
+print_char_string(char *line, const uchar_t *data, uint16_t len)
+{
+ char charbuf[MAX_CHAR_STRING_SIZE];
+ const uchar_t *data_bak = data;
+
+ data = get_char_string(data, charbuf, len);
+ (void) sprintf(line, "%s", charbuf);
+ return (data - data_bak);
+}
+
+/*
+ * header: the entire message header, this is where we start to
+ * count the offset of the compression scheme
+ * data: the start of the domain name
+ * namebuf: user supplied buffer
+ * return: the next byte after what we have parsed
+ */
+static const uchar_t *
+get_domain_name(const uchar_t *header, const uchar_t *data,
+ const uchar_t *data_end, char *namebuf, char *namend)
+{
+ uint8_t len;
+ char *name = namebuf;
+
+ /*
+ * From RFC1035, a domain name is a sequence of labels, where each
+ * label consists of a length octet followed by that number of
+ * octets. The domain name terminates with the zero length octet
+ * for the null label of the root.
+ */
+
+ while (name < (namend - 1)) {
+ if ((data_end - data) < (ptrdiff_t)(sizeof (uint8_t))) {
+ /* The length octet is off the end of the packet. */
+ break;
+ }
+ GETINT8(len, data);
+ if (len == 0) {
+ /*
+ * Domain names end with a length byte of zero,
+ * which represents the null label of the root.
+ */
+ break;
+ }
+ /*
+ * test if we are using the compression scheme
+ */
+ if ((len & 0xc0) == 0xc0) {
+ uint16_t offset;
+ const uchar_t *label_ptr;
+
+ /*
+ * From RFC1035, message compression allows a
+ * domain name or a list of labels at the end of a
+ * domain name to be replaced with a pointer to a
+ * prior occurance of the same name. In this
+ * scheme, the pointer is a two octet sequence
+ * where the most significant two bits are set, and
+ * the remaining 14 bits are the offset from the
+ * start of the message of the next label.
+ */
+ data--;
+ if ((data_end - data) <
+ (ptrdiff_t)(sizeof (uint16_t))) {
+ /*
+ * The offset octets aren't entirely
+ * contained within this pakcet.
+ */
+ data = data_end;
+ break;
+ }
+ GETINT16(offset, data);
+ label_ptr = header + (offset & 0x3fff);
+ /*
+ * We must verify that the offset is valid by
+ * checking that it is less than the current data
+ * pointer and that it isn't off the end of the
+ * packet.
+ */
+ if (label_ptr > data || label_ptr >= data_end)
+ break;
+ (void) get_domain_name(header, label_ptr, data_end,
+ name, namend);
+ return (data);
+ } else {
+ if (len > (data_end - data)) {
+ /*
+ * The label isn't entirely contained
+ * within the packet. Don't read it. The
+ * caller checks that the data pointer is
+ * not beyond the end after we've
+ * incremented it.
+ */
+ data = data_end;
+ break;
+ }
+ while (len > 0 && name < (namend - 2)) {
+ *name = *data;
+ name++;
+ data++;
+ len--;
+ }
+ *name = '.';
+ name++;
+ }
+ }
+ *name = '\0';
+ return (data);
+}
+
+static size_t
+print_domain_name(char *line, const uchar_t *header, const uchar_t *data,
+ const uchar_t *data_end)
+{
+ char name[NS_MAXDNAME];
+ const uchar_t *new_data;
+
+ new_data = get_domain_name(header, data, data_end, name,
+ name + sizeof (name));
+
+ (void) sprintf(line, "%s", name);
+ return (new_data - data);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c
new file mode 100644
index 0000000..5c6bde0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c
@@ -0,0 +1,1802 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/sysmacros.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <sys/ib/clients/ibd/ibd.h>
+#include <sys/ethernet.h>
+#include <sys/vlan.h>
+#include <sys/zone.h>
+#include <inet/iptun.h>
+#include <sys/byteorder.h>
+#include <limits.h>
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <net/trill.h>
+
+#include "at.h"
+#include "snoop.h"
+
+static headerlen_fn_t ether_header_len, fddi_header_len, tr_header_len,
+ ib_header_len, ipnet_header_len, ipv4_header_len, ipv6_header_len;
+static interpreter_fn_t interpret_ether, interpret_fddi, interpret_tr,
+ interpret_ib, interpret_ipnet, interpret_iptun;
+static void addr_copy_swap(struct ether_addr *, struct ether_addr *);
+static int tr_machdr_len(char *, int *, int *);
+
+interface_t *interface;
+interface_t INTERFACES[] = {
+
+ /* IEEE 802.3 CSMA/CD network */
+ { DL_CSMACD, 1550, 12, 2, ETHERTYPE_IP, ETHERTYPE_IPV6,
+ ether_header_len, interpret_ether, B_TRUE },
+
+ /* Ethernet Bus */
+ { DL_ETHER, 1550, 12, 2, ETHERTYPE_IP, ETHERTYPE_IPV6,
+ ether_header_len, interpret_ether, B_TRUE },
+
+ /* Fiber Distributed data interface */
+ { DL_FDDI, 4500, 19, 2, ETHERTYPE_IP, ETHERTYPE_IPV6,
+ fddi_header_len, interpret_fddi, B_FALSE },
+
+ /* Token Ring interface */
+ { DL_TPR, 17800, 0, 2, ETHERTYPE_IP, ETHERTYPE_IPV6,
+ tr_header_len, interpret_tr, B_FALSE },
+
+ /* Infiniband */
+ { DL_IB, 4096, 0, 2, ETHERTYPE_IP, ETHERTYPE_IPV6,
+ ib_header_len, interpret_ib, B_TRUE },
+
+ /* ipnet */
+ { DL_IPNET, INT_MAX, 1, 1, IPV4_VERSION, IPV6_VERSION,
+ ipnet_header_len, interpret_ipnet, B_TRUE },
+
+ /* IPv4 tunnel */
+ { DL_IPV4, 0, 9, 1, IPPROTO_ENCAP, IPPROTO_IPV6,
+ ipv4_header_len, interpret_iptun, B_FALSE },
+
+ /* IPv6 tunnel */
+ { DL_IPV6, 0, 40, 1, IPPROTO_ENCAP, IPPROTO_IPV6,
+ ipv6_header_len, interpret_iptun, B_FALSE },
+
+ /* 6to4 tunnel */
+ { DL_6TO4, 0, 9, 1, IPPROTO_ENCAP, IPPROTO_IPV6,
+ ipv4_header_len, interpret_iptun, B_FALSE },
+
+ { (uint_t)-1, 0, 0, 0, 0, NULL, NULL, B_FALSE }
+};
+
+/* externals */
+extern char *dlc_header;
+extern int pi_frame;
+extern int pi_time_hour;
+extern int pi_time_min;
+extern int pi_time_sec;
+extern int pi_time_usec;
+
+char *printether();
+char *print_ethertype();
+static char *print_etherinfo();
+
+char *print_fc();
+char *print_smttype();
+char *print_smtclass();
+
+struct ether_addr ether_broadcast = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+static char *data; /* current data buffer */
+static int datalen; /* current data buffer length */
+static const struct ether_addr all_isis_rbridges = ALL_ISIS_RBRIDGES;
+
+uint_t
+interpret_ether(int flags, char *header, int elen, int origlen)
+{
+ struct ether_header *e = (struct ether_header *)header;
+ uchar_t *off, *ieeestart;
+ int len;
+ int ieee8023 = 0;
+ extern char *dst_name;
+ int ethertype;
+ struct ether_vlan_extinfo *evx = NULL;
+ int blen = MAX(origlen, ETHERMTU);
+ boolean_t trillpkt = B_FALSE;
+ uint16_t tci = 0;
+
+ if (data != NULL && datalen != 0 && datalen < blen) {
+ free(data);
+ data = NULL;
+ datalen = 0;
+ }
+ if (!data) {
+ data = (char *)malloc(blen);
+ if (!data)
+ pr_err("Warning: malloc failure");
+ datalen = blen;
+ }
+inner_pkt:
+ if (origlen < 14) {
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "RUNT (short packet - %d bytes)",
+ origlen);
+ }
+ if (flags & F_DTAIL)
+ show_header("RUNT: ", "Short packet", origlen);
+ return (elen);
+ }
+ if (elen < 14)
+ return (elen);
+
+ if (memcmp(&e->ether_dhost, &ether_broadcast,
+ sizeof (struct ether_addr)) == 0)
+ dst_name = "(broadcast)";
+ else if (e->ether_dhost.ether_addr_octet[0] & 1)
+ dst_name = "(multicast)";
+
+ ethertype = ntohs(e->ether_type);
+
+ /*
+ * The 14 byte ether header screws up alignment
+ * of the rest of the packet for 32 bit aligned
+ * architectures like SPARC. Alas, we have to copy
+ * the rest of the packet in order to align it.
+ */
+ len = elen - sizeof (struct ether_header);
+ off = (uchar_t *)(e + 1);
+
+ if (ethertype == ETHERTYPE_VLAN) {
+ if (origlen < sizeof (struct ether_vlan_header)) {
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "RUNT (short VLAN packet - %d bytes)",
+ origlen);
+ }
+ if (flags & F_DTAIL) {
+ show_header("RUNT: ", "Short VLAN packet",
+ origlen);
+ }
+ return (elen);
+ }
+ if (len < sizeof (struct ether_vlan_extinfo))
+ return (elen);
+
+ evx = (struct ether_vlan_extinfo *)off;
+ off += sizeof (struct ether_vlan_extinfo);
+ len -= sizeof (struct ether_vlan_extinfo);
+
+ ethertype = ntohs(evx->ether_type);
+ tci = ntohs(evx->ether_tci);
+ }
+
+ if (ethertype <= 1514) {
+ /*
+ * Fake out the IEEE 802.3 packets.
+ * Should be DSAP=0xAA, SSAP=0xAA, control=0x03
+ * then three padding bytes of zero (OUI),
+ * followed by a normal ethernet-type packet.
+ */
+ ieee8023 = ethertype;
+ ieeestart = off;
+ if (off[0] == 0xAA && off[1] == 0xAA) {
+ ethertype = ntohs(*(ushort_t *)(off + 6));
+ off += 8;
+ len -= 8;
+ } else {
+ ethertype = 0;
+ off += 3;
+ len -= 3;
+ }
+ }
+
+ if (flags & F_SUM) {
+ /*
+ * Set the flag that says don't display VLAN information.
+ * If it needs to change, that will be done later if the
+ * packet is VLAN tagged and if snoop is in its default
+ * summary mode.
+ */
+ set_vlan_id(0);
+ if (evx == NULL) {
+ if (ethertype == 0 && ieee8023 > 0) {
+ (void) sprintf(get_sum_line(),
+ "ETHER 802.3 SSAP %02X DSAP %02X, "
+ "size=%d bytes", ieeestart[0], ieeestart[1],
+ origlen);
+ } else {
+ (void) sprintf(get_sum_line(),
+ "ETHER Type=%04X (%s), size=%d bytes",
+ ethertype, print_ethertype(ethertype),
+ origlen);
+ }
+ } else {
+ if (ethertype == 0 && ieee8023 > 0) {
+ (void) sprintf(get_sum_line(),
+ "ETHER 802.3 SSAP %02X DSAP %02X, "
+ "VLAN ID=%hu, size=%d bytes", ieeestart[0],
+ ieeestart[1], VLAN_ID(tci), origlen);
+ } else {
+ (void) sprintf(get_sum_line(),
+ "ETHER Type=%04X (%s), VLAN ID=%hu, "
+ "size=%d bytes", ethertype,
+ print_ethertype(ethertype), VLAN_ID(tci),
+ origlen);
+ }
+
+ if (!(flags & F_ALLSUM))
+ set_vlan_id(VLAN_ID(tci));
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ETHER: ", "Ether Header", elen);
+ show_space();
+ if (!trillpkt) {
+ (void) sprintf(get_line(0, 0),
+ "Packet %d arrived at %d:%02d:%d.%05d",
+ pi_frame,
+ pi_time_hour, pi_time_min, pi_time_sec,
+ pi_time_usec / 10);
+ (void) sprintf(get_line(0, 0),
+ "Packet size = %d bytes",
+ elen, elen);
+ }
+ (void) sprintf(get_line(0, 6),
+ "Destination = %s, %s",
+ printether(&e->ether_dhost),
+ print_etherinfo(&e->ether_dhost));
+ (void) sprintf(get_line(6, 6),
+ "Source = %s, %s",
+ printether(&e->ether_shost),
+ print_etherinfo(&e->ether_shost));
+ if (evx != NULL) {
+ (void) sprintf(get_line(0, 0),
+ "VLAN ID = %hu", VLAN_ID(tci));
+ (void) sprintf(get_line(0, 0),
+ "VLAN Priority = %hu", VLAN_PRI(tci));
+ }
+ if (ieee8023 > 0) {
+ (void) sprintf(get_line(12, 2),
+ "IEEE 802.3 length = %d bytes", ieee8023);
+ /* Print LLC only for non-TCP/IP packets */
+ if (ethertype == 0) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "SSAP = %02X, DSAP = %02X, CTRL = %02X",
+ ieeestart[0], ieeestart[1], ieeestart[2]);
+ }
+ }
+ if (ethertype != 0 || ieee8023 == 0)
+ (void) sprintf(get_line(12, 2),
+ "Ethertype = %04X (%s)",
+ ethertype, print_ethertype(ethertype));
+ show_space();
+ }
+
+ /*
+ * We cannot trust the length field in the header to be correct.
+ * But we should continue to process the packet. Then user can
+ * notice something funny in the header.
+ * Go to the next protocol layer only if data have been
+ * copied.
+ */
+ if (len > 0 && (off + len <= (uchar_t *)e + elen)) {
+ (void) memmove(data, off, len);
+
+ if (!trillpkt && ethertype == ETHERTYPE_TRILL) {
+ ethertype = interpret_trill(flags, &e, data, &len);
+ /* Decode inner Ethernet frame */
+ if (ethertype != 0) {
+ evx = NULL;
+ trillpkt = B_TRUE;
+ (void) memmove(data, e, len);
+ e = (struct ether_header *)data;
+ origlen = len;
+ elen = len;
+ goto inner_pkt;
+ }
+ }
+
+ switch (ethertype) {
+ case ETHERTYPE_IP:
+ (void) interpret_ip(flags, (struct ip *)data, len);
+ break;
+ /* Just in case it is decided to add this type */
+ case ETHERTYPE_IPV6:
+ (void) interpret_ipv6(flags, (ip6_t *)data, len);
+ break;
+ case ETHERTYPE_ARP:
+ case ETHERTYPE_REVARP:
+ interpret_arp(flags, (struct arphdr *)data, len);
+ break;
+ case ETHERTYPE_PPPOED:
+ case ETHERTYPE_PPPOES:
+ (void) interpret_pppoe(flags, (poep_t *)data, len);
+ break;
+ case ETHERTYPE_AARP: /* AppleTalk */
+ interpret_aarp(flags, data, len);
+ break;
+ case ETHERTYPE_AT:
+ interpret_at(flags, (struct ddp_hdr *)data, len);
+ break;
+ case 0:
+ if (ieee8023 == 0)
+ break;
+ switch (ieeestart[0]) {
+ case 0xFE:
+ interpret_isis(flags, data, len,
+ memcmp(&e->ether_dhost, &all_isis_rbridges,
+ sizeof (struct ether_addr)) == 0);
+ break;
+ case 0x42:
+ interpret_bpdu(flags, data, len);
+ break;
+ }
+ break;
+ }
+ }
+
+ return (elen);
+}
+
+/*
+ * Return the length of the ethernet header. In the case
+ * where we have a VLAN tagged packet, return the length of
+ * the ethernet header plus the length of the VLAN tag.
+ *
+ * INPUTS: e - A buffer pointer. Passing a NULL pointer
+ * is not allowed, e must be non-NULL.
+ * OUTPUTS: Return the size of an untagged ethernet header
+ * if the packet is not VLAN tagged, and the size
+ * of an untagged ethernet header plus the size of
+ * a VLAN header otherwise.
+ */
+uint_t
+ether_header_len(char *e, size_t msgsize)
+{
+ uint16_t ether_type = 0;
+
+ if (msgsize < sizeof (struct ether_header))
+ return (0);
+
+ e += (offsetof(struct ether_header, ether_type));
+
+ GETINT16(ether_type, e);
+
+ if (ether_type == (uint16_t)ETHERTYPE_VLAN) {
+ return (sizeof (struct ether_vlan_header));
+ } else {
+ return (sizeof (struct ether_header));
+ }
+}
+
+
+/*
+ * Table of Ethertypes.
+ * Some of the more popular entries
+ * are at the beginning of the table
+ * to reduce search time.
+ */
+struct ether_type {
+ int e_type;
+ char *e_name;
+} ether_type [] = {
+ETHERTYPE_IP, "IP",
+ETHERTYPE_ARP, "ARP",
+ETHERTYPE_REVARP, "RARP",
+ETHERTYPE_IPV6, "IPv6",
+ETHERTYPE_PPPOED, "PPPoE Discovery",
+ETHERTYPE_PPPOES, "PPPoE Session",
+ETHERTYPE_TRILL, "TRILL",
+/* end of popular entries */
+ETHERTYPE_PUP, "Xerox PUP",
+0x0201, "Xerox PUP",
+0x0400, "Nixdorf",
+0x0600, "Xerox NS IDP",
+0x0601, "XNS Translation",
+0x0801, "X.75 Internet",
+0x0802, "NBS Internet",
+0x0803, "ECMA Internet",
+0x0804, "CHAOSnet",
+0x0805, "X.25 Level 3",
+0x0807, "XNS Compatibility",
+0x081C, "Symbolics Private",
+0x0888, "Xyplex",
+0x0889, "Xyplex",
+0x088A, "Xyplex",
+0x0900, "Ungermann-Bass network debugger",
+0x0A00, "Xerox IEEE802.3 PUP",
+0x0A01, "Xerox IEEE802.3 PUP Address Translation",
+0x0BAD, "Banyan Systems",
+0x0BAF, "Banyon VINES Echo",
+0x1000, "Berkeley Trailer negotiation",
+0x1000, "IP trailer (0)",
+0x1001, "IP trailer (1)",
+0x1002, "IP trailer (2)",
+0x1003, "IP trailer (3)",
+0x1004, "IP trailer (4)",
+0x1005, "IP trailer (5)",
+0x1006, "IP trailer (6)",
+0x1007, "IP trailer (7)",
+0x1008, "IP trailer (8)",
+0x1009, "IP trailer (9)",
+0x100a, "IP trailer (10)",
+0x100b, "IP trailer (11)",
+0x100c, "IP trailer (12)",
+0x100d, "IP trailer (13)",
+0x100e, "IP trailer (14)",
+0x100f, "IP trailer (15)",
+0x1234, "DCA - Multicast",
+0x1600, "VALID system protocol",
+0x1989, "Aviator",
+0x3C00, "3Com NBP virtual circuit datagram",
+0x3C01, "3Com NBP System control datagram",
+0x3C02, "3Com NBP Connect request (virtual cct)",
+0x3C03, "3Com NBP Connect response",
+0x3C04, "3Com NBP Connect complete",
+0x3C05, "3Com NBP Close request (virtual cct)",
+0x3C06, "3Com NBP Close response",
+0x3C07, "3Com NBP Datagram (like XNS IDP)",
+0x3C08, "3Com NBP Datagram broadcast",
+0x3C09, "3Com NBP Claim NetBIOS name",
+0x3C0A, "3Com NBP Delete Netbios name",
+0x3C0B, "3Com NBP Remote adaptor status request",
+0x3C0C, "3Com NBP Remote adaptor response",
+0x3C0D, "3Com NBP Reset",
+0x4242, "PCS Basic Block Protocol",
+0x4321, "THD - Diddle",
+0x5208, "BBN Simnet Private",
+0x6000, "DEC unass, experimental",
+0x6001, "DEC Dump/Load",
+0x6002, "DEC Remote Console",
+0x6003, "DECNET Phase IV, DNA Routing",
+0x6004, "DEC LAT",
+0x6005, "DEC Diagnostic",
+0x6006, "DEC customer protocol",
+0x6007, "DEC Local Area VAX Cluster (LAVC)",
+0x6008, "DEC unass (AMBER?)",
+0x6009, "DEC unass (MUMPS?)",
+0x6010, "3Com",
+0x6011, "3Com",
+0x6012, "3Com",
+0x6013, "3Com",
+0x6014, "3Com",
+0x7000, "Ungermann-Bass download",
+0x7001, "Ungermann-Bass NIUs",
+0x7002, "Ungermann-Bass diagnostic/loopback",
+0x7003, "Ungermann-Bass ? (NMC to/from UB Bridge)",
+0x7005, "Ungermann-Bass Bridge Spanning Tree",
+0x7007, "OS/9 Microware",
+0x7009, "OS/9 Net?",
+0x7020, "Sintrom",
+0x7021, "Sintrom",
+0x7022, "Sintrom",
+0x7023, "Sintrom",
+0x7024, "Sintrom",
+0x7025, "Sintrom",
+0x7026, "Sintrom",
+0x7027, "Sintrom",
+0x7028, "Sintrom",
+0x7029, "Sintrom",
+0x8003, "Cronus VLN",
+0x8004, "Cronus Direct",
+0x8005, "HP Probe protocol",
+0x8006, "Nestar",
+0x8008, "AT&T/Stanford Univ",
+0x8010, "Excelan",
+0x8013, "SGI diagnostic",
+0x8014, "SGI network games",
+0x8015, "SGI reserved",
+0x8016, "SGI XNS NameServer, bounce server",
+0x8019, "Apollo DOMAIN",
+0x802E, "Tymshare",
+0x802F, "Tigan,",
+0x8036, "Aeonic Systems",
+0x8037, "IPX (Novell Netware)",
+0x8038, "DEC LanBridge Management",
+0x8039, "DEC unass (DSM/DTP?)",
+0x803A, "DEC unass (Argonaut Console?)",
+0x803B, "DEC unass (VAXELN?)",
+0x803C, "DEC unass (NMSV? DNA Naming Service?)",
+0x803D, "DEC Ethernet CSMA/CD Encryption Protocol",
+0x803E, "DEC unass (DNA Time Service?)",
+0x803F, "DEC LAN Traffic Monitor Protocol",
+0x8040, "DEC unass (NetBios Emulator?)",
+0x8041, "DEC unass (MS/DOS?, Local Area System Transport?)",
+0x8042, "DEC unass",
+0x8044, "Planning Research Corp.",
+0x8046, "AT&T",
+0x8047, "AT&T",
+0x8049, "ExperData",
+0x805B, "VMTP",
+0x805C, "Stanford V Kernel, version 6.0",
+0x805D, "Evans & Sutherland",
+0x8060, "Little Machines",
+0x8062, "Counterpoint",
+0x8065, "University of Mass. at Amherst",
+0x8066, "University of Mass. at Amherst",
+0x8067, "Veeco Integrated Automation",
+0x8068, "General Dynamics",
+0x8069, "AT&T",
+0x806A, "Autophon",
+0x806C, "ComDesign",
+0x806D, "Compugraphic Corp",
+0x806E, "Landmark",
+0x806F, "Landmark",
+0x8070, "Landmark",
+0x8071, "Landmark",
+0x8072, "Landmark",
+0x8073, "Landmark",
+0x8074, "Landmark",
+0x8075, "Landmark",
+0x8076, "Landmark",
+0x8077, "Landmark",
+0x807A, "Matra",
+0x807B, "Dansk Data Elektronik",
+0x807C, "Merit Internodal",
+0x807D, "Vitalink",
+0x807E, "Vitalink",
+0x807F, "Vitalink",
+0x8080, "Vitalink TransLAN III Management",
+0x8081, "Counterpoint",
+0x8082, "Counterpoint",
+0x8083, "Counterpoint",
+0x8088, "Xyplex",
+0x8089, "Xyplex",
+0x808A, "Xyplex",
+0x809B, "EtherTalk (AppleTalk over Ethernet)",
+0x809C, "Datability",
+0x809D, "Datability",
+0x809E, "Datability",
+0x809F, "Spider Systems",
+0x80A3, "Nixdorf",
+0x80A4, "Siemens Gammasonics",
+0x80C0, "DCA Data Exchange Cluster",
+0x80C6, "Pacer Software",
+0x80C7, "Applitek Corp",
+0x80C8, "Intergraph",
+0x80C9, "Intergraph",
+0x80CB, "Intergraph",
+0x80CC, "Intergraph",
+0x80CA, "Intergraph",
+0x80CD, "Harris Corp",
+0x80CE, "Harris Corp",
+0x80CF, "Taylor Instrument",
+0x80D0, "Taylor Instrument",
+0x80D1, "Taylor Instrument",
+0x80D2, "Taylor Instrument",
+0x80D3, "Rosemount Corp",
+0x80D4, "Rosemount Corp",
+0x80D5, "IBM SNA Services over Ethernet",
+0x80DD, "Varian Associates",
+0x80DE, "TRFS",
+0x80DF, "TRFS",
+0x80E0, "Allen-Bradley",
+0x80E1, "Allen-Bradley",
+0x80E2, "Allen-Bradley",
+0x80E3, "Allen-Bradley",
+0x80E4, "Datability",
+0x80F2, "Retix",
+0x80F3, "AARP (Appletalk)",
+0x80F4, "Kinetics",
+0x80F5, "Kinetics",
+0x80F7, "Apollo",
+0x80FF, "Wellfleet Communications",
+0x8102, "Wellfleet Communications",
+0x8107, "Symbolics Private",
+0x8108, "Symbolics Private",
+0x8109, "Symbolics Private",
+0x812B, "Talaris",
+0x8130, "Waterloo",
+0x8131, "VG Lab",
+0x8137, "Novell (old) NetWare IPX",
+0x8138, "Novell",
+0x814C, "SNMP over Ethernet",
+0x817D, "XTP",
+0x81D6, "Lantastic",
+0x8888, "HP LanProbe test?",
+0x9000, "Loopback",
+0x9001, "3Com, XNS Systems Management",
+0x9002, "3Com, TCP/IP Systems Management",
+0x9003, "3Com, loopback detection",
+0xAAAA, "DECNET (VAX 6220 DEBNI)",
+0xFF00, "BBN VITAL-LanBridge cache wakeups",
+0, "",
+};
+
+char *
+print_fc(uint_t type)
+{
+
+ switch (type) {
+ case 0x50: return ("LLC");
+ case 0x4f: return ("SMT NSA");
+ case 0x41: return ("SMT Info");
+ default: return ("Unknown");
+ }
+}
+
+char *
+print_smtclass(uint_t type)
+{
+ switch (type) {
+ case 0x01: return ("NIF");
+ case 0x02: return ("SIF Conf");
+ case 0x03: return ("SIF Oper");
+ case 0x04: return ("ECF");
+ case 0x05: return ("RAF");
+ case 0x06: return ("RDF");
+ case 0x07: return ("SRF");
+ case 0x08: return ("PMF Get");
+ case 0x09: return ("PMF Change");
+ case 0x0a: return ("PMF Add");
+ case 0x0b: return ("PMF Remove");
+ case 0xff: return ("ESF");
+ default: return ("Unknown");
+ }
+
+}
+char *
+print_smttype(uint_t type)
+{
+ switch (type) {
+ case 0x01: return ("Announce");
+ case 0x02: return ("Request");
+ case 0x03: return ("Response");
+ default: return ("Unknown");
+ }
+
+}
+char *
+print_ethertype(int type)
+{
+ int i;
+
+ for (i = 0; ether_type[i].e_type; i++)
+ if (type == ether_type[i].e_type)
+ return (ether_type[i].e_name);
+ if (type < 1500)
+ return ("LLC/802.3");
+
+ return ("Unknown");
+}
+
+#define MAX_RDFLDS 14 /* changed to 14 from 8 as per IEEE */
+#define TR_FN_ADDR 0x80 /* dest addr is functional */
+#define TR_SR_ADDR 0x80 /* MAC utilizes source route */
+#define ACFCDASA_LEN 14 /* length of AC|FC|DA|SA */
+#define TR_MAC_MASK 0xc0
+#define TR_AC 0x00 /* Token Ring access control */
+#define TR_LLC_FC 0x40 /* Token Ring llc frame control */
+#define LSAP_SNAP 0xaa
+#define LLC_SNAP_HDR_LEN 8
+#define LLC_HDR1_LEN 3 /* DON'T use sizeof(struct llc_hdr1) */
+#define CNTL_LLC_UI 0x03 /* un-numbered information packet */
+
+/*
+ * Source Routing Route Information field.
+ */
+struct tr_ri {
+#if defined(_BIT_FIELDS_HTOL)
+ uchar_t rt:3; /* routing type */
+ uchar_t len:5; /* length */
+ uchar_t dir:1; /* direction bit */
+ uchar_t mtu:3; /* largest frame */
+ uchar_t res:4; /* reserved */
+#elif defined(_BIT_FIELDS_LTOH)
+ uchar_t len:5; /* length */
+ uchar_t rt:3; /* routing type */
+ uchar_t res:4; /* reserved */
+ uchar_t mtu:3; /* largest frame */
+ uchar_t dir:1; /* direction bit */
+#endif
+/*
+ * In little endian machine, the ring field has to be stored in a
+ * ushort_t type. This implies that it is not possible to have a
+ * layout of bit field to represent bridge and ring.
+ *
+ * If the compiler uses _BIT_FIELDS_HTOL and it is a big endian
+ * machine, the following bit field definition will work.
+ *
+ * struct tr_rd {
+ * ushort_t bridge:4;
+ * ushort_t ring:12;
+ * } rd[MAX_RDFLDS];
+ *
+ * If the compiler uses _BIT_FIELDS_LTOH and it is a big endian
+ * machine, the definition can be changed to
+ *
+ * struct tr_rd {
+ * ushort_t bridge:4;
+ * ushort_t ring:12;
+ * } rd[MAX_RDFLDS];
+ *
+ * With little endian machine, we need to use 2 macroes. For
+ * simplicity, since the macroes work for both big and little
+ * endian machines, we will not use bit fields for the
+ * definition.
+ */
+#define bridge(route) (ntohs((ushort_t)(route)) & 0x0F)
+#define ring(route) (ntohs((ushort_t)(route)) >> 4)
+
+ ushort_t rd[MAX_RDFLDS]; /* route designator fields */
+};
+
+struct tr_header {
+ uchar_t ac;
+ uchar_t fc;
+ struct ether_addr dhost;
+ struct ether_addr shost;
+ struct tr_ri ri;
+};
+
+struct llc_snap_hdr {
+ uchar_t d_lsap; /* destination service access point */
+ uchar_t s_lsap; /* source link service access point */
+ uchar_t control; /* short control field */
+ uchar_t org[3]; /* Ethernet style organization field */
+ ushort_t type; /* Ethernet style type field */
+};
+
+struct ether_addr tokenbroadcastaddr2 = {
+ 0xc0, 0x00, 0xff, 0xff, 0xff, 0xff
+};
+
+int Mtutab[] = {516, 1470, 2052, 4472, 8144, 11407, 17800};
+
+char *
+print_sr(struct tr_ri *rh)
+{
+ int hops, ii;
+ static char line[512];
+
+ sprintf(line, "TR Source Route dir=%d, mtu=%d",
+ rh->dir, Mtutab[rh->mtu]);
+
+ hops = (int)(rh->len - 2) / (int)2;
+
+ if (hops) {
+ sprintf(line+strlen(line), ", Route: ");
+ for (ii = 0; ii < hops; ii++) {
+ if (! bridge(rh->rd[ii])) {
+ sprintf(line+strlen(line), "(%d)",
+ ring(rh->rd[ii]));
+ } else {
+ sprintf(line+strlen(line), "(%d)%d",
+ ring(rh->rd[ii]), bridge(rh->rd[ii]));
+ }
+ }
+ }
+ return (&line[0]);
+}
+
+uint_t
+interpret_tr(int flags, caddr_t e, int elen, int origlen)
+{
+ struct tr_header *mh;
+ struct tr_ri *rh;
+ uchar_t fc;
+ struct llc_snap_hdr *snaphdr;
+ char *off;
+ int maclen, len;
+ boolean_t data_copied = B_FALSE;
+ extern char *dst_name, *src_name;
+ int ethertype;
+ int is_llc = 0, is_snap = 0, source_routing = 0;
+ int blen = MAX(origlen, 17800);
+
+ if (data != NULL && datalen != 0 && datalen < blen) {
+ free(data);
+ data = NULL;
+ datalen = 0;
+ }
+ if (!data) {
+ data = (char *)malloc(blen);
+ if (!data)
+ pr_err("Warning: malloc failure");
+ datalen = blen;
+ }
+
+ if (origlen < ACFCDASA_LEN) {
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "RUNT (short packet - %d bytes)",
+ origlen);
+ }
+ if (flags & F_DTAIL)
+ show_header("RUNT: ", "Short packet", origlen);
+ return (elen);
+ }
+ if (elen < ACFCDASA_LEN)
+ return (elen);
+
+ mh = (struct tr_header *)e;
+ rh = (struct tr_ri *)&mh->ri;
+ fc = mh->fc;
+
+ if (is_llc = tr_machdr_len(e, &maclen, &source_routing)) {
+ snaphdr = (struct llc_snap_hdr *)(e + maclen);
+ if (snaphdr->d_lsap == LSAP_SNAP &&
+ snaphdr->s_lsap == LSAP_SNAP &&
+ snaphdr->control == CNTL_LLC_UI) {
+ is_snap = 1;
+ }
+ }
+
+ if (memcmp(&mh->dhost, &ether_broadcast,
+ sizeof (struct ether_addr)) == 0)
+ dst_name = "(broadcast)";
+ else if (memcmp(&mh->dhost, &tokenbroadcastaddr2,
+ sizeof (struct ether_addr)) == 0)
+ dst_name = "(mac broadcast)";
+ else if (mh->dhost.ether_addr_octet[0] & TR_FN_ADDR)
+ dst_name = "(functional)";
+
+ if (is_snap)
+ ethertype = ntohs(snaphdr->type);
+ else {
+ src_name = print_etherinfo(&mh->shost);
+ dst_name = print_etherinfo(&mh->dhost);
+ }
+
+ /*
+ * The 14 byte ether header screws up alignment
+ * of the rest of the packet for 32 bit aligned
+ * architectures like SPARC. Alas, we have to copy
+ * the rest of the packet in order to align it.
+ */
+ if (is_llc) {
+ if (is_snap) {
+ len = elen - (maclen + LLC_SNAP_HDR_LEN);
+ off = (char *)(e + maclen + LLC_SNAP_HDR_LEN);
+ } else {
+ len = elen - (maclen + LLC_HDR1_LEN);
+ off = (char *)(e + maclen + LLC_HDR1_LEN);
+ }
+ } else {
+ len = elen - maclen;
+ off = (char *)(e + maclen);
+ }
+
+ if (len > 0 && (off + len <= (char *)e + elen)) {
+ (void) memcpy(data, off, len);
+ data_copied = B_TRUE;
+ }
+
+ if (flags & F_SUM) {
+ if (source_routing)
+ sprintf(get_sum_line(), print_sr(rh));
+
+ if (is_llc) {
+ if (is_snap) {
+ (void) sprintf(get_sum_line(), "TR LLC w/SNAP "
+ "Type=%04X (%s), size=%d bytes",
+ ethertype,
+ print_ethertype(ethertype),
+ origlen);
+ } else {
+ (void) sprintf(get_sum_line(), "TR LLC, but no "
+ "SNAP encoding, size = %d bytes",
+ origlen);
+ }
+ } else {
+ (void) sprintf(get_sum_line(),
+ "TR MAC FC=%02X (%s), size = %d bytes",
+ fc, print_fc(fc), origlen);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("TR: ", "TR Header", elen);
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ "Packet %d arrived at %d:%02d:%d.%05d",
+ pi_frame,
+ pi_time_hour, pi_time_min, pi_time_sec,
+ pi_time_usec / 10);
+ (void) sprintf(get_line(0, 0),
+ "Packet size = %d bytes",
+ elen);
+ (void) sprintf(get_line(0, 1),
+ "Frame Control = %02x (%s)",
+ fc, print_fc(fc));
+ (void) sprintf(get_line(2, 6),
+ "Destination = %s, %s",
+ printether(&mh->dhost),
+ print_etherinfo(&mh->dhost));
+ (void) sprintf(get_line(8, 6),
+ "Source = %s, %s",
+ printether(&mh->shost),
+ print_etherinfo(&mh->shost));
+
+ if (source_routing)
+ sprintf(get_line(ACFCDASA_LEN, rh->len), print_sr(rh));
+
+ if (is_llc) {
+ (void) sprintf(get_line(maclen, 1),
+ "Dest Service Access Point = %02x",
+ snaphdr->d_lsap);
+ (void) sprintf(get_line(maclen+1, 1),
+ "Source Service Access Point = %02x",
+ snaphdr->s_lsap);
+ (void) sprintf(get_line(maclen+2, 1),
+ "Control = %02x",
+ snaphdr->control);
+ if (is_snap) {
+ (void) sprintf(get_line(maclen+3, 3),
+ "SNAP Protocol Id = %02x%02x%02x",
+ snaphdr->org[0], snaphdr->org[1],
+ snaphdr->org[2]);
+ }
+ }
+
+ if (is_snap) {
+ (void) sprintf(get_line(maclen+6, 2),
+ "SNAP Type = %04X (%s)",
+ ethertype, print_ethertype(ethertype));
+ }
+
+ show_space();
+ }
+
+ /* go to the next protocol layer */
+ if (is_snap && data_copied) {
+ switch (ethertype) {
+ case ETHERTYPE_IP:
+ (void) interpret_ip(flags, (struct ip *)data, len);
+ break;
+ /* Just in case it is decided to add this type */
+ case ETHERTYPE_IPV6:
+ (void) interpret_ipv6(flags, (ip6_t *)data, len);
+ break;
+ case ETHERTYPE_ARP:
+ case ETHERTYPE_REVARP:
+ interpret_arp(flags, (struct arphdr *)data, len);
+ break;
+ case ETHERTYPE_AARP: /* AppleTalk */
+ interpret_aarp(flags, data, len);
+ break;
+ case ETHERTYPE_AT:
+ interpret_at(flags, (struct ddp_hdr *)data, len);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return (elen);
+}
+
+
+/*
+ * stuffs length of mac and ri fields into *lenp
+ * returns:
+ * 0: mac frame
+ * 1: llc frame
+ */
+static int
+tr_machdr_len(char *e, int *lenp, int *source_routing)
+{
+ struct tr_header *mh;
+ struct tr_ri *rh;
+ uchar_t fc;
+
+ mh = (struct tr_header *)e;
+ rh = (struct tr_ri *)&mh->ri;
+ fc = mh->fc;
+
+ if (mh->shost.ether_addr_octet[0] & TR_SR_ADDR) {
+ *lenp = ACFCDASA_LEN + rh->len;
+ *source_routing = 1;
+ } else {
+ *lenp = ACFCDASA_LEN;
+ *source_routing = 0;
+ }
+
+ if ((fc & TR_MAC_MASK) == 0)
+ return (0); /* it's a MAC frame */
+ else
+ return (1); /* it's an LLC frame */
+}
+
+uint_t
+tr_header_len(char *e, size_t msgsize)
+{
+ struct llc_snap_hdr *snaphdr;
+ int len = 0, source_routing;
+
+ if (tr_machdr_len(e, &len, &source_routing) == 0)
+ return (len); /* it's a MAC frame */
+
+ if (msgsize < sizeof (struct llc_snap_hdr))
+ return (0);
+
+ snaphdr = (struct llc_snap_hdr *)(e + len);
+ if (snaphdr->d_lsap == LSAP_SNAP &&
+ snaphdr->s_lsap == LSAP_SNAP &&
+ snaphdr->control == CNTL_LLC_UI)
+ len += LLC_SNAP_HDR_LEN; /* it's a SNAP frame */
+ else
+ len += LLC_HDR1_LEN;
+
+ return (len);
+}
+
+struct fddi_header {
+ uchar_t fc;
+ struct ether_addr dhost, shost;
+ uchar_t dsap, ssap, ctl, proto_id[3];
+ ushort_t type;
+};
+
+uint_t
+interpret_fddi(int flags, caddr_t e, int elen, int origlen)
+{
+ struct fddi_header fhdr, *f = &fhdr;
+ char *off;
+ int len;
+ boolean_t data_copied = B_FALSE;
+ extern char *dst_name, *src_name;
+ int ethertype;
+ int is_llc = 0, is_smt = 0, is_snap = 0;
+ int blen = MAX(origlen, 4500);
+
+ if (data != NULL && datalen != 0 && datalen < blen) {
+ free(data);
+ data = NULL;
+ datalen = 0;
+ }
+ if (!data) {
+ data = (char *)malloc(blen);
+ if (!data)
+ pr_err("Warning: malloc failure");
+ datalen = blen;
+ }
+
+ if (origlen < 13) {
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "RUNT (short packet - %d bytes)",
+ origlen);
+ }
+ if (flags & F_DTAIL)
+ show_header("RUNT: ", "Short packet", origlen);
+ return (elen);
+ }
+ if (elen < 13)
+ return (elen);
+
+ (void) memcpy(&f->fc, e, sizeof (f->fc));
+ addr_copy_swap(&f->dhost, (struct ether_addr *)(e+1));
+ addr_copy_swap(&f->shost, (struct ether_addr *)(e+7));
+
+ if ((f->fc&0x50) == 0x50) {
+ is_llc = 1;
+ (void) memcpy(&f->dsap, e+13, sizeof (f->dsap));
+ (void) memcpy(&f->ssap, e+14, sizeof (f->ssap));
+ (void) memcpy(&f->ctl, e+15, sizeof (f->ctl));
+ if (f->dsap == 0xaa && f->ssap == 0xaa) {
+ is_snap = 1;
+ (void) memcpy(&f->proto_id, e+16, sizeof (f->proto_id));
+ (void) memcpy(&f->type, e+19, sizeof (f->type));
+ }
+ } else {
+ if ((f->fc&0x41) == 0x41 || (f->fc&0x4f) == 0x4f) {
+ is_smt = 1;
+ }
+ }
+
+
+ if (memcmp(&f->dhost, &ether_broadcast,
+ sizeof (struct ether_addr)) == 0)
+ dst_name = "(broadcast)";
+ else if (f->dhost.ether_addr_octet[0] & 0x01)
+ dst_name = "(multicast)";
+
+ if (is_snap)
+ ethertype = ntohs(f->type);
+ else {
+ src_name = print_etherinfo(&f->shost);
+ dst_name = print_etherinfo(&f->dhost);
+ }
+
+ /*
+ * The 14 byte ether header screws up alignment
+ * of the rest of the packet for 32 bit aligned
+ * architectures like SPARC. Alas, we have to copy
+ * the rest of the packet in order to align it.
+ */
+ if (is_llc) {
+ if (is_snap) {
+ len = elen - 21;
+ off = (char *)(e + 21);
+ } else {
+ len = elen - 16;
+ off = (char *)(e + 16);
+ }
+ } else {
+ len = elen - 13;
+ off = (char *)(e + 13);
+ }
+
+ if (len > 0 && (off + len <= (char *)e + elen)) {
+ (void) memcpy(data, off, len);
+ data_copied = B_TRUE;
+ }
+
+ if (flags & F_SUM) {
+ if (is_llc) {
+ if (is_snap) {
+ (void) sprintf(get_sum_line(),
+ "FDDI LLC Type=%04X (%s), size = %d bytes",
+ ethertype,
+ print_ethertype(ethertype),
+ origlen);
+ } else {
+ (void) sprintf(get_sum_line(), "LLC, but no "
+ "SNAP encoding, size = %d bytes",
+ origlen);
+ }
+ } else if (is_smt) {
+ (void) sprintf(get_sum_line(), "SMT Type=%02X (%s), "
+ "Class = %02X (%s), size = %d bytes",
+ *(uchar_t *)(data+1), print_smttype(*(data+1)),
+ *data, print_smtclass(*data), origlen);
+ } else {
+ (void) sprintf(get_sum_line(),
+ "FC=%02X (%s), size = %d bytes",
+ f->fc, print_fc(f->fc), origlen);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("FDDI: ", "FDDI Header", elen);
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ "Packet %d arrived at %d:%02d:%d.%05d",
+ pi_frame,
+ pi_time_hour, pi_time_min, pi_time_sec,
+ pi_time_usec / 10);
+ (void) sprintf(get_line(0, 0),
+ "Packet size = %d bytes",
+ elen, elen);
+ (void) sprintf(get_line(0, 6),
+ "Destination = %s, %s",
+ printether(&f->dhost),
+ print_etherinfo(&f->dhost));
+ (void) sprintf(get_line(6, 6),
+ "Source = %s, %s",
+ printether(&f->shost),
+ print_etherinfo(&f->shost));
+
+ if (is_llc) {
+ (void) sprintf(get_line(12, 2),
+ "Frame Control = %02x (%s)",
+ f->fc, print_fc(f->fc));
+ (void) sprintf(get_line(12, 2),
+ "Dest Service Access Point = %02x",
+ f->dsap);
+ (void) sprintf(get_line(12, 2),
+ "Source Service Access Point = %02x",
+ f->ssap);
+ (void) sprintf(get_line(12, 2),
+ "Control = %02x",
+ f->ctl);
+ if (is_snap) {
+ (void) sprintf(get_line(12, 2),
+ "Protocol Id = %02x%02x%02x",
+ f->proto_id[0], f->proto_id[1],
+ f->proto_id[2]);
+ }
+ } else if (is_smt) {
+ (void) sprintf(get_line(12, 2),
+ "Frame Control = %02x (%s)",
+ f->fc, print_fc(f->fc));
+ (void) sprintf(get_line(12, 2),
+ "Class = %02x (%s)",
+ (uchar_t)*data, print_smtclass(*data));
+ (void) sprintf(get_line(12, 2),
+ "Type = %02x (%s)",
+ *(uchar_t *)(data+1), print_smttype(*(data+1)));
+ } else {
+ (void) sprintf(get_line(12, 2),
+ "FC=%02X (%s), size = %d bytes",
+ f->fc, print_fc(f->fc), origlen);
+ }
+
+ if (is_snap) {
+ (void) sprintf(get_line(12, 2),
+ "LLC Type = %04X (%s)",
+ ethertype, print_ethertype(ethertype));
+ }
+
+ show_space();
+ }
+
+ /* go to the next protocol layer */
+ if (is_llc && is_snap && f->ctl == 0x03 && data_copied) {
+ switch (ethertype) {
+ case ETHERTYPE_IP:
+ (void) interpret_ip(flags, (struct ip *)data, len);
+ break;
+ /* Just in case it is decided to add this type */
+ case ETHERTYPE_IPV6:
+ (void) interpret_ipv6(flags, (ip6_t *)data, len);
+ break;
+ case ETHERTYPE_ARP:
+ case ETHERTYPE_REVARP:
+ interpret_arp(flags, (struct arphdr *)data, len);
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ return (elen);
+}
+
+uint_t
+fddi_header_len(char *e, size_t msgsize)
+{
+ struct fddi_header fhdr, *f = &fhdr;
+
+ if (msgsize < sizeof (struct fddi_header))
+ return (0);
+
+ (void) memcpy(&f->fc, e, sizeof (f->fc));
+ (void) memcpy(&f->dhost, e+1, sizeof (struct ether_addr));
+ (void) memcpy(&f->shost, e+7, sizeof (struct ether_addr));
+
+ if ((f->fc&0x50) == 0x50) {
+ (void) memcpy(&f->dsap, e+13, sizeof (f->dsap));
+ (void) memcpy(&f->ssap, e+14, sizeof (f->ssap));
+ (void) memcpy(&f->ctl, e+15, sizeof (f->ctl));
+ if (f->dsap == 0xaa && f->ssap == 0xaa) {
+ return (21);
+ }
+ return (16);
+ } else {
+ if ((f->fc&0x41) == 0x41 || (f->fc&0x4f) == 0x4f) {
+ return (13);
+ }
+ }
+ /* Return the default FDDI header length. */
+ return (13);
+}
+
+/*
+ * Print the given Ethernet address
+ */
+char *
+printether(struct ether_addr *p)
+{
+ static char buf[256];
+
+ sprintf(buf, "%x:%x:%x:%x:%x:%x",
+ p->ether_addr_octet[0],
+ p->ether_addr_octet[1],
+ p->ether_addr_octet[2],
+ p->ether_addr_octet[3],
+ p->ether_addr_octet[4],
+ p->ether_addr_octet[5]);
+
+ return (buf);
+}
+
+/*
+ * Table of Ethernet Address Assignments
+ * Some of the more popular entries
+ * are at the beginning of the table
+ * to reduce search time. Note that the
+ * e-block's are stored in host byte-order.
+ */
+struct block_type {
+ int e_block;
+ char *e_name;
+} ether_block [] = {
+0x080020, "Sun",
+0x0000C6, "HP",
+0x08002B, "DEC",
+0x00000F, "NeXT",
+0x00000C, "Cisco",
+0x080069, "Silicon Graphics",
+0x000069, "Silicon Graphics",
+0x0000A7, "Network Computing Devices (NCD X-terminal)",
+0x08005A, "IBM",
+0x0000AC, "Apollo",
+0x0180C2, "Standard MAC Group Address",
+/* end of popular entries */
+0x000002, "BBN",
+0x000010, "Sytek",
+0x000011, "Tektronix",
+0x000018, "Webster (?)",
+0x00001B, "Novell",
+0x00001D, "Cabletron",
+0x000020, "DIAB (Data Industrier AB)",
+0x000021, "SC&C",
+0x000022, "Visual Technology",
+0x000029, "IMC",
+0x00002A, "TRW",
+0x00003D, "AT&T",
+0x000049, "Apricot Ltd.",
+0x000055, "AT&T",
+0x00005A, "S & Koch",
+0x00005A, "Xerox 806 (unregistered)",
+0x00005E, "U.S. Department of Defense (IANA)",
+0x000065, "Network General",
+0x00006B, "MIPS",
+0x000077, "MIPS",
+0x000079, "NetWare (?)",
+0x00007A, "Ardent",
+0x00007B, "Research Machines",
+0x00007D, "Harris (3M) (old)",
+0x000080, "Imagen(?)",
+0x000081, "Synoptics",
+0x000084, "Aquila (?)",
+0x000086, "Gateway (?)",
+0x000089, "Cayman Systems Gatorbox",
+0x000093, "Proteon",
+0x000094, "Asante",
+0x000098, "Cross Com",
+0x00009F, "Ameristar Technology",
+0x0000A2, "Wellfleet",
+0x0000A3, "Network Application Technology",
+0x0000A4, "Acorn",
+0x0000A6, "Network General",
+0x0000A7, "Network Computing Devices (NCD X-terminal)",
+0x0000A9, "Network Systems",
+0x0000AA, "Xerox",
+0x0000B3, "CIMLinc",
+0x0000B5, "Datability Terminal Server",
+0x0000B7, "Dove Fastnet",
+0x0000BC, "Allen-Bradley",
+0x0000C0, "Western Digital",
+0x0000C8, "Altos",
+0x0000C9, "Emulex Terminal Server",
+0x0000D0, "Develcon Electronics, Ltd.",
+0x0000D1, "Adaptec Inc. Nodem product",
+0x0000D7, "Dartmouth College (NED Router)",
+0x0000DD, "Gould",
+0x0000DE, "Unigraph",
+0x0000E2, "Acer Counterpoint",
+0x0000E8, "Accton Technology Corporation",
+0x0000EE, "Network Designers Limited(?)",
+0x0000EF, "Alantec",
+0x0000F3, "Gandalf",
+0x0000FD, "High Level Hardware (Orion, UK)",
+0x000143, "IEEE 802",
+0x001700, "Kabel",
+0x004010, "Sonic",
+0x00608C, "3Com",
+0x00800F, "SMC",
+0x008019, "Dayna Communications Etherprint product",
+0x00802D, "Xylogics, Inc. Annex terminal servers",
+0x008035, "Technology Works",
+0x008087, "Okidata",
+0x00808C, "Frontier Software Development",
+0x0080C7, "Xircom Inc.",
+0x0080D0, "Computer Products International",
+0x0080D3, "Shiva Appletalk-Ethernet interface",
+0x0080D4, "Chase Limited",
+0x0080F1, "Opus",
+0x00AA00, "Intel",
+0x00B0D0, "Computer Products International",
+0x00DD00, "Ungermann-Bass",
+0x00DD01, "Ungermann-Bass",
+0x00EFE5, "IBM (3Com card)",
+0x020406, "BBN",
+0x026060, "3Com",
+0x026086, "Satelcom MegaPac (UK)",
+0x02E6D3, "Bus-Tech, Inc. (BTI)",
+0x080001, "Computer Vision",
+0x080002, "3Com (Formerly Bridge)",
+0x080003, "ACC (Advanced Computer Communications)",
+0x080005, "Symbolics",
+0x080007, "Apple",
+0x080008, "BBN",
+0x080009, "Hewlett-Packard",
+0x08000A, "Nestar Systems",
+0x08000B, "Unisys",
+0x08000D, "ICL",
+0x08000E, "NCR",
+0x080010, "AT&T",
+0x080011, "Tektronix, Inc.",
+0x080017, "NSC",
+0x08001A, "Data General",
+0x08001B, "Data General",
+0x08001E, "Apollo",
+0x080022, "NBI",
+0x080025, "CDC",
+0x080026, "Norsk Data (Nord)",
+0x080027, "PCS Computer Systems GmbH",
+0x080028, "TI Explorer",
+0x08002E, "Metaphor",
+0x08002F, "Prime Computer",
+0x080036, "Intergraph CAE stations",
+0x080037, "Fujitsu-Xerox",
+0x080038, "Bull",
+0x080039, "Spider Systems",
+0x08003B, "Torus Systems",
+0x08003E, "Motorola VME bus processor module",
+0x080041, "DCA Digital Comm. Assoc.",
+0x080046, "Sony",
+0x080047, "Sequent",
+0x080049, "Univation",
+0x08004C, "Encore",
+0x08004E, "BICC",
+0x080056, "Stanford University",
+0x080057, "Evans & Sutherland (?)",
+0x080067, "Comdesign",
+0x080068, "Ridge",
+0x08006A, "ATTst (?)",
+0x08006E, "Excelan",
+0x080075, "DDE (Danish Data Elektronik A/S)",
+0x080077, "TSL (now Retix)",
+0x08007C, "Vitalink TransLAN III",
+0x080080, "XIOS",
+0x080081, "Crosfield Electronics",
+0x080086, "Imagen/QMS",
+0x080087, "Xyplex terminal server",
+0x080089, "Kinetics AppleTalk-Ethernet interface",
+0x08008B, "Pyramid",
+0x08008D, "XyVision",
+0x080090, "Retix Inc Bridge",
+0x10005A, "IBM",
+0x1000D4, "DEC",
+0x400003, "NetWare",
+0x800010, "AT&T",
+0xAA0004, "DEC (DECNET)",
+0xC00000, "SMC",
+0, "",
+};
+
+/*
+ * The oui argument should be in host byte-order to conform with
+ * the above array's values.
+ */
+char *
+ether_ouiname(uint32_t oui)
+{
+ uint_t i;
+
+ for (i = 0; ether_block[i].e_block != 0; i++)
+ if (oui == ether_block[i].e_block)
+ return (ether_block[i].e_name);
+
+ return (NULL);
+}
+
+/*
+ * Print the additional Ethernet address info
+ */
+static char *
+print_etherinfo(struct ether_addr *eaddr)
+{
+ uint_t addr = 0;
+ char *p = (char *)&addr + 1;
+ char *ename;
+
+ (void) memcpy(p, eaddr, 3);
+
+ if (memcmp(eaddr, &ether_broadcast, sizeof (struct ether_addr)) == 0)
+ return ("(broadcast)");
+
+ addr = ntohl(addr); /* make it right for little-endians */
+ ename = ether_ouiname(addr);
+
+ if (ename != NULL)
+ return (ename);
+ else
+ return ((eaddr->ether_addr_octet[0] & 1) ? "(multicast)" : "");
+}
+
+static uchar_t endianswap[] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
+};
+
+static void
+addr_copy_swap(struct ether_addr *pd, struct ether_addr *ps)
+{
+ pd->ether_addr_octet[0] = endianswap[ps->ether_addr_octet[0]];
+ pd->ether_addr_octet[1] = endianswap[ps->ether_addr_octet[1]];
+ pd->ether_addr_octet[2] = endianswap[ps->ether_addr_octet[2]];
+ pd->ether_addr_octet[3] = endianswap[ps->ether_addr_octet[3]];
+ pd->ether_addr_octet[4] = endianswap[ps->ether_addr_octet[4]];
+ pd->ether_addr_octet[5] = endianswap[ps->ether_addr_octet[5]];
+}
+
+/* ARGSUSED */
+uint_t
+ib_header_len(char *hdr, size_t msgsize)
+{
+ return (IPOIB_HDRSIZE);
+}
+
+static uint_t
+interpret_ib(int flags, char *header, int elen, int origlen)
+{
+ struct ipoib_header *hdr = (struct ipoib_header *)header;
+ char *off;
+ int len;
+ unsigned short ethertype;
+ int blen = MAX(origlen, 4096);
+
+ if (data != NULL && datalen != 0 && datalen < blen) {
+ free(data);
+ data = NULL;
+ datalen = 0;
+ }
+ if (data == NULL) {
+ data = malloc(blen);
+ if (data == NULL)
+ pr_err("Warning: malloc failure");
+ datalen = blen;
+ }
+ if (origlen < IPOIB_HDRSIZE) {
+ if (flags & F_SUM)
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "RUNT (short packet - %d bytes)", origlen);
+ if (flags & F_DTAIL)
+ show_header("RUNT: ", "Short packet", origlen);
+ return (elen);
+ }
+ if (elen < IPOIB_HDRSIZE)
+ return (elen);
+
+ /*
+ * It is not possible to understand just by looking
+ * at the header whether this was a broad/multi cast
+ * packet; thus dst_name is not updated.
+ */
+ ethertype = ntohs(hdr->ipoib_type);
+ len = elen - IPOIB_HDRSIZE;
+ off = (char *)(hdr + 1);
+ (void) memcpy(data, off, len);
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "IPIB Type=%04X (%s), size = %d bytes",
+ ethertype,
+ print_ethertype(ethertype),
+ origlen);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("IPIB: ", "IPIB Header", elen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Packet %d arrived at %d:%02d:%d.%02d",
+ pi_frame, pi_time_hour, pi_time_min,
+ pi_time_sec, pi_time_usec / 10000);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Packet size = %d bytes", elen, elen);
+ (void) snprintf(get_line(0, 2), get_line_remain(),
+ "Ethertype = %04X (%s)", ethertype,
+ print_ethertype(ethertype));
+ show_space();
+ }
+
+ /* Go to the next protocol layer */
+ switch (ethertype) {
+ case ETHERTYPE_IP:
+ (void) interpret_ip(flags, (struct ip *)data, len);
+ break;
+ case ETHERTYPE_IPV6:
+ (void) interpret_ipv6(flags, (ip6_t *)data, len);
+ break;
+ case ETHERTYPE_ARP:
+ case ETHERTYPE_REVARP:
+ interpret_arp(flags, (struct arphdr *)data, len);
+ break;
+ }
+
+ return (elen);
+}
+
+/* ARGSUSED */
+uint_t
+ipnet_header_len(char *hdr, size_t msgsize)
+{
+ return (sizeof (dl_ipnetinfo_t));
+}
+
+#define MAX_UINT64_STR 22
+static uint_t
+interpret_ipnet(int flags, char *header, int elen, int origlen)
+{
+ dl_ipnetinfo_t dl;
+ size_t len = elen - sizeof (dl_ipnetinfo_t);
+ char *off = (char *)header + sizeof (dl_ipnetinfo_t);
+ int blen = MAX(origlen, 8252);
+ char szone[MAX_UINT64_STR];
+ char dzone[MAX_UINT64_STR];
+
+ (void) memcpy(&dl, header, sizeof (dl));
+ if (data != NULL && datalen != 0 && datalen < blen) {
+ free(data);
+ data = NULL;
+ datalen = 0;
+ }
+ if (data == NULL) {
+ data = (char *)malloc(blen);
+ if (!data)
+ pr_err("Warning: malloc failure");
+ datalen = blen;
+ }
+
+ if (dl.dli_zsrc == ALL_ZONES)
+ sprintf(szone, "Unknown");
+ else
+ sprintf(szone, "%lu", BE_32(dl.dli_zsrc));
+
+ if (dl.dli_zdst == ALL_ZONES)
+ sprintf(dzone, "Unknown");
+ else
+ sprintf(dzone, "%lu", BE_32(dl.dli_zdst));
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "IPNET src zone %s dst zone %s", szone, dzone);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("IPNET: ", "IPNET Header", elen);
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ "Packet %d arrived at %d:%02d:%d.%05d",
+ pi_frame,
+ pi_time_hour, pi_time_min, pi_time_sec,
+ pi_time_usec / 10);
+ (void) sprintf(get_line(0, 0),
+ "Packet size = %d bytes",
+ elen);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "dli_version = %d", dl.dli_version);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "dli_family = %d", dl.dli_family);
+ (void) snprintf(get_line(0, 2), get_line_remain(),
+ "dli_zsrc = %s", szone);
+ (void) snprintf(get_line(0, 2), get_line_remain(),
+ "dli_zdst = %s", dzone);
+ show_space();
+ }
+ memcpy(data, off, len);
+
+ switch (dl.dli_family) {
+ case AF_INET:
+ (void) interpret_ip(flags, (struct ip *)data, len);
+ break;
+ case AF_INET6:
+ (void) interpret_ipv6(flags, (ip6_t *)data, len);
+ break;
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+uint_t
+ipv4_header_len(char *hdr, size_t msgsize)
+{
+ return (msgsize < sizeof (ipha_t) ? 0 : IPH_HDR_LENGTH((ipha_t *)hdr));
+}
+
+/*
+ * The header length needs to include all potential extension headers, as the
+ * caller expects to use this length as an offset to the inner network layer
+ * header to be used as a filter offset. IPsec headers aren't passed up here,
+ * and neither are fragmentation headers.
+ */
+uint_t
+ipv6_header_len(char *hdr, size_t msgsize)
+{
+ ip6_t *ip6hdr = (ip6_t *)hdr;
+ ip6_hbh_t *exthdr;
+ uint_t hdrlen = sizeof (ip6_t), exthdrlen;
+ char *pptr;
+ uint8_t nxt;
+
+ if (msgsize < sizeof (ip6_t))
+ return (0);
+
+ nxt = ip6hdr->ip6_nxt;
+ pptr = (char *)(ip6hdr + 1);
+
+ while (nxt != IPPROTO_ENCAP && nxt != IPPROTO_IPV6) {
+ switch (nxt) {
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_ROUTING:
+ if (msgsize < hdrlen + sizeof (ip6_hbh_t))
+ return (0);
+ exthdr = (ip6_hbh_t *)pptr;
+ exthdrlen = 8 + exthdr->ip6h_len * 8;
+ hdrlen += exthdrlen;
+ pptr += exthdrlen;
+ nxt = exthdr->ip6h_nxt;
+ break;
+ default:
+ /*
+ * This is garbage, there's no way to know where the
+ * inner IP header is.
+ */
+ return (0);
+ }
+ }
+
+ return (hdrlen);
+}
+
+/* ARGSUSED */
+uint_t
+interpret_iptun(int flags, char *header, int elen, int origlen)
+{
+ (void) interpret_ip(flags, (struct ip *)header, elen);
+ return (elen);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_filter.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_filter.c
new file mode 100644
index 0000000..16ea65e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_filter.c
@@ -0,0 +1,2845 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2012 Milan Jurik. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/vlan.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <inet/ip6.h>
+#include <inet/ip.h>
+#include <netinet/if_ether.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcent.h>
+#include <sys/dlpi.h>
+
+#include <snoop.h>
+#include "snoop_vlan.h"
+
+#define IPV4_ONLY 0
+#define IPV6_ONLY 1
+#define IPV4_AND_IPV6 2
+
+/*
+ * The following constants represent the offsets in bytes from the beginning
+ * of the IP(v6) header of the source and destination IP(v6) addresses.
+ * These are useful when generating filter code.
+ */
+#define IPV4_SRCADDR_OFFSET 12
+#define IPV4_DSTADDR_OFFSET 16
+#define IPV6_SRCADDR_OFFSET 8
+#define IPV6_DSTADDR_OFFSET 24
+#define IP_VERS(p) (((*(uchar_t *)p) & 0xf0) >> 4)
+#define MASKED_IPV4_VERS 0x40
+#define MASKED_IPV6_VERS 0x60
+#define IP_HDR_LEN(p) (((*(uchar_t *)p) & 0xf) * 4)
+#define TCP_HDR_LEN(p) ((((*((uchar_t *)p+12)) >> 4) & 0xf) * 4)
+
+/*
+ * Coding the constant below is tacky, but the compiler won't let us
+ * be more clever. E.g., &((struct ip *)0)->ip_xxx
+ */
+#define IP_PROTO_OF(p) (((uchar_t *)p)[9])
+
+/*
+ * AppleTalk uses 802.2 Ethernet encapsulation with LLC/SNAP headers,
+ * for 8 octets of overhead, and the common AppleTalk DDP Ethernet
+ * header is another 4 octets.
+ *
+ * The following constants represents the offsets in bytes from the beginning
+ * of the Ethernet payload to various parts of the DDP header.
+ */
+
+#define AT_DST_NET_OFFSET 12
+#define AT_SRC_NET_OFFSET 14
+#define AT_DST_NODE_OFFSET 16
+#define AT_SRC_NODE_OFFSET 17
+
+/*
+ * Offset for the source and destination zoneid in the ipnet header.
+ */
+#define IPNET_SRCZONE_OFFSET 16
+#define IPNET_DSTZONE_OFFSET 20
+
+int eaddr; /* need ethernet addr */
+
+int opstack; /* operand stack depth */
+
+/*
+ * These are the operators of the user-level filter.
+ * STOP ends execution of the filter expression and
+ * returns the truth value at the top of the stack.
+ * OP_LOAD_OCTET, OP_LOAD_SHORT and OP_LOAD_LONG pop
+ * an offset value from the stack and load a value of
+ * an appropriate size from the packet (octet, short or
+ * long). The offset is computed from a base value that
+ * may be set via the OP_OFFSET operators.
+ * OP_EQ, OP_NE, OP_GT, OP_GE, OP_LT, OP_LE pop two values
+ * from the stack and return the result of their comparison.
+ * OP_AND, OP_OR, OP_XOR pop two values from the stack and
+ * do perform a bitwise operation on them - returning a result
+ * to the stack. OP_NOT inverts the bits of the value on the
+ * stack.
+ * OP_BRFL and OP_BRTR branch to an offset in the code array
+ * depending on the value at the top of the stack: true (not 0)
+ * or false (0).
+ * OP_ADD, OP_SUB, OP_MUL, OP_DIV and OP_REM pop two values
+ * from the stack and perform arithmetic.
+ * The OP_OFFSET operators change the base from which the
+ * OP_LOAD operators compute their offsets.
+ * OP_OFFSET_ZERO sets the offset to zero - beginning of packet.
+ * OP_OFFSET_LINK sets the base to the first octet after
+ * the link (DLC) header. OP_OFFSET_IP, OP_OFFSET_TCP,
+ * and OP_OFFSET_UDP do the same for those headers - they
+ * set the offset base to the *end* of the header - not the
+ * beginning. The OP_OFFSET_RPC operator is a bit unusual.
+ * It points the base at the cached RPC header. For the
+ * purposes of selection, RPC reply headers look like call
+ * headers except for the direction value.
+ * OP_OFFSET_ETHERTYPE sets base according to the following
+ * algorithm:
+ * if the packet is not VLAN tagged, then set base to
+ * the ethertype field in the ethernet header
+ * else set base to the ethertype field of the VLAN header
+ * OP_OFFSET_POP restores the offset base to the value prior
+ * to the most recent OP_OFFSET call.
+ */
+enum optype {
+ OP_STOP = 0,
+ OP_LOAD_OCTET,
+ OP_LOAD_SHORT,
+ OP_LOAD_LONG,
+ OP_LOAD_CONST,
+ OP_LOAD_LENGTH,
+ OP_EQ,
+ OP_NE,
+ OP_GT,
+ OP_GE,
+ OP_LT,
+ OP_LE,
+ OP_AND,
+ OP_OR,
+ OP_XOR,
+ OP_NOT,
+ OP_BRFL,
+ OP_BRTR,
+ OP_ADD,
+ OP_SUB,
+ OP_MUL,
+ OP_DIV,
+ OP_REM,
+ OP_OFFSET_POP,
+ OP_OFFSET_ZERO,
+ OP_OFFSET_LINK,
+ OP_OFFSET_IP,
+ OP_OFFSET_TCP,
+ OP_OFFSET_UDP,
+ OP_OFFSET_RPC,
+ OP_OFFSET_SLP,
+ OP_OFFSET_ETHERTYPE,
+ OP_LAST
+};
+
+static char *opnames[] = {
+ "STOP",
+ "LOAD_OCTET",
+ "LOAD_SHORT",
+ "LOAD_LONG",
+ "LOAD_CONST",
+ "LOAD_LENGTH",
+ "EQ",
+ "NE",
+ "GT",
+ "GE",
+ "LT",
+ "LE",
+ "AND",
+ "OR",
+ "XOR",
+ "NOT",
+ "BRFL",
+ "BRTR",
+ "ADD",
+ "SUB",
+ "MUL",
+ "DIV",
+ "REM",
+ "OFFSET_POP",
+ "OFFSET_ZERO",
+ "OFFSET_ETHER",
+ "OFFSET_IP",
+ "OFFSET_TCP",
+ "OFFSET_UDP",
+ "OFFSET_RPC",
+ "OP_OFFSET_SLP",
+ "OFFSET_ETHERTYPE",
+ ""
+};
+
+#define MAXOPS 1024
+#define MAXSS 64
+static uint_t oplist[MAXOPS]; /* array of operators */
+static uint_t *curr_op; /* last op generated */
+
+extern int valid_slp(uchar_t *, int); /* decides if a SLP msg is valid */
+extern struct hostent *lgetipnodebyname(const char *, int, int, int *);
+
+static void alternation();
+static uint_t chain();
+static void codeprint();
+static void emitop();
+static void emitval();
+static void expression();
+static struct xid_entry *find_rpc();
+static void optimize();
+static void ethertype_match();
+
+/*
+ * Get a ushort from a possibly unaligned character buffer.
+ *
+ * INPUTS: buffer - where the data is. Must be at least
+ * sizeof(uint16_t) bytes long.
+ * OUPUTS: An unsigned short that contains the data at buffer.
+ * No calls to ntohs or htons are done on the data.
+ */
+static uint16_t
+get_u16(uchar_t *buffer)
+{
+ uint8_t *bufraw = buffer;
+
+ /*
+ * ntohs is used only as a cheap way to flip the bits
+ * around on a little endian platform. The value will
+ * still be in host order or network order, depending on
+ * the order it was in when it was passed in.
+ */
+ return (ntohs(bufraw[0] << 8 | bufraw[1]));
+}
+
+/*
+ * Returns the ULP for an IPv4 or IPv6 packet
+ * Assumes that the packet has already been checked to verify
+ * that it's either IPv4 or IPv6
+ *
+ * XXX Will need to be updated for AH and ESP
+ * XXX when IPsec is supported for v6.
+ */
+static uchar_t
+ip_proto_of(uchar_t *ip)
+{
+ uchar_t nxt;
+ boolean_t not_done = B_TRUE;
+ uchar_t *ptr = ip;
+
+ switch (IP_VERS(ip)) {
+ case IPV4_VERSION:
+ return (IP_PROTO_OF(ip));
+ case IPV6_VERSION:
+
+ nxt = ip[6];
+ ptr += 40; /* size of ip6 header */
+ do {
+ switch (nxt) {
+ /*
+ * XXX Add IPsec headers here when supported for v6
+ * XXX (the AH will have a different size...)
+ */
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_ROUTING:
+ case IPPROTO_FRAGMENT:
+ case IPPROTO_DSTOPTS:
+ ptr += (8 * (ptr[1] + 1));
+ nxt = *ptr;
+ break;
+
+ default:
+ not_done = B_FALSE;
+ break;
+ }
+ } while (not_done);
+ return (nxt);
+ default:
+ break; /* shouldn't get here... */
+ }
+ return (0);
+}
+
+/*
+ * Returns the total IP header length.
+ * For v4, this includes any options present.
+ * For v6, this is the length of the IPv6 header plus
+ * any extension headers present.
+ *
+ * XXX Will need to be updated for AH and ESP
+ * XXX when IPsec is supported for v6.
+ */
+static int
+ip_hdr_len(uchar_t *ip)
+{
+ uchar_t nxt;
+ int hdr_len;
+ boolean_t not_done = B_TRUE;
+ int len = 40; /* IPv6 header size */
+ uchar_t *ptr = ip;
+
+ switch (IP_VERS(ip)) {
+ case IPV4_VERSION:
+ return (IP_HDR_LEN(ip));
+ case IPV6_VERSION:
+ nxt = ip[6];
+ ptr += len;
+ do {
+ switch (nxt) {
+ /*
+ * XXX Add IPsec headers here when supported for v6
+ * XXX (the AH will have a different size...)
+ */
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_ROUTING:
+ case IPPROTO_FRAGMENT:
+ case IPPROTO_DSTOPTS:
+ hdr_len = (8 * (ptr[1] + 1));
+ len += hdr_len;
+ ptr += hdr_len;
+ nxt = *ptr;
+ break;
+
+ default:
+ not_done = B_FALSE;
+ break;
+ }
+ } while (not_done);
+ return (len);
+ default:
+ break;
+ }
+ return (0); /* not IP */
+}
+
+static void
+codeprint()
+{
+ uint_t *op;
+
+ printf("User filter:\n");
+
+ for (op = oplist; *op; op++) {
+ if (*op <= OP_LAST)
+ printf("\t%2d: %s\n", op - oplist, opnames[*op]);
+ else
+ printf("\t%2d: (%d)\n", op - oplist, *op);
+
+ switch (*op) {
+ case OP_LOAD_CONST:
+ case OP_BRTR:
+ case OP_BRFL:
+ op++;
+ if ((int)*op < 0)
+ printf("\t%2d: 0x%08x (%d)\n",
+ op - oplist, *op, *op);
+ else
+ printf("\t%2d: %d (0x%08x)\n",
+ op - oplist, *op, *op);
+ }
+ }
+ printf("\t%2d: STOP\n", op - oplist);
+ printf("\n");
+}
+
+
+/*
+ * Take a pass through the generated code and optimize
+ * branches. A branch true (BRTR) that has another BRTR
+ * at its destination can use the address of the destination
+ * BRTR. A BRTR that points to a BRFL (branch false) should
+ * point to the address following the BRFL.
+ * A similar optimization applies to BRFL operators.
+ */
+static void
+optimize(uint_t *oplistp)
+{
+ uint_t *op;
+
+ for (op = oplistp; *op; op++) {
+ switch (*op) {
+ case OP_LOAD_CONST:
+ op++;
+ break;
+ case OP_BRTR:
+ op++;
+ optimize(&oplist[*op]);
+ if (oplist[*op] == OP_BRFL)
+ *op += 2;
+ else if (oplist[*op] == OP_BRTR)
+ *op = oplist[*op + 1];
+ break;
+ case OP_BRFL:
+ op++;
+ optimize(&oplist[*op]);
+ if (oplist[*op] == OP_BRTR)
+ *op += 2;
+ else if (oplist[*op] == OP_BRFL)
+ *op = oplist[*op + 1];
+ break;
+ }
+ }
+}
+
+/*
+ * RPC packets are tough to filter.
+ * While the call packet has all the interesting
+ * info: program number, version, procedure etc,
+ * the reply packet has none of this information.
+ * If we want to do useful filtering based on this
+ * information then we have to stash the information
+ * from the call packet, and use the XID in the reply
+ * to find the stashed info. The stashed info is
+ * kept in a circular lifo, assuming that a call packet
+ * will be followed quickly by its reply.
+ */
+
+struct xid_entry {
+ unsigned x_xid; /* The XID (32 bits) */
+ unsigned x_dir; /* CALL or REPLY */
+ unsigned x_rpcvers; /* Protocol version (2) */
+ unsigned x_prog; /* RPC program number */
+ unsigned x_vers; /* RPC version number */
+ unsigned x_proc; /* RPC procedure number */
+};
+static struct xid_entry xe_table[XID_CACHE_SIZE];
+static struct xid_entry *xe_first = &xe_table[0];
+static struct xid_entry *xe = &xe_table[0];
+static struct xid_entry *xe_last = &xe_table[XID_CACHE_SIZE - 1];
+
+static struct xid_entry *
+find_rpc(struct rpc_msg *rpc)
+{
+ struct xid_entry *x;
+
+ for (x = xe; x >= xe_first; x--)
+ if (x->x_xid == rpc->rm_xid)
+ return (x);
+ for (x = xe_last; x > xe; x--)
+ if (x->x_xid == rpc->rm_xid)
+ return (x);
+ return (NULL);
+}
+
+static void
+stash_rpc(struct rpc_msg *rpc)
+{
+ struct xid_entry *x;
+
+ if (find_rpc(rpc))
+ return;
+
+ x = xe++;
+ if (xe > xe_last)
+ xe = xe_first;
+ x->x_xid = rpc->rm_xid;
+ x->x_dir = htonl(REPLY);
+ x->x_prog = rpc->rm_call.cb_prog;
+ x->x_vers = rpc->rm_call.cb_vers;
+ x->x_proc = rpc->rm_call.cb_proc;
+}
+
+/*
+ * SLP can multicast requests, and recieve unicast replies in which
+ * neither the source nor destination port is identifiable as a SLP
+ * port. Hence, we need to do as RPC does, and keep track of packets we
+ * are interested in. For SLP, however, we use ports, not XIDs, and
+ * a smaller cache size is more efficient since every incoming packet
+ * needs to be checked.
+ */
+
+#define SLP_CACHE_SIZE 64
+static uint_t slp_table[SLP_CACHE_SIZE];
+static int slp_index = 0;
+
+/*
+ * Returns the index of dport in the table if found, otherwise -1.
+ */
+static int
+find_slp(uint_t dport) {
+ int i;
+
+ if (!dport)
+ return (0);
+
+ for (i = slp_index; i >= 0; i--)
+ if (slp_table[i] == dport) {
+ return (i);
+ }
+ for (i = SLP_CACHE_SIZE - 1; i > slp_index; i--)
+ if (slp_table[i] == dport) {
+ return (i);
+ }
+ return (-1);
+}
+
+static void stash_slp(uint_t sport) {
+ if (slp_table[slp_index - 1] == sport)
+ /* avoid redundancy due to multicast retransmissions */
+ return;
+
+ slp_table[slp_index++] = sport;
+ if (slp_index == SLP_CACHE_SIZE)
+ slp_index = 0;
+}
+
+/*
+ * This routine takes a packet and returns true or false
+ * according to whether the filter expression selects it
+ * or not.
+ * We assume here that offsets for short and long values
+ * are even - we may die with an alignment error if the
+ * CPU doesn't support odd addresses. Note that long
+ * values are loaded as two shorts so that 32 bit word
+ * alignment isn't important.
+ *
+ * IPv6 is a bit stickier to handle than IPv4...
+ */
+
+int
+want_packet(uchar_t *pkt, int len, int origlen)
+{
+ uint_t stack[MAXSS]; /* operand stack */
+ uint_t *op; /* current operator */
+ uint_t *sp; /* top of operand stack */
+ uchar_t *base; /* base for offsets into packet */
+ uchar_t *ip; /* addr of IP header, unaligned */
+ uchar_t *tcp; /* addr of TCP header, unaligned */
+ uchar_t *udp; /* addr of UDP header, unaligned */
+ struct rpc_msg rpcmsg; /* addr of RPC header */
+ struct rpc_msg *rpc;
+ int newrpc = 0;
+ uchar_t *slphdr; /* beginning of SLP header */
+ uint_t slp_sport, slp_dport;
+ int off, header_size;
+ uchar_t *offstack[MAXSS]; /* offset stack */
+ uchar_t **offp; /* current offset */
+ uchar_t *opkt = NULL;
+ uint_t olen;
+
+ sp = stack;
+ *sp = 1;
+ base = pkt;
+ offp = offstack;
+
+ header_size = (*interface->header_len)((char *)pkt, len);
+
+ for (op = oplist; *op; op++) {
+ switch ((enum optype) *op) {
+ case OP_LOAD_OCTET:
+ if ((base + *sp) > (pkt + len))
+ return (0); /* packet too short */
+
+ *sp = *((uchar_t *)(base + *sp));
+ break;
+ case OP_LOAD_SHORT:
+ off = *sp;
+
+ if ((base + off + sizeof (uint16_t) - 1) > (pkt + len))
+ return (0); /* packet too short */
+
+ *sp = ntohs(get_u16((uchar_t *)(base + off)));
+ break;
+ case OP_LOAD_LONG:
+ off = *sp;
+
+ if ((base + off + sizeof (uint32_t) - 1) > (pkt + len))
+ return (0); /* packet too short */
+
+ /*
+ * Handle 3 possible alignments
+ */
+ switch ((((unsigned)base) + off) % sizeof (uint_t)) {
+ case 0:
+ *sp = *(uint_t *)(base + off);
+ break;
+
+ case 2:
+ *((ushort_t *)(sp)) =
+ *((ushort_t *)(base + off));
+ *(((ushort_t *)sp) + 1) =
+ *((ushort_t *)(base + off) + 1);
+ break;
+
+ case 1:
+ case 3:
+ *((uchar_t *)(sp)) =
+ *((uchar_t *)(base + off));
+ *(((uchar_t *)sp) + 1) =
+ *((uchar_t *)(base + off) + 1);
+ *(((uchar_t *)sp) + 2) =
+ *((uchar_t *)(base + off) + 2);
+ *(((uchar_t *)sp) + 3) =
+ *((uchar_t *)(base + off) + 3);
+ break;
+ }
+ *sp = ntohl(*sp);
+ break;
+ case OP_LOAD_CONST:
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = *(++op);
+ break;
+ case OP_LOAD_LENGTH:
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = origlen;
+ break;
+ case OP_EQ:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp = *sp == *(sp + 1);
+ break;
+ case OP_NE:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp = *sp != *(sp + 1);
+ break;
+ case OP_GT:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp = *sp > *(sp + 1);
+ break;
+ case OP_GE:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp = *sp >= *(sp + 1);
+ break;
+ case OP_LT:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp = *sp < *(sp + 1);
+ break;
+ case OP_LE:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp = *sp <= *(sp + 1);
+ break;
+ case OP_AND:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp &= *(sp + 1);
+ break;
+ case OP_OR:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp |= *(sp + 1);
+ break;
+ case OP_XOR:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp ^= *(sp + 1);
+ break;
+ case OP_NOT:
+ *sp = !*sp;
+ break;
+ case OP_BRFL:
+ op++;
+ if (!*sp)
+ op = &oplist[*op] - 1;
+ break;
+ case OP_BRTR:
+ op++;
+ if (*sp)
+ op = &oplist[*op] - 1;
+ break;
+ case OP_ADD:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp += *(sp + 1);
+ break;
+ case OP_SUB:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp -= *(sp + 1);
+ break;
+ case OP_MUL:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp *= *(sp + 1);
+ break;
+ case OP_DIV:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp /= *(sp + 1);
+ break;
+ case OP_REM:
+ if (sp < &stack[1])
+ return (0);
+ sp--;
+ *sp %= *(sp + 1);
+ break;
+ case OP_OFFSET_POP:
+ if (offp < &offstack[0])
+ return (0);
+ base = *offp--;
+ if (opkt != NULL) {
+ pkt = opkt;
+ len = olen;
+ opkt = NULL;
+ }
+ break;
+ case OP_OFFSET_ZERO:
+ if (offp >= &offstack[MAXSS])
+ return (0);
+ *++offp = base;
+ base = pkt;
+ break;
+ case OP_OFFSET_LINK:
+ if (offp >= &offstack[MAXSS])
+ return (0);
+ *++offp = base;
+ base = pkt + header_size;
+ /*
+ * If the offset exceeds the packet length,
+ * we should not be interested in this packet...
+ * Just return 0.
+ */
+ if (base > pkt + len) {
+ return (0);
+ }
+ break;
+ case OP_OFFSET_IP:
+ if (offp >= &offstack[MAXSS])
+ return (0);
+ *++offp = base;
+ ip = pkt + header_size;
+ base = ip + ip_hdr_len(ip);
+ if (base == ip) {
+ return (0); /* not IP */
+ }
+ if (base > pkt + len) {
+ return (0); /* bad pkt */
+ }
+ break;
+ case OP_OFFSET_TCP:
+ if (offp >= &offstack[MAXSS])
+ return (0);
+ *++offp = base;
+ ip = pkt + header_size;
+ tcp = ip + ip_hdr_len(ip);
+ if (tcp == ip) {
+ return (0); /* not IP */
+ }
+ base = tcp + TCP_HDR_LEN(tcp);
+ if (base > pkt + len) {
+ return (0);
+ }
+ break;
+ case OP_OFFSET_UDP:
+ if (offp >= &offstack[MAXSS])
+ return (0);
+ *++offp = base;
+ ip = pkt + header_size;
+ udp = ip + ip_hdr_len(ip);
+ if (udp == ip) {
+ return (0); /* not IP */
+ }
+ base = udp + sizeof (struct udphdr);
+ if (base > pkt + len) {
+ return (0);
+ }
+ break;
+ case OP_OFFSET_RPC:
+ if (offp >= &offstack[MAXSS])
+ return (0);
+ *++offp = base;
+ ip = pkt + header_size;
+ rpc = NULL;
+
+ if (IP_VERS(ip) != IPV4_VERSION &&
+ IP_VERS(ip) != IPV6_VERSION) {
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 0;
+ break;
+ }
+
+ switch (ip_proto_of(ip)) {
+ case IPPROTO_UDP:
+ udp = ip + ip_hdr_len(ip);
+ rpc = (struct rpc_msg *)(udp +
+ sizeof (struct udphdr));
+ break;
+ case IPPROTO_TCP:
+ tcp = ip + ip_hdr_len(ip);
+ /*
+ * Need to skip an extra 4 for the xdr_rec
+ * field.
+ */
+ rpc = (struct rpc_msg *)(tcp +
+ TCP_HDR_LEN(tcp) + 4);
+ break;
+ }
+ /*
+ * We need to have at least 24 bytes of a RPC
+ * packet to look at to determine the validity
+ * of it.
+ */
+ if (rpc == NULL || (uchar_t *)rpc + 24 > pkt + len) {
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 0;
+ break;
+ }
+ /* align */
+ (void) memcpy(&rpcmsg, rpc, 24);
+ if (!valid_rpc((char *)&rpcmsg, 24)) {
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 0;
+ break;
+ }
+ if (ntohl(rpcmsg.rm_direction) == CALL) {
+ base = (uchar_t *)rpc;
+ newrpc = 1;
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 1;
+ } else {
+ opkt = pkt;
+ olen = len;
+
+ pkt = base = (uchar_t *)find_rpc(&rpcmsg);
+ len = sizeof (struct xid_entry);
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = base != NULL;
+ }
+ break;
+ case OP_OFFSET_SLP:
+ slphdr = NULL;
+ ip = pkt + header_size;
+
+ if (IP_VERS(ip) != IPV4_VERSION &&
+ IP_VERS(ip) != IPV6_VERSION) {
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 0;
+ break;
+ }
+
+ switch (ip_proto_of(ip)) {
+ struct udphdr udp_h;
+ struct tcphdr tcp_h;
+ case IPPROTO_UDP:
+ udp = ip + ip_hdr_len(ip);
+ /* align */
+ memcpy(&udp_h, udp, sizeof (udp_h));
+ slp_sport = ntohs(udp_h.uh_sport);
+ slp_dport = ntohs(udp_h.uh_dport);
+ slphdr = udp + sizeof (struct udphdr);
+ break;
+ case IPPROTO_TCP:
+ tcp = ip + ip_hdr_len(ip);
+ /* align */
+ memcpy(&tcp_h, tcp, sizeof (tcp_h));
+ slp_sport = ntohs(tcp_h.th_sport);
+ slp_dport = ntohs(tcp_h.th_dport);
+ slphdr = tcp + TCP_HDR_LEN(tcp);
+ break;
+ }
+ if (slphdr == NULL || slphdr > pkt + len) {
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 0;
+ break;
+ }
+ if (slp_sport == 427 || slp_dport == 427) {
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 1;
+ if (slp_sport != 427 && slp_dport == 427)
+ stash_slp(slp_sport);
+ break;
+ } else if (find_slp(slp_dport) != -1) {
+ if (valid_slp(slphdr, len)) {
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 1;
+ break;
+ }
+ /* else fallthrough to reject */
+ }
+ if (sp >= &stack[MAXSS])
+ return (0);
+ *(++sp) = 0;
+ break;
+ case OP_OFFSET_ETHERTYPE:
+ /*
+ * Set base to the location of the ethertype as
+ * appropriate for this link type. Note that it's
+ * not called "ethertype" for every link type, but
+ * we need to call it something.
+ */
+ if (offp >= &offstack[MAXSS])
+ return (0);
+ *++offp = base;
+ base = pkt + interface->network_type_offset;
+
+ /*
+ * Below, we adjust the offset for unusual
+ * link-layer headers that may have the protocol
+ * type in a variable location beyond what was set
+ * above.
+ */
+ switch (interface->mac_type) {
+ case DL_ETHER:
+ case DL_CSMACD:
+ /*
+ * If this is a VLAN-tagged packet, we need
+ * to point to the ethertype field in the
+ * VLAN header. Move past the ethertype
+ * field in the ethernet header.
+ */
+ if (ntohs(get_u16(base)) == ETHERTYPE_VLAN)
+ base += (ENCAP_ETHERTYPE_OFF);
+ break;
+ }
+ if (base > pkt + len) {
+ /* Went too far, drop the packet */
+ return (0);
+ }
+ break;
+ }
+ }
+
+ if (*sp && newrpc)
+ stash_rpc(&rpcmsg);
+
+ return (*sp);
+}
+
+static void
+load_const(uint_t constval)
+{
+ emitop(OP_LOAD_CONST);
+ emitval(constval);
+}
+
+static void
+load_value(int offset, int len)
+{
+ if (offset >= 0)
+ load_const(offset);
+
+ switch (len) {
+ case 1:
+ emitop(OP_LOAD_OCTET);
+ break;
+ case 2:
+ emitop(OP_LOAD_SHORT);
+ break;
+ case 4:
+ emitop(OP_LOAD_LONG);
+ break;
+ }
+}
+
+/*
+ * Emit code to compare a field in
+ * the packet against a constant value.
+ */
+static void
+compare_value(uint_t offset, uint_t len, uint_t val)
+{
+ load_const(val);
+ load_value(offset, len);
+ emitop(OP_EQ);
+}
+
+static void
+compare_addr_v4(uint_t offset, uint_t len, uint_t val)
+{
+ load_const(ntohl(val));
+ load_value(offset, len);
+ emitop(OP_EQ);
+}
+
+static void
+compare_addr_v6(uint_t offset, uint_t len, struct in6_addr val)
+{
+ int i;
+ uint32_t value;
+
+ for (i = 0; i < len; i += 4) {
+ value = ntohl(*(uint32_t *)&val.s6_addr[i]);
+ load_const(value);
+ load_value(offset + i, 4);
+ emitop(OP_EQ);
+ if (i != 0)
+ emitop(OP_AND);
+ }
+}
+
+/*
+ * Same as above except do the comparison
+ * after and'ing a mask value. Useful
+ * for comparing IP network numbers
+ */
+static void
+compare_value_mask(uint_t offset, uint_t len, uint_t val, int mask)
+{
+ load_value(offset, len);
+ load_const(mask);
+ emitop(OP_AND);
+ load_const(val);
+ emitop(OP_EQ);
+}
+
+/*
+ * Compare two zoneid's. The arg val passed in is stored in network
+ * byte order.
+ */
+static void
+compare_value_zone(uint_t offset, uint32_t val)
+{
+ load_const(ntohl(val));
+ load_value(offset, 4);
+ emitop(OP_EQ);
+}
+
+/* Emit an operator into the code array */
+static void
+emitop(enum optype opcode)
+{
+ if (curr_op >= &oplist[MAXOPS])
+ pr_err("expression too long");
+ *curr_op++ = opcode;
+}
+
+/*
+ * Remove n operators recently emitted into
+ * the code array. Used by alternation().
+ */
+static void
+unemit(int numops)
+{
+ curr_op -= numops;
+}
+
+
+/*
+ * Same as emitop except that we're emitting
+ * a value that's not an operator.
+ */
+static void
+emitval(uint_t val)
+{
+ if (curr_op >= &oplist[MAXOPS])
+ pr_err("expression too long");
+ *curr_op++ = val;
+}
+
+/*
+ * Used to chain forward branches together
+ * for later resolution by resolve_chain().
+ */
+static uint_t
+chain(int p)
+{
+ uint_t pos = curr_op - oplist;
+
+ emitval(p);
+ return (pos);
+}
+
+/*
+ * Proceed backward through the code array
+ * following a chain of forward references.
+ * At each reference install the destination
+ * branch offset.
+ */
+static void
+resolve_chain(uint_t p)
+{
+ uint_t n;
+ uint_t pos = curr_op - oplist;
+
+ while (p) {
+ n = oplist[p];
+ oplist[p] = pos;
+ p = n;
+ }
+}
+
+#define EQ(val) (strcmp(token, val) == 0)
+
+char *tkp, *sav_tkp;
+char *token;
+enum { EOL, ALPHA, NUMBER, FIELD, ADDR_IP, ADDR_ETHER, SPECIAL,
+ ADDR_IP6, ADDR_AT } tokentype;
+uint_t tokenval;
+
+/*
+ * This is the scanner. Each call returns the next
+ * token in the filter expression. A token is either:
+ * EOL: The end of the line - no more tokens.
+ * ALPHA: A name that begins with a letter and contains
+ * letters or digits, hyphens or underscores.
+ * NUMBER: A number. The value can be represented as
+ * a decimal value (1234) or an octal value
+ * that begins with zero (066) or a hex value
+ * that begins with 0x or 0X (0xff).
+ * FIELD: A name followed by a left square bracket.
+ * ADDR_IP: An IP address. Any sequence of digits
+ * separated by dots e.g. 109.104.40.13
+ * ADDR_ETHER: An ethernet address. Any sequence of hex
+ * digits separated by colons e.g. 8:0:20:0:76:39
+ * SPECIAL: A special character e.g. ">" or "(". The scanner
+ * correctly handles digraphs - two special characters
+ * that constitute a single token e.g. "==" or ">=".
+ * ADDR_IP6: An IPv6 address.
+ *
+ * ADDR_AT: An AppleTalk Phase II address. A sequence of two numbers
+ * separated by a dot.
+ *
+ * The current token is maintained in "token" and and its
+ * type in "tokentype". If tokentype is NUMBER then the
+ * value is held in "tokenval".
+ */
+
+static const char *namechars =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.";
+static const char *numchars = "0123456789abcdefABCDEFXx:.";
+
+void
+next()
+{
+ static int savechar;
+ char *p;
+ int size, size1;
+ int base, colons, dots, alphas, double_colon;
+
+ colons = 0;
+ double_colon = 0;
+
+ if (*tkp == '\0') {
+ token = tkp;
+ *tkp = savechar;
+ }
+
+ sav_tkp = tkp;
+
+ while (isspace(*tkp)) tkp++;
+ token = tkp;
+ if (*token == '\0') {
+ tokentype = EOL;
+ return;
+ }
+
+ /* A token containing ':' cannot be ALPHA type */
+ tkp = token + strspn(token, numchars);
+ for (p = token; p < tkp; p++) {
+ if (*p == ':') {
+ colons++;
+ if (*(p+1) == ':')
+ double_colon++;
+ }
+ }
+
+ tkp = token;
+ if (isalpha(*tkp) && !colons) {
+ tokentype = ALPHA;
+ tkp += strspn(tkp, namechars);
+ if (*tkp == '[') {
+ tokentype = FIELD;
+ *tkp++ = '\0';
+ }
+ } else
+
+ /*
+ * RFC1123 states that host names may now start with digits. Need
+ * to change parser to account for this. Also, need to distinguish
+ * between 1.2.3.4 and 1.2.3.a where the first case is an IP address
+ * and the second is a domain name. 333aaa needs to be distinguished
+ * from 0x333aaa. The first is a host name and the second is a number.
+ *
+ * The (colons > 1) conditional differentiates between ethernet
+ * and IPv6 addresses, and an expression of the form base[expr:size],
+ * which can only contain one ':' character.
+ */
+ if (isdigit(*tkp) || colons > 1) {
+ tkp = token + strspn(token, numchars);
+ dots = alphas = 0;
+ for (p = token; p < tkp; p++) {
+ if (*p == '.')
+ dots++;
+ else if (isalpha(*p))
+ alphas = 1;
+ }
+ if (colons > 1) {
+ if (colons == 5 && double_colon == 0) {
+ tokentype = ADDR_ETHER;
+ } else {
+ tokentype = ADDR_IP6;
+ }
+ } else if (dots) {
+ size = tkp - token;
+ size1 = strspn(token, "0123456789.");
+ if (dots == 1 && size == size1) {
+ tokentype = ADDR_AT;
+ } else
+ if (dots != 3 || size != size1) {
+ tokentype = ALPHA;
+ if (*tkp != '\0' && !isspace(*tkp)) {
+ tkp += strspn(tkp, namechars);
+ if (*tkp == '[') {
+ tokentype = FIELD;
+ *tkp++ = '\0';
+ }
+ }
+ } else
+ tokentype = ADDR_IP;
+ } else if (token + strspn(token, namechars) <= tkp) {
+ /*
+ * With the above check, if there are more
+ * characters after the last digit, assume
+ * that it is not a number.
+ */
+ tokentype = NUMBER;
+ p = tkp;
+ tkp = token;
+ base = 10;
+ if (*tkp == '0') {
+ base = 8;
+ tkp++;
+ if (*tkp == 'x' || *tkp == 'X')
+ base = 16;
+ }
+ if ((base == 10 || base == 8) && alphas) {
+ tokentype = ALPHA;
+ tkp = p;
+ } else if (base == 16) {
+ size = 2 + strspn(token+2,
+ "0123456789abcdefABCDEF");
+ size1 = p - token;
+ if (size != size1) {
+ tokentype = ALPHA;
+ tkp = p;
+ } else
+ /*
+ * handles the case of 0x so an error message
+ * is not printed. Treats 0x as 0.
+ */
+ if (size == 2) {
+ tokenval = 0;
+ tkp = token +2;
+ } else {
+ tokenval = strtoul(token, &tkp, base);
+ }
+ } else {
+ tokenval = strtoul(token, &tkp, base);
+ }
+ } else {
+ tokentype = ALPHA;
+ tkp += strspn(tkp, namechars);
+ if (*tkp == '[') {
+ tokentype = FIELD;
+ *tkp++ = '\0';
+ }
+ }
+ } else {
+ tokentype = SPECIAL;
+ tkp++;
+ if ((*token == '=' && *tkp == '=') ||
+ (*token == '>' && *tkp == '=') ||
+ (*token == '<' && *tkp == '=') ||
+ (*token == '!' && *tkp == '='))
+ tkp++;
+ }
+
+ savechar = *tkp;
+ *tkp = '\0';
+}
+
+typedef struct match_type {
+ char *m_name;
+ int m_offset;
+ int m_size;
+ int m_value;
+ int m_depend;
+ enum optype m_optype;
+} match_type_t;
+
+static match_type_t ether_match_types[] = {
+ /*
+ * Table initialized assuming Ethernet data link headers.
+ * m_offset is an offset beyond the offset op, which is why
+ * the offset is zero for when snoop needs to check an ethertype.
+ */
+ "ip", 0, 2, ETHERTYPE_IP, -1, OP_OFFSET_ETHERTYPE,
+ "ip6", 0, 2, ETHERTYPE_IPV6, -1, OP_OFFSET_ETHERTYPE,
+ "arp", 0, 2, ETHERTYPE_ARP, -1, OP_OFFSET_ETHERTYPE,
+ "rarp", 0, 2, ETHERTYPE_REVARP, -1, OP_OFFSET_ETHERTYPE,
+ "pppoed", 0, 2, ETHERTYPE_PPPOED, -1, OP_OFFSET_ETHERTYPE,
+ "pppoes", 0, 2, ETHERTYPE_PPPOES, -1, OP_OFFSET_ETHERTYPE,
+ "tcp", 9, 1, IPPROTO_TCP, 0, OP_OFFSET_LINK,
+ "tcp", 6, 1, IPPROTO_TCP, 1, OP_OFFSET_LINK,
+ "udp", 9, 1, IPPROTO_UDP, 0, OP_OFFSET_LINK,
+ "udp", 6, 1, IPPROTO_UDP, 1, OP_OFFSET_LINK,
+ "icmp", 9, 1, IPPROTO_ICMP, 0, OP_OFFSET_LINK,
+ "icmp6", 6, 1, IPPROTO_ICMPV6, 1, OP_OFFSET_LINK,
+ "ospf", 9, 1, IPPROTO_OSPF, 0, OP_OFFSET_LINK,
+ "ospf", 6, 1, IPPROTO_OSPF, 1, OP_OFFSET_LINK,
+ "ip-in-ip", 9, 1, IPPROTO_ENCAP, 0, OP_OFFSET_LINK,
+ "esp", 9, 1, IPPROTO_ESP, 0, OP_OFFSET_LINK,
+ "esp", 6, 1, IPPROTO_ESP, 1, OP_OFFSET_LINK,
+ "ah", 9, 1, IPPROTO_AH, 0, OP_OFFSET_LINK,
+ "ah", 6, 1, IPPROTO_AH, 1, OP_OFFSET_LINK,
+ "sctp", 9, 1, IPPROTO_SCTP, 0, OP_OFFSET_LINK,
+ "sctp", 6, 1, IPPROTO_SCTP, 1, OP_OFFSET_LINK,
+ 0, 0, 0, 0, 0, 0
+};
+
+static match_type_t ipnet_match_types[] = {
+ /*
+ * Table initialized assuming Ethernet data link headers.
+ * m_offset is an offset beyond the offset op, which is why
+ * the offset is zero for when snoop needs to check an ethertype.
+ */
+ "ip", 0, 1, IPV4_VERSION, -1, OP_OFFSET_ETHERTYPE,
+ "ip6", 0, 1, IPV6_VERSION, -1, OP_OFFSET_ETHERTYPE,
+ "tcp", 9, 1, IPPROTO_TCP, 0, OP_OFFSET_LINK,
+ "tcp", 6, 1, IPPROTO_TCP, 1, OP_OFFSET_LINK,
+ "udp", 9, 1, IPPROTO_UDP, 0, OP_OFFSET_LINK,
+ "udp", 6, 1, IPPROTO_UDP, 1, OP_OFFSET_LINK,
+ "icmp", 9, 1, IPPROTO_ICMP, 0, OP_OFFSET_LINK,
+ "icmp6", 6, 1, IPPROTO_ICMPV6, 1, OP_OFFSET_LINK,
+ "ospf", 9, 1, IPPROTO_OSPF, 0, OP_OFFSET_LINK,
+ "ospf", 6, 1, IPPROTO_OSPF, 1, OP_OFFSET_LINK,
+ "ip-in-ip", 9, 1, IPPROTO_ENCAP, 0, OP_OFFSET_LINK,
+ "esp", 9, 1, IPPROTO_ESP, 0, OP_OFFSET_LINK,
+ "esp", 6, 1, IPPROTO_ESP, 1, OP_OFFSET_LINK,
+ "ah", 9, 1, IPPROTO_AH, 0, OP_OFFSET_LINK,
+ "ah", 6, 1, IPPROTO_AH, 1, OP_OFFSET_LINK,
+ "sctp", 9, 1, IPPROTO_SCTP, 0, OP_OFFSET_LINK,
+ "sctp", 6, 1, IPPROTO_SCTP, 1, OP_OFFSET_LINK,
+ 0, 0, 0, 0, 0, 0
+};
+
+static match_type_t iptun_match_types[] = {
+ "ip", 0, 1, IPPROTO_ENCAP, -1, OP_OFFSET_ETHERTYPE,
+ "ip6", 0, 1, IPPROTO_IPV6, -1, OP_OFFSET_ETHERTYPE,
+ "tcp", 9, 1, IPPROTO_TCP, 0, OP_OFFSET_LINK,
+ "tcp", 6, 1, IPPROTO_TCP, 1, OP_OFFSET_LINK,
+ "udp", 9, 1, IPPROTO_UDP, 0, OP_OFFSET_LINK,
+ "udp", 6, 1, IPPROTO_UDP, 1, OP_OFFSET_LINK,
+ "icmp", 9, 1, IPPROTO_ICMP, 0, OP_OFFSET_LINK,
+ "icmp6", 6, 1, IPPROTO_ICMPV6, 1, OP_OFFSET_LINK,
+ "ospf", 9, 1, IPPROTO_OSPF, 0, OP_OFFSET_LINK,
+ "ospf", 6, 1, IPPROTO_OSPF, 1, OP_OFFSET_LINK,
+ "ip-in-ip", 9, 1, IPPROTO_ENCAP, 0, OP_OFFSET_LINK,
+ "esp", 9, 1, IPPROTO_ESP, 0, OP_OFFSET_LINK,
+ "esp", 6, 1, IPPROTO_ESP, 1, OP_OFFSET_LINK,
+ "ah", 9, 1, IPPROTO_AH, 0, OP_OFFSET_LINK,
+ "ah", 6, 1, IPPROTO_AH, 1, OP_OFFSET_LINK,
+ "sctp", 9, 1, IPPROTO_SCTP, 0, OP_OFFSET_LINK,
+ "sctp", 6, 1, IPPROTO_SCTP, 1, OP_OFFSET_LINK,
+ 0, 0, 0, 0, 0, 0
+};
+
+static void
+generate_check(match_type_t match_types[], int index, int type)
+{
+ match_type_t *mtp = &match_types[index];
+ /*
+ * Note: this code assumes the above dependencies are
+ * not cyclic. This *should* always be true.
+ */
+ if (mtp->m_depend != -1)
+ generate_check(match_types, mtp->m_depend, type);
+
+ emitop(mtp->m_optype);
+ load_value(mtp->m_offset, mtp->m_size);
+ load_const(mtp->m_value);
+ emitop(OP_OFFSET_POP);
+
+ emitop(OP_EQ);
+
+ if (mtp->m_depend != -1)
+ emitop(OP_AND);
+}
+
+/*
+ * Generate code based on the keyword argument.
+ * This word is looked up in the match_types table
+ * and checks a field within the packet for a given
+ * value e.g. ether or ip type field. The match
+ * can also have a dependency on another entry e.g.
+ * "tcp" requires that the packet also be "ip".
+ */
+static int
+comparison(char *s)
+{
+ unsigned int i, n_checks = 0;
+ match_type_t *match_types;
+
+ switch (interface->mac_type) {
+ case DL_ETHER:
+ match_types = ether_match_types;
+ break;
+ case DL_IPNET:
+ match_types = ipnet_match_types;
+ break;
+ case DL_IPV4:
+ case DL_IPV6:
+ case DL_6TO4:
+ match_types = iptun_match_types;
+ break;
+ default:
+ return (0);
+ }
+
+ for (i = 0; match_types[i].m_name != NULL; i++) {
+ if (strcmp(s, match_types[i].m_name) != 0)
+ continue;
+
+ n_checks++;
+ generate_check(match_types, i, interface->mac_type);
+ if (n_checks > 1)
+ emitop(OP_OR);
+ }
+
+ return (n_checks > 0);
+}
+
+enum direction { ANY, TO, FROM };
+enum direction dir;
+
+/*
+ * Generate code to match an IP address. The address
+ * may be supplied either as a hostname or in dotted format.
+ * For source packets both the IP source address and ARP
+ * src are checked.
+ * Note: we don't check packet type here - whether IP or ARP.
+ * It's possible that we'll do an improper match.
+ */
+static void
+ipaddr_match(enum direction which, char *hostname, int inet_type)
+{
+ bool_t found_host;
+ int m = 0, n = 0;
+ uint_t *addr4ptr;
+ uint_t addr4;
+ struct in6_addr *addr6ptr;
+ int h_addr_index;
+ struct hostent *hp = NULL;
+ int error_num = 0;
+ boolean_t freehp = B_FALSE;
+ boolean_t first = B_TRUE;
+
+ /*
+ * The addr4offset and addr6offset variables simplify the code which
+ * generates the address comparison filter. With these two variables,
+ * duplicate code need not exist for the TO and FROM case.
+ * A value of -1 describes the ANY case (TO and FROM).
+ */
+ int addr4offset;
+ int addr6offset;
+
+ found_host = 0;
+
+ if (tokentype == ADDR_IP) {
+ hp = lgetipnodebyname(hostname, AF_INET, 0, &error_num);
+ if (hp == NULL) {
+ hp = getipnodebyname(hostname, AF_INET, 0, &error_num);
+ freehp = 1;
+ }
+ if (hp == NULL) {
+ if (error_num == TRY_AGAIN) {
+ pr_err("couldn't resolve %s (try again later)",
+ hostname);
+ } else {
+ pr_err("couldn't resolve %s", hostname);
+ }
+ }
+ inet_type = IPV4_ONLY;
+ } else if (tokentype == ADDR_IP6) {
+ hp = lgetipnodebyname(hostname, AF_INET6, 0, &error_num);
+ if (hp == NULL) {
+ hp = getipnodebyname(hostname, AF_INET6, 0, &error_num);
+ freehp = 1;
+ }
+ if (hp == NULL) {
+ if (error_num == TRY_AGAIN) {
+ pr_err("couldn't resolve %s (try again later)",
+ hostname);
+ } else {
+ pr_err("couldn't resolve %s", hostname);
+ }
+ }
+ inet_type = IPV6_ONLY;
+ } else {
+ /* Some hostname i.e. tokentype is ALPHA */
+ switch (inet_type) {
+ case IPV4_ONLY:
+ /* Only IPv4 address is needed */
+ hp = lgetipnodebyname(hostname, AF_INET, 0, &error_num);
+ if (hp == NULL) {
+ hp = getipnodebyname(hostname, AF_INET, 0,
+ &error_num);
+ freehp = 1;
+ }
+ if (hp != NULL) {
+ found_host = 1;
+ }
+ break;
+ case IPV6_ONLY:
+ /* Only IPv6 address is needed */
+ hp = lgetipnodebyname(hostname, AF_INET6, 0,
+ &error_num);
+ if (hp == NULL) {
+ hp = getipnodebyname(hostname, AF_INET6, 0,
+ &error_num);
+ freehp = 1;
+ }
+ if (hp != NULL) {
+ found_host = 1;
+ }
+ break;
+ case IPV4_AND_IPV6:
+ /* Both IPv4 and IPv6 are needed */
+ hp = lgetipnodebyname(hostname, AF_INET6,
+ AI_ALL | AI_V4MAPPED, &error_num);
+ if (hp == NULL) {
+ hp = getipnodebyname(hostname, AF_INET6,
+ AI_ALL | AI_V4MAPPED, &error_num);
+ freehp = 1;
+ }
+ if (hp != NULL) {
+ found_host = 1;
+ }
+ break;
+ default:
+ found_host = 0;
+ }
+
+ if (!found_host) {
+ if (error_num == TRY_AGAIN) {
+ pr_err("could not resolve %s (try again later)",
+ hostname);
+ } else {
+ pr_err("could not resolve %s", hostname);
+ }
+ }
+ }
+
+ if (hp == NULL)
+ return;
+
+ switch (which) {
+ case TO:
+ addr4offset = IPV4_DSTADDR_OFFSET;
+ addr6offset = IPV6_DSTADDR_OFFSET;
+ break;
+ case FROM:
+ addr4offset = IPV4_SRCADDR_OFFSET;
+ addr6offset = IPV6_SRCADDR_OFFSET;
+ break;
+ case ANY:
+ addr4offset = -1;
+ addr6offset = -1;
+ break;
+ }
+
+ /*
+ * The code below generates the filter.
+ */
+ if (hp->h_addrtype == AF_INET) {
+ ethertype_match(interface->network_type_ip);
+ emitop(OP_BRFL);
+ n = chain(n);
+ emitop(OP_OFFSET_LINK);
+ h_addr_index = 0;
+ addr4ptr = (uint_t *)hp->h_addr_list[h_addr_index];
+ while (addr4ptr != NULL) {
+ if (addr4offset == -1) {
+ compare_addr_v4(IPV4_SRCADDR_OFFSET, 4,
+ *addr4ptr);
+ emitop(OP_BRTR);
+ m = chain(m);
+ compare_addr_v4(IPV4_DSTADDR_OFFSET, 4,
+ *addr4ptr);
+ } else {
+ compare_addr_v4(addr4offset, 4, *addr4ptr);
+ }
+ addr4ptr = (uint_t *)hp->h_addr_list[++h_addr_index];
+ if (addr4ptr != NULL) {
+ emitop(OP_BRTR);
+ m = chain(m);
+ }
+ }
+ if (m != 0) {
+ resolve_chain(m);
+ }
+ emitop(OP_OFFSET_POP);
+ resolve_chain(n);
+ } else {
+ /* first pass: IPv4 addresses */
+ h_addr_index = 0;
+ addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index];
+ first = B_TRUE;
+ while (addr6ptr != NULL) {
+ if (IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
+ if (first) {
+ ethertype_match(
+ interface->network_type_ip);
+ emitop(OP_BRFL);
+ n = chain(n);
+ emitop(OP_OFFSET_LINK);
+ first = B_FALSE;
+ } else {
+ emitop(OP_BRTR);
+ m = chain(m);
+ }
+ IN6_V4MAPPED_TO_INADDR(addr6ptr,
+ (struct in_addr *)&addr4);
+ if (addr4offset == -1) {
+ compare_addr_v4(IPV4_SRCADDR_OFFSET, 4,
+ addr4);
+ emitop(OP_BRTR);
+ m = chain(m);
+ compare_addr_v4(IPV4_DSTADDR_OFFSET, 4,
+ addr4);
+ } else {
+ compare_addr_v4(addr4offset, 4, addr4);
+ }
+ }
+ addr6ptr = (struct in6_addr *)
+ hp->h_addr_list[++h_addr_index];
+ }
+ /* second pass: IPv6 addresses */
+ h_addr_index = 0;
+ addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index];
+ first = B_TRUE;
+ while (addr6ptr != NULL) {
+ if (!IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
+ if (first) {
+ /*
+ * bypass check for IPv6 addresses
+ * when we have an IPv4 packet
+ */
+ if (n != 0) {
+ emitop(OP_BRTR);
+ m = chain(m);
+ emitop(OP_BRFL);
+ m = chain(m);
+ resolve_chain(n);
+ n = 0;
+ }
+ ethertype_match(
+ interface->network_type_ipv6);
+ emitop(OP_BRFL);
+ n = chain(n);
+ emitop(OP_OFFSET_LINK);
+ first = B_FALSE;
+ } else {
+ emitop(OP_BRTR);
+ m = chain(m);
+ }
+ if (addr6offset == -1) {
+ compare_addr_v6(IPV6_SRCADDR_OFFSET,
+ 16, *addr6ptr);
+ emitop(OP_BRTR);
+ m = chain(m);
+ compare_addr_v6(IPV6_DSTADDR_OFFSET,
+ 16, *addr6ptr);
+ } else {
+ compare_addr_v6(addr6offset, 16,
+ *addr6ptr);
+ }
+ }
+ addr6ptr = (struct in6_addr *)
+ hp->h_addr_list[++h_addr_index];
+ }
+ if (m != 0) {
+ resolve_chain(m);
+ }
+ emitop(OP_OFFSET_POP);
+ resolve_chain(n);
+ }
+
+ /* only free struct hostent returned by getipnodebyname() */
+ if (freehp) {
+ freehostent(hp);
+ }
+}
+
+/*
+ * Match on zoneid. The arg zone passed in is in network byte order.
+ */
+static void
+zone_match(enum direction which, uint32_t zone)
+{
+
+ switch (which) {
+ case TO:
+ compare_value_zone(IPNET_DSTZONE_OFFSET, zone);
+ break;
+ case FROM:
+ compare_value_zone(IPNET_SRCZONE_OFFSET, zone);
+ break;
+ case ANY:
+ compare_value_zone(IPNET_SRCZONE_OFFSET, zone);
+ compare_value_zone(IPNET_DSTZONE_OFFSET, zone);
+ emitop(OP_OR);
+ }
+}
+
+/*
+ * Generate code to match an AppleTalk address. The address
+ * must be given as two numbers with a dot between
+ *
+ */
+static void
+ataddr_match(enum direction which, char *hostname)
+{
+ uint_t net;
+ uint_t node;
+ uint_t m, n;
+
+ sscanf(hostname, "%u.%u", &net, &node);
+
+ emitop(OP_OFFSET_LINK);
+ switch (which) {
+ case TO:
+ compare_value(AT_DST_NET_OFFSET, 2, net);
+ emitop(OP_BRFL);
+ m = chain(0);
+ compare_value(AT_DST_NODE_OFFSET, 1, node);
+ resolve_chain(m);
+ break;
+ case FROM:
+ compare_value(AT_SRC_NET_OFFSET, 2, net);
+ emitop(OP_BRFL);
+ m = chain(0);
+ compare_value(AT_SRC_NODE_OFFSET, 1, node);
+ resolve_chain(m);
+ break;
+ case ANY:
+ compare_value(AT_DST_NET_OFFSET, 2, net);
+ emitop(OP_BRFL);
+ m = chain(0);
+ compare_value(AT_DST_NODE_OFFSET, 1, node);
+ resolve_chain(m);
+ emitop(OP_BRTR);
+ n = chain(0);
+ compare_value(AT_SRC_NET_OFFSET, 2, net);
+ emitop(OP_BRFL);
+ m = chain(0);
+ compare_value(AT_SRC_NODE_OFFSET, 1, node);
+ resolve_chain(m);
+ resolve_chain(n);
+ break;
+ }
+ emitop(OP_OFFSET_POP);
+}
+
+/*
+ * Compare ethernet addresses. The address may
+ * be provided either as a hostname or as a
+ * 6 octet colon-separated address.
+ */
+static void
+etheraddr_match(enum direction which, char *hostname)
+{
+ uint_t addr;
+ ushort_t *addrp;
+ int to_offset, from_offset;
+ struct ether_addr e, *ep = NULL;
+ int m;
+
+ /*
+ * First, check the interface type for whether src/dest address
+ * is determinable; if not, retreat early.
+ */
+ switch (interface->mac_type) {
+ case DL_ETHER:
+ from_offset = ETHERADDRL;
+ to_offset = 0;
+ break;
+
+ case DL_IB:
+ /*
+ * If an ethernet address is attempted to be used
+ * on an IPoIB interface, flag error. Link address
+ * based filtering is unsupported on IPoIB, so there
+ * is no ipibaddr_match() or parsing support for IPoIB
+ * 20 byte link addresses.
+ */
+ pr_err("filter option unsupported on media");
+ break;
+
+ case DL_FDDI:
+ from_offset = 7;
+ to_offset = 1;
+ break;
+
+ default:
+ /*
+ * Where do we find "ether" address for FDDI & TR?
+ * XXX can improve? ~sparker
+ */
+ load_const(1);
+ return;
+ }
+
+ if (isxdigit(*hostname))
+ ep = ether_aton(hostname);
+ if (ep == NULL) {
+ if (ether_hostton(hostname, &e))
+ if (!arp_for_ether(hostname, &e))
+ pr_err("cannot obtain ether addr for %s",
+ hostname);
+ ep = &e;
+ }
+ memcpy(&addr, (ushort_t *)ep, 4);
+ addrp = (ushort_t *)ep + 2;
+
+ emitop(OP_OFFSET_ZERO);
+ switch (which) {
+ case TO:
+ compare_value(to_offset, 4, ntohl(addr));
+ emitop(OP_BRFL);
+ m = chain(0);
+ compare_value(to_offset + 4, 2, ntohs(*addrp));
+ resolve_chain(m);
+ break;
+ case FROM:
+ compare_value(from_offset, 4, ntohl(addr));
+ emitop(OP_BRFL);
+ m = chain(0);
+ compare_value(from_offset + 4, 2, ntohs(*addrp));
+ resolve_chain(m);
+ break;
+ case ANY:
+ compare_value(to_offset, 4, ntohl(addr));
+ compare_value(to_offset + 4, 2, ntohs(*addrp));
+ emitop(OP_AND);
+ emitop(OP_BRTR);
+ m = chain(0);
+
+ compare_value(from_offset, 4, ntohl(addr));
+ compare_value(from_offset + 4, 2, ntohs(*addrp));
+ emitop(OP_AND);
+ resolve_chain(m);
+ break;
+ }
+ emitop(OP_OFFSET_POP);
+}
+
+static void
+ethertype_match(int val)
+{
+ int ether_offset = interface->network_type_offset;
+
+ /*
+ * If the user is interested in ethertype VLAN,
+ * then we need to set the offset to the beginning of the packet.
+ * But if the user is interested in another ethertype,
+ * such as IPv4, then we need to take into consideration
+ * the fact that the packet might be VLAN tagged.
+ */
+ if (interface->mac_type == DL_ETHER ||
+ interface->mac_type == DL_CSMACD) {
+ if (val != ETHERTYPE_VLAN) {
+ /*
+ * OP_OFFSET_ETHERTYPE puts us at the ethertype
+ * field whether or not there is a VLAN tag,
+ * so ether_offset goes to zero if we get here.
+ */
+ emitop(OP_OFFSET_ETHERTYPE);
+ ether_offset = 0;
+ } else {
+ emitop(OP_OFFSET_ZERO);
+ }
+ }
+ compare_value(ether_offset, interface->network_type_len, val);
+ if (interface->mac_type == DL_ETHER ||
+ interface->mac_type == DL_CSMACD) {
+ emitop(OP_OFFSET_POP);
+ }
+}
+
+/*
+ * Match a network address. The host part
+ * is masked out. The network address may
+ * be supplied either as a netname or in
+ * IP dotted format. The mask to be used
+ * for the comparison is assumed from the
+ * address format (see comment below).
+ */
+static void
+netaddr_match(enum direction which, char *netname)
+{
+ uint_t addr;
+ uint_t mask = 0xff000000;
+ uint_t m;
+ struct netent *np;
+
+ if (isdigit(*netname)) {
+ addr = inet_network(netname);
+ } else {
+ np = getnetbyname(netname);
+ if (np == NULL)
+ pr_err("net %s not known", netname);
+ addr = np->n_net;
+ }
+
+ /*
+ * Left justify the address and figure
+ * out a mask based on the supplied address.
+ * Set the mask according to the number of zero
+ * low-order bytes.
+ * Note: this works only for whole octet masks.
+ */
+ if (addr) {
+ while ((addr & ~mask) != 0) {
+ mask |= (mask >> 8);
+ }
+ }
+
+ emitop(OP_OFFSET_LINK);
+ switch (which) {
+ case TO:
+ compare_value_mask(16, 4, addr, mask);
+ break;
+ case FROM:
+ compare_value_mask(12, 4, addr, mask);
+ break;
+ case ANY:
+ compare_value_mask(12, 4, addr, mask);
+ emitop(OP_BRTR);
+ m = chain(0);
+ compare_value_mask(16, 4, addr, mask);
+ resolve_chain(m);
+ break;
+ }
+ emitop(OP_OFFSET_POP);
+}
+
+/*
+ * Match either a UDP or TCP port number.
+ * The port number may be provided either as
+ * port name as listed in /etc/services ("nntp") or as
+ * the port number itself (2049).
+ */
+static void
+port_match(enum direction which, char *portname)
+{
+ struct servent *sp;
+ uint_t m, port;
+
+ if (isdigit(*portname)) {
+ port = atoi(portname);
+ } else {
+ sp = getservbyname(portname, NULL);
+ if (sp == NULL)
+ pr_err("invalid port number or name: %s", portname);
+ port = ntohs(sp->s_port);
+ }
+
+ emitop(OP_OFFSET_IP);
+
+ switch (which) {
+ case TO:
+ compare_value(2, 2, port);
+ break;
+ case FROM:
+ compare_value(0, 2, port);
+ break;
+ case ANY:
+ compare_value(2, 2, port);
+ emitop(OP_BRTR);
+ m = chain(0);
+ compare_value(0, 2, port);
+ resolve_chain(m);
+ break;
+ }
+ emitop(OP_OFFSET_POP);
+}
+
+/*
+ * Generate code to match packets with a specific
+ * RPC program number. If the progname is a name
+ * it is converted to a number via /etc/rpc.
+ * The program version and/or procedure may be provided
+ * as extra qualifiers.
+ */
+static void
+rpc_match_prog(enum direction which, char *progname, int vers, int proc)
+{
+ struct rpcent *rpc;
+ uint_t prog;
+ uint_t m, n;
+
+ if (isdigit(*progname)) {
+ prog = atoi(progname);
+ } else {
+ rpc = (struct rpcent *)getrpcbyname(progname);
+ if (rpc == NULL)
+ pr_err("invalid program name: %s", progname);
+ prog = rpc->r_number;
+ }
+
+ emitop(OP_OFFSET_RPC);
+ emitop(OP_BRFL);
+ n = chain(0);
+
+ compare_value(12, 4, prog);
+ emitop(OP_BRFL);
+ m = chain(0);
+ if (vers >= 0) {
+ compare_value(16, 4, vers);
+ emitop(OP_BRFL);
+ m = chain(m);
+ }
+ if (proc >= 0) {
+ compare_value(20, 4, proc);
+ emitop(OP_BRFL);
+ m = chain(m);
+ }
+
+ switch (which) {
+ case TO:
+ compare_value(4, 4, CALL);
+ emitop(OP_BRFL);
+ m = chain(m);
+ break;
+ case FROM:
+ compare_value(4, 4, REPLY);
+ emitop(OP_BRFL);
+ m = chain(m);
+ break;
+ }
+ resolve_chain(m);
+ resolve_chain(n);
+ emitop(OP_OFFSET_POP);
+}
+
+/*
+ * Generate code to parse a field specification
+ * and load the value of the field from the packet
+ * onto the operand stack.
+ * The field offset may be specified relative to the
+ * beginning of the ether header, IP header, UDP header,
+ * or TCP header. An optional size specification may
+ * be provided following a colon. If no size is given
+ * one byte is assumed e.g.
+ *
+ * ether[0] The first byte of the ether header
+ * ip[2:2] The second 16 bit field of the IP header
+ */
+static void
+load_field()
+{
+ int size = 1;
+ int s;
+
+
+ if (EQ("ether"))
+ emitop(OP_OFFSET_ZERO);
+ else if (EQ("ip") || EQ("ip6") || EQ("pppoed") || EQ("pppoes"))
+ emitop(OP_OFFSET_LINK);
+ else if (EQ("udp") || EQ("tcp") || EQ("icmp") || EQ("ip-in-ip") ||
+ EQ("ah") || EQ("esp"))
+ emitop(OP_OFFSET_IP);
+ else
+ pr_err("invalid field type");
+ next();
+ s = opstack;
+ expression();
+ if (opstack != s + 1)
+ pr_err("invalid field offset");
+ opstack--;
+ if (*token == ':') {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("field size expected");
+ size = tokenval;
+ if (size != 1 && size != 2 && size != 4)
+ pr_err("field size invalid");
+ next();
+ }
+ if (*token != ']')
+ pr_err("right bracket expected");
+
+ load_value(-1, size);
+ emitop(OP_OFFSET_POP);
+}
+
+/*
+ * Check that the operand stack
+ * contains n arguments
+ */
+static void
+checkstack(int numargs)
+{
+ if (opstack != numargs)
+ pr_err("invalid expression at \"%s\".", token);
+}
+
+static void
+primary()
+{
+ int m, m2, s;
+
+ for (;;) {
+ if (tokentype == FIELD) {
+ load_field();
+ opstack++;
+ next();
+ break;
+ }
+
+ if (comparison(token)) {
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("not") || EQ("!")) {
+ next();
+ s = opstack;
+ primary();
+ checkstack(s + 1);
+ emitop(OP_NOT);
+ break;
+ }
+
+ if (EQ("(")) {
+ next();
+ s = opstack;
+ expression();
+ checkstack(s + 1);
+ if (!EQ(")"))
+ pr_err("right paren expected");
+ next();
+ }
+
+ if (EQ("to") || EQ("dst")) {
+ dir = TO;
+ next();
+ continue;
+ }
+
+ if (EQ("from") || EQ("src")) {
+ dir = FROM;
+ next();
+ continue;
+ }
+
+ if (EQ("ether")) {
+ eaddr = 1;
+ next();
+ continue;
+ }
+
+ if (EQ("proto")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("IP proto type expected");
+ emitop(OP_OFFSET_LINK);
+ compare_value(IPV4_TYPE_HEADER_OFFSET, 1, tokenval);
+ emitop(OP_OFFSET_POP);
+ opstack++;
+ next();
+ continue;
+ }
+
+ if (EQ("broadcast")) {
+ /*
+ * Be tricky: FDDI ether dst address begins at
+ * byte one. Since the address is really six
+ * bytes long, this works for FDDI & ethernet.
+ * XXX - Token ring?
+ */
+ emitop(OP_OFFSET_ZERO);
+ if (interface->mac_type == DL_IB)
+ pr_err("filter option unsupported on media");
+ compare_value(1, 4, 0xffffffff);
+ emitop(OP_OFFSET_POP);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("multicast")) {
+ /* XXX Token ring? */
+ emitop(OP_OFFSET_ZERO);
+ if (interface->mac_type == DL_FDDI) {
+ compare_value_mask(1, 1, 0x01, 0x01);
+ } else if (interface->mac_type == DL_IB) {
+ pr_err("filter option unsupported on media");
+ } else {
+ compare_value_mask(0, 1, 0x01, 0x01);
+ }
+ emitop(OP_OFFSET_POP);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("decnet")) {
+ /* XXX Token ring? */
+ if (interface->mac_type == DL_FDDI) {
+ load_value(19, 2); /* ether type */
+ load_const(0x6000);
+ emitop(OP_GE);
+ emitop(OP_BRFL);
+ m = chain(0);
+ load_value(19, 2); /* ether type */
+ load_const(0x6009);
+ emitop(OP_LE);
+ resolve_chain(m);
+ } else {
+ emitop(OP_OFFSET_ETHERTYPE);
+ load_value(0, 2); /* ether type */
+ load_const(0x6000);
+ emitop(OP_GE);
+ emitop(OP_BRFL);
+ m = chain(0);
+ load_value(0, 2); /* ether type */
+ load_const(0x6009);
+ emitop(OP_LE);
+ resolve_chain(m);
+ emitop(OP_OFFSET_POP);
+ }
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("vlan-id")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("vlan id expected");
+ emitop(OP_OFFSET_ZERO);
+ ethertype_match(ETHERTYPE_VLAN);
+ emitop(OP_BRFL);
+ m = chain(0);
+ compare_value_mask(VLAN_ID_OFFSET, 2, tokenval,
+ VLAN_ID_MASK);
+ resolve_chain(m);
+ emitop(OP_OFFSET_POP);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("apple")) {
+ /*
+ * Appletalk also appears in 802.2
+ * packets, so check for the ethertypes
+ * at offset 12 and 20 in the MAC header.
+ */
+ ethertype_match(ETHERTYPE_AT);
+ emitop(OP_BRTR);
+ m = chain(0);
+ ethertype_match(ETHERTYPE_AARP);
+ emitop(OP_BRTR);
+ m = chain(m);
+ compare_value(20, 2, ETHERTYPE_AT); /* 802.2 */
+ emitop(OP_BRTR);
+ m = chain(m);
+ compare_value(20, 2, ETHERTYPE_AARP); /* 802.2 */
+ resolve_chain(m);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("vlan")) {
+ ethertype_match(ETHERTYPE_VLAN);
+ compare_value_mask(VLAN_ID_OFFSET, 2, 0, VLAN_ID_MASK);
+ emitop(OP_NOT);
+ emitop(OP_AND);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("bootp") || EQ("dhcp")) {
+ ethertype_match(interface->network_type_ip);
+ emitop(OP_BRFL);
+ m = chain(0);
+ emitop(OP_OFFSET_LINK);
+ compare_value(9, 1, IPPROTO_UDP);
+ emitop(OP_OFFSET_POP);
+ emitop(OP_BRFL);
+ m = chain(m);
+ emitop(OP_OFFSET_IP);
+ compare_value(0, 4,
+ (IPPORT_BOOTPS << 16) | IPPORT_BOOTPC);
+ emitop(OP_BRTR);
+ m2 = chain(0);
+ compare_value(0, 4,
+ (IPPORT_BOOTPC << 16) | IPPORT_BOOTPS);
+ resolve_chain(m2);
+ emitop(OP_OFFSET_POP);
+ resolve_chain(m);
+ opstack++;
+ dir = ANY;
+ next();
+ break;
+ }
+
+ if (EQ("dhcp6")) {
+ ethertype_match(interface->network_type_ipv6);
+ emitop(OP_BRFL);
+ m = chain(0);
+ emitop(OP_OFFSET_LINK);
+ compare_value(6, 1, IPPROTO_UDP);
+ emitop(OP_OFFSET_POP);
+ emitop(OP_BRFL);
+ m = chain(m);
+ emitop(OP_OFFSET_IP);
+ compare_value(2, 2, IPPORT_DHCPV6S);
+ emitop(OP_BRTR);
+ m2 = chain(0);
+ compare_value(2, 2, IPPORT_DHCPV6C);
+ resolve_chain(m2);
+ emitop(OP_OFFSET_POP);
+ resolve_chain(m);
+ opstack++;
+ dir = ANY;
+ next();
+ break;
+ }
+
+ if (EQ("ethertype")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("ether type expected");
+ ethertype_match(tokenval);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("pppoe")) {
+ ethertype_match(ETHERTYPE_PPPOED);
+ ethertype_match(ETHERTYPE_PPPOES);
+ emitop(OP_OR);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("inet")) {
+ next();
+ if (EQ("host"))
+ next();
+ if (tokentype != ALPHA && tokentype != ADDR_IP)
+ pr_err("host/IPv4 addr expected after inet");
+ ipaddr_match(dir, token, IPV4_ONLY);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("inet6")) {
+ next();
+ if (EQ("host"))
+ next();
+ if (tokentype != ALPHA && tokentype != ADDR_IP6)
+ pr_err("host/IPv6 addr expected after inet6");
+ ipaddr_match(dir, token, IPV6_ONLY);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("length")) {
+ emitop(OP_LOAD_LENGTH);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("less")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("packet length expected");
+ emitop(OP_LOAD_LENGTH);
+ load_const(tokenval);
+ emitop(OP_LT);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("greater")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("packet length expected");
+ emitop(OP_LOAD_LENGTH);
+ load_const(tokenval);
+ emitop(OP_GT);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("nofrag")) {
+ emitop(OP_OFFSET_LINK);
+ compare_value_mask(6, 2, 0, 0x1fff);
+ emitop(OP_OFFSET_POP);
+ emitop(OP_BRFL);
+ m = chain(0);
+ ethertype_match(interface->network_type_ip);
+ resolve_chain(m);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("net") || EQ("dstnet") || EQ("srcnet")) {
+ if (EQ("dstnet"))
+ dir = TO;
+ else if (EQ("srcnet"))
+ dir = FROM;
+ next();
+ netaddr_match(dir, token);
+ dir = ANY;
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("port") || EQ("srcport") || EQ("dstport")) {
+ if (EQ("dstport"))
+ dir = TO;
+ else if (EQ("srcport"))
+ dir = FROM;
+ next();
+ port_match(dir, token);
+ dir = ANY;
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("rpc")) {
+ uint_t vers, proc;
+ char savetoken[32];
+
+ vers = proc = -1;
+ next();
+ (void) strlcpy(savetoken, token, sizeof (savetoken));
+ next();
+ if (*token == ',') {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("version number expected");
+ vers = tokenval;
+ next();
+ }
+ if (*token == ',') {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("proc number expected");
+ proc = tokenval;
+ next();
+ }
+ rpc_match_prog(dir, savetoken, vers, proc);
+ dir = ANY;
+ opstack++;
+ break;
+ }
+
+ if (EQ("slp")) {
+ /* filter out TCP handshakes */
+ emitop(OP_OFFSET_LINK);
+ compare_value(9, 1, IPPROTO_TCP);
+ emitop(OP_LOAD_CONST);
+ emitval(52);
+ emitop(OP_LOAD_CONST);
+ emitval(2);
+ emitop(OP_LOAD_SHORT);
+ emitop(OP_GE);
+ emitop(OP_AND); /* proto == TCP && len < 52 */
+ emitop(OP_NOT);
+ emitop(OP_BRFL); /* pkt too short to be a SLP call */
+ m = chain(0);
+
+ emitop(OP_OFFSET_POP);
+ emitop(OP_OFFSET_SLP);
+ resolve_chain(m);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("ldap")) {
+ dir = ANY;
+ port_match(dir, "ldap");
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("and") || EQ("or")) {
+ break;
+ }
+
+ if (EQ("zone")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("zoneid expected");
+ zone_match(dir, BE_32((uint32_t)(tokenval)));
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("gateway")) {
+ next();
+ if (eaddr || tokentype != ALPHA)
+ pr_err("hostname required: %s", token);
+ etheraddr_match(dir, token);
+ dir = ANY;
+ emitop(OP_BRFL);
+ m = chain(0);
+ ipaddr_match(dir, token, IPV4_AND_IPV6);
+ emitop(OP_NOT);
+ resolve_chain(m);
+ opstack++;
+ next();
+ }
+
+ if (EQ("host") || EQ("between") ||
+ tokentype == ALPHA || /* assume its a hostname */
+ tokentype == ADDR_IP ||
+ tokentype == ADDR_IP6 ||
+ tokentype == ADDR_AT ||
+ tokentype == ADDR_ETHER) {
+ if (EQ("host") || EQ("between"))
+ next();
+ if (eaddr || tokentype == ADDR_ETHER) {
+ etheraddr_match(dir, token);
+ } else if (tokentype == ALPHA) {
+ ipaddr_match(dir, token, IPV4_AND_IPV6);
+ } else if (tokentype == ADDR_AT) {
+ ataddr_match(dir, token);
+ } else if (tokentype == ADDR_IP) {
+ ipaddr_match(dir, token, IPV4_ONLY);
+ } else {
+ ipaddr_match(dir, token, IPV6_ONLY);
+ }
+ dir = ANY;
+ eaddr = 0;
+ opstack++;
+ next();
+ break;
+ }
+
+ if (tokentype == NUMBER) {
+ load_const(tokenval);
+ opstack++;
+ next();
+ break;
+ }
+
+ break; /* unknown token */
+ }
+}
+
+struct optable {
+ char *op_tok;
+ enum optype op_type;
+};
+
+static struct optable
+mulops[] = {
+ "*", OP_MUL,
+ "/", OP_DIV,
+ "%", OP_REM,
+ "&", OP_AND,
+ "", OP_STOP,
+};
+
+static struct optable
+addops[] = {
+ "+", OP_ADD,
+ "-", OP_SUB,
+ "|", OP_OR,
+ "^", OP_XOR,
+ "", OP_STOP,
+};
+
+static struct optable
+compareops[] = {
+ "==", OP_EQ,
+ "=", OP_EQ,
+ "!=", OP_NE,
+ ">", OP_GT,
+ ">=", OP_GE,
+ "<", OP_LT,
+ "<=", OP_LE,
+ "", OP_STOP,
+};
+
+/*
+ * Using the table, find the operator
+ * that corresponds to the token.
+ * Return 0 if not found.
+ */
+static int
+find_op(char *tok, struct optable *table)
+{
+ struct optable *op;
+
+ for (op = table; *op->op_tok; op++) {
+ if (strcmp(tok, op->op_tok) == 0)
+ return (op->op_type);
+ }
+
+ return (0);
+}
+
+static void
+expr_mul()
+{
+ int op;
+ int s = opstack;
+
+ primary();
+ while (op = find_op(token, mulops)) {
+ next();
+ primary();
+ checkstack(s + 2);
+ emitop(op);
+ opstack--;
+ }
+}
+
+static void
+expr_add()
+{
+ int op, s = opstack;
+
+ expr_mul();
+ while (op = find_op(token, addops)) {
+ next();
+ expr_mul();
+ checkstack(s + 2);
+ emitop(op);
+ opstack--;
+ }
+}
+
+static void
+expr_compare()
+{
+ int op, s = opstack;
+
+ expr_add();
+ while (op = find_op(token, compareops)) {
+ next();
+ expr_add();
+ checkstack(s + 2);
+ emitop(op);
+ opstack--;
+ }
+}
+
+/*
+ * Alternation ("and") is difficult because
+ * an implied "and" is acknowledge between
+ * two adjacent primaries. Just keep calling
+ * the lower-level expression routine until
+ * no value is added to the opstack.
+ */
+static void
+alternation()
+{
+ int m = 0;
+ int s = opstack;
+
+ expr_compare();
+ checkstack(s + 1);
+ for (;;) {
+ if (EQ("and"))
+ next();
+ emitop(OP_BRFL);
+ m = chain(m);
+ expr_compare();
+ if (opstack != s + 2)
+ break;
+ opstack--;
+ }
+ unemit(2);
+ resolve_chain(m);
+}
+
+static void
+expression()
+{
+ int m = 0;
+ int s = opstack;
+
+ alternation();
+ while (EQ("or") || EQ(",")) {
+ emitop(OP_BRTR);
+ m = chain(m);
+ next();
+ alternation();
+ checkstack(s + 2);
+ opstack--;
+ }
+ resolve_chain(m);
+}
+
+/*
+ * Take n args from the argv list
+ * and concatenate them into a single string.
+ */
+char *
+concat_args(char **argv, int argc)
+{
+ int i, len;
+ char *str, *p;
+
+ /* First add the lengths of all the strings */
+ len = 0;
+ for (i = 0; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+
+ /* allocate the big string */
+ str = (char *)malloc(len);
+ if (str == NULL)
+ pr_err("no mem");
+
+ p = str;
+
+ /*
+ * Concat the strings into the big
+ * string using a space as separator
+ */
+ for (i = 0; i < argc; i++) {
+ strcpy(p, argv[i]);
+ p += strlen(p);
+ *p++ = ' ';
+ }
+ *--p = '\0';
+
+ return (str);
+}
+
+/*
+ * Take the expression in the string "expr"
+ * and compile it into the code array.
+ * Print the generated code if the print
+ * arg is set.
+ */
+void
+compile(char *expr, int print)
+{
+ expr = strdup(expr);
+ if (expr == NULL)
+ pr_err("no mem");
+ curr_op = oplist;
+ tkp = expr;
+ dir = ANY;
+
+ next();
+ if (tokentype != EOL)
+ expression();
+ emitop(OP_STOP);
+ if (tokentype != EOL)
+ pr_err("invalid expression");
+ optimize(oplist);
+ if (print)
+ codeprint();
+}
+
+/*
+ * Lookup hostname in the arp cache.
+ */
+boolean_t
+arp_for_ether(char *hostname, struct ether_addr *ep)
+{
+ struct arpreq ar;
+ struct hostent *hp;
+ struct sockaddr_in *sin;
+ int error_num;
+ int s;
+
+ memset(&ar, 0, sizeof (ar));
+ sin = (struct sockaddr_in *)&ar.arp_pa;
+ sin->sin_family = AF_INET;
+ hp = getipnodebyname(hostname, AF_INET, 0, &error_num);
+ if (hp == NULL) {
+ return (B_FALSE);
+ }
+ memcpy(&sin->sin_addr, hp->h_addr, sizeof (sin->sin_addr));
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ return (B_FALSE);
+ }
+ if (ioctl(s, SIOCGARP, &ar) < 0) {
+ close(s);
+ return (B_FALSE);
+ }
+ close(s);
+ memcpy(ep->ether_addr_octet, ar.arp_ha.sa_data, sizeof (*ep));
+ return (B_TRUE);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_http.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_http.c
new file mode 100644
index 0000000..b6e6b9d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_http.c
@@ -0,0 +1,141 @@
+/*
+ * 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 (c) 1996-1998,2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Protocol interpreter for the Hypertext Transfer Protocol (HTTP)
+ *
+ * Relevant standards:
+ * Berners-Lee, T., et al: Hypertext Transfer Protocol -- HTTP/1.0.
+ * RFC 1945, May 1996
+ * Fielding, R., et al: Hypertext Transfer Protocol -- HTTP/1.1.
+ * RFC 2068, June 1999
+ */
+
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "snoop.h"
+
+#define CR 13 /* "carriage return" character */
+#define LF 10 /* "line feed" character */
+
+/*
+ * Summary lines: packet contents starting with less than MINCHARS
+ * printable characters will not be printed. MAXCHARS is the maximum
+ * number of characters printed.
+ * Detail lines: NLINES is the maximum number of content lines to print
+ */
+#define MINCHARS 10
+#define MAXCHARS 80
+#define NLINES 5
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+static int printable(const char *line, const char *endp);
+
+int
+interpret_http(int flags, char *line, int fraglen)
+{
+ char *p, *q, *endp;
+ int c;
+ int lineno;
+
+ endp = line + fraglen;
+
+ if (flags & F_SUM) {
+ c = printable(line, endp - 1);
+ if (c < MINCHARS) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "HTTP (body)");
+ } else {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "HTTP %.*s", MIN(c, MAXCHARS), line);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("HTTP: ", "HyperText Transfer Protocol", fraglen);
+ show_space();
+
+ lineno = 0;
+ for (p = line; p < endp && lineno < NLINES; p = q + 1) {
+ c = printable(p, endp - 1);
+
+ /* stop if no printables, except if at line end */
+ if (c == 0 && *p != CR && *p != LF)
+ break;
+
+ /*
+ * A line may be terminated either by an CR LF sequence
+ * (DOS, Mac), or by LF alone
+ */
+
+ q = memchr(p, CR, (endp - p));
+ if (q != NULL) {
+ if (q < endp - 1 && q[1] == LF)
+ ++q; /* ignore subsequent LF character */
+ } else {
+ q = memchr(p, LF, (endp - p));
+ /* no CR/LF: use end of buffer */
+ if (q == NULL)
+ q = endp - 1;
+ }
+
+ /* truncate lines containing non-printable characters */
+ (void) snprintf(get_line(0, c), get_line_remain(),
+ "%.*s", c, p);
+ ++lineno;
+ }
+
+ if (p < endp) /* there was more data to be printed */
+ (void) snprintf(get_line(0, 5), get_line_remain(),
+ "[...]");
+
+ show_space();
+ }
+
+ return (fraglen);
+}
+
+/*
+ * Return the length of the initial string starting with "startp" and
+ * ending with "endp" (inclusively) consisting only of printable
+ * characters.
+ */
+
+static int
+printable(const char *startp, const char *endp)
+{
+ const char *p = startp;
+
+ while (p <= endp && (isprint(*p) || *p == '\t'))
+ p++;
+
+ return (p - startp);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_icmp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_icmp.c
new file mode 100644
index 0000000..4f6fa8a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_icmp.c
@@ -0,0 +1,1003 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <sys/stropts.h>
+#include <sys/sysmacros.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip6.h>
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include "snoop.h"
+#include "snoop_mip.h"
+
+static void interpret_options(char *, int);
+static void interpret_mldv2qry(icmp6_t *, int);
+static void interpret_mldv2rpt(icmp6_t *, int);
+
+
+/* Mobile-IP routines from snoop_mip.c */
+extern void interpret_icmp_mip_ext(uchar_t *, int);
+extern const char *get_mip_adv_desc(uint8_t);
+
+/* Router advertisement message structure. */
+struct icmp_ra_addr {
+ uint32_t addr;
+ uint32_t preference;
+};
+
+/*ARGSUSED*/
+void
+interpret_icmp(int flags, struct icmp *icmp, int iplen, int ilen)
+{
+ char *pt, *pc, *px;
+ char *line;
+ char buff[67627]; /* Router adv. can have 256 routers .... */
+ /* Each router has a name 256 char long .. */
+ char extbuff[MAXHOSTNAMELEN + 1];
+ struct udphdr *orig_uhdr;
+ int num_rtr_addrs = 0;
+ extern char *prot_nest_prefix;
+
+ if (ilen < ICMP_MINLEN)
+ return; /* incomplete header */
+
+ pt = "Unknown";
+ pc = "";
+ px = "";
+
+ switch (icmp->icmp_type) {
+ case ICMP_ECHOREPLY:
+ pt = "Echo reply";
+ (void) sprintf(buff, "ID: %d Sequence number: %d",
+ ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
+ pc = buff;
+ break;
+ case ICMP_UNREACH:
+ pt = "Destination unreachable";
+ switch (icmp->icmp_code) {
+ case ICMP_UNREACH_NET:
+ if (ilen >= ICMP_ADVLENMIN) {
+ (void) sprintf(buff, "Net %s unreachable",
+ addrtoname(AF_INET,
+ &icmp->icmp_ip.ip_dst));
+ pc = buff;
+ } else {
+ pc = "Bad net";
+ }
+ break;
+ case ICMP_UNREACH_HOST:
+ if (ilen >= ICMP_ADVLENMIN) {
+ (void) sprintf(buff, "Host %s unreachable",
+ addrtoname(AF_INET,
+ &icmp->icmp_ip.ip_dst));
+ pc = buff;
+ } else {
+ pc = "Bad host";
+ }
+ break;
+ case ICMP_UNREACH_PROTOCOL:
+ if (ilen >= ICMP_ADVLENMIN) {
+ (void) sprintf(buff, "Bad protocol %d",
+ icmp->icmp_ip.ip_p);
+ pc = buff;
+ } else {
+ pc = "Bad protocol";
+ }
+ break;
+ case ICMP_UNREACH_PORT:
+ if (ilen >= ICMP_ADVLENMIN) {
+ orig_uhdr = (struct udphdr *)((uchar_t *)icmp +
+ ICMP_MINLEN + icmp->icmp_ip.ip_hl * 4);
+ switch (icmp->icmp_ip.ip_p) {
+ case IPPROTO_TCP:
+ (void) sprintf(buff, "TCP port %d"
+ " unreachable",
+ ntohs(orig_uhdr->uh_dport));
+ pc = buff;
+ break;
+ case IPPROTO_UDP:
+ (void) sprintf(buff, "UDP port %d"
+ " unreachable",
+ ntohs(orig_uhdr->uh_dport));
+ pc = buff;
+ break;
+ default:
+ pc = "Port unreachable";
+ break;
+ }
+ } else {
+ pc = "Bad port";
+ }
+ break;
+ case ICMP_UNREACH_NEEDFRAG:
+ if (ntohs(icmp->icmp_nextmtu) != 0) {
+ (void) sprintf(buff, "Needed to fragment:"
+ " next hop MTU = %d",
+ ntohs(icmp->icmp_nextmtu));
+ pc = buff;
+ } else {
+ pc = "Needed to fragment";
+ }
+ break;
+ case ICMP_UNREACH_SRCFAIL:
+ pc = "Source route failed";
+ break;
+ case ICMP_UNREACH_NET_UNKNOWN:
+ pc = "Unknown network";
+ break;
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ pc = "Unknown host";
+ break;
+ case ICMP_UNREACH_ISOLATED:
+ pc = "Source host isolated";
+ break;
+ case ICMP_UNREACH_NET_PROHIB:
+ pc = "Net administratively prohibited";
+ break;
+ case ICMP_UNREACH_HOST_PROHIB:
+ pc = "Host administratively prohibited";
+ break;
+ case ICMP_UNREACH_TOSNET:
+ pc = "Net unreachable for this TOS";
+ break;
+ case ICMP_UNREACH_TOSHOST:
+ pc = "Host unreachable for this TOS";
+ break;
+ case ICMP_UNREACH_FILTER_PROHIB:
+ pc = "Communication administratively prohibited";
+ break;
+ case ICMP_UNREACH_HOST_PRECEDENCE:
+ pc = "Host precedence violation";
+ break;
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ pc = "Precedence cutoff in effect";
+ break;
+ default:
+ break;
+ }
+ break;
+ case ICMP_SOURCEQUENCH:
+ pt = "Packet lost, slow down";
+ break;
+ case ICMP_REDIRECT:
+ pt = "Redirect";
+ switch (icmp->icmp_code) {
+ case ICMP_REDIRECT_NET:
+ pc = "for network";
+ break;
+ case ICMP_REDIRECT_HOST:
+ pc = "for host";
+ break;
+ case ICMP_REDIRECT_TOSNET:
+ pc = "for tos and net";
+ break;
+ case ICMP_REDIRECT_TOSHOST:
+ pc = "for tos and host";
+ break;
+ default:
+ break;
+ }
+ (void) sprintf(buff, "%s %s to %s",
+ pc, addrtoname(AF_INET, &icmp->icmp_ip.ip_dst),
+ addrtoname(AF_INET, &icmp->icmp_gwaddr));
+ pc = buff;
+ break;
+ case ICMP_ECHO:
+ pt = "Echo request";
+ (void) sprintf(buff, "ID: %d Sequence number: %d",
+ ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
+ pc = buff;
+ break;
+ case ICMP_ROUTERADVERT:
+
+#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs
+#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa
+#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime
+
+ pt = "Router advertisement";
+ (void) sprintf(buff, "Lifetime %ds [%d]:",
+ ntohs(icmp->icmp_lifetime), icmp->icmp_num_addrs);
+ if (icmp->icmp_wpa == 2) {
+ struct icmp_ra_addr *ra;
+ char ra_buf[MAXHOSTNAMELEN + 32];
+ char ra_ext_buf[50];
+ struct in_addr sin;
+ int icmp_ra_len;
+ int i;
+
+ /* Cannot trust anything from the network... */
+ num_rtr_addrs = MIN((ilen - ICMP_MINLEN) / 8,
+ icmp->icmp_num_addrs);
+
+ ra = (struct icmp_ra_addr *)icmp->icmp_data;
+ for (i = 0; i < num_rtr_addrs; i++) {
+ sin.s_addr = ra->addr;
+ (void) snprintf(ra_buf, sizeof (ra_buf),
+ " {%s %u}",
+ addrtoname(AF_INET, &sin),
+ ntohl(ra->preference));
+ if (strlcat(buff, ra_buf, sizeof (buff)) >=
+ sizeof (buff)) {
+ buff[sizeof (buff) -
+ strlen("<Too Long>)")] = '\0';
+ (void) strlcat(buff, "<Too Long>",
+ sizeof (buff));
+ break;
+ }
+ ra++;
+ }
+
+ icmp_ra_len = ICMP_MINLEN + num_rtr_addrs *
+ sizeof (struct icmp_ra_addr);
+ if (ilen > icmp_ra_len) {
+ int curr_len = ilen - icmp_ra_len;
+ int ocurr_len;
+ exthdr_t *exthdr = (exthdr_t *)ra;
+
+ extbuff[0] = '\0';
+
+ while (curr_len > 0) {
+ /* Append Mobile-IP description */
+ (void) snprintf(ra_ext_buf,
+ sizeof (ra_ext_buf), ", %s",
+ get_mip_adv_desc(exthdr->type));
+ (void) strlcat(extbuff, ra_ext_buf,
+ sizeof (extbuff));
+
+ /* Special case for padding */
+ if (exthdr->type ==
+ ICMP_ADV_MSG_PADDING_EXT) {
+
+ curr_len--;
+ exthdr = (exthdr_t *)
+ ((char *)exthdr + 1);
+ continue;
+ }
+
+ /* else normal extension */
+ ocurr_len = curr_len;
+ curr_len -= sizeof (*exthdr) +
+ exthdr->length;
+ /* detect bad length */
+ if (ocurr_len < curr_len)
+ break;
+ exthdr = (exthdr_t *)
+ ((char *)exthdr +
+ sizeof (*exthdr) +
+ exthdr->length);
+ }
+ px = extbuff;
+ }
+ pc = buff;
+ }
+ break;
+ case ICMP_ROUTERSOLICIT:
+ pt = "Router solicitation";
+ break;
+ case ICMP_TIMXCEED:
+ pt = "Time exceeded";
+ switch (icmp->icmp_code) {
+ case ICMP_TIMXCEED_INTRANS:
+ pc = "in transit";
+ break;
+ case ICMP_TIMXCEED_REASS:
+ pc = "in reassembly";
+ break;
+ default:
+ break;
+ }
+ break;
+ case ICMP_PARAMPROB:
+ pt = "IP parameter problem";
+ switch (icmp->icmp_code) {
+ case ICMP_PARAMPROB_OPTABSENT:
+ pc = "Required option missing";
+ break;
+ case ICMP_PARAMPROB_BADLENGTH:
+ pc = "Bad length";
+ break;
+ case 0: /* Should this be the default? */
+ (void) sprintf(buff, "Problem at octet %d\n",
+ icmp->icmp_pptr);
+ pc = buff;
+ default:
+ break;
+ }
+ break;
+ case ICMP_TSTAMP:
+ pt = "Timestamp request";
+ break;
+ case ICMP_TSTAMPREPLY:
+ pt = "Timestamp reply";
+ break;
+ case ICMP_IREQ:
+ pt = "Information request";
+ break;
+ case ICMP_IREQREPLY:
+ pt = "Information reply";
+ break;
+ case ICMP_MASKREQ:
+ pt = "Address mask request";
+ break;
+ case ICMP_MASKREPLY:
+ pt = "Address mask reply";
+ (void) sprintf(buff, "Mask = 0x%x", ntohl(icmp->icmp_mask));
+ pc = buff;
+ break;
+ default:
+ break;
+ }
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+ if (*pc) {
+ if (*px) {
+ (void) sprintf(line, "ICMP %s (%s)%s",
+ pt, pc, px);
+ } else {
+ (void) sprintf(line, "ICMP %s (%s)", pt, pc);
+ }
+ } else {
+ (void) sprintf(line, "ICMP %s", pt);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ICMP: ", "ICMP Header", ilen);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Type = %d (%s)",
+ icmp->icmp_type, pt);
+ if (*pc) {
+ (void) sprintf(get_line(0, 0), "Code = %d (%s)",
+ icmp->icmp_code, pc);
+ } else {
+ (void) sprintf(get_line(0, 0), "Code = %d",
+ icmp->icmp_code);
+ }
+ (void) sprintf(get_line(0, 0), "Checksum = %x",
+ ntohs(icmp->icmp_cksum));
+
+ if (icmp->icmp_type == ICMP_UNREACH ||
+ icmp->icmp_type == ICMP_REDIRECT) {
+ if (ilen > 28) {
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ "[ subject header follows ]");
+ show_space();
+ prot_nest_prefix = "ICMP:";
+ (void) interpret_ip(flags,
+ (struct ip *)icmp->icmp_data, 28);
+ prot_nest_prefix = "";
+ }
+ } else if (icmp->icmp_type == ICMP_PARAMPROB) {
+ if (ilen > 28) {
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ "[ subject header follows ]");
+ show_space();
+ prot_nest_prefix = "ICMP:";
+ (void) interpret_ip(flags,
+ (struct ip *)icmp->icmp_data, 28);
+ prot_nest_prefix = "";
+ }
+ } else if (icmp->icmp_type == ICMP_ROUTERADVERT) {
+ if (icmp->icmp_wpa == 2) {
+ int icmp_ra_len;
+
+ show_space();
+ icmp_ra_len = ICMP_MINLEN +
+ num_rtr_addrs *
+ sizeof (struct icmp_ra_addr);
+ prot_nest_prefix = "";
+ if (ilen > icmp_ra_len) {
+ interpret_icmp_mip_ext(
+ (uchar_t *)icmp + icmp_ra_len,
+ ilen - icmp_ra_len);
+ }
+ }
+ }
+ show_space();
+ }
+}
+
+/*ARGSUSED*/
+void
+interpret_icmpv6(flags, icmp6, iplen, ilen)
+ int flags;
+ icmp6_t *icmp6;
+ int iplen, ilen;
+{
+ char *pt, *pc;
+ char *line;
+ extern char *prot_nest_prefix;
+ char addrstr[INET6_ADDRSTRLEN];
+ char buff[2048];
+
+ if (ilen < ICMP6_MINLEN)
+ return; /* incomplete header */
+
+ pt = "Unknown";
+ pc = "";
+
+ switch (icmp6->icmp6_type) {
+ case ICMP6_DST_UNREACH:
+ pt = "Destination unreachable";
+ switch (icmp6->icmp6_code) {
+ case ICMP6_DST_UNREACH_NOROUTE:
+ pc = "No route to destination";
+ break;
+ case ICMP6_DST_UNREACH_ADMIN:
+ pc = "Communication administratively prohibited";
+ break;
+ case ICMP6_DST_UNREACH_ADDR:
+ pc = "Address unreachable";
+ break;
+ case ICMP6_DST_UNREACH_NOPORT:
+ if (ilen >= ICMP6_MINLEN + IPV6_HDR_LEN +
+ sizeof (struct udphdr)) {
+
+ ip6_t *orig_ip6hdr = (ip6_t *)&icmp6[1];
+
+ switch (orig_ip6hdr->ip6_nxt) {
+ case IPPROTO_TCP: {
+ struct tcphdr *orig_thdr =
+ (struct tcphdr *)&orig_ip6hdr[1];
+
+ (void) sprintf(buff, "TCP port %hu"
+ " unreachable",
+ ntohs(orig_thdr->th_dport));
+ pc = buff;
+ break;
+ }
+ case IPPROTO_UDP: {
+ struct udphdr *orig_uhdr =
+ (struct udphdr *)&orig_ip6hdr[1];
+
+ (void) sprintf(buff, "UDP port %hu"
+ " unreachable",
+ ntohs(orig_uhdr->uh_dport));
+ pc = buff;
+ break;
+ }
+ default:
+ pc = "Port unreachable";
+ break;
+ }
+ } else {
+ pc = "Bad port";
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case ICMP6_PACKET_TOO_BIG:
+ pt = "Packet too big";
+ break;
+ case ND_REDIRECT:
+ pt = "Redirect";
+ break;
+ case ICMP6_TIME_EXCEEDED:
+ pt = "Time exceeded";
+ switch (icmp6->icmp6_code) {
+ case ICMP6_TIME_EXCEED_TRANSIT:
+ pc = "Hop limit exceeded in transit";
+ break;
+ case ICMP6_TIME_EXCEED_REASSEMBLY:
+ pc = "Fragment reassembly time exceeded";
+ break;
+ default:
+ break;
+ }
+ break;
+ case ICMP6_PARAM_PROB:
+ pt = "Parameter problem";
+ switch (icmp6->icmp6_code) {
+ case ICMP6_PARAMPROB_HEADER:
+ pc = "Erroneous header field";
+ break;
+ case ICMP6_PARAMPROB_NEXTHEADER:
+ pc = "Unrecognized next header type";
+ break;
+ case ICMP6_PARAMPROB_OPTION:
+ pc = "Unrecognized IPv6 option";
+ break;
+ }
+ break;
+ case ICMP6_ECHO_REQUEST:
+ pt = "Echo request";
+ (void) sprintf(buff, "ID: %d Sequence number: %d",
+ ntohs(icmp6->icmp6_id), ntohs(icmp6->icmp6_seq));
+ pc = buff;
+ break;
+ case ICMP6_ECHO_REPLY:
+ pt = "Echo reply";
+ (void) sprintf(buff, "ID: %d Sequence number: %d",
+ ntohs(icmp6->icmp6_id), ntohs(icmp6->icmp6_seq));
+ pc = buff;
+ break;
+ case MLD_LISTENER_QUERY:
+ if (ilen == MLD_MINLEN)
+ pt = "Group membership query - MLDv1";
+ else if (ilen >= MLD_V2_QUERY_MINLEN)
+ pt = "Group membership query - MLDv2";
+ else
+ pt = "Unknown membership query";
+ break;
+ case MLD_LISTENER_REPORT:
+ pt = "Group membership report - MLDv1";
+ break;
+ case MLD_LISTENER_REDUCTION:
+ pt = "Group membership termination - MLDv1";
+ break;
+ case MLD_V2_LISTENER_REPORT:
+ pt = "Group membership report - MLDv2";
+ break;
+ case ND_ROUTER_SOLICIT:
+ pt = "Router solicitation";
+ break;
+ case ND_ROUTER_ADVERT:
+ pt = "Router advertisement";
+ break;
+ case ND_NEIGHBOR_SOLICIT:
+ pt = "Neighbor solicitation";
+ break;
+ case ND_NEIGHBOR_ADVERT:
+ pt = "Neighbor advertisement";
+ break;
+ default:
+ break;
+ }
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+ if (*pc)
+ (void) sprintf(line, "ICMPv6 %s (%s)", pt, pc);
+ else
+ (void) sprintf(line, "ICMPv6 %s", pt);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ICMPv6: ", "ICMPv6 Header", ilen);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Type = %d (%s)",
+ icmp6->icmp6_type, pt);
+ if (*pc)
+ (void) sprintf(get_line(0, 0), "Code = %d (%s)",
+ icmp6->icmp6_code, pc);
+ else
+ (void) sprintf(get_line(0, 0), "Code = %d",
+ icmp6->icmp6_code);
+ (void) sprintf(get_line(0, 0), "Checksum = %x",
+ ntohs(icmp6->icmp6_cksum));
+
+ switch (icmp6->icmp6_type) {
+ case ICMP6_DST_UNREACH:
+ if (ilen > ICMP6_MINLEN + IPV6_HDR_LEN) {
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ "[ subject header follows ]");
+ show_space();
+ prot_nest_prefix = "ICMPv6:";
+ (void) interpret_ipv6(flags, (ip6_t *)&icmp6[1],
+ ICMP6_MINLEN + IPV6_HDR_LEN);
+ prot_nest_prefix = "";
+ }
+ break;
+ case ICMP6_PACKET_TOO_BIG:
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ " Packet too big MTU = %d",
+ ntohl(icmp6->icmp6_mtu));
+ show_space();
+ break;
+ case ND_REDIRECT: {
+ nd_redirect_t *rd = (nd_redirect_t *)icmp6;
+
+ (void) sprintf(get_line(0, 0), "Target address= %s",
+ inet_ntop(AF_INET6, (char *)&rd->nd_rd_target,
+ addrstr, INET6_ADDRSTRLEN));
+
+ (void) sprintf(get_line(0, 0),
+ "Destination address= %s",
+ inet_ntop(AF_INET6, (char *)&rd->nd_rd_dst,
+ addrstr, INET6_ADDRSTRLEN));
+ show_space();
+ interpret_options((char *)icmp6 + sizeof (*rd),
+ ilen - sizeof (*rd));
+ break;
+ }
+ case ND_NEIGHBOR_SOLICIT: {
+ struct nd_neighbor_solicit *ns;
+ if (ilen < sizeof (*ns))
+ break;
+ ns = (struct nd_neighbor_solicit *)icmp6;
+ (void) sprintf(get_line(0, 0), "Target node = %s, %s",
+ inet_ntop(AF_INET6, (char *)&ns->nd_ns_target,
+ addrstr, INET6_ADDRSTRLEN),
+ addrtoname(AF_INET6, &ns->nd_ns_target));
+ show_space();
+ interpret_options((char *)icmp6 + sizeof (*ns),
+ ilen - sizeof (*ns));
+ break;
+ }
+
+ case ND_NEIGHBOR_ADVERT: {
+ struct nd_neighbor_advert *na;
+
+ if (ilen < sizeof (*na))
+ break;
+ na = (struct nd_neighbor_advert *)icmp6;
+ (void) sprintf(get_line(0, 0), "Target node = %s, %s",
+ inet_ntop(AF_INET6, (char *)&na->nd_na_target,
+ addrstr, INET6_ADDRSTRLEN),
+ addrtoname(AF_INET6, &na->nd_na_target));
+ (void) sprintf(get_line(0, 0),
+ "Router flag: %s, Solicited flag: %s, "
+ "Override flag: %s",
+ na->nd_na_flags_reserved & ND_NA_FLAG_ROUTER ?
+ "SET" : "NOT SET",
+ na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED ?
+ "SET" : "NOT SET",
+ na->nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE ?
+ "SET" : "NOT SET");
+
+ show_space();
+ interpret_options((char *)icmp6 + sizeof (*na),
+ ilen - sizeof (*na));
+ }
+ break;
+
+ case ND_ROUTER_SOLICIT: {
+ if (ilen < sizeof (struct nd_router_solicit))
+ break;
+ interpret_options(
+ (char *)icmp6 + sizeof (struct nd_router_solicit),
+ ilen - sizeof (struct nd_router_solicit));
+ break;
+ }
+
+ case ND_ROUTER_ADVERT: {
+ struct nd_router_advert *ra;
+
+ if (ilen < sizeof (*ra))
+ break;
+ ra = (struct nd_router_advert *)icmp6;
+ (void) sprintf(get_line(0, 0),
+ "Max hops= %d, Router lifetime= %d",
+ ra->nd_ra_curhoplimit,
+ ntohs(ra->nd_ra_router_lifetime));
+
+ (void) sprintf(get_line(0, 0),
+ "Managed addr conf flag: %s, Other conf flag: %s",
+ ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED ?
+ "SET" : "NOT SET",
+ ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER ?
+ "SET" : "NOT SET");
+
+ (void) sprintf(get_line(0, 0),
+ "Reachable time: %u, Reachable retrans time %u",
+ ntohl(ra->nd_ra_reachable),
+ ntohl(ra->nd_ra_retransmit));
+ show_space();
+
+ interpret_options((char *)icmp6 + sizeof (*ra),
+ ilen - sizeof (*ra));
+ break;
+ }
+ case ICMP6_PARAM_PROB:
+ if (ilen < sizeof (*icmp6))
+ break;
+ (void) sprintf(get_line(0, 0), "Ptr = %u",
+ ntohl(icmp6->icmp6_pptr));
+ show_space();
+ break;
+
+ case MLD_LISTENER_QUERY: {
+ struct mld_hdr *mldg = (struct mld_hdr *)icmp6;
+
+ if (ilen < MLD_MINLEN)
+ break;
+
+ if (ilen >= MLD_V2_QUERY_MINLEN) {
+ interpret_mldv2qry(icmp6, ilen);
+ } else {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Multicast address= %s",
+ inet_ntop(AF_INET6, mldg->mld_addr.s6_addr,
+ addrstr, INET6_ADDRSTRLEN));
+ }
+ show_space();
+ break;
+ }
+
+ case MLD_LISTENER_REPORT:
+ case MLD_LISTENER_REDUCTION: {
+ struct mld_hdr *mldg;
+
+ if (ilen < sizeof (*mldg))
+ break;
+ mldg = (struct mld_hdr *)icmp6;
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Multicast address= %s", inet_ntop(AF_INET6,
+ mldg->mld_addr.s6_addr, addrstr, INET6_ADDRSTRLEN));
+ show_space();
+ break;
+ }
+
+ case MLD_V2_LISTENER_REPORT: {
+ interpret_mldv2rpt(icmp6, ilen);
+ show_space();
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+}
+
+static void
+interpret_options(optc, ilen)
+ char *optc;
+ int ilen;
+{
+#define PREFIX_OPTION_LENGTH 4
+#define MTU_OPTION_LENGTH 1
+
+#define PREFIX_INFINITY 0xffffffffUL
+
+ struct nd_opt_hdr *opt;
+
+ for (; ilen >= sizeof (*opt); ) {
+ opt = (struct nd_opt_hdr *)optc;
+ if (opt->nd_opt_len == 0)
+ return;
+ switch (opt->nd_opt_type) {
+ case ND_OPT_SOURCE_LINKADDR:
+ case ND_OPT_TARGET_LINKADDR:
+ {
+ struct nd_opt_lla *lopt;
+ char *buf, chbuf[128];
+ uint_t addr_len;
+ int i;
+
+ if (ilen < (int)opt->nd_opt_len * 8)
+ break;
+
+ buf = chbuf;
+
+ lopt = (struct nd_opt_lla *)opt;
+ if (lopt->nd_opt_lla_type == ND_OPT_SOURCE_LINKADDR) {
+ (void) sprintf(get_line(0, 0),
+ "+++ ICMPv6 Source LL Addr option +++");
+ } else {
+ (void) sprintf(get_line(0, 0),
+ "+++ ICMPv6 Target LL Addr option +++");
+ }
+
+ /*
+ * The option length is in 8 octet units, and
+ * includes the first two bytes (the type and
+ * lenght fields) of the option.
+ */
+ addr_len = lopt->nd_opt_lla_len * 8 - 2;
+ for (i = 0; i < addr_len; i++) {
+ snprintf(buf, sizeof (chbuf) - (buf - chbuf),
+ "%x:", lopt->nd_opt_lla_hdw_addr[i]);
+ buf += strlen(buf);
+ if (buf >= &chbuf[sizeof (chbuf)]) {
+ buf = NULL;
+ chbuf[sizeof (chbuf) -
+ strlen("<Too Long>)")] = '\0';
+ (void) strlcat(chbuf, "<Too Long>",
+ sizeof (chbuf));
+ break;
+ }
+ }
+ if (buf)
+ *(buf - 1) = '\0'; /* Erase last colon */
+ (void) sprintf(get_line(0, 0),
+ "Link Layer address: %s", chbuf);
+ show_space();
+ break;
+ }
+ case ND_OPT_MTU: {
+ struct nd_opt_mtu *mopt;
+ if (opt->nd_opt_len != MTU_OPTION_LENGTH ||
+ ilen < sizeof (struct nd_opt_mtu))
+ break;
+ (void) sprintf(get_line(0, 0),
+ "+++ ICMPv6 MTU option +++");
+ mopt = (struct nd_opt_mtu *)opt;
+ (void) sprintf(get_line(0, 0),
+ "MTU = %u ", ntohl(mopt->nd_opt_mtu_mtu));
+ show_space();
+ break;
+ }
+ case ND_OPT_PREFIX_INFORMATION: {
+ struct nd_opt_prefix_info *popt;
+ char validstr[30];
+ char preferredstr[30];
+ char prefixstr[INET6_ADDRSTRLEN];
+
+ if (opt->nd_opt_len != PREFIX_OPTION_LENGTH ||
+ ilen < sizeof (struct nd_opt_prefix_info))
+ break;
+ popt = (struct nd_opt_prefix_info *)opt;
+ (void) sprintf(get_line(0, 0),
+ "+++ ICMPv6 Prefix option +++");
+ (void) sprintf(get_line(0, 0),
+ "Prefix length = %d ", popt->nd_opt_pi_prefix_len);
+ (void) sprintf(get_line(0, 0),
+ "Onlink flag: %s, Autonomous addr conf flag: %s",
+ popt->nd_opt_pi_flags_reserved &
+ ND_OPT_PI_FLAG_ONLINK ? "SET" : "NOT SET",
+ popt->nd_opt_pi_flags_reserved &
+ ND_OPT_PI_FLAG_AUTO ? "SET" : "NOT SET");
+
+ if (ntohl(popt->nd_opt_pi_valid_time) ==
+ PREFIX_INFINITY)
+ sprintf(validstr, "INFINITY");
+ else
+ sprintf(validstr, "%lu",
+ ntohl(popt->nd_opt_pi_valid_time));
+
+ if (ntohl(popt->nd_opt_pi_preferred_time) ==
+ PREFIX_INFINITY)
+ sprintf(preferredstr, "INFINITY");
+ else
+ sprintf(preferredstr, "%lu",
+ ntohl(popt->nd_opt_pi_preferred_time));
+
+ (void) sprintf(get_line(0, 0),
+ "Valid Lifetime %s, Preferred Lifetime %s",
+ validstr, preferredstr);
+ (void) sprintf(get_line(0, 0), "Prefix %s",
+ inet_ntop(AF_INET6,
+ (char *)&popt->nd_opt_pi_prefix, prefixstr,
+ INET6_ADDRSTRLEN));
+ show_space();
+ }
+ default:
+ break;
+ }
+ optc += opt->nd_opt_len * 8;
+ ilen -= opt->nd_opt_len * 8;
+ }
+}
+
+static void
+interpret_mldv2qry(icmp6_t *icmp6, int ilen)
+{
+ mld2q_t *qry;
+ in6_addr_t *src;
+ int rem = ilen;
+ int srccnt;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ if (ilen < sizeof (*qry)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Malformed MLD Query");
+ return;
+ }
+ qry = (mld2q_t *)icmp6;
+ rem -= sizeof (*qry);
+ srccnt = ntohs(qry->mld2q_numsrc);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Multicast address= %s", inet_ntop(AF_INET6,
+ &qry->mld2q_addr.s6_addr, addrstr, INET6_ADDRSTRLEN));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
+
+ src = (in6_addr_t *)&qry[1];
+ while (srccnt > 0 && rem >= sizeof (*src)) {
+ rem -= sizeof (*src);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ inet_ntop(AF_INET6, src, addrstr, INET6_ADDRSTRLEN));
+
+ srccnt--;
+ src++;
+ }
+}
+
+#define MAX_MLDV2_REPORT_TYPE 6
+
+const char *mldv2rpt_types[] = {
+ "<unknown>",
+ "MODE_IS_INCLUDE",
+ "MODE_IS_EXCLUDE",
+ "CHANGE_TO_INCLUDE",
+ "CHANGE_TO_EXCLUDE",
+ "ALLOW_NEW_SOURCES",
+ "BLOCK_OLD_SOURCES",
+};
+
+static void
+interpret_mldv2rpt(icmp6_t *icmp6, int ilen)
+{
+ mld2r_t *rpt;
+ mld2mar_t *mar;
+ in6_addr_t *src;
+ int rem = ilen, auxlen;
+ uint16_t marcnt, srccnt;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ if (ilen < sizeof (*rpt)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Malformed MLDv2 Report");
+ return;
+ }
+ rpt = (mld2r_t *)icmp6;
+ mar = (mld2mar_t *)&rpt[1];
+ marcnt = ntohs(rpt->mld2r_nummar);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d Multicast Address Record%s:", marcnt, (marcnt == 1) ? "" : "s");
+ rem -= sizeof (*rpt);
+ while (marcnt > 0 && rem >= sizeof (*mar)) {
+ rem -= sizeof (*mar);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Multicast address= %s type = %s", inet_ntop(AF_INET6,
+ &mar->mld2mar_group.s6_addr, addrstr, INET6_ADDRSTRLEN),
+ (mar->mld2mar_type > MAX_MLDV2_REPORT_TYPE) ?
+ "<unknown>" : mldv2rpt_types[mar->mld2mar_type]);
+ srccnt = ntohs(mar->mld2mar_numsrc);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
+
+ src = (in6_addr_t *)&mar[1];
+ while (srccnt > 0 && rem >= sizeof (*src)) {
+ rem -= sizeof (*src);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " %s", inet_ntop(AF_INET6, src, addrstr,
+ INET6_ADDRSTRLEN));
+
+ srccnt--;
+ src++;
+ }
+
+ marcnt--;
+ auxlen = mar->mld2mar_auxlen * 4;
+ rem -= auxlen;
+ mar = (mld2mar_t *)((uint8_t *)src + auxlen);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_igmp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_igmp.c
new file mode 100644
index 0000000..b683c81
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_igmp.c
@@ -0,0 +1,226 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <sys/stropts.h>
+#include <sys/sysmacros.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/igmp.h>
+#include <inet/ip.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include "snoop.h"
+
+static void interpret_igmpv3qry(struct igmp *, int);
+static void interpret_igmpv3rpt(struct igmp *, int);
+
+
+/*ARGSUSED*/
+void
+interpret_igmp(int flags, char *data, int iplen, int ilen)
+{
+ const char *pt;
+ char *line;
+ struct igmp *igmp = (struct igmp *)data;
+ char addrstr[INET_ADDRSTRLEN];
+
+ if (ilen < IGMP_MINLEN) {
+ /* incomplete header */
+ line = get_sum_line();
+ (void) snprintf(line, MAXLINE, "Malformed IGMP packet");
+ return;
+ }
+
+ switch (igmp->igmp_type) {
+ case IGMP_MEMBERSHIP_QUERY:
+ if (ilen == IGMP_MINLEN) {
+ if (igmp->igmp_code == 0)
+ pt = "v1 membership query";
+ else
+ pt = "v2 membership query";
+ } else if (ilen >= IGMP_V3_QUERY_MINLEN) {
+ pt = "v3 membership query";
+ } else {
+ pt = "Unknown membership query";
+ }
+ break;
+ case IGMP_V1_MEMBERSHIP_REPORT:
+ pt = "v1 membership report";
+ break;
+ case IGMP_V2_MEMBERSHIP_REPORT:
+ pt = "v2 membership report";
+ break;
+ case IGMP_V3_MEMBERSHIP_REPORT:
+ pt = "v3 membership report";
+ break;
+ case IGMP_V2_LEAVE_GROUP:
+ pt = "v2 leave group";
+ break;
+
+ default:
+ pt = "Unknown";
+ break;
+ }
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+ (void) snprintf(line, MAXLINE, "IGMP %s", pt);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("IGMP: ", "IGMP Header", ilen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Type = %d (%s)", igmp->igmp_type, pt);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Max Response Time = %d", igmp->igmp_code);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Checksum = %x", ntohs(igmp->igmp_cksum));
+
+ if (igmp->igmp_type == IGMP_MEMBERSHIP_QUERY &&
+ ilen >= IGMP_V3_QUERY_MINLEN) {
+ interpret_igmpv3qry(igmp, ilen);
+ } else if (igmp->igmp_type == IGMP_V3_MEMBERSHIP_REPORT) {
+ interpret_igmpv3rpt(igmp, ilen);
+ } else {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Group = %s",
+ inet_ntop(AF_INET, &igmp->igmp_group.s_addr,
+ addrstr, INET_ADDRSTRLEN));
+ }
+
+ show_space();
+ }
+}
+
+static void
+interpret_igmpv3qry(struct igmp *igmp, int ilen)
+{
+ struct igmp3q *qry;
+ struct in_addr *src;
+ int rem = ilen;
+ int srccnt;
+ char addrstr[INET_ADDRSTRLEN];
+
+ if (ilen < sizeof (*qry)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Malformed IGMP Query");
+ return;
+ }
+
+ qry = (struct igmp3q *)igmp;
+ rem -= sizeof (*qry);
+ srccnt = ntohs(qry->igmp3q_numsrc);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Group = %s", inet_ntop(AF_INET, &qry->igmp3q_group, addrstr,
+ INET_ADDRSTRLEN));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
+
+ src = (struct in_addr *)&qry[1];
+ while (srccnt > 0 && rem >= sizeof (*src)) {
+ rem -= sizeof (*src);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ inet_ntop(AF_INET, &src->s_addr, addrstr, INET_ADDRSTRLEN));
+
+ srccnt--;
+ src++;
+ }
+}
+
+#define MAX_IGMPV3_REPORT_TYPE 6
+
+const char *igmpv3rpt_types[] = {
+ "<unknown>",
+ "MODE_IS_INCLUDE",
+ "MODE_IS_EXCLUDE",
+ "CHANGE_TO_INCLUDE",
+ "CHANGE_TO_EXCLUDE",
+ "ALLOW_NEW_SOURCES",
+ "BLOCK_OLD_SOURCES",
+};
+
+static void
+interpret_igmpv3rpt(struct igmp *igmp, int ilen)
+{
+ struct igmp3r *rpt;
+ struct grphdr *grh;
+ struct in_addr *src;
+ int rem = ilen, auxlen;
+ uint16_t grhcnt, srccnt;
+ char addrstr[INET_ADDRSTRLEN];
+
+ if (ilen < sizeof (*rpt)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Malformed IGMPv3 Report");
+ return;
+ }
+
+ rpt = (struct igmp3r *)igmp;
+ grh = (struct grphdr *)&rpt[1];
+ grhcnt = ntohs(rpt->igmp3r_numrec);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d Group Record%s:", grhcnt, (grhcnt == 1) ? "" : "s");
+ rem -= sizeof (*rpt);
+ while (grhcnt > 0 && rem >= sizeof (*grh)) {
+ rem -= sizeof (*grh);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Group = %s type = %s", inet_ntop(AF_INET,
+ &grh->grphdr_group.s_addr, addrstr, INET_ADDRSTRLEN),
+ (grh->grphdr_type > MAX_IGMPV3_REPORT_TYPE) ?
+ "<unknown>" : igmpv3rpt_types[grh->grphdr_type]);
+ srccnt = ntohs(grh->grphdr_numsrc);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
+
+ src = (struct in_addr *)&grh[1];
+ while (srccnt > 0 && rem >= sizeof (*src)) {
+ rem -= sizeof (*src);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " %s", inet_ntop(AF_INET, &src->s_addr, addrstr,
+ INET_ADDRSTRLEN));
+
+ srccnt--;
+ src++;
+ }
+
+ grhcnt--;
+ auxlen = grh->grphdr_auxlen * 4;
+ rem -= auxlen;
+ grh = (struct grphdr *)((uint8_t *)src + auxlen);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ip.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ip.c
new file mode 100644
index 0000000..af37d85
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ip.c
@@ -0,0 +1,1490 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2012 Milan Jurik. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <sys/stropts.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+#include <netinet/if_ether.h>
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <tsol/label.h>
+#include <sys/tsol/tndb.h>
+#include <sys/tsol/label_macro.h>
+
+#include "snoop.h"
+
+
+/*
+ * IPv6 extension header masks. These are used by the print_ipv6_extensions()
+ * function to return information to the caller about which extension headers
+ * were processed. This can be useful if the caller wants to know if the
+ * packet is an IPv6 fragment, for example.
+ */
+#define SNOOP_HOPOPTS 0x01U
+#define SNOOP_ROUTING 0x02U
+#define SNOOP_DSTOPTS 0x04U
+#define SNOOP_FRAGMENT 0x08U
+#define SNOOP_AH 0x10U
+#define SNOOP_ESP 0x20U
+#define SNOOP_IPV6 0x40U
+
+static void prt_routing_hdr(int, const struct ip6_rthdr *);
+static void prt_fragment_hdr(int, const struct ip6_frag *);
+static void prt_hbh_options(int, const struct ip6_hbh *);
+static void prt_dest_options(int, const struct ip6_dest *);
+static void print_route(const uchar_t *);
+static void print_ipoptions(const uchar_t *, int);
+static void print_ripso(const uchar_t *);
+static void print_cipso(const uchar_t *);
+
+/* Keep track of how many nested IP headers we have. */
+unsigned int encap_levels;
+unsigned int total_encap_levels = 1;
+
+int
+interpret_ip(int flags, const struct ip *ip, int fraglen)
+{
+ uchar_t *data;
+ char buff[24];
+ boolean_t isfrag = B_FALSE;
+ boolean_t morefrag;
+ uint16_t fragoffset;
+ int hdrlen;
+ uint16_t iplen, uitmp;
+
+ if (ip->ip_v == IPV6_VERSION) {
+ iplen = interpret_ipv6(flags, (ip6_t *)ip, fraglen);
+ return (iplen);
+ }
+
+ if (encap_levels == 0)
+ total_encap_levels = 0;
+ encap_levels++;
+ total_encap_levels++;
+
+ hdrlen = ip->ip_hl * 4;
+ data = ((uchar_t *)ip) + hdrlen;
+ iplen = ntohs(ip->ip_len) - hdrlen;
+ fraglen -= hdrlen;
+ if (fraglen > iplen)
+ fraglen = iplen;
+ if (fraglen < 0) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "IP truncated: header missing %d bytes", -fraglen);
+ encap_levels--;
+ return (fraglen + iplen);
+ }
+ /*
+ * We flag this as a fragment if the more fragments bit is set, or
+ * if the fragment offset is non-zero.
+ */
+ morefrag = (ntohs(ip->ip_off) & IP_MF) == 0 ? B_FALSE : B_TRUE;
+ fragoffset = (ntohs(ip->ip_off) & 0x1FFF) * 8;
+ if (morefrag || fragoffset != 0)
+ isfrag = B_TRUE;
+
+ src_name = addrtoname(AF_INET, &ip->ip_src);
+ dst_name = addrtoname(AF_INET, &ip->ip_dst);
+
+ if (flags & F_SUM) {
+ if (isfrag) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "%s IP fragment ID=%d Offset=%-4d MF=%d TOS=0x%x "
+ "TTL=%d",
+ getproto(ip->ip_p),
+ ntohs(ip->ip_id),
+ fragoffset,
+ morefrag,
+ ip->ip_tos,
+ ip->ip_ttl);
+ } else {
+ (void) strlcpy(buff, inet_ntoa(ip->ip_dst),
+ sizeof (buff));
+ uitmp = ntohs(ip->ip_len);
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "IP D=%s S=%s LEN=%u%s, ID=%d, TOS=0x%x, TTL=%d",
+ buff,
+ inet_ntoa(ip->ip_src),
+ uitmp,
+ iplen > fraglen ? "?" : "",
+ ntohs(ip->ip_id),
+ ip->ip_tos,
+ ip->ip_ttl);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("IP: ", "IP Header", iplen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Version = %d", ip->ip_v);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Header length = %d bytes", hdrlen);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Type of service = 0x%02x", ip->ip_tos);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " xxx. .... = %d (precedence)",
+ ip->ip_tos >> 5);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " %s", getflag(ip->ip_tos, IPTOS_LOWDELAY,
+ "low delay", "normal delay"));
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(ip->ip_tos, IPTOS_THROUGHPUT,
+ "high throughput", "normal throughput"));
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(ip->ip_tos, IPTOS_RELIABILITY,
+ "high reliability", "normal reliability"));
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(ip->ip_tos, IPTOS_ECT,
+ "ECN capable transport", "not ECN capable transport"));
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(ip->ip_tos, IPTOS_CE,
+ "ECN congestion experienced",
+ "no ECN congestion experienced"));
+ /* warning: ip_len is signed in netinet/ip.h */
+ uitmp = ntohs(ip->ip_len);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Total length = %u bytes%s", uitmp,
+ iplen > fraglen ? " -- truncated" : "");
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Identification = %d", ntohs(ip->ip_id));
+ /* warning: ip_off is signed in netinet/ip.h */
+ uitmp = ntohs(ip->ip_off);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Flags = 0x%x", uitmp >> 12);
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(uitmp >> 8, IP_DF >> 8,
+ "do not fragment", "may fragment"));
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(uitmp >> 8, IP_MF >> 8,
+ "more fragments", "last fragment"));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Fragment offset = %u bytes",
+ fragoffset);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Time to live = %d seconds/hops",
+ ip->ip_ttl);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Protocol = %d (%s)", ip->ip_p,
+ getproto(ip->ip_p));
+ /*
+ * XXX need to compute checksum and print whether it's correct
+ */
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Header checksum = %04x",
+ ntohs(ip->ip_sum));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Source address = %s, %s",
+ inet_ntoa(ip->ip_src), addrtoname(AF_INET, &ip->ip_src));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Destination address = %s, %s",
+ inet_ntoa(ip->ip_dst), addrtoname(AF_INET, &ip->ip_dst));
+
+ /* Print IP options - if any */
+
+ print_ipoptions((const uchar_t *)(ip + 1),
+ hdrlen - sizeof (struct ip));
+ show_space();
+ }
+
+ /*
+ * If we are in detail mode, and this is not the first fragment of
+ * a fragmented packet, print out a little line stating this.
+ * Otherwise, go to the next protocol layer only if this is not a
+ * fragment, or we are in detail mode and this is the first fragment
+ * of a fragmented packet.
+ */
+ if (flags & F_DTAIL && fragoffset != 0) {
+ (void) snprintf(get_detail_line(0, 0), MAXLINE,
+ "%s: [%d byte(s) of data, continuation of IP ident=%d]",
+ getproto(ip->ip_p),
+ iplen,
+ ntohs(ip->ip_id));
+ } else if (!isfrag || (flags & F_DTAIL) && isfrag && fragoffset == 0) {
+ /* go to the next protocol layer */
+
+ if (fraglen > 0) {
+ switch (ip->ip_p) {
+ case IPPROTO_IP:
+ break;
+ case IPPROTO_ENCAP:
+ (void) interpret_ip(flags,
+ /* LINTED: alignment */
+ (const struct ip *)data, fraglen);
+ break;
+ case IPPROTO_ICMP:
+ (void) interpret_icmp(flags,
+ /* LINTED: alignment */
+ (struct icmp *)data, iplen, fraglen);
+ break;
+ case IPPROTO_IGMP:
+ interpret_igmp(flags, data, iplen, fraglen);
+ break;
+ case IPPROTO_GGP:
+ break;
+ case IPPROTO_TCP:
+ (void) interpret_tcp(flags,
+ (struct tcphdr *)data, iplen, fraglen);
+ break;
+
+ case IPPROTO_ESP:
+ (void) interpret_esp(flags, data, iplen,
+ fraglen);
+ break;
+ case IPPROTO_AH:
+ (void) interpret_ah(flags, data, iplen,
+ fraglen);
+ break;
+
+ case IPPROTO_OSPF:
+ interpret_ospf(flags, data, iplen, fraglen);
+ break;
+
+ case IPPROTO_EGP:
+ case IPPROTO_PUP:
+ break;
+ case IPPROTO_UDP:
+ (void) interpret_udp(flags,
+ (struct udphdr *)data, iplen, fraglen);
+ break;
+
+ case IPPROTO_IDP:
+ case IPPROTO_HELLO:
+ case IPPROTO_ND:
+ case IPPROTO_RAW:
+ break;
+ case IPPROTO_IPV6: /* IPV6 encap */
+ /* LINTED: alignment */
+ (void) interpret_ipv6(flags, (ip6_t *)data,
+ iplen);
+ break;
+ case IPPROTO_SCTP:
+ (void) interpret_sctp(flags,
+ (struct sctp_hdr *)data, iplen, fraglen);
+ break;
+ }
+ }
+ }
+
+ encap_levels--;
+ return (iplen);
+}
+
+int
+interpret_ipv6(int flags, const ip6_t *ip6h, int fraglen)
+{
+ uint8_t *data;
+ int hdrlen, iplen;
+ int version, flow, class;
+ uchar_t proto;
+ boolean_t isfrag = B_FALSE;
+ uint8_t extmask;
+ /*
+ * The print_srcname and print_dstname strings are the hostname
+ * parts of the verbose IPv6 header output, including the comma
+ * and the space after the litteral address strings.
+ */
+ char print_srcname[MAXHOSTNAMELEN + 2];
+ char print_dstname[MAXHOSTNAMELEN + 2];
+ char src_addrstr[INET6_ADDRSTRLEN];
+ char dst_addrstr[INET6_ADDRSTRLEN];
+
+ iplen = ntohs(ip6h->ip6_plen);
+ hdrlen = IPV6_HDR_LEN;
+ fraglen -= hdrlen;
+ if (fraglen < 0)
+ return (fraglen + hdrlen);
+ data = ((uint8_t *)ip6h) + hdrlen;
+
+ proto = ip6h->ip6_nxt;
+
+ src_name = addrtoname(AF_INET6, &ip6h->ip6_src);
+ dst_name = addrtoname(AF_INET6, &ip6h->ip6_dst);
+
+ /*
+ * Use endian-aware masks to extract traffic class and
+ * flowinfo. Also, flowinfo is now 20 bits and class 8
+ * rather than 24 and 4.
+ */
+ class = ntohl((ip6h->ip6_vcf & IPV6_FLOWINFO_TCLASS) >> 20);
+ flow = ntohl(ip6h->ip6_vcf & IPV6_FLOWINFO_FLOWLABEL);
+
+ /*
+ * NOTE: the F_SUM and F_DTAIL flags are mutually exclusive,
+ * so the code within the first part of the following if statement
+ * will not affect the detailed printing of the packet.
+ */
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "IPv6 S=%s D=%s LEN=%d HOPS=%d CLASS=0x%x FLOW=0x%x",
+ src_name, dst_name, iplen, ip6h->ip6_hops, class, flow);
+ } else if (flags & F_DTAIL) {
+
+ (void) inet_ntop(AF_INET6, &ip6h->ip6_src, src_addrstr,
+ INET6_ADDRSTRLEN);
+ (void) inet_ntop(AF_INET6, &ip6h->ip6_dst, dst_addrstr,
+ INET6_ADDRSTRLEN);
+
+ version = ntohl(ip6h->ip6_vcf) >> 28;
+
+ if (strcmp(src_name, src_addrstr) == 0) {
+ print_srcname[0] = '\0';
+ } else {
+ snprintf(print_srcname, sizeof (print_srcname),
+ ", %s", src_name);
+ }
+
+ if (strcmp(dst_name, dst_addrstr) == 0) {
+ print_dstname[0] = '\0';
+ } else {
+ snprintf(print_dstname, sizeof (print_dstname),
+ ", %s", dst_name);
+ }
+
+ show_header("IPv6: ", "IPv6 Header", iplen);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Version = %d", version);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Traffic Class = %d", class);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Flow label = 0x%x", flow);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Payload length = %d", iplen);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Next Header = %d (%s)", proto,
+ getproto(proto));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Hop Limit = %d", ip6h->ip6_hops);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Source address = %s%s", src_addrstr, print_srcname);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Destination address = %s%s", dst_addrstr, print_dstname);
+
+ show_space();
+ }
+
+ /*
+ * Print IPv6 Extension Headers, or skip them in the summary case.
+ * Set isfrag to true if one of the extension headers encounterred
+ * was a fragment header.
+ */
+ if (proto == IPPROTO_HOPOPTS || proto == IPPROTO_DSTOPTS ||
+ proto == IPPROTO_ROUTING || proto == IPPROTO_FRAGMENT) {
+ extmask = print_ipv6_extensions(flags, &data, &proto, &iplen,
+ &fraglen);
+ if ((extmask & SNOOP_FRAGMENT) != 0) {
+ isfrag = B_TRUE;
+ }
+ }
+
+ /*
+ * We only want to print upper layer information if this is not
+ * a fragment, or if we're printing in detail. Note that the
+ * proto variable will be set to IPPROTO_NONE if this is a fragment
+ * with a non-zero fragment offset.
+ */
+ if (!isfrag || flags & F_DTAIL) {
+ /* go to the next protocol layer */
+
+ switch (proto) {
+ case IPPROTO_IP:
+ break;
+ case IPPROTO_ENCAP:
+ /* LINTED: alignment */
+ (void) interpret_ip(flags, (const struct ip *)data,
+ fraglen);
+ break;
+ case IPPROTO_ICMPV6:
+ /* LINTED: alignment */
+ (void) interpret_icmpv6(flags, (icmp6_t *)data, iplen,
+ fraglen);
+ break;
+ case IPPROTO_IGMP:
+ interpret_igmp(flags, data, iplen, fraglen);
+ break;
+ case IPPROTO_GGP:
+ break;
+ case IPPROTO_TCP:
+ (void) interpret_tcp(flags, (struct tcphdr *)data,
+ iplen, fraglen);
+ break;
+ case IPPROTO_ESP:
+ (void) interpret_esp(flags, data, iplen, fraglen);
+ break;
+ case IPPROTO_AH:
+ (void) interpret_ah(flags, data, iplen, fraglen);
+ break;
+ case IPPROTO_EGP:
+ case IPPROTO_PUP:
+ break;
+ case IPPROTO_UDP:
+ (void) interpret_udp(flags, (struct udphdr *)data,
+ iplen, fraglen);
+ break;
+ case IPPROTO_IDP:
+ case IPPROTO_HELLO:
+ case IPPROTO_ND:
+ case IPPROTO_RAW:
+ break;
+ case IPPROTO_IPV6:
+ /* LINTED: alignment */
+ (void) interpret_ipv6(flags, (const ip6_t *)data,
+ iplen);
+ break;
+ case IPPROTO_SCTP:
+ (void) interpret_sctp(flags, (struct sctp_hdr *)data,
+ iplen, fraglen);
+ break;
+ case IPPROTO_OSPF:
+ interpret_ospf6(flags, data, iplen, fraglen);
+ break;
+ }
+ }
+
+ return (iplen);
+}
+
+/*
+ * ip_ext: data including the extension header.
+ * iplen: length of the data remaining in the packet.
+ * Returns a mask of IPv6 extension headers it processed.
+ */
+uint8_t
+print_ipv6_extensions(int flags, uint8_t **hdr, uint8_t *next, int *iplen,
+ int *fraglen)
+{
+ uint8_t *data_ptr;
+ uchar_t proto = *next;
+ boolean_t is_extension_header;
+ struct ip6_hbh *ipv6ext_hbh;
+ struct ip6_dest *ipv6ext_dest;
+ struct ip6_rthdr *ipv6ext_rthdr;
+ struct ip6_frag *ipv6ext_frag;
+ uint32_t exthdrlen;
+ uint8_t extmask = 0;
+
+ if ((hdr == NULL) || (*hdr == NULL) || (next == NULL) || (iplen == 0))
+ return (0);
+
+ data_ptr = *hdr;
+ is_extension_header = B_TRUE;
+ while (is_extension_header) {
+
+ /*
+ * There must be at least enough data left to read the
+ * next header and header length fields from the next
+ * header.
+ */
+ if (*fraglen < 2) {
+ return (extmask);
+ }
+
+ switch (proto) {
+ case IPPROTO_HOPOPTS:
+ ipv6ext_hbh = (struct ip6_hbh *)data_ptr;
+ exthdrlen = 8 + ipv6ext_hbh->ip6h_len * 8;
+ if (*fraglen <= exthdrlen) {
+ return (extmask);
+ }
+ prt_hbh_options(flags, ipv6ext_hbh);
+ extmask |= SNOOP_HOPOPTS;
+ proto = ipv6ext_hbh->ip6h_nxt;
+ break;
+ case IPPROTO_DSTOPTS:
+ ipv6ext_dest = (struct ip6_dest *)data_ptr;
+ exthdrlen = 8 + ipv6ext_dest->ip6d_len * 8;
+ if (*fraglen <= exthdrlen) {
+ return (extmask);
+ }
+ prt_dest_options(flags, ipv6ext_dest);
+ extmask |= SNOOP_DSTOPTS;
+ proto = ipv6ext_dest->ip6d_nxt;
+ break;
+ case IPPROTO_ROUTING:
+ ipv6ext_rthdr = (struct ip6_rthdr *)data_ptr;
+ exthdrlen = 8 + ipv6ext_rthdr->ip6r_len * 8;
+ if (*fraglen <= exthdrlen) {
+ return (extmask);
+ }
+ prt_routing_hdr(flags, ipv6ext_rthdr);
+ extmask |= SNOOP_ROUTING;
+ proto = ipv6ext_rthdr->ip6r_nxt;
+ break;
+ case IPPROTO_FRAGMENT:
+ /* LINTED: alignment */
+ ipv6ext_frag = (struct ip6_frag *)data_ptr;
+ exthdrlen = sizeof (struct ip6_frag);
+ if (*fraglen <= exthdrlen) {
+ return (extmask);
+ }
+ prt_fragment_hdr(flags, ipv6ext_frag);
+ extmask |= SNOOP_FRAGMENT;
+ /*
+ * If this is not the first fragment, forget about
+ * the rest of the packet, snoop decoding is
+ * stateless.
+ */
+ if ((ipv6ext_frag->ip6f_offlg & IP6F_OFF_MASK) != 0)
+ proto = IPPROTO_NONE;
+ else
+ proto = ipv6ext_frag->ip6f_nxt;
+ break;
+ default:
+ is_extension_header = B_FALSE;
+ break;
+ }
+
+ if (is_extension_header) {
+ *iplen -= exthdrlen;
+ *fraglen -= exthdrlen;
+ data_ptr += exthdrlen;
+ }
+ }
+
+ *next = proto;
+ *hdr = data_ptr;
+ return (extmask);
+}
+
+static void
+print_ipoptions(const uchar_t *opt, int optlen)
+{
+ int len;
+ int remain;
+ char *line;
+ const char *truncstr;
+
+ if (optlen <= 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "No options");
+ return;
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Options: (%d bytes)", optlen);
+
+ while (optlen > 0) {
+ line = get_line(0, 0);
+ remain = get_line_remain();
+ len = opt[1];
+ truncstr = len > optlen ? "?" : "";
+ switch (opt[0]) {
+ case IPOPT_EOL:
+ (void) strlcpy(line, " - End of option list", remain);
+ return;
+ case IPOPT_NOP:
+ (void) strlcpy(line, " - No op", remain);
+ len = 1;
+ break;
+ case IPOPT_RR:
+ (void) snprintf(line, remain,
+ " - Record route (%d bytes%s)", len, truncstr);
+ print_route(opt);
+ break;
+ case IPOPT_TS:
+ (void) snprintf(line, remain,
+ " - Time stamp (%d bytes%s)", len, truncstr);
+ break;
+ case IPOPT_SECURITY:
+ (void) snprintf(line, remain, " - RIPSO (%d bytes%s)",
+ len, truncstr);
+ print_ripso(opt);
+ break;
+ case IPOPT_COMSEC:
+ (void) snprintf(line, remain, " - CIPSO (%d bytes%s)",
+ len, truncstr);
+ print_cipso(opt);
+ break;
+ case IPOPT_LSRR:
+ (void) snprintf(line, remain,
+ " - Loose source route (%d bytes%s)", len,
+ truncstr);
+ print_route(opt);
+ break;
+ case IPOPT_SATID:
+ (void) snprintf(line, remain,
+ " - SATNET Stream id (%d bytes%s)",
+ len, truncstr);
+ break;
+ case IPOPT_SSRR:
+ (void) snprintf(line, remain,
+ " - Strict source route, (%d bytes%s)", len,
+ truncstr);
+ print_route(opt);
+ break;
+ default:
+ (void) snprintf(line, remain,
+ " - Option %d (unknown - %d bytes%s) %s",
+ opt[0], len, truncstr,
+ tohex((char *)&opt[2], len - 2));
+ break;
+ }
+ if (len <= 0) {
+ (void) snprintf(line, remain,
+ " - Incomplete option len %d", len);
+ break;
+ }
+ opt += len;
+ optlen -= len;
+ }
+}
+
+static void
+print_route(const uchar_t *opt)
+{
+ int len, pointer, remain;
+ struct in_addr addr;
+ char *line;
+
+ len = opt[1];
+ pointer = opt[2];
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Pointer = %d", pointer);
+
+ pointer -= IPOPT_MINOFF;
+ opt += (IPOPT_OFFSET + 1);
+ len -= (IPOPT_OFFSET + 1);
+
+ while (len > 0) {
+ line = get_line(0, 0);
+ remain = get_line_remain();
+ memcpy((char *)&addr, opt, sizeof (addr));
+ if (addr.s_addr == INADDR_ANY)
+ (void) strlcpy(line, " -", remain);
+ else
+ (void) snprintf(line, remain, " %s",
+ addrtoname(AF_INET, &addr));
+ if (pointer == 0)
+ (void) strlcat(line, " <-- (current)", remain);
+
+ opt += sizeof (addr);
+ len -= sizeof (addr);
+ pointer -= sizeof (addr);
+ }
+}
+
+char *
+getproto(int p)
+{
+ switch (p) {
+ case IPPROTO_HOPOPTS: return ("IPv6-HopOpts");
+ case IPPROTO_IPV6: return ("IPv6");
+ case IPPROTO_ROUTING: return ("IPv6-Route");
+ case IPPROTO_FRAGMENT: return ("IPv6-Frag");
+ case IPPROTO_RSVP: return ("RSVP");
+ case IPPROTO_ENCAP: return ("IP-in-IP");
+ case IPPROTO_AH: return ("AH");
+ case IPPROTO_ESP: return ("ESP");
+ case IPPROTO_ICMP: return ("ICMP");
+ case IPPROTO_ICMPV6: return ("ICMPv6");
+ case IPPROTO_DSTOPTS: return ("IPv6-DstOpts");
+ case IPPROTO_IGMP: return ("IGMP");
+ case IPPROTO_GGP: return ("GGP");
+ case IPPROTO_TCP: return ("TCP");
+ case IPPROTO_EGP: return ("EGP");
+ case IPPROTO_PUP: return ("PUP");
+ case IPPROTO_UDP: return ("UDP");
+ case IPPROTO_IDP: return ("IDP");
+ case IPPROTO_HELLO: return ("HELLO");
+ case IPPROTO_ND: return ("ND");
+ case IPPROTO_EON: return ("EON");
+ case IPPROTO_RAW: return ("RAW");
+ case IPPROTO_OSPF: return ("OSPF");
+ default: return ("");
+ }
+}
+
+static void
+prt_routing_hdr(int flags, const struct ip6_rthdr *ipv6ext_rthdr)
+{
+ uint8_t nxt_hdr;
+ uint8_t type;
+ uint32_t len;
+ uint8_t segleft;
+ uint32_t numaddrs;
+ int i;
+ struct ip6_rthdr0 *ipv6ext_rthdr0;
+ struct in6_addr *addrs;
+ char addr[INET6_ADDRSTRLEN];
+
+ /* in summary mode, we don't do anything. */
+ if (flags & F_SUM) {
+ return;
+ }
+
+ nxt_hdr = ipv6ext_rthdr->ip6r_nxt;
+ type = ipv6ext_rthdr->ip6r_type;
+ len = 8 * (ipv6ext_rthdr->ip6r_len + 1);
+ segleft = ipv6ext_rthdr->ip6r_segleft;
+
+ show_header("IPv6-Route: ", "IPv6 Routing Header", 0);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Next header = %d (%s)", nxt_hdr, getproto(nxt_hdr));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Header length = %d", len);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Routing type = %d", type);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Segments left = %d", segleft);
+
+ if (type == IPV6_RTHDR_TYPE_0) {
+ /*
+ * XXX This loop will print all addresses in the routing header,
+ * XXX not just the segments left.
+ * XXX (The header length field is twice the number of
+ * XXX addresses)
+ * XXX At some future time, we may want to change this
+ * XXX to differentiate between the hops yet to do
+ * XXX and the hops already taken.
+ */
+ /* LINTED: alignment */
+ ipv6ext_rthdr0 = (struct ip6_rthdr0 *)ipv6ext_rthdr;
+ numaddrs = ipv6ext_rthdr0->ip6r0_len / 2;
+ addrs = (struct in6_addr *)(ipv6ext_rthdr0 + 1);
+ for (i = 0; i < numaddrs; i++) {
+ (void) inet_ntop(AF_INET6, &addrs[i], addr,
+ INET6_ADDRSTRLEN);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "address[%d]=%s", i, addr);
+ }
+ }
+
+ show_space();
+}
+
+static void
+prt_fragment_hdr(int flags, const struct ip6_frag *ipv6ext_frag)
+{
+ boolean_t morefrag;
+ uint16_t fragoffset;
+ uint8_t nxt_hdr;
+ uint32_t fragident;
+
+ /* extract the various fields from the fragment header */
+ nxt_hdr = ipv6ext_frag->ip6f_nxt;
+ morefrag = (ipv6ext_frag->ip6f_offlg & IP6F_MORE_FRAG) == 0
+ ? B_FALSE : B_TRUE;
+ fragoffset = ntohs(ipv6ext_frag->ip6f_offlg & IP6F_OFF_MASK);
+ fragident = ntohl(ipv6ext_frag->ip6f_ident);
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "IPv6 fragment ID=%u Offset=%-4d MF=%d",
+ fragident,
+ fragoffset,
+ morefrag);
+ } else { /* F_DTAIL */
+ show_header("IPv6-Frag: ", "IPv6 Fragment Header", 0);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Next Header = %d (%s)", nxt_hdr, getproto(nxt_hdr));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Fragment Offset = %d", fragoffset);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "More Fragments Flag = %s", morefrag ? "true" : "false");
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Identification = %u", fragident);
+
+ show_space();
+ }
+}
+
+static void
+print_ip6opt_ls(const uchar_t *data, unsigned int op_len)
+{
+ uint32_t doi;
+ uint8_t sotype, solen;
+ uint16_t value, value2;
+ char *cp;
+ int remlen;
+ boolean_t printed;
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Labeled Security Option len = %u bytes%s", op_len,
+ op_len < sizeof (uint32_t) || (op_len & 1) != 0 ? "?" : "");
+ if (op_len < sizeof (uint32_t))
+ return;
+ GETINT32(doi, data);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " DOI = %d (%s)", doi, doi == IP6LS_DOI_V4 ? "IPv4" : "???");
+ op_len -= sizeof (uint32_t);
+ while (op_len > 0) {
+ GETINT8(sotype, data);
+ if (op_len < 2) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " truncated %u suboption (no len)", sotype);
+ break;
+ }
+ GETINT8(solen, data);
+ if (solen < 2 || solen > op_len) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " bad %u suboption (len 2 <= %u <= %u)",
+ sotype, solen, op_len);
+ if (solen < 2)
+ solen = 2;
+ if (solen > op_len)
+ solen = op_len;
+ }
+ op_len -= solen;
+ solen -= 2;
+ cp = get_line(0, 0);
+ remlen = get_line_remain();
+ (void) strlcpy(cp, " ", remlen);
+ cp += 4;
+ remlen -= 4;
+ printed = B_TRUE;
+ switch (sotype) {
+ case IP6LS_TT_LEVEL:
+ if (solen != 2) {
+ printed = B_FALSE;
+ break;
+ }
+ GETINT16(value, data);
+ (void) snprintf(cp, remlen, "Level %u", value);
+ solen = 0;
+ break;
+ case IP6LS_TT_VECTOR:
+ (void) strlcpy(cp, "Bit-Vector: ", remlen);
+ remlen -= strlen(cp);
+ cp += strlen(cp);
+ while (solen > 1) {
+ GETINT16(value, data);
+ solen -= 2;
+ (void) snprintf(cp, remlen, "%04x", value);
+ remlen -= strlen(cp);
+ cp += strlen(cp);
+ }
+ break;
+ case IP6LS_TT_ENUM:
+ (void) strlcpy(cp, "Enumeration:", remlen);
+ remlen -= strlen(cp);
+ cp += strlen(cp);
+ while (solen > 1) {
+ GETINT16(value, data);
+ solen -= 2;
+ (void) snprintf(cp, remlen, " %u", value);
+ remlen -= strlen(cp);
+ cp += strlen(cp);
+ }
+ break;
+ case IP6LS_TT_RANGES:
+ (void) strlcpy(cp, "Ranges:", remlen);
+ remlen -= strlen(cp);
+ cp += strlen(cp);
+ while (solen > 3) {
+ GETINT16(value, data);
+ GETINT16(value2, data);
+ solen -= 4;
+ (void) snprintf(cp, remlen, " %u-%u", value,
+ value2);
+ remlen -= strlen(cp);
+ cp += strlen(cp);
+ }
+ break;
+ case IP6LS_TT_V4:
+ (void) strlcpy(cp, "IPv4 Option", remlen);
+ print_ipoptions(data, solen);
+ solen = 0;
+ break;
+ case IP6LS_TT_DEST:
+ (void) snprintf(cp, remlen,
+ "Destination-Only Data length %u", solen);
+ solen = 0;
+ break;
+ default:
+ (void) snprintf(cp, remlen,
+ " unknown %u suboption (len %u)", sotype, solen);
+ solen = 0;
+ break;
+ }
+ if (solen != 0) {
+ if (printed) {
+ cp = get_line(0, 0);
+ remlen = get_line_remain();
+ }
+ (void) snprintf(cp, remlen,
+ " malformed %u suboption (remaining %u)",
+ sotype, solen);
+ data += solen;
+ }
+ }
+}
+
+static void
+prt_hbh_options(int flags, const struct ip6_hbh *ipv6ext_hbh)
+{
+ const uint8_t *data, *ndata;
+ uint32_t len;
+ uint8_t op_type;
+ uint8_t op_len;
+ uint8_t nxt_hdr;
+
+ /* in summary mode, we don't do anything. */
+ if (flags & F_SUM) {
+ return;
+ }
+
+ show_header("IPv6-HopOpts: ", "IPv6 Hop-by-Hop Options Header", 0);
+ show_space();
+
+ /*
+ * Store the lengh of this ext hdr in bytes. The caller has
+ * ensured that there is at least len bytes of data left.
+ */
+ len = ipv6ext_hbh->ip6h_len * 8 + 8;
+
+ ndata = (const uint8_t *)ipv6ext_hbh + 2;
+ len -= 2;
+
+ nxt_hdr = ipv6ext_hbh->ip6h_nxt;
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Next Header = %u (%s)", nxt_hdr, getproto(nxt_hdr));
+
+ while (len > 0) {
+ data = ndata;
+ GETINT8(op_type, data);
+ /* This is the only one-octet IPv6 option */
+ if (op_type == IP6OPT_PAD1) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "pad1 option ");
+ len--;
+ ndata = data;
+ continue;
+ }
+ GETINT8(op_len, data);
+ if (len < 2 || op_len + 2 > len) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Error: option %u truncated (%u + 2 > %u)",
+ op_type, op_len, len);
+ op_len = len - 2;
+ /*
+ * Continue processing the malformed option so that we
+ * can display as much as possible.
+ */
+ }
+
+ /* advance pointers to the next option */
+ len -= op_len + 2;
+ ndata = data + op_len;
+
+ /* process this option */
+ switch (op_type) {
+ case IP6OPT_PADN:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "padN option len = %u", op_len);
+ break;
+ case IP6OPT_JUMBO: {
+ uint32_t payload_len;
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Jumbo Payload Option len = %u bytes%s", op_len,
+ op_len == sizeof (uint32_t) ? "" : "?");
+ if (op_len == sizeof (uint32_t)) {
+ GETINT32(payload_len, data);
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Jumbo Payload Length = %u bytes",
+ payload_len);
+ }
+ break;
+ }
+ case IP6OPT_ROUTER_ALERT: {
+ uint16_t value;
+ const char *label[] = {"MLD", "RSVP", "AN"};
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Router Alert Option len = %u bytes%s", op_len,
+ op_len == sizeof (uint16_t) ? "" : "?");
+ if (op_len == sizeof (uint16_t)) {
+ GETINT16(value, data);
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Alert Type = %d (%s)", value,
+ value < sizeof (label) / sizeof (label[0]) ?
+ label[value] : "???");
+ }
+ break;
+ }
+ case IP6OPT_LS:
+ print_ip6opt_ls(data, op_len);
+ break;
+ default:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Option type = %u, len = %u", op_type, op_len);
+ break;
+ }
+ }
+
+ show_space();
+}
+
+static void
+prt_dest_options(int flags, const struct ip6_dest *ipv6ext_dest)
+{
+ const uint8_t *data, *ndata;
+ uint32_t len;
+ uint8_t op_type;
+ uint32_t op_len;
+ uint8_t nxt_hdr;
+ uint8_t value;
+
+ /* in summary mode, we don't do anything. */
+ if (flags & F_SUM) {
+ return;
+ }
+
+ show_header("IPv6-DstOpts: ", "IPv6 Destination Options Header", 0);
+ show_space();
+
+ /*
+ * Store the length of this ext hdr in bytes. The caller has
+ * ensured that there is at least len bytes of data left.
+ */
+ len = ipv6ext_dest->ip6d_len * 8 + 8;
+
+ ndata = (const uint8_t *)ipv6ext_dest + 2; /* skip hdr/len */
+ len -= 2;
+
+ nxt_hdr = ipv6ext_dest->ip6d_nxt;
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Next Header = %u (%s)", nxt_hdr, getproto(nxt_hdr));
+
+ while (len > 0) {
+ data = ndata;
+ GETINT8(op_type, data);
+ if (op_type == IP6OPT_PAD1) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "pad1 option ");
+ len--;
+ ndata = data;
+ continue;
+ }
+ GETINT8(op_len, data);
+ if (len < 2 || op_len + 2 > len) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Error: option %u truncated (%u + 2 > %u)",
+ op_type, op_len, len);
+ op_len = len - 2;
+ /*
+ * Continue processing the malformed option so that we
+ * can display as much as possible.
+ */
+ }
+
+ /* advance pointers to the next option */
+ len -= op_len + 2;
+ ndata = data + op_len;
+
+ /* process this option */
+ switch (op_type) {
+ case IP6OPT_PADN:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "padN option len = %u", op_len);
+ break;
+ case IP6OPT_TUNNEL_LIMIT:
+ GETINT8(value, data);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "tunnel encapsulation limit len = %d, value = %d",
+ op_len, value);
+ break;
+ case IP6OPT_LS:
+ print_ip6opt_ls(data, op_len);
+ break;
+ default:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Option type = %u, len = %u", op_type, op_len);
+ break;
+ }
+ }
+
+ show_space();
+}
+
+#define ALABEL_MAXLEN 256
+
+static char ascii_label[ALABEL_MAXLEN];
+static char *plabel = ascii_label;
+
+struct snoop_pair {
+ int val;
+ const char *name;
+};
+
+static struct snoop_pair ripso_class_tbl[] = {
+ TSOL_CL_TOP_SECRET, "TOP SECRET",
+ TSOL_CL_SECRET, "SECRET",
+ TSOL_CL_CONFIDENTIAL, "CONFIDENTIAL",
+ TSOL_CL_UNCLASSIFIED, "UNCLASSIFIED",
+ -1, NULL
+};
+
+static struct snoop_pair ripso_prot_tbl[] = {
+ TSOL_PA_GENSER, "GENSER",
+ TSOL_PA_SIOP_ESI, "SIOP-ESI",
+ TSOL_PA_SCI, "SCI",
+ TSOL_PA_NSA, "NSA",
+ TSOL_PA_DOE, "DOE",
+ 0x04, "UNASSIGNED",
+ 0x02, "UNASSIGNED",
+ -1, NULL
+};
+
+static struct snoop_pair *
+get_pair_byval(struct snoop_pair pairlist[], int val)
+{
+ int i;
+
+ for (i = 0; pairlist[i].name != NULL; i++)
+ if (pairlist[i].val == val)
+ return (&pairlist[i]);
+ return (NULL);
+}
+
+static void
+print_ripso(const uchar_t *opt)
+{
+ struct snoop_pair *ripso_class;
+ int i, index, prot_len;
+ boolean_t first_prot;
+ char line[100], *ptr;
+
+ prot_len = opt[1] - 3;
+ if (prot_len < 0)
+ return;
+
+ show_header("RIPSO: ", "Revised IP Security Option", 0);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Type = Basic Security Option (%d), Length = %d", opt[0], opt[1]);
+
+ /*
+ * Display Classification Level
+ */
+ ripso_class = get_pair_byval(ripso_class_tbl, (int)opt[2]);
+ if (ripso_class == NULL)
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Classification = Unknown (0x%02x)", opt[2]);
+ else
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Classification = %s (0x%02x)",
+ ripso_class->name, ripso_class->val);
+
+ /*
+ * Display Protection Authority Flags
+ */
+ (void) snprintf(line, sizeof (line), "Protection Authority = ");
+ ptr = line;
+ first_prot = B_TRUE;
+ for (i = 0; i < prot_len; i++) {
+ index = 0;
+ while (ripso_prot_tbl[index].name != NULL) {
+ if (opt[3 + i] & ripso_prot_tbl[index].val) {
+ ptr = strchr(ptr, 0);
+ if (!first_prot) {
+ (void) strlcpy(ptr, ", ",
+ sizeof (line) - (ptr - line));
+ ptr = strchr(ptr, 0);
+ }
+ (void) snprintf(ptr,
+ sizeof (line) - (ptr - line),
+ "%s (0x%02x)",
+ ripso_prot_tbl[index].name,
+ ripso_prot_tbl[index].val);
+ }
+ index++;
+ }
+ if ((opt[3 + i] & 1) == 0)
+ break;
+ }
+ if (!first_prot)
+ (void) snprintf(get_line(0, 0), get_line_remain(), "%s", line);
+ else
+ (void) snprintf(get_line(0, 0), get_line_remain(), "%sNone",
+ line);
+}
+
+#define CIPSO_GENERIC_ARRAY_LEN 200
+
+/*
+ * Return 1 if CIPSO SL and Categories are all 1's; 0 otherwise.
+ *
+ * Note: opt starts with "Tag Type":
+ *
+ * |tag_type(1)|tag_length(1)|align(1)|sl(1)|categories(variable)|
+ *
+ */
+static boolean_t
+cipso_high(const uchar_t *opt)
+{
+ int i;
+
+ if (((int)opt[1] + 6) < IP_MAX_OPT_LENGTH)
+ return (B_FALSE);
+ for (i = 0; i < ((int)opt[1] - 3); i++)
+ if (opt[3 + i] != 0xff)
+ return (B_FALSE);
+ return (B_TRUE);
+}
+
+/*
+ * Converts CIPSO label to SL.
+ *
+ * Note: opt starts with "Tag Type":
+ *
+ * |tag_type(1)|tag_length(1)|align(1)|sl(1)|categories(variable)|
+ *
+ */
+static void
+cipso2sl(const uchar_t *opt, bslabel_t *sl, int *high)
+{
+ int i, taglen;
+ uchar_t *q = (uchar_t *)&((_bslabel_impl_t *)sl)->compartments;
+
+ *high = 0;
+ taglen = opt[1];
+ memset((caddr_t)sl, 0, sizeof (bslabel_t));
+
+ if (cipso_high(opt)) {
+ BSLHIGH(sl);
+ *high = 1;
+ } else {
+ LCLASS_SET((_bslabel_impl_t *)sl, opt[3]);
+ for (i = 0; i < taglen - TSOL_TT1_MIN_LENGTH; i++)
+ q[i] = opt[TSOL_TT1_MIN_LENGTH + i];
+ }
+ SETBLTYPE(sl, SUN_SL_ID);
+}
+
+static int
+interpret_cipso_tagtype1(const uchar_t *opt)
+{
+ int i, taglen, ishigh;
+ bslabel_t sl;
+ char line[CIPSO_GENERIC_ARRAY_LEN], *ptr;
+
+ taglen = opt[1];
+ if (taglen < TSOL_TT1_MIN_LENGTH ||
+ taglen > TSOL_TT1_MAX_LENGTH)
+ return (taglen);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Tag Type = %d, Tag Length = %d", opt[0], opt[1]);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Sensitivity Level = 0x%02x", opt[3]);
+ ptr = line;
+ for (i = 0; i < taglen - TSOL_TT1_MIN_LENGTH; i++) {
+ (void) snprintf(ptr, sizeof (line) - (ptr - line), "%02x",
+ opt[TSOL_TT1_MIN_LENGTH + i]);
+ ptr = strchr(ptr, 0);
+ }
+ if (i != 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Categories = ");
+ (void) snprintf(get_line(0, 0), get_line_remain(), "\t%s",
+ line);
+ } else {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Categories = None");
+ }
+ cipso2sl(opt, &sl, &ishigh);
+ if (is_system_labeled()) {
+ if (bsltos(&sl, &plabel, ALABEL_MAXLEN,
+ LONG_CLASSIFICATION|LONG_WORDS|VIEW_INTERNAL) < 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "The Sensitivity Level and Categories can't be "
+ "mapped to a valid SL");
+ } else {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "The Sensitivity Level and Categories are mapped "
+ "to the SL:");
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "\t%s", ascii_label);
+ }
+ }
+ return (taglen);
+}
+
+/*
+ * The following struct definition #define's are copied from TS1.x. They are
+ * not used here (except TTYPE_3_MAX_TOKENS), but included as a reference for
+ * the tag type 3 packet format.
+ */
+#define TTYPE_3_MAX_TOKENS 7
+
+/*
+ * Display CIPSO tag type 3 which is defined by MAXSIX.
+ */
+static int
+interpret_cipso_tagtype3(const uchar_t *opt)
+{
+ uchar_t tagtype;
+ int index, numtokens, taglen;
+ uint16_t mask;
+ uint32_t token;
+ static const char *name[] = {
+ "SL",
+ "NCAV",
+ "INTEG",
+ "SID",
+ "undefined",
+ "undefined",
+ "IL",
+ "PRIVS",
+ "LUID",
+ "PID",
+ "IDS",
+ "ACL"
+ };
+
+ tagtype = *opt++;
+ (void) memcpy(&mask, opt + 3, sizeof (mask));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Tag Type = %d (MAXSIX)", tagtype);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Generation = 0x%02x%02x%02x, Mask = 0x%04x", opt[0], opt[1],
+ opt[2], mask);
+ opt += 3 + sizeof (mask);
+
+ /*
+ * Display tokens
+ */
+ numtokens = 0;
+ index = 0;
+ while (mask != 0 && numtokens < TTYPE_3_MAX_TOKENS) {
+ if (mask & 0x0001) {
+ (void) memcpy(&token, opt, sizeof (token));
+ opt += sizeof (token);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Attribute = %s, Token = 0x%08x",
+ index < sizeof (name) / sizeof (*name) ?
+ name[index] : "unknown", token);
+ numtokens++;
+ }
+ mask = mask >> 1;
+ index++;
+ }
+
+ taglen = 6 + numtokens * 4;
+ return (taglen);
+}
+
+static void
+print_cipso(const uchar_t *opt)
+{
+ int optlen, taglen, tagnum;
+ uint32_t doi;
+ char line[CIPSO_GENERIC_ARRAY_LEN];
+ char *oldnest;
+
+ optlen = opt[1];
+ if (optlen < TSOL_CIPSO_MIN_LENGTH || optlen > TSOL_CIPSO_MAX_LENGTH)
+ return;
+
+ oldnest = prot_nest_prefix;
+ prot_nest_prefix = prot_prefix;
+ show_header("CIPSO: ", "Common IP Security Option", 0);
+ show_space();
+
+ /*
+ * Display CIPSO Header
+ */
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Type = CIPSO (%d), Length = %d", opt[0], opt[1]);
+ (void) memcpy(&doi, opt + 2, sizeof (doi));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Domain of Interpretation = %u", (unsigned)ntohl(doi));
+
+ if (opt[1] == TSOL_CIPSO_MIN_LENGTH) { /* no tags */
+ show_space();
+ prot_prefix = prot_nest_prefix;
+ prot_nest_prefix = oldnest;
+ return;
+ }
+ optlen -= TSOL_CIPSO_MIN_LENGTH;
+ opt += TSOL_CIPSO_MIN_LENGTH;
+
+ /*
+ * Display Each Tag
+ */
+ tagnum = 1;
+ while (optlen >= TSOL_TT1_MIN_LENGTH) {
+ (void) snprintf(line, sizeof (line), "Tag# %d", tagnum);
+ show_header("CIPSO: ", line, 0);
+ /*
+ * We handle tag type 1 and 3 only. Note, tag type 3
+ * is MAXSIX defined.
+ */
+ switch (opt[0]) {
+ case 1:
+ taglen = interpret_cipso_tagtype1(opt);
+ break;
+ case 3:
+ taglen = interpret_cipso_tagtype3(opt);
+ break;
+ default:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Unknown Tag Type %d", opt[0]);
+ show_space();
+ prot_prefix = prot_nest_prefix;
+ prot_nest_prefix = oldnest;
+ return;
+ }
+
+ /*
+ * Move to the next tag
+ */
+ if (taglen <= 0)
+ break;
+ optlen -= taglen;
+ opt += taglen;
+ tagnum++;
+ }
+ show_space();
+ prot_prefix = prot_nest_prefix;
+ prot_nest_prefix = oldnest;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipaddr.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipaddr.c
new file mode 100644
index 0000000..c42c55d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipaddr.c
@@ -0,0 +1,425 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netdb.h>
+#include <string.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <arpa/inet.h>
+#include "snoop.h"
+
+static sigjmp_buf nisjmp;
+
+#define MAXHASH 1024 /* must be a power of 2 */
+
+#define SEPARATORS " \t\n"
+
+struct hostdata {
+ struct hostdata *h_next;
+ char *h_hostname;
+ int h_pktsout;
+ int h_pktsin;
+};
+
+struct hostdata4 {
+ struct hostdata4 *h4_next;
+ char *h4_hostname;
+ int h4_pktsout;
+ int h4_pktsin;
+ struct in_addr h4_addr;
+};
+
+struct hostdata6 {
+ struct hostdata6 *h6_next;
+ char *h6_hostname;
+ int h6_pktsout;
+ int h6_pktsin;
+ struct in6_addr h6_addr;
+};
+
+static struct hostdata *addhost(int, const void *, const char *, char **);
+
+static struct hostdata4 *h_table4[MAXHASH];
+static struct hostdata6 *h_table6[MAXHASH];
+
+#define iphash(e) ((e) & (MAXHASH-1))
+
+/* ARGSUSED */
+static void
+wakeup(int n)
+{
+ siglongjmp(nisjmp, 1);
+}
+
+extern char *inet_ntoa();
+
+static struct hostdata *
+iplookup(struct in_addr ipaddr)
+{
+ register struct hostdata4 *h;
+ struct hostent *hp = NULL;
+ struct netent *np;
+ int error_num;
+ struct hostdata *retval;
+
+ for (h = h_table4[iphash(ipaddr.s_addr)]; h; h = h->h4_next) {
+ if (h->h4_addr.s_addr == ipaddr.s_addr)
+ return ((struct hostdata *)h);
+ }
+
+ /* not found. Put it in */
+
+ if (ipaddr.s_addr == htonl(INADDR_BROADCAST))
+ return (addhost(AF_INET, &ipaddr, "BROADCAST", NULL));
+ if (ipaddr.s_addr == htonl(INADDR_ANY))
+ return (addhost(AF_INET, &ipaddr, "OLD-BROADCAST", NULL));
+
+ /*
+ * Set an alarm here so we don't get held up by
+ * an unresponsive name server.
+ * Give it 3 sec to do its work.
+ */
+ if (! rflg && sigsetjmp(nisjmp, 1) == 0) {
+ (void) snoop_alarm(3, wakeup);
+ hp = getipnodebyaddr((char *)&ipaddr, sizeof (int),
+ AF_INET, &error_num);
+ if (hp == NULL && inet_lnaof(ipaddr) == 0) {
+ np = getnetbyaddr(inet_netof(ipaddr), AF_INET);
+ if (np)
+ return (addhost(AF_INET, &ipaddr, np->n_name,
+ np->n_aliases));
+ }
+ (void) snoop_alarm(0, wakeup);
+ }
+
+ retval = addhost(AF_INET, &ipaddr,
+ hp ? hp->h_name : inet_ntoa(ipaddr),
+ hp ? hp->h_aliases : NULL);
+ if (hp != NULL)
+ freehostent(hp);
+ return (retval);
+}
+
+static struct hostdata *
+ip6lookup(const struct in6_addr *ip6addr)
+{
+ struct hostdata6 *h;
+ struct hostent *hp = NULL;
+ int error_num;
+ char addrstr[INET6_ADDRSTRLEN];
+ char *addname;
+ struct hostdata *retval;
+
+ for (h = h_table6[iphash(((uint32_t *)ip6addr)[3])]; h;
+ h = h->h6_next) {
+ if (IN6_ARE_ADDR_EQUAL(&h->h6_addr, ip6addr))
+ return ((struct hostdata *)h);
+ }
+
+ /* not in the hash table, put it in */
+ if (IN6_IS_ADDR_UNSPECIFIED(ip6addr))
+ return (addhost(AF_INET6, ip6addr, "UNSPECIFIED", NULL));
+
+ /*
+ * Set an alarm here so we don't get held up by
+ * an unresponsive name server.
+ * Give it 3 sec to do its work.
+ */
+ if (! rflg && sigsetjmp(nisjmp, 1) == 0) {
+ (void) snoop_alarm(3, wakeup);
+ hp = getipnodebyaddr(ip6addr, sizeof (struct in6_addr),
+ AF_INET6, &error_num);
+ (void) snoop_alarm(0, wakeup);
+ } else {
+ hp = NULL;
+ }
+
+ if (hp != NULL)
+ addname = hp->h_name;
+ else {
+ (void) inet_ntop(AF_INET6, ip6addr, addrstr, INET6_ADDRSTRLEN);
+ addname = addrstr;
+ }
+
+ retval = addhost(AF_INET6, ip6addr, addname, hp ? hp->h_aliases : NULL);
+ if (hp != NULL)
+ freehostent(hp);
+ return (retval);
+}
+
+static struct hostdata *
+addhost(int family, const void *ipaddr, const char *name, char **aliases)
+{
+ struct hostdata **hp, *n = NULL;
+ extern FILE *namefile;
+ int hashval;
+ static char aname[128];
+ char *np;
+ static struct hostdata h;
+ int ind;
+
+ switch (family) {
+ case AF_INET:
+ n = (struct hostdata *)malloc(sizeof (struct hostdata4));
+ if (n == NULL)
+ goto alloc_failed;
+
+ memset(n, 0, sizeof (struct hostdata4));
+ n->h_hostname = strdup(name);
+ if (n->h_hostname == NULL)
+ goto alloc_failed;
+
+ ((struct hostdata4 *)n)->h4_addr =
+ *(const struct in_addr *)ipaddr;
+ hashval = ((struct in_addr *)ipaddr)->s_addr;
+ hp = (struct hostdata **)&h_table4[iphash(hashval)];
+ break;
+ case AF_INET6:
+ n = (struct hostdata *)malloc(sizeof (struct hostdata6));
+ if (n == NULL)
+ goto alloc_failed;
+
+ memset(n, 0, sizeof (struct hostdata6));
+ n->h_hostname = strdup(name);
+ if (n->h_hostname == NULL)
+ goto alloc_failed;
+
+ memcpy(&((struct hostdata6 *)n)->h6_addr, ipaddr,
+ sizeof (struct in6_addr));
+ hashval = ((const int *)ipaddr)[3];
+ hp = (struct hostdata **)&h_table6[iphash(hashval)];
+ break;
+ default:
+ fprintf(stderr, "snoop: ERROR: Unknown address family: %d",
+ family);
+ exit(1);
+ }
+
+ n->h_next = *hp;
+ *hp = n;
+
+ if (namefile != NULL) {
+ if (family == AF_INET) {
+ np = inet_ntoa(*(const struct in_addr *)ipaddr);
+ if (np) {
+ (void) fprintf(namefile, "%s\t%s", np, name);
+ if (aliases) {
+ for (ind = 0;
+ aliases[ind] != NULL;
+ ind++) {
+ (void) fprintf(namefile, " %s",
+ aliases[ind]);
+ }
+ }
+ (void) fprintf(namefile, "\n");
+ }
+ } else if (family == AF_INET6) {
+ np = (char *)inet_ntop(AF_INET6, (void *)ipaddr, aname,
+ sizeof (aname));
+ if (np) {
+ (void) fprintf(namefile, "%s\t%s", np, name);
+ if (aliases) {
+ for (ind = 0;
+ aliases[ind] != NULL;
+ ind++) {
+ (void) fprintf(namefile, " %s",
+ aliases[ind]);
+ }
+ }
+ (void) fprintf(namefile, "\n");
+ }
+ } else {
+ (void) fprintf(stderr, "addhost: unknown family %d\n",
+ family);
+ }
+ }
+ return (n);
+
+alloc_failed:
+ if (n)
+ free(n);
+ (void) fprintf(stderr, "addhost: no mem\n");
+
+ aname[0] = '\0';
+ memset(&h, 0, sizeof (struct hostdata));
+ h.h_hostname = aname;
+ return (&h);
+}
+
+char *
+addrtoname(int family, const void *ipaddr)
+{
+ switch (family) {
+ case AF_INET:
+ return (iplookup(*(const struct in_addr *)ipaddr)->h_hostname);
+ case AF_INET6:
+ return (ip6lookup((const struct in6_addr *)ipaddr)->h_hostname);
+ }
+ (void) fprintf(stderr, "snoop: ERROR: unknown address family: %d\n",
+ family);
+ exit(1);
+ /* NOTREACHED */
+}
+
+void
+load_names(fname)
+ char *fname;
+{
+ char buf[1024];
+ char *addr, *name, *alias;
+ FILE *f;
+ unsigned int addrv4;
+ struct in6_addr addrv6;
+ int family;
+ void *naddr;
+
+ (void) fprintf(stderr, "Loading name file %s\n", fname);
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ perror(fname);
+ return;
+ }
+
+ while (fgets(buf, 1024, f) != NULL) {
+ addr = strtok(buf, SEPARATORS);
+ if (addr == NULL || *addr == '#')
+ continue;
+ if (inet_pton(AF_INET6, addr, (void *)&addrv6) == 1) {
+ family = AF_INET6;
+ naddr = (void *)&addrv6;
+ } else if ((addrv4 = inet_addr(addr)) != (ulong_t)-1) {
+ family = AF_INET;
+ naddr = (void *)&addrv4;
+ }
+ name = strtok(NULL, SEPARATORS);
+ if (name == NULL)
+ continue;
+ while ((alias = strtok(NULL, SEPARATORS)) != NULL &&
+ (*alias != '#')) {
+ (void) addhost(family, naddr, alias, NULL);
+ }
+ (void) addhost(family, naddr, name, NULL);
+ /* Note: certain addresses such as broadcast are skipped */
+ }
+
+ (void) fclose(f);
+}
+
+/*
+ * lgetipnodebyname: looks up hostname in cached address data. This allows
+ * filtering on hostnames from the .names file to work properly, and
+ * avoids name clashes between domains. Note that only the first of the
+ * ipv4, ipv6, or v4mapped address will be returned, because the
+ * cache does not contain information on multi-homed hosts.
+ */
+/*ARGSUSED*/
+struct hostent *
+lgetipnodebyname(const char *name, int af, int flags, int *error_num)
+{
+ int i;
+ struct hostdata4 *h;
+ struct hostdata6 *h6;
+ static struct hostent he; /* host entry */
+ static struct in6_addr h46_addr[MAXADDRS]; /* v4mapped address */
+ static char h_name[MAXHOSTNAMELEN]; /* hostname */
+ static char *list[MAXADDRS]; /* addr_list array */
+ struct hostent *hp = &he;
+ int ind;
+
+ (void) memset((char *)hp, 0, sizeof (struct hostent));
+ hp->h_name = h_name;
+ h_name[0] = '\0';
+ strcpy(h_name, name);
+
+ hp->h_addrtype = AF_INET6;
+
+ hp->h_addr_list = list;
+ for (i = 0; i < MAXADDRS; i++)
+ hp->h_addr_list[i] = NULL;
+ ind = 0;
+
+ /* ipv6 lookup */
+ if (af == AF_INET6) {
+ hp->h_length = sizeof (struct in6_addr);
+ for (i = 0; i < MAXHASH; i++) {
+ for (h6 = h_table6[i]; h6; h6 = h6->h6_next) {
+ if (strcmp(name, h6->h6_hostname) == 0) {
+ if (ind >= MAXADDRS - 1) {
+ /* too many addresses */
+ return (hp);
+ }
+ /* found ipv6 addr */
+ hp->h_addr_list[ind] =
+ (char *)&h6->h6_addr;
+ ind++;
+ }
+ }
+ }
+ }
+ /* ipv4 or v4mapped lookup */
+ if (af == AF_INET || (flags & AI_ALL)) {
+ for (i = 0; i < MAXHASH; i++) {
+ for (h = h_table4[i]; h; h = h->h4_next) {
+ if (strcmp(name, h->h4_hostname) == 0) {
+ if (ind >= MAXADDRS - 1) {
+ /* too many addresses */
+ return (hp);
+ }
+ if (af == AF_INET) {
+ /* found ipv4 addr */
+ hp->h_addrtype = AF_INET;
+ hp->h_length =
+ sizeof (struct in_addr);
+ hp->h_addr_list[ind] =
+ (char *)&h->h4_addr;
+ ind++;
+ } else {
+ /* found v4mapped addr */
+ hp->h_length =
+ sizeof (struct in6_addr);
+ hp->h_addr_list[ind] =
+ (char *)&h46_addr[ind];
+ IN6_INADDR_TO_V4MAPPED(
+ &h->h4_addr,
+ &h46_addr[ind]);
+ ind++;
+ }
+ }
+ }
+ }
+ }
+ return (ind > 0 ? hp : NULL);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipsec.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipsec.c
new file mode 100644
index 0000000..9212cbc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ipsec.c
@@ -0,0 +1,243 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/time.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+#include <netinet/if_ether.h>
+#include <inet/ipsecesp.h>
+#include <inet/ipsecah.h>
+#include "snoop.h"
+
+/* ARGSUSED */
+int
+interpret_esp(int flags, uint8_t *hdr, int iplen, int fraglen)
+{
+ /* LINTED: alignment */
+ esph_t *esph = (esph_t *)hdr;
+ esph_t *aligned_esph;
+ esph_t storage; /* In case hdr isn't aligned. */
+ char *line;
+
+ if (fraglen < sizeof (esph_t))
+ return (fraglen); /* incomplete header */
+
+ if (!IS_P2ALIGNED(hdr, 4)) {
+ aligned_esph = &storage;
+ bcopy(hdr, aligned_esph, sizeof (esph_t));
+ } else {
+ aligned_esph = esph;
+ }
+
+ if (flags & F_SUM) {
+ line = (char *)get_sum_line();
+ /*
+ * sprintf() is safe because line guarantees us 80 columns,
+ * and SPI and replay certainly won't exceed that.
+ */
+ (void) sprintf(line, "ESP SPI=0x%x Replay=%u",
+ ntohl(aligned_esph->esph_spi),
+ ntohl(aligned_esph->esph_replay));
+ line += strlen(line);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ESP: ", "Encapsulating Security Payload",
+ sizeof (esph_t));
+ show_space();
+ /*
+ * sprintf() is safe because get_line guarantees us 80 columns,
+ * and SPI and replay certainly won't exceed that.
+ */
+ (void) sprintf(get_line((char *)&esph->esph_spi - dlc_header,
+ 4), "SPI = 0x%x", ntohl(aligned_esph->esph_spi));
+ (void) sprintf(get_line((char *)&esph->esph_replay -
+ dlc_header, 4), "Replay = %u",
+ ntohl(aligned_esph->esph_replay));
+ (void) sprintf(get_line((char *)(esph + 1) - dlc_header,
+ 4), " ....ENCRYPTED DATA....");
+ }
+
+ return (sizeof (esph_t));
+}
+
+int
+interpret_ah(int flags, uint8_t *hdr, int iplen, int fraglen)
+{
+ /* LINTED: alignment */
+ ah_t *ah = (ah_t *)hdr;
+ ah_t *aligned_ah;
+ ah_t storage; /* In case hdr isn't aligned. */
+ char *line, *buff;
+ uint_t ahlen, auth_data_len;
+ uint8_t *auth_data, *data;
+ int new_iplen;
+ uint8_t proto;
+
+ if (fraglen < sizeof (ah_t))
+ return (fraglen); /* incomplete header */
+
+ if (!IS_P2ALIGNED(hdr, 4)) {
+ aligned_ah = (ah_t *)&storage;
+ bcopy(hdr, &storage, sizeof (ah_t));
+ } else {
+ aligned_ah = ah;
+ }
+
+ /*
+ * "+ 8" is for the "constant" part that's not included in the AH
+ * length.
+ *
+ * The AH RFC specifies the length field in "length in 4-byte units,
+ * not counting the first 8 bytes". So if an AH is 24 bytes long,
+ * the length field will contain "4". (4 * 4 + 8 == 24).
+ */
+ ahlen = (aligned_ah->ah_length << 2) + 8;
+ fraglen -= ahlen;
+ if (fraglen < 0)
+ return (fraglen + ahlen); /* incomplete header */
+
+ auth_data_len = ahlen - sizeof (ah_t);
+ auth_data = (uint8_t *)(ah + 1);
+ data = auth_data + auth_data_len;
+
+ if (flags & F_SUM) {
+ line = (char *)get_sum_line();
+ (void) sprintf(line, "AH SPI=0x%x Replay=%u",
+ ntohl(aligned_ah->ah_spi), ntohl(aligned_ah->ah_replay));
+ line += strlen(line);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("AH: ", "Authentication Header", ahlen);
+ show_space();
+ (void) sprintf(get_line((char *)&ah->ah_nexthdr - dlc_header,
+ 1), "Next header = %d (%s)", aligned_ah->ah_nexthdr,
+ getproto(aligned_ah->ah_nexthdr));
+ (void) sprintf(get_line((char *)&ah->ah_length - dlc_header, 1),
+ "AH length = %d (%d bytes)", aligned_ah->ah_length, ahlen);
+ (void) sprintf(get_line((char *)&ah->ah_reserved - dlc_header,
+ 2), "<Reserved field = 0x%x>",
+ ntohs(aligned_ah->ah_reserved));
+ (void) sprintf(get_line((char *)&ah->ah_spi - dlc_header, 4),
+ "SPI = 0x%x", ntohl(aligned_ah->ah_spi));
+ (void) sprintf(get_line((char *)&ah->ah_replay - dlc_header, 4),
+ "Replay = %u", ntohl(aligned_ah->ah_replay));
+
+ /*
+ * 2 for two hex digits per auth_data byte
+ * plus one byte for trailing null byte.
+ */
+ buff = malloc(auth_data_len * 2 + 1);
+ if (buff != NULL) {
+ int i;
+
+ for (i = 0; i < auth_data_len; i++)
+ sprintf(buff + i * 2, "%02x", auth_data[i]);
+ }
+
+ (void) sprintf(get_line((char *)auth_data - dlc_header,
+ auth_data_len), "ICV = %s",
+ (buff == NULL) ? "<out of memory>" : buff);
+
+ /* malloc(3c) says I can call free even if buff == NULL */
+ free(buff);
+
+ show_space();
+ }
+
+ new_iplen = iplen - ahlen;
+ proto = aligned_ah->ah_nexthdr;
+
+ /*
+ * Print IPv6 Extension Headers, or skip them in the summary case.
+ */
+ if (proto == IPPROTO_HOPOPTS || proto == IPPROTO_DSTOPTS ||
+ proto == IPPROTO_ROUTING || proto == IPPROTO_FRAGMENT) {
+ (void) print_ipv6_extensions(flags, &data, &proto, &iplen,
+ &fraglen);
+ }
+
+ if (fraglen > 0)
+ switch (proto) {
+ case IPPROTO_ENCAP:
+ /* LINTED: alignment */
+ (void) interpret_ip(flags, (struct ip *)data,
+ new_iplen);
+ break;
+ case IPPROTO_IPV6:
+ (void) interpret_ipv6(flags, (ip6_t *)data,
+ new_iplen);
+ break;
+ case IPPROTO_ICMP:
+ (void) interpret_icmp(flags,
+ /* LINTED: alignment */
+ (struct icmp *)data, new_iplen, fraglen);
+ break;
+ case IPPROTO_ICMPV6:
+ /* LINTED: alignment */
+ (void) interpret_icmpv6(flags, (icmp6_t *)data,
+ new_iplen, fraglen);
+ break;
+ case IPPROTO_TCP:
+ (void) interpret_tcp(flags,
+ (struct tcphdr *)data, new_iplen, fraglen);
+ break;
+
+ case IPPROTO_ESP:
+ (void) interpret_esp(flags, data, new_iplen,
+ fraglen);
+ break;
+
+ case IPPROTO_AH:
+ (void) interpret_ah(flags, data, new_iplen,
+ fraglen);
+ break;
+
+ case IPPROTO_UDP:
+ (void) interpret_udp(flags,
+ (struct udphdr *)data, new_iplen, fraglen);
+ break;
+ /* default case is to not print anything else */
+ }
+
+ return (ahlen);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_isis.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_isis.c
new file mode 100644
index 0000000..be7cae5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_isis.c
@@ -0,0 +1,99 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ethernet.h>
+#include <sys/vlan.h>
+#include <net/trill.h>
+
+#include <snoop.h>
+
+#define PDUTYPE_OFFSET 4
+#define PDUTYPE_HELLO1 15
+#define PDUTYPE_HELLO2 16
+#define PDUTYPE_HELLOP2P 17
+#define PDUTYPE_LSP1 18
+#define PDUTYPE_LSP2 20
+#define PDUTYPE_CSN1 24
+#define PDUTYPE_CSN2 25
+#define PDUTYPE_PSN1 26
+#define PDUTYPE_PSN2 27
+
+int
+interpret_isis(int flags, char *data, int dlen, boolean_t istrill)
+{
+ uint8_t pdutypenum;
+ char *pdutype;
+
+ pdutypenum = *(data+ PDUTYPE_OFFSET);
+ switch (pdutypenum) {
+ case PDUTYPE_HELLO1:
+ case PDUTYPE_HELLO2:
+ pdutype = "Hello";
+ break;
+ case PDUTYPE_HELLOP2P:
+ pdutype = "P2P Hello";
+ break;
+ case PDUTYPE_LSP1:
+ case PDUTYPE_LSP2:
+ pdutype = "Link State";
+ break;
+ case PDUTYPE_CSN1:
+ case PDUTYPE_CSN2:
+ pdutype = "CSN";
+ break;
+ case PDUTYPE_PSN1:
+ case PDUTYPE_PSN2:
+ pdutype = "PSN";
+ break;
+ default:
+ pdutype = "Unknown";
+ break;
+ }
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "%s %s L:%d", istrill ? "Core TRILL IS-IS" : "IS-IS",
+ pdutype, dlen);
+ }
+
+ if (flags & F_DTAIL) {
+ if (istrill) {
+ show_header("TRILL-IS-IS: ",
+ "Core TRILL IS-IS Frame", dlen);
+ } else {
+ show_header("IS-IS: ",
+ "IS-IS Frame", dlen);
+ }
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Frame type = %02X (%s)", pdutypenum, pdutype);
+ show_trailer();
+ }
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ldap.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ldap.c
new file mode 100644
index 0000000..2877ad7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ldap.c
@@ -0,0 +1,1470 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <netinet/udp.h>
+#include "snoop.h"
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+extern char *src_name;
+extern char *dst_name;
+#define MAX_CTX (10)
+#define LINE_LEN (255)
+#define BUF_SIZE (16000)
+static int ldap = 0; /* flag to control initialization */
+struct ctx {
+ int src;
+ int dst;
+ char *src_name;
+ char *dst_name;
+};
+char *osibuff = NULL;
+int osilen = 0;
+char scrbuffer[BUF_SIZE]; /* buffer to accumulate data until a */
+ /* complete LDAPmessage is received */
+char resultcode[LINE_LEN]; /* These are used */
+char operation[LINE_LEN]; /* by -V option. */
+char bb[LINE_LEN];
+
+int gi_osibuf[MAX_CTX];
+int otyp[MAX_CTX];
+int olen[MAX_CTX];
+int level[MAX_CTX];
+
+void decode_ldap(char *buf, int len);
+
+#define X unsigned char
+typedef X * A;
+#define INT(a) ((int)(a))
+#define SCRUB (void) strcat(scrbuffer, bb);
+
+static X hex; /* input hex octet */
+static A *PTRaclass; /* application tag table pointer */
+
+/*
+ * ASN.1 Message Printing Macros
+ */
+
+#define asnshw1(a) {(void)sprintf(bb, a); SCRUB }
+#define asnshw2(a, b) {(void)sprintf(bb, a, b); SCRUB }
+#define asnshw3(a, b, c) {(void)sprintf(bb, a, b, c); SCRUB }
+#define asnshw4(a, b, c, d) {(void)sprintf(bb, a, b, c, d); SCRUB }
+#define asnshw5(a, b, c, d, e) {(void)sprintf(bb, a, b, c, d, e); SCRUB }
+
+/*
+ * Local Types And Variables
+ */
+
+/*
+ * Object identifier oid to name mapping description type
+ */
+
+typedef struct {
+ A oidname; /* object identifier string name */
+ X oidcode[16]; /* object identifier hexa code */
+} oidelmT;
+typedef oidelmT *oidelmTp;
+
+/*
+ * Snoop's entry point to ldap decoding
+ */
+
+void
+interpret_ldap(flags, data, fraglen, src, dst)
+int flags;
+char *data;
+int fraglen;
+int src;
+int dst;
+{
+
+ if (!ldap) {
+ init_ldap();
+ ldap = 1;
+ }
+
+ (void) decode_ldap(data, fraglen);
+
+ if (flags & F_DTAIL) {
+ /* i.e. when snoop is run with -v (verbose) */
+ show_header("LDAP: ",
+ "Lightweight Directory Access Protocol Header", fraglen);
+ show_space();
+ printf("%s", scrbuffer);
+ }
+
+ if (flags & F_SUM) {
+ /* i.e. when snoop is run with -V (summary) */
+ (void) strcpy(data, "");
+
+ if (strlen(operation) != 0) {
+ (void) strcat(data, " ");
+ (void) strncat(data, operation, 30);
+ (void) strcpy(operation, "");
+ }
+
+ if (strlen(resultcode) != 0) {
+ (void) strcat(data, " ");
+ (void) strncat(data, resultcode, 30);
+ (void) strcpy(resultcode, "");
+ }
+
+ if (dst == 389) {
+ (void) sprintf(get_sum_line(),
+ "LDAP C port=%d%s", src, data);
+ }
+ if (src == 389) {
+ (void) sprintf(get_sum_line(),
+ "LDAP R port=%d%s", dst, data);
+ }
+ }
+
+ (void) strcpy(scrbuffer, "");
+}
+
+/*
+ * Known object identifiers: customize to add your own oids
+ */
+
+static oidelmT OidTab[] = {
+/*
+ * X.500 Standardized Attribute Types
+ */
+{(A)"ObjectClass", { 0x03, 0x55, 0x04, 0x00 }},
+{(A)"AliasObjectName", { 0x03, 0x55, 0x04, 0x01 }},
+{(A)"KnowledgeInfo", { 0x03, 0x55, 0x04, 0x02 }},
+{(A)"CommonName", { 0x03, 0x55, 0x04, 0x03 }},
+{(A)"Surname", { 0x03, 0x55, 0x04, 0x04 }},
+{(A)"SerialNumber", { 0x03, 0x55, 0x04, 0x05 }},
+{(A)"CountryName", { 0x03, 0x55, 0x04, 0x06 }},
+{(A)"LocalityName", { 0x03, 0x55, 0x04, 0x07 }},
+{(A)"StateOrProvinceName", { 0x03, 0x55, 0x04, 0x08 }},
+{(A)"StreetAddress", { 0x03, 0x55, 0x04, 0x09 }},
+{(A)"OrganizationName", { 0x03, 0x55, 0x04, 0x0a }},
+{(A)"OrganizationUnitName", { 0x03, 0x55, 0x04, 0x0b }},
+{(A)"Title", { 0x03, 0x55, 0x04, 0x0c }},
+{(A)"Description", { 0x03, 0x55, 0x04, 0x0d }},
+{(A)"SearchGuide", { 0x03, 0x55, 0x04, 0x0e }},
+{(A)"BusinessCategory", { 0x03, 0x55, 0x04, 0x0f }},
+{(A)"PostalAddress", { 0x03, 0x55, 0x04, 0x10 }},
+{(A)"PostalCode", { 0x03, 0x55, 0x04, 0x11 }},
+{(A)"PostOfficeBox", { 0x03, 0x55, 0x04, 0x12 }},
+{(A)"PhysicalDeliveryOffice", { 0x03, 0x55, 0x04, 0x13 }},
+{(A)"TelephoneNUmber", { 0x03, 0x55, 0x04, 0x14 }},
+{(A)"TelexNumber", { 0x03, 0x55, 0x04, 0x15 }},
+{(A)"TeletexTerminalId", { 0x03, 0x55, 0x04, 0x16 }},
+{(A)"FaxTelephoneNumber", { 0x03, 0x55, 0x04, 0x17 }},
+{(A)"X121Address", { 0x03, 0x55, 0x04, 0x18 }},
+{(A)"IsdnAddress", { 0x03, 0x55, 0x04, 0x19 }},
+{(A)"RegisteredAddress", { 0x03, 0x55, 0x04, 0x1a }},
+{(A)"DestinationIndicator", { 0x03, 0x55, 0x04, 0x1b }},
+{(A)"PreferDeliveryMethod", { 0x03, 0x55, 0x04, 0x1c }},
+{(A)"PresentationAddress", { 0x03, 0x55, 0x04, 0x1d }},
+{(A)"SupportedApplContext", { 0x03, 0x55, 0x04, 0x1e }},
+{(A)"Member", { 0x03, 0x55, 0x04, 0x1f }},
+{(A)"Owner", { 0x03, 0x55, 0x04, 0x20 }},
+{(A)"RoleOccupant", { 0x03, 0x55, 0x04, 0x21 }},
+{(A)"SeeAlso", { 0x03, 0x55, 0x04, 0x22 }},
+{(A)"Password", { 0x03, 0x55, 0x04, 0x23 }},
+{(A)"UserCertificate", { 0x03, 0x55, 0x04, 0x24 }},
+{(A)"CaCertificate", { 0x03, 0x55, 0x04, 0x25 }},
+{(A)"AuthorityRevList", { 0x03, 0x55, 0x04, 0x26 }},
+{(A)"CertificateRevList", { 0x03, 0x55, 0x04, 0x27 }},
+{(A)"CrossCertificatePair", { 0x03, 0x55, 0x04, 0x28 }},
+
+/*
+ * X.500 Standardized Object Classes
+ */
+{(A)"Top", { 0x03, 0x55, 0x06, 0x00 }},
+{(A)"Alias", { 0x03, 0x55, 0x06, 0x01 }},
+{(A)"Country", { 0x03, 0x55, 0x06, 0x02 }},
+{(A)"Locality", { 0x03, 0x55, 0x06, 0x03 }},
+{(A)"Organization", { 0x03, 0x55, 0x06, 0x04 }},
+{(A)"OrganizationUnit", { 0x03, 0x55, 0x06, 0x05 }},
+{(A)"Person", { 0x03, 0x55, 0x06, 0x06 }},
+{(A)"OrganizationPersion", { 0x03, 0x55, 0x06, 0x07 }},
+{(A)"OrganizationRole", { 0x03, 0x55, 0x06, 0x08 }},
+{(A)"Group", { 0x03, 0x55, 0x06, 0x09 }},
+{(A)"ResidentialPerson", { 0x03, 0x55, 0x06, 0x0A }},
+{(A)"ApplicationProcess", { 0x03, 0x55, 0x06, 0x0B }},
+{(A)"ApplicationEntity", { 0x03, 0x55, 0x06, 0x0C }},
+{(A)"Dsa", { 0x03, 0x55, 0x06, 0x0D }},
+{(A)"Device", { 0x03, 0x55, 0x06, 0x0E }},
+{(A)"StrongAuthenticUser", { 0x03, 0x55, 0x06, 0x0F }},
+{(A)"CaAuthority", { 0x03, 0x55, 0x06, 0x10 }},
+
+/*
+ * ACSE Protocol Object Identifiers
+ */
+{(A)"Asn1BER-TS", { 0x02, 0x51, 0x01 }},
+{(A)"Private-TS", { 0x06, 0x2b, 0xce, 0x06, 0x01, 0x04, 0x06 }},
+{(A)"ACSE-AS", { 0x04, 0x52, 0x01, 0x00, 0x01 }},
+
+/*
+ * Directory Protocol Oids
+ */
+{(A)"DirAccess-AC", { 0x03, 0x55, 0x03, 0x01 }},
+{(A)"DirSystem-AC", { 0x03, 0x55, 0x03, 0x02 }},
+
+{(A)"DirAccess-AS", { 0x03, 0x55, 0x09, 0x01 }},
+{(A)"DirSystem-AS", { 0x03, 0x55, 0x09, 0x02 }},
+
+/*
+ * and add your private object identifiers here ...
+ */
+};
+
+#define OIDNB (sizeof (OidTab) / sizeof (oidelmT)) /* total oid nb */
+
+/*
+ * asn.1 tag class definition
+ */
+
+static A class[] = { /* tag class */
+ (A)"UNIV ",
+ (A)"APPL ",
+ (A)"CTXs ",
+ (A)"PRIV "
+};
+
+/*
+ * universal tag definition
+ */
+
+static A uclass[] = { /* universal tag assignment */
+(A)"EndOfContents", /* 0 */
+(A)"Boolean", /* 1 */
+(A)"Integer", /* 2 */
+(A)"BitString", /* 3 */
+(A)"OctetString", /* 4 */
+(A)"Null", /* 5 */
+(A)"Oid", /* 6 */
+(A)"ObjDescriptor", /* 7 */
+(A)"External", /* 8 */
+(A)"Real", /* 9 */
+(A)"Enumerated", /* 10 */
+(A)"Reserved", /* 11 */
+(A)"Reserved", /* 12 */
+(A)"Reserved", /* 13 */
+(A)"Reserved", /* 14 */
+(A)"Reserved", /* 15 */
+(A)"Sequence", /* 16 */
+(A)"Set", /* 17 */
+(A)"NumericString", /* 18 */
+(A)"PrintableString", /* 19 */
+(A)"T.61String", /* 20 */
+(A)"VideotexString", /* 21 */
+(A)"IA5String", /* 22 */
+(A)"UTCTime", /* 23 */
+(A)"GeneralizedTime", /* 24 */
+(A)"GraphicString", /* 25 */
+(A)"VisibleString", /* 26 */
+(A)"GeneralString", /* 27 */
+(A)"Reserved", /* 28 */
+(A)"Reserved", /* 29 */
+(A)"Reserved", /* 30 */
+(A)"Reserved" /* 31 */
+};
+
+static A MHSaclass[] = { /* mhs application tag assignment */
+(A)"Bind Request", /* 0 */
+(A)"Bind Response",
+(A)"Unbind Request",
+(A)"Search Request",
+(A)"Search ResEntry",
+(A)"Search ResDone", /* 5 */
+(A)"Modify Request",
+(A)"Modify Response",
+(A)"Add Request",
+(A)"Add Response", /* 9 */
+(A)"Del Request",
+(A)"Del Response",
+(A)"ModDN Request",
+(A)"ModDN Response",
+(A)"Compare Request", /* 14 */
+(A)"Compare Response",
+(A)"Abandon Request",
+(A)"", /* 17 */
+(A)"", /* 18 */
+(A)"Search ResRef", /* 19 */
+(A)"", /* 20 */
+(A)"", /* 21 */
+(A)"", /* 22 */
+(A)"Extended Request",
+(A)"Extended Response",
+(A)"", /* 25 */
+(A)"", /* 26 */
+(A)"", /* 27 */
+(A)"", /* 28 */
+(A)"", /* 29 */
+(A)"", /* 30 */
+(A)"" /* 31 */
+};
+
+
+static A DFTaclass[] = { /* Default Application Tag Assignment */
+(A)"", /* 0 */
+(A)"", /* 1 */
+(A)"", /* 2 */
+(A)"", /* 3 */
+(A)"", /* 4 */
+(A)"", /* 5 */
+(A)"", /* 6 */
+(A)"", /* 7 */
+(A)"", /* 8 */
+(A)"", /* 9 */
+(A)"", /* 10 */
+(A)"", /* 11 */
+(A)"", /* 12 */
+(A)"", /* 13 */
+(A)"", /* 14 */
+(A)"", /* 15 */
+(A)"", /* 16 */
+(A)"", /* 17 */
+(A)"", /* 18 */
+(A)"", /* 19 */
+(A)"", /* 20 */
+(A)"", /* 21 */
+(A)"", /* 22 */
+(A)"", /* 23 */
+(A)"", /* 24 */
+(A)"", /* 25 */
+(A)"", /* 26 */
+(A)"", /* 27 */
+(A)"", /* 28 */
+(A)"", /* 29 */
+(A)"", /* 30 */
+(A)"" /* 31 */
+};
+
+typedef struct asndefS {
+char *name;
+int type;
+int application;
+int nbson;
+struct {
+ char *sonname;
+ struct asndefS *sondef;
+ long tag;
+ } son[50];
+} asndefT, * asndefTp;
+
+#define SEQUENCE 0x0002
+#define SEQUENCEOF 0x0003
+#define SET 0x0004
+#define PRINTABLE 0x0008
+#define ENUM 0x0010
+#define BITSTRING 0x0020
+#define EXTENSION 0x0040
+#define CONTENTTYPE 0x0080
+#define CONTENT 0x0100
+#define CHOICE 0x0200
+
+static asndefT RTSpasswd = { "RTS Authentification data", SET, -1, 2, {
+ {"MTA Name", 0, 0},
+ {"MTA Password", 0, 1}}};
+static asndefT RTSudata = { "RTS User data", SET, -1, 1, {
+ {0, &RTSpasswd, 1}}};
+
+static asndefT baseObject = {"Base Object", PRINTABLE, -1, 0, {0}};
+
+static asndefT scope = {"Scope", ENUM, -1, 3, {
+ {"BaseObject", 0, 0},
+ {"singleLevel", 0, 1},
+ {"wholeSubtree", 0, 2}}};
+
+static asndefT derefAliases = {"DerefAliases", ENUM, -1, 4, {
+ {"neverDerefAliases", 0, 0},
+ {"derefInSearching", 0, 1},
+ {"derefFindingBaseObj", 0, 2},
+ {"derefAlways", 0, 3}}};
+
+static asndefT filter;
+static asndefT and = {"And", SET, -1, 1, {
+ {0, &filter, -1}}};
+static asndefT or = {"Or", SET, -1, 1, {
+ {0, &filter, -1}}};
+static asndefT not = {"Not", SET, -1, 1, {
+ {0, &filter, -1}}};
+static asndefT equalityMatch = {"Equality Match", SEQUENCE, -1, 2, {
+ {"Attr Descr", 0, -1},
+ {"Value", 0, -1}}};
+static asndefT substrings = {"Substring", SEQUENCE, -1, 2, {
+ {"Type", 0, -1},
+ {"Substrings (initial)", 0, 0},
+ {"Substrings (any)", 0, 1},
+ {"Substring (final)", 0, 2}}};
+static asndefT greaterOrEqual = {"Greater Or Equal", SEQUENCE, -1, 2, {
+ {"Attr Descr", 0, -1},
+ {"Value", 0, -1}}};
+static asndefT lessOrEqual = {"Less Or Equal", SEQUENCE, -1, 2, {
+ {"Attr Descr", 0, -1},
+ {"Value", 0, -1}}};
+static asndefT approxMatch = {"Approx Match", SEQUENCE, -1, 2, {
+ {"Attr Descr", 0, -1},
+ {"Value", 0, -1}}};
+static asndefT extensibleMatch = {"Extensible Match", SEQUENCE, -1, 4, {
+ {"MatchingRule", 0, 1},
+ {"Type", 0, 2},
+ {"MatchValue", 0, 3},
+ {"dnAttributes", 0, 4}}};
+
+static asndefT filter = {"Filter", CHOICE, -1, 10, {
+ {0, &and, 0},
+ {0, &or, 1},
+ {0, &not, 2},
+ {0, &equalityMatch, 3},
+ {0, &substrings, 4},
+ {0, &greaterOrEqual, 5},
+ {0, &lessOrEqual, 6},
+ {"Filter: Present", 0, 7},
+ {0, &approxMatch, 8},
+ {0, &extensibleMatch, 9}}};
+
+static asndefT attributedescription = \
+ {"Attribute Description", PRINTABLE, -1, 0, {0}};
+static asndefT attributes = {"Attribute List", SEQUENCEOF, -1, 1, {
+ {0, &attributedescription, -1}}};
+
+static asndefT searchRequest = {"Operation", SEQUENCE, 3, 8, {
+ {0, &baseObject, -1},
+ {0, &scope, -1},
+ {0, &derefAliases, -1},
+ {"SizeLimit", 0, -1},
+ {"TimeLimit", 0, -1},
+ {"TypesOnly", 0, -1},
+ {0, &filter, -1},
+ {0, &attributes, -1}}};
+
+static asndefT objectName = {"Object Name", PRINTABLE, -1, 0, {0}};
+
+static asndefT ldapEntry = {"Entry", PRINTABLE, -1, 0, {0}};
+static asndefT relativeLdapEntry = \
+ {"Relative LDAP Entry", PRINTABLE, -1, 0, {0}};
+static asndefT newSuperior = {"New Superior", PRINTABLE, -1, 0, {0}};
+
+static asndefT vals = {"Vals", SET, -1, 1, {
+ {"Value", 0, -1}}};
+
+static asndefT attribute = {"Attribute", SEQUENCE, -1, 2, {
+ {"Type", 0, -1},
+ {0, &vals, -1}}};
+
+static asndefT partialAttributes = {"Partial Attributes", SEQUENCEOF, -1, 1, {
+ {0, &attribute, -1}}};
+
+static asndefT searchResEntry = {"Operation", SEQUENCE, 4, 2, {
+ {0, &objectName, -1},
+ {0, &partialAttributes, -1}}};
+
+static asndefT authChoice = {"Authentication Choice", CHOICE, -1, 2, {
+ {"Authentication: Simple", 0, 0},
+ {"Authentication: SASL", 0, 3}}};
+
+static asndefT bindRequest = {"Operation", SEQUENCE, 0, 3, {
+ {"Version", 0, -1},
+ {0, &objectName, -1},
+ {0, &authChoice, -1}}};
+
+static asndefT resultCode = {"Result Code", ENUM, -1, 39, {
+ {"Success", 0, 0},
+ {"Operation Error", 0, 1},
+ {"Protocol Error", 0, 2},
+ {"Time Limit Exceeded", 0, 3},
+ {"Size Limit Exceeded", 0, 4},
+ {"Compare False", 0, 5},
+ {"Compare True", 0, 6},
+ {"Auth Method Not supported", 0, 7},
+ {"Strong Auth Required", 0, 8},
+ {"Referral", 0, 10},
+ {"Admin Limit Exceeded", 0, 11},
+ {"Unavailable Critical Extension", 0, 12},
+ {"Confidentiality required", 0, 13},
+ {"SASL Bind In Progress", 0, 14},
+ {"No Such Attribute", 0, 16},
+ {"Undefined Attribute Type", 0, 17},
+ {"Inappropriate Matching", 0, 18},
+ {"Constraint violation", 0, 19},
+ {"Attribute or Value Exists", 0, 20},
+ {"Invalid Attribute Syntax", 0, 21},
+ {"No Such Object", 0, 32},
+ {"Alias Problem", 0, 33},
+ {"Invalid DN Syntax", 0, 34},
+ {"Alias Dereferencing Problem", 0, 36},
+ {"Inappropriate Authentication", 0, 48},
+ {"Invalid Credentials", 0, 49},
+ {"Insufficient Access Rights", 0, 50},
+ {"Busy", 0, 51},
+ {"Unavailable", 0, 52},
+ {"Unwilling To Perform", 0, 53},
+ {"Loop Detect", 0, 54},
+ {"Naming Violation", 0, 64},
+ {"ObjectClass violation", 0, 65},
+ {"Not Allowed On Non Leaf", 0, 66},
+ {"Not Allowed On RDN", 0, 67},
+ {"Entry Already Exists", 0, 68},
+ {"ObjectClass Mods Prohibited", 0, 69},
+ {"Affects Multiple DSAs", 0, 71},
+ {"Other", 0, 80}}};
+
+
+static asndefT referral = {"Referral", SEQUENCEOF, -1, 1, {
+ {"LDAP URL", 0, -1}}};
+
+static asndefT ldapResult = {"LDAP Result", SEQUENCE, -1, 4, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3}}};
+
+static asndefT bindResponse = {"Operation", SEQUENCE, 1, 5, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3},
+ {"SASL Credentials", 0, 7}}};
+
+static asndefT unbindRequest = {"Operation", SEQUENCE, 2, 0, {0}};
+
+static asndefT searchResDone = {"Operation", SEQUENCE, 5, 4, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3}}};
+
+static asndefT seqModOperation = {"Operation", ENUM, -1, 4, {
+ {"Add", 0, 0},
+ {"Delete", 0, 1},
+ {"Replace", 0, 2}}};
+
+static asndefT seqModModification = {"Modification", SEQUENCE, -1, 1, {
+ {0, &attribute, -1}}};
+
+static asndefT seqModification = {"", SEQUENCE, -1, 2, {
+ {0, &seqModOperation, -1},
+ {0, &seqModModification, -1}}};
+
+static asndefT modification = {"Modification", SEQUENCEOF, -1, 1, {
+ {0, &seqModification, -1}}};
+
+static asndefT modifyRequest = {"Operation", SEQUENCE, 6, 2, {
+ {0, &objectName, -1},
+ {0, &modification, -1}}};
+
+static asndefT modifyResponse = {"Operation", SEQUENCE, 7, 4, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3}}};
+
+static asndefT addAttributes = {"Attributes", SEQUENCEOF, -1, 1, {
+ {0, &attribute, -1}}};
+
+static asndefT addRequest = {"Operation", SEQUENCE, 8, 2, {
+ {0, &ldapEntry, -1},
+ {0, &addAttributes, -1}}};
+
+static asndefT addResponse = {"Operation", SEQUENCE, 9, 4, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3}}};
+
+static asndefT delRequest = {"Operation", SEQUENCE, 10, 1, {
+ {0, &ldapEntry, -1}}};
+
+static asndefT delResponse = {"Operation", SEQUENCE, 11, 4, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3}}};
+
+static asndefT modifyDNRequest = {"Operation", SEQUENCE, 12, 4, {
+ {0, &ldapEntry, -1},
+ {0, &relativeLdapEntry, -1},
+ {"Delete Old RDN", 0, -1},
+ {0, &newSuperior, 0}}};
+
+static asndefT modifyDNResponse = {"Operation", SEQUENCE, 13, 4, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3}}};
+
+static asndefT ava = {"Ava", SEQUENCE, -1, 2, {
+ {"Attr Descr", 0, -1},
+ {"Value", 0, -1}}};
+
+static asndefT compareRequest = {"Operation", SEQUENCE, 14, 2, {
+ {0, &ldapEntry, -1},
+ {0, &ava, 0}}};
+
+static asndefT compareResponse = {"Operation", SEQUENCE, 15, 4, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3}}};
+
+static asndefT abandonRequest = {"Operation", SEQUENCE, 16, 1, {
+ {"Message ID", 0, -1}}};
+
+static asndefT searchResRef = {"Operation", SEQUENCEOF, 19, 1, {
+ {"LDAP URL", 0, -1}}};
+
+static asndefT extendedRequest = {"Operation", SEQUENCE, 14, 2, {
+ {"Request Name", 0, 0},
+ {"Request Value", 0, 1}}};
+
+static asndefT extendedResponse = {"Operation", SEQUENCE, 24, 6, {
+ {0, &resultCode, -1},
+ {"Matched DN", 0, -1},
+ {"Error Message", 0, -1},
+ {0, &referral, 3},
+ {"Response Name", 0, 10},
+ {"Response", 0, 11}}};
+
+static asndefT protocolOp = {"Protocol Op", CHOICE, -1, 20, {
+ {0, &bindRequest, 0},
+ {0, &bindResponse, 1},
+ {0, &unbindRequest, 2},
+ {0, &searchRequest, 3},
+ {0, &searchResEntry, 4},
+ {0, &searchResDone, 5},
+ {0, &modifyRequest, 6},
+ {0, &modifyResponse, 7},
+ {0, &addRequest, 8},
+ {0, &addResponse, 9},
+ {0, &delRequest, 10},
+ {0, &delResponse, 11},
+ {0, &modifyDNRequest, 12},
+ {0, &modifyDNResponse, 13},
+ {0, &compareRequest, 14},
+ {0, &compareResponse, 15},
+ {0, &abandonRequest, 16},
+ {0, &searchResRef, 19},
+ {0, &extendedRequest, 23},
+ {0, &extendedResponse, 24}}};
+
+static asndefT control = {"Control", SEQUENCE, -1, 3, {
+ {"LDAP OID", 0, -1},
+ {"Criticality", 0, -1},
+ {"Control value", 0, -1}}};
+
+static asndefT controls = {"Controls List", SEQUENCEOF, -1, 1, {
+ {0, &control, -1}}};
+
+static asndefT LDAPMessage = { "LDAPMessage", SEQUENCE, -1, 3, {
+ {"Message ID", 0, -1},
+ {0, &protocolOp, -1},
+ {0, &controls, 0}}};
+
+static asndefT MPDU = { "MPDU", SET, -1, 1,
+ {{0, &LDAPMessage, 0}}};
+
+static int mytype[] = {
+0, /* EndOfContents */
+0, /* Boolean */
+0, /* Integer */
+BITSTRING, /* BitString */
+0, /* OctetString */
+0, /* Null */
+0, /* Oid */
+0, /* ObjDescriptor */
+0, /* External */
+0, /* Real */
+ENUM, /* Enumerated */
+0, /* Reserved */
+0, /* Reserved */
+0, /* Reserved */
+0, /* Reserved */
+0, /* Reserved */
+SEQUENCE, /* Sequence */
+SET, /* Set */
+0, /* NumericString */
+0, /* PrintableString */
+0, /* T.61String */
+0, /* VideotexString */
+0, /* IA5String */
+0, /* UTCTime */
+0, /* GeneralizedTime */
+0, /* GraphicString */
+0, /* VisibleString */
+0, /* GeneralString */
+0, /* Reserved */
+0, /* Reserved */
+0, /* Reserved */
+0, /* Reserved */
+};
+
+/*
+ * Find object identifier in known oid table
+ * A oid - oid hexa string
+ * int olg - oid length
+ */
+static int
+oidmap(A oid, int olg)
+{
+ register int ix, goon;
+ register A oidptr, tabptr, tabend;
+
+/* returns (oid table size) if not found */
+
+ for (ix = 0; ix < OIDNB; ix++) {
+ oidptr = oid; tabptr = (&(OidTab[ix].oidcode[0]));
+ if (olg == INT(*tabptr++)) {
+ tabend = tabptr + olg;
+ goon = 1;
+ while (goon != 0 && tabptr < tabend) {
+ if (*tabptr++ != *oidptr++)
+ goon = 0;
+ }
+ if (goon != 0)
+ return (ix);
+ }
+ }
+ return (OIDNB);
+}
+
+/*
+ * Read an hexacode and convert it into ASCII
+ */
+static int getnext(int ctxnum)
+{
+ static X c[3]; /* c[0-3] will contain ascii values on exit */
+ hex = 0;
+ if (gi_osibuf[ctxnum] == osilen)
+ return (-1);
+ hex = osibuff[gi_osibuf[ctxnum]++];
+ (void) sprintf((char *)c, "%02x", (hex&0x00FF));
+ return (0);
+}
+
+/*
+ * Skip everything that is not an LDAPMessage
+ */
+static char *skipjunk(len, pdu)
+int len;
+char *pdu;
+{
+ int tag;
+ char *buf = pdu;
+ int offset = 0;
+ while (len > 0) {
+ /* size minumum for a sequence + integer = 5 */
+ /* LDAPMessage::= SEQUENCE */
+ if ((len > 5) && (buf[0] == 0x30)) {
+ tag = buf[1]&0x00ff;
+ if (tag < 0x80) {
+ /* length is one one octet */
+ offset = 1;
+ } else {
+ /* length is multiple octet. */
+ offset = 1+ tag&0x007f;
+ }
+ /* Make sure we don't read past the end */
+ /* of the buffer */
+ if (len - (1+offset) > 0) {
+ /* skip after the length */
+ tag = buf[1+offset]&0x00ff;
+ if (tag == 0x02) { /* INTEGER */
+ /* looks like a valid PDU */
+ return (buf);
+ }
+ }
+ }
+ len --;
+ buf++;
+ }
+ return (buf);
+}
+
+
+#define GETNEXT(a) (void)getnext(a);
+
+/*
+ * main routine: decode a TLV; to be called recursively
+ *
+ * pdulen: current pdu's length
+ */
+static int
+decpdu(int pdulen, asndefTp ASNDESC, int ctxnum)
+{
+ X scrlin[99]; /* screen line */
+ X oidstr[80]; /* oid hexa string */
+ int slen; /* screen line length */
+ int stlv; /* sub-tlv length */
+ int oix; /* oid table index */
+ int effnb; /* effectively traced octet nb */
+ int i = 0, j = 0;
+ int ai = -2;
+ asndefTp SASNDESC = 0;
+ asndefTp TMPDESC = 0;
+ asndefTp GR_TMPDESC = 0;
+ int tmpai = 0;
+ int gr_tmpai = 0;
+ int dontprint = 0;
+ int already = 0;
+ static int rlen = 0; /* tlv's real length */
+
+ ++level[ctxnum]; /* level indicator */
+ effnb = 0;
+
+ /*
+ * Decode the current TLV segment
+ */
+ while (pdulen > 1) {
+
+ if (getnext(ctxnum)) {
+ break;
+ }
+ if (strlen(scrbuffer)) asnshw2("%s ", "LDAP:");
+ /* screen printing according to level indicator */
+ for (i = 1; i < level[ctxnum]; ++i) asnshw1(" ");
+
+ /* get tag */
+ otyp[ctxnum] = INT(hex); /* single octet type only */
+ --pdulen;
+ ++effnb;
+
+ /* get length */
+ GETNEXT(ctxnum);
+ olen[ctxnum] = INT(hex); /* tlv length */
+ --pdulen;
+ ++effnb;
+
+ /* Continuing decoding of current TLV... */
+ /*
+ * Snoop's lower layers do not allow us
+ * to know the true length for
+ * datastream protocols like LDAP.
+ */
+
+ /*
+ * if length is less than 128, we
+ * already have the real TLV length.
+ */
+ if (olen[ctxnum] < 128) { /* short length form */
+ rlen = olen[ctxnum];
+ } else { /* long and any form length */
+ /* else we do more getnext()'s */
+ for (rlen = 0, olen[ctxnum] &= 0x0F;
+ (olen[ctxnum]) && (pdulen > 0);
+ --olen[ctxnum], --pdulen, ++effnb) {
+ GETNEXT(ctxnum);
+ rlen = (rlen << 8) | INT(hex);
+ }
+ if (!rlen) {
+ pdulen = 0x7fffffff;
+ }
+ }
+
+ /*
+ * print the tag class and number
+ */
+ i = otyp[ctxnum]&0x1F;
+ switch (otyp[ctxnum] >> 6) { /* class */
+ case 0: /* universal */
+ if (ASNDESC && i != 0) {
+ int dobreak = 0;
+ switch (ASNDESC->type) {
+ case CONTENT:
+ SASNDESC = ASNDESC;
+ break;
+ case SET:
+ for (ai = 0;
+ ai < ASNDESC->nbson && i < 32 &&
+ ASNDESC->son[ai].sondef &&
+ /*
+ * For this test SEQUENCE & SEQUENCE OF
+ * are same, so suppress the last bit
+ */
+ (ASNDESC->son[ai].sondef
+ ->type&0xFE)
+ != mytype[i]; ++ai);
+ if (ai < ASNDESC->nbson) {
+ SASNDESC =
+ ASNDESC->son[ai].sondef;
+ if (ASNDESC->son[ai].sonname != NULL) {
+
+ if (ASNDESC->son[ai].sondef != NULL &&
+ ASNDESC->son[ai].sondef->name !=
+ NULL) {
+ asnshw2("%s ", "LDAP:");
+ asnshw4(" %c[%s %s]",
+ ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sonname,
+ ASNDESC->son[ai].sondef->name);
+ } else {
+ asnshw2("%s ", "");
+ asnshw3(" %c[%s]",
+ ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sonname);
+ } /* end if */
+
+ dobreak = 1;
+
+ } else if (ASNDESC->son[ai].sondef !=
+ NULL &&
+ ASNDESC->son[ai].sondef->name !=
+ NULL) {
+ asnshw2("%s ", "LDAP:");
+ asnshw3(" %c[%s]",
+ ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sondef->name);
+ dobreak = 1;
+ } /* end if */
+ } /* end if */
+ break;
+ case CHOICE:
+ if (GR_TMPDESC) {
+ ASNDESC = TMPDESC;
+ TMPDESC = GR_TMPDESC;
+ GR_TMPDESC = 0;
+ } else if (TMPDESC) {
+ ASNDESC = TMPDESC;
+ TMPDESC = 0;
+ }
+ if (gr_tmpai) {
+ ai = tmpai;
+ tmpai = gr_tmpai;
+ gr_tmpai = 0;
+ } else if (tmpai) {
+ ai = tmpai;
+ tmpai = 0;
+ }
+ break;
+
+ case SEQUENCE:
+ if (ai == -2) {
+ ai = 0;
+ } else {
+ do {
+ ai++;
+ } while \
+ (ai < ASNDESC->nbson && i < 32 && mytype[i] && \
+ ASNDESC->son[ai].sondef &&
+ /*
+ * For this test SEQUENCE & SEQUENCE OF
+ * are the same, so suppress last bit
+ */
+ (ASNDESC->son[ai].sondef->type&0xFE) != mytype[i]);
+ } /* end if */
+ if (ai < ASNDESC->nbson) {
+ SASNDESC = \
+ ASNDESC->son[ai].sondef;
+ if (ASNDESC->son[ai].sonname) {
+ if \
+ (ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->name) {
+ asnshw4 \
+ (" %c[%s %s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sonname,
+ ASNDESC->son[ai].sondef->name);
+ } else {
+ asnshw3 \
+ (" %c[%s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sonname);
+ } /* end if */
+ dobreak = 1;
+ } else if \
+ (ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->name) {
+ asnshw3 \
+ (" %c[%s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sondef->name);
+ dobreak = 1;
+ } /* end if */
+ } /* end if */
+ break;
+ case SEQUENCEOF:
+ ai = 0;
+ SASNDESC = ASNDESC->son[ai].sondef;
+ if (ASNDESC->son[ai].sonname) {
+ if (ASNDESC->son[ai].sondef && \
+ ASNDESC->son[ai].sondef->name) {
+ asnshw4 \
+ (" %c[%s %s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sonname,
+ ASNDESC->son[ai].sondef->name);
+ } else {
+ asnshw3 \
+ (" %c[%s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sonname);
+ } /* end if */
+ dobreak = 1;
+ } else if \
+ (ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->name) {
+ asnshw3 \
+ (" %c[%s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ ASNDESC->son[ai].sondef->name);
+ dobreak = 1;
+ } /* end if */
+ } /* end switch */
+ if (dobreak) {
+ break;
+ } /* end if */
+ } /* end if */
+ if (uclass[i]) {
+ asnshw3 \
+ (" %c[%s]", ((otyp[ctxnum]&0x20)?'*':' '), uclass[i]);
+ } else {
+ asnshw4 \
+ (" %c[%s%d]", ((otyp[ctxnum]&0x20)?'*':' '),
+ class[0], i);
+ }
+ break;
+ case 1: /* application */
+
+ if (ASNDESC) {
+
+ for (ai = 0; ai < ASNDESC->nbson; ++ai) {
+ int i2 = 0;
+
+ if \
+ (ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->type == CHOICE) {
+ while \
+ (i2 < ASNDESC->son[ai].sondef->nbson &&
+ ASNDESC->son[ai].sondef->son[i2].sondef && \
+ ASNDESC->son[ai].sondef->son[i2].sondef->application != i) {
+ i2++;
+ continue;
+ }
+ if \
+ (i2 == ASNDESC->son[ai].sondef->nbson) {
+ ai = ASNDESC->nbson;
+ break;
+ }
+ if (TMPDESC) {
+ GR_TMPDESC = TMPDESC;
+ gr_tmpai = tmpai;
+ }
+ TMPDESC = ASNDESC;
+ ASNDESC = ASNDESC->son[ai].sondef;
+ tmpai = ai;
+ ai = i2;
+ }
+
+ if (ASNDESC->son[ai].sondef && \
+ ASNDESC->son[ai].sondef->application == i) {
+ SASNDESC = \
+ ASNDESC->son[ai].sondef;
+ if (ASNDESC->son[ai].sonname) {
+ if \
+ (ASNDESC->son[ai].sondef->name) {
+ asnshw3 \
+ (" %s %s", ASNDESC->son[ai].sonname,
+ ASNDESC->son[ai].sondef->name);
+ } else {
+ asnshw2 \
+ (" %s", ASNDESC->son[ai].sonname);
+ } /* end if */
+ } else if \
+ (ASNDESC->son[ai].sondef->name) {
+ asnshw2 \
+ (" %s", ASNDESC->son[ai].sondef->name);
+ } /* end if */
+ break;
+ } /* end if */
+ } /* end for */
+ if (ai >= ASNDESC->nbson) {
+ ai = -1; /* not found */
+ } /* end if */
+ } /* end if */
+ if (PTRaclass[i]) {
+ asnshw5 \
+ (" %c[%s%d: %s]", ((otyp[ctxnum]&0x20)?'*':' '),
+ class[1], i, PTRaclass[i]);
+ (void) strcpy(operation, (char *)PTRaclass[i]);
+ } else {
+ asnshw4 \
+ (" %c[%s%d]", ((otyp[ctxnum]&0x20)?'*':' '), \
+ class[1], i);
+ }
+ break;
+
+ case 2: /* context-specific */
+
+ if (TMPDESC) {
+ ASNDESC = TMPDESC;
+ TMPDESC = GR_TMPDESC;
+ already = 1;
+ }
+ if (ASNDESC) {
+
+ for (ai = 0; ai < ASNDESC->nbson; ++ai) {
+ if \
+ (!already && ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->type == CHOICE) {
+ int i2 = 0;
+ while \
+ (i2 < ASNDESC->son[ai].sondef->nbson &&
+ ASNDESC->son[ai].sondef->son[i2].tag != i) {
+ i2++;
+ continue;
+ }
+ if (i2 == \
+ ASNDESC->son[ai].sondef->nbson) {
+ ai = ASNDESC->nbson;
+ break;
+ }
+ if (TMPDESC) {
+ GR_TMPDESC = TMPDESC;
+ gr_tmpai = tmpai;
+ }
+ TMPDESC = ASNDESC;
+ ASNDESC = \
+ ASNDESC->son[ai].sondef;
+ tmpai = ai;
+ ai = i2;
+ }
+
+ if \
+ (ASNDESC->son[ai].tag == i) {
+ SASNDESC = \
+ ASNDESC->son[ai].sondef;
+ if (ASNDESC->son[ai].sonname) {
+ if \
+ (ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->name) {
+ asnshw3 \
+ (" %s %s", ASNDESC->son[ai].sonname,
+ ASNDESC->son[ai].sondef->name);
+ } else {
+ asnshw2 \
+ (" %s", ASNDESC->son[ai].sonname);
+ } /* end if */
+ } else if \
+ (ASNDESC->son[ai].sondef &&
+ ASNDESC->son[ai].sondef->name) {
+ asnshw2 \
+ (" %s", ASNDESC->son[ai].sondef->name);
+ } /* end if */
+ break;
+ } /* end if */
+ } /* end for */
+ if (ai >= ASNDESC->nbson) {
+ ai = -1; /* not found */
+ } /* end if */
+ } /* end if */
+ asnshw3 \
+ (" %c[%d]", ((otyp[ctxnum]&0x20)?'*':' '), i);
+ break;
+
+ case 3: /* private */
+ asnshw4 \
+ (" %c[%s%d]", ((otyp[ctxnum]&0x20)?'*':' '), \
+ class[3], i);
+ } /* esac: tag */
+
+ /*
+ * print the length - as a debug tool only.
+ */
+ /* asnshw2(" Length=%d ",rlen); */
+ asnshw1("\n");
+ if (rlen > pdulen) {
+ asnshw1("*** Decode length error,");
+ asnshw2(" PDU length = %d ***\n", pdulen);
+ rlen = pdulen;
+ }
+
+ /*
+ * recursive interpretation of the value if constructor
+ */
+ if (otyp[ctxnum]&0x20) { /* constructor */
+
+ stlv = decpdu((rlen?rlen:pdulen), \
+ ASNDESC && ai != -1 ?(ai == -2 ? ASNDESC:
+ ASNDESC->son[ai].sondef):0, ctxnum);
+ /* recursive decoding */
+ pdulen -= stlv;
+ effnb += stlv;
+ } else if (otyp[ctxnum] == 0x06) {
+ /*
+ * interpretation of the object identifier
+ */
+ for (j = 0; (rlen) && (pdulen > 0); \
+ --rlen, --pdulen, ++effnb) {
+ GETNEXT(ctxnum);
+ oidstr[j++] = hex;
+ }
+
+ /* interpret the object identifier */
+ oidstr[j++] = '\0';
+ oix = oidmap(oidstr, j-1);
+ asnshw1("\n");
+ if (oix >= 0 && oix < OIDNB) { /* recognized obj id */
+ asnshw2("%s\n", OidTab[oix].oidname);
+ } else {
+ asnshw1("Unknown Oid\n");
+ }
+ } else {
+ /*
+ * interpretation of other primitive tags
+ */
+ if (!otyp[ctxnum] && !rlen) {
+ /* end of contents: any form length */
+ pdulen = 0;
+ } else {
+ X hexstr[5];
+ int k = 0;
+ int klen = rlen;
+ if (SASNDESC && SASNDESC->type == CONTENT && \
+ SASNDESC->nbson && SASNDESC->son[0].sondef) {
+ (void)
+ decpdu(rlen, SASNDESC->son[0].sondef, ctxnum);
+ } else {
+ if (rlen < 200) {
+ for (j = 0, slen = 0; \
+ (rlen) && (pdulen > 0);
+ --rlen, --pdulen, ++effnb) {
+ if (!slen) {
+ (void) \
+ strcpy((char *)scrlin, "LDAP: "); j += 7;
+ for \
+ (i = 0; i < level[ctxnum]; ++i) {
+ scrlin[j++] = ' ';
+ scrlin[j++] = ' ';
+ scrlin[j++] = ' ';
+ scrlin[j++] = ' ';
+ }
+ }
+
+ GETNEXT(ctxnum);
+ if (k < 5) {
+ hexstr[k++] = hex;
+ } /* end if */
+ if (!isprint(hex)) {
+ hex = '_';
+ dontprint = 1;
+ }
+ scrlin[j++] = hex;
+ if ((slen += 2) >= \
+ (72 - (level[ctxnum] * 3))) {
+ slen = 0;
+ scrlin[j] = 0;
+ if (!dontprint) {
+ asnshw2 \
+ ("%s\n", scrlin);
+ }
+ j = 0;
+ }
+ } /* rof: primitive values */
+ if (slen) {
+ scrlin[j] = 0;
+ if (!dontprint) {
+ asnshw2("%s\n", scrlin);
+ }
+ }
+ dontprint = 0;
+ } else {
+ asnshw2("%s ", "LDAP:");
+ for (i = 0; i < level[ctxnum]; ++i) {
+ asnshw1(" ");
+ scrlin[j++] = ' ';
+ scrlin[j++] = ' ';
+ scrlin[j++] = ' ';
+ }
+
+ for (j = 0; (rlen) && (pdulen > 0); \
+ --rlen, --pdulen, ++effnb) {
+ GETNEXT(ctxnum);
+ if (k < 5) {
+ hexstr[k++] = hex;
+ }
+ }
+ (void) strcpy \
+ ((char *)scrlin, \
+ "*** NOT PRINTED - Too long value ***");
+ asnshw2("%s\n", scrlin);
+ }
+
+ if \
+ (SASNDESC && SASNDESC->type == BITSTRING &&\
+ klen <= 5) {
+ unsigned long bitstr = 0;
+ for (i = 1; i < 5; ++i) {
+ bitstr = \
+ ((bitstr) << 8) + ((i < klen)?hexstr[i]:0);
+ } /* end for */
+ for \
+ (i = 0; i < SASNDESC->nbson; ++i) {
+ if ((bitstr & \
+ ((unsigned long)SASNDESC->son[i].sondef)) ==
+ ((unsigned long)SASNDESC->son[i].tag)) {
+ if \
+ (SASNDESC->son[i].sonname) {
+ int k;
+ asnshw2 \
+ ("%s ", "LDAP:");
+ for \
+ (k = 0; k < level[ctxnum]; ++k) {
+ asnshw1(" ");
+ }
+ asnshw2 \
+ ("%s", SASNDESC->son[i].sonname);
+ } /* end if */
+ } /* end if */
+ } /* end for */
+ } /* end if */
+ if (SASNDESC && \
+ (SASNDESC->type == ENUM ||
+ SASNDESC->type == CONTENTTYPE) && klen <= 5) {
+ unsigned long value = 0;
+ for (i = 0; i < klen; ++i) {
+ value = \
+ ((value) << 8) + hexstr[i];
+ } /* end for */
+ for \
+ (i = 0; i < SASNDESC->nbson; ++i) {
+ if \
+ (value == ((unsigned long)SASNDESC->son[i].tag)) {
+ if \
+ (SASNDESC->son[i].sonname) {
+ int k;
+ asnshw2 \
+ ("%s ", "LDAP:");
+ for \
+ (k = 0; k < level[ctxnum]; ++k) {
+ asnshw1(" ");
+ }
+ asnshw2 \
+ ("%s\n", SASNDESC->son[i].sonname);
+ (void) \
+ strcpy(resultcode, SASNDESC->son[i].sonname);
+ } /* end if */
+ break;
+ } /* end if */
+ } /* end for */
+ } /* end if */
+
+ } /* end if */
+ } /* fi: constructor/obj-id/primitive */
+ } /* fi: tag analysis */
+ } /* elihw: len>1 */
+ --level[ctxnum];
+ return (effnb);
+}
+
+
+/* init_ldap initializes various buffers and variables */
+/* it is called one-time (in snoop_filter.c) only. */
+
+void
+init_ldap()
+{
+ int i;
+
+ for (i = 0; i < MAX_CTX; i++) {
+ gi_osibuf[i] = 0;
+ level[i] = 0;
+ }
+}
+static void
+ldapdump(char *data, int datalen)
+{
+ char *p;
+ ushort_t *p16 = (ushort_t *)data;
+ char *p8 = data;
+ int i, left, len;
+ int chunk = 16; /* 16 bytes per line */
+
+ asnshw1("LDAP: Skipping until next full LDAPMessage\n");
+
+ for (p = data; p < data + datalen; p += chunk) {
+ asnshw2("LDAP:\t%4d: ", p - data);
+ left = (data + datalen) - p;
+ len = MIN(chunk, left);
+ for (i = 0; i < (len / 2); i++)
+ asnshw2("%04x ", ntohs(*p16++) & 0xffff);
+ if (len % 2) {
+ asnshw2("%02x ", *((unsigned char *)p16));
+ }
+ for (i = 0; i < (chunk - left) / 2; i++)
+ asnshw1(" ");
+
+ asnshw1(" ");
+ for (i = 0; i < len; i++, p8++)
+ asnshw2("%c", isprint(*p8) ? *p8 : '.');
+ asnshw1("\n");
+ }
+
+ asnshw1("LDAP:\n");
+}
+
+/* decode_ldap is the entry point for the main decoding function */
+/* decpdu(). decode_ldap() is only called by interpret_ldap. */
+
+void
+decode_ldap(char *buf, int len)
+{
+ asndefTp ASNDESC = 0;
+ char *newbuf;
+ int skipped = 0;
+
+ PTRaclass = MHSaclass;
+ ASNDESC = &MPDU;
+
+
+ newbuf = skipjunk(len, buf);
+ if (newbuf > buf) {
+ skipped = newbuf-buf;
+ ldapdump(buf, newbuf-buf);
+ }
+ buf = newbuf;
+ len = len-skipped;
+ osibuff = buf; /* Undecoded buf is passed by interpret_ldap */
+ osilen = len; /* length of tcp data is also passed */
+
+ (void) decpdu(len, ASNDESC, 0);
+ gi_osibuf[0] = 0;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.c
new file mode 100644
index 0000000..84155b2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.c
@@ -0,0 +1,869 @@
+/*
+ * 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 <stdio.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <protocols/routed.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "snoop.h"
+#include "snoop_mip.h"
+
+/*
+ * This defines the length of internal, unbounded buffers. We set
+ * this to be MAXLINE (the maximum verbose display line length) -
+ * 64, which should be enough for all necessary descriptions.
+ */
+#define BUFLEN MAXLINE - 64
+
+extern char *dlc_header;
+extern char *addrtoname();
+
+enum EXT_TYPE { ADV, REG };
+
+/*
+ * This defines the interface for all extention interpreter
+ * functions. The function will be called with following
+ * parameters:
+ *
+ * type: IN The type code for this extention
+ * len IN The length of the payload (i.e. the
+ * length field in an extension header)
+ * payload IN A pointer to the beginning of the
+ * extension payload
+ */
+typedef void interpreter_f(uint8_t type, uint8_t len, uchar_t *payload);
+
+struct ext_dispatch {
+ uint8_t type;
+ interpreter_f *pfunc;
+};
+
+/* Description structure -- maps type to description */
+struct ext_desc {
+ uint8_t type;
+ const char *desc;
+};
+
+/*
+ * Interpreter function prototypes for both adv and reg. These
+ * all must implement the interpret_f interface defined above.
+ */
+static void spi_ext(uint8_t, uint8_t, uchar_t *);
+static void key_ext(uint8_t, uint8_t, uchar_t *);
+static void trav_ext(uint8_t, uint8_t, uchar_t *);
+static void empty_ext(uint8_t, uint8_t, uchar_t *);
+static void nai_ext(uint8_t, uint8_t, uchar_t *);
+static void chall_ext(uint8_t, uint8_t, uchar_t *);
+static void ma_ext(uint8_t, uint8_t, uchar_t *);
+static void prefix_ext(uint8_t, uint8_t, uchar_t *);
+static void unk_ext(uint8_t, uint8_t, uchar_t *);
+
+/* R E G I S T R A T I O N */
+
+#define REG_TBL_LEN 10 /* update this when adding to the table */
+
+/* Reg: type to description mapping table */
+static struct ext_desc reg_desc[] = {
+ MN_HA_AUTH, "(Mobile-Home Authentication Extension)",
+ MN_FA_AUTH, "(Mobile-Foreign Authentication Extension",
+ FA_HA_AUTH, "(Foreign-Home Authentication Extension)",
+ GEN_AUTH, "(Generalized Authentication Extension)",
+ MN_HA_KEY, "(Mobile-Home Key Extension)",
+ MN_FA_KEY, "(Mobile-Foreign Key Extension)",
+ MN_HA_TRAVERSE, "(Firewall Traversal Extension)",
+ ENCAP_DELIV, "(Encapsulating Delivery Style Extension)",
+ MN_NAI, "(Mobile Node Network Access Identifier)",
+ FA_CHALLENGE, "(Mobile-Foreign Agent Challenge)",
+ 0, "(Unrecognized Extension)"
+};
+
+#define GENAUTH_TBL_LEN 1 /* update this when adding to the table */
+
+/* Subtypes for Generic Authentication Extension type (type 36) */
+static struct ext_desc genauth_desc[] = {
+ GEN_AUTH_MN_AAA, "(MN-AAA Authentication Subtype)",
+ 0, "(Unrecognized Subtype)"
+};
+
+/* Reg: type to function mapping table */
+static struct ext_dispatch reg_dispatch[] = {
+ MN_HA_AUTH, spi_ext,
+ MN_FA_AUTH, spi_ext,
+ FA_HA_AUTH, spi_ext,
+ GEN_AUTH, spi_ext,
+ MN_HA_KEY, key_ext,
+ MN_FA_KEY, key_ext,
+ MN_HA_TRAVERSE, trav_ext,
+ ENCAP_DELIV, empty_ext,
+ MN_NAI, nai_ext,
+ FA_CHALLENGE, chall_ext,
+ 0, unk_ext
+};
+
+/* A D V E R T I S E M E N T */
+
+#define ADV_TBL_LEN 5 /* update this when adding to the table */
+
+/* Adv: type to description mapping table */
+static struct ext_desc adv_desc[] = {
+ ICMP_ADV_MSG_PADDING_EXT, "(Padding)",
+ ICMP_ADV_MSG_MOBILITY_AGT_EXT, "(Mobility Agent Extension)",
+ ICMP_ADV_MSG_PREFIX_LENGTH_EXT, "(Prefix Lengths)",
+ ICMP_ADV_MSG_FA_CHALLENGE, "(Foreign Agent Challenge)",
+ ICMP_ADV_MSG_FA_NAI, "(Foreign Agent NAI)",
+ 0, "(Unrecognized Extension)"
+};
+
+/* Adv: type to function mapping table */
+static struct ext_dispatch adv_dispatch[] = {
+ ICMP_ADV_MSG_PADDING_EXT, NULL, /* never called */
+ ICMP_ADV_MSG_MOBILITY_AGT_EXT, ma_ext,
+ ICMP_ADV_MSG_PREFIX_LENGTH_EXT, prefix_ext,
+ ICMP_ADV_MSG_FA_CHALLENGE, chall_ext,
+ ICMP_ADV_MSG_FA_NAI, nai_ext,
+ 0, unk_ext
+};
+
+#define GETSPI(payload, hi, low) \
+ (void) memcpy(&hi, payload, sizeof (hi)); \
+ (void) memcpy(&low, payload + sizeof (hi), sizeof (low))
+
+static void dumphex(uchar_t *payload, int payload_len, char *buf, char *msg) {
+ int index;
+
+ for (index = 0; index < payload_len; index++) {
+ (void) sprintf(&buf[index * 3], " %.2x", payload[index]);
+ }
+
+ (void) sprintf(get_line((char *)payload-dlc_header, 1), msg, buf);
+}
+
+static const char *get_desc(struct ext_desc table[], uint8_t type, int max) {
+ int i;
+
+ for (i = 0; i < max && table[i].type != type; i++)
+ /* NO_OP */;
+
+ return (table[i].desc);
+}
+
+/*
+ * The following is an accessor for the description table, used by
+ * snoop_icmp.c. This maintains the encapsulation of the internal
+ * description table.
+ */
+const char *get_mip_adv_desc(uint8_t type) {
+ return (get_desc(adv_desc, type, ADV_TBL_LEN));
+}
+
+static interpreter_f *get_interpreter(struct ext_dispatch table[],
+ uint8_t type,
+ int max) {
+ int i;
+
+ for (i = 0; i < max && table[i].type != type; i++)
+ /* NO_OP */;
+
+ return (table[i].pfunc);
+}
+
+static int
+interpret_extensions(uchar_t *ext,
+ int regext_size,
+ enum EXT_TYPE etype) {
+
+ int curr_size = regext_size; /* remaining total for all exts */
+ exthdr_t *exthdr;
+ gen_exthdr_t *gen_exthdr;
+ const char *st;
+ uchar_t *p;
+ interpreter_f *f;
+ uint8_t ext_type;
+ uint16_t ext_len;
+ uint_t ext_hdrlen;
+
+ show_space();
+ exthdr = (exthdr_t *)ALIGN(ext);
+
+
+ do {
+ ext_type = exthdr->type;
+ if (ext_type == GEN_AUTH) {
+ gen_exthdr = (gen_exthdr_t *)exthdr;
+ ext_hdrlen = sizeof (gen_exthdr_t);
+ ext_len = ntohs(gen_exthdr->length);
+ } else {
+ ext_hdrlen = sizeof (exthdr_t);
+ ext_len = exthdr->length;
+ }
+
+ if (!((etype == ADV && ext_type == ICMP_ADV_MSG_PADDING_EXT &&
+ curr_size >= 1) ||
+ curr_size >= ext_hdrlen + ext_len))
+ break;
+
+ /* Print description for this extension */
+ if (etype == ADV) {
+ st = get_desc(adv_desc, ext_type, ADV_TBL_LEN);
+ } else /* REG */ {
+ st = get_desc(reg_desc, ext_type, REG_TBL_LEN);
+ }
+
+ (void) sprintf(get_line((char *)exthdr-dlc_header, 1),
+ "Extension header type = %d %s", ext_type, st);
+
+ if (ext_type == GEN_AUTH) {
+ st = get_desc(genauth_desc, gen_exthdr->subtype,
+ GENAUTH_TBL_LEN);
+ (void) sprintf(get_line((char *)exthdr-dlc_header, 1),
+ "Subtype = %d %s", gen_exthdr->subtype, st);
+ }
+
+ /* Special case for 1-byte padding */
+ if (etype == ADV && ext_type == ICMP_ADV_MSG_PADDING_EXT) {
+ exthdr = (exthdr_t *)((uchar_t *)exthdr + 1);
+ curr_size--;
+ continue;
+ }
+
+ (void) sprintf(get_line((char *)&exthdr->length-dlc_header, 1),
+ "Length = %d", ext_len);
+
+ /* Parse out the extension's payload */
+ p = (uchar_t *)exthdr + ext_hdrlen;
+ curr_size -= (ext_hdrlen + ext_len);
+
+ if (etype == ADV) {
+ f = get_interpreter(adv_dispatch, ext_type, ADV_TBL_LEN);
+ } else /* REG */ {
+ f = get_interpreter(reg_dispatch, ext_type, REG_TBL_LEN);
+ }
+
+ f(ext_type, ext_len, p);
+
+ show_space();
+ exthdr = (exthdr_t *)(p + ext_len);
+ } while (B_TRUE);
+
+ return (0);
+}
+
+void interpret_icmp_mip_ext(uchar_t *p, int len) {
+ show_space();
+ show_header("ICMP: ", " MIP Advertisement Extensions ", len);
+ show_space();
+
+ interpret_extensions(p, len, ADV);
+}
+
+void
+interpret_mip_cntrlmsg(int flags, uchar_t *msg, int fraglen) {
+ char *pt, *pc = NULL;
+ char *line;
+ regreq_t rreq[1];
+ regrep_t rrep[1];
+ int regext_size;
+ uchar_t *regext_data;
+ struct in_addr addr_temp;
+
+
+ /* First byte of the message should be the type */
+ switch (*msg) {
+ case REG_TYPE_REQ:
+ if (fraglen < sizeof (regreq_t))
+ return;
+ pt = (flags & F_DTAIL ? "registration request ":"reg rqst ");
+
+ (void) memcpy(rreq, msg, sizeof (*rreq));
+ regext_size = fraglen - sizeof (regreq_t);
+ regext_data = msg + sizeof (*rreq);
+ break;
+ case REG_TYPE_REP:
+ if (fraglen < sizeof (regrep_t))
+ return;
+ pt = (flags & F_DTAIL ? "registration reply ":"reg reply ");
+
+ (void) memcpy(rrep, msg, sizeof (*rrep));
+ regext_size = fraglen - sizeof (regrep_t);
+ regext_data = msg + sizeof (*rrep);
+
+ switch (rrep->code) {
+ case REPLY_CODE_ACK:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL)) ?
+ "OK" : "OK code 0";
+ break;
+ case REPLY_CODE_ACK_NO_SIMULTANEOUS:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "OK simultaneous bindings" : "OK code 1";
+ break;
+ case REPLY_CODE_FA_NACK_UNSPECIFIED:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: unspecified":"FA denial: code 64";
+ break;
+ case REPLY_CODE_FA_NACK_PROHIBITED:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: prohibited":"FA denial: code 65";
+ break;
+ case REPLY_CODE_FA_NACK_RESOURCES:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: no resources":"FA denial: code 66";
+ break;
+ case REPLY_CODE_FA_NACK_MN_AUTH:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: MN auth failed":"FA denial: code 67";
+ break;
+ case REPLY_CODE_FA_NACK_HA_AUTH:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: HA auth failed":
+ "FA denial: code 68";
+ break;
+ case REPLY_CODE_FA_NACK_LIFETIME:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: lifetime":"FA denial: code 69";
+ break;
+ case REPLY_CODE_FA_NACK_BAD_REQUEST:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: bad request": "FA: code 70";
+ break;
+ case REPLY_CODE_FA_NACK_BAD_REPLY:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: bad Reply":"FA denial: code 71";
+ break;
+ case REPLY_CODE_FA_NACK_ENCAP_UNAVAILABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: encapsulation":"FA denial: code 72";
+ break;
+ case REPLY_CODE_FA_NACK_VJ_UNAVAILABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: VJ compression":"FA denial: code 73";
+ break;
+ case REPLY_CODE_FA_NACK_BIDIR_TUNNEL_UNAVAILABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: reverse tunnel unavailable":
+ "FA denial: code 74";
+ break;
+ case REPLY_CODE_FA_NACK_BIDIR_TUNNEL_NO_TBIT:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: reverse tunnel: missing T-bit":
+ "FA denial: code 75";
+ break;
+ case REPLY_CODE_FA_NACK_BIDIR_TUNNEL_TOO_DISTANT:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: reverse tunnel: too distant":
+ "FA denial: code 76";
+ break;
+ case REPLY_CODE_FA_NACK_ICMP_HA_NET_UNREACHABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: home network unreachable":
+ "FA denial: code 80";
+ break;
+ case REPLY_CODE_FA_NACK_ICMP_HA_HOST_UNREACHABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: HA host unreachable":
+ "FA denial: code 81";
+ break;
+ case REPLY_CODE_FA_NACK_ICMP_HA_PORT_UNREACHABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: HA port unreachable":
+ "FA denial: code 82";
+ break;
+ case REPLY_CODE_FA_NACK_ICMP_HA_UNREACHABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: HA unreachable":"FA denial: code 88";
+ break;
+ case REPLY_CODE_FA_NACK_UNIQUE_HOMEADDR_REQD:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: Unique Home Addr Required":
+ "FA denial: code 96";
+ break;
+ case REPLY_CODE_FA_NACK_MISSING_NAI:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: Missing NAI":
+ "FA denial: code 97";
+ break;
+ case REPLY_CODE_FA_NACK_MISSING_HOME_AGENT:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: Missing Home Agent":
+ "FA denial: code 98";
+ break;
+ case REPLY_CODE_FA_NACK_UNKNOWN_CHALLENGE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: Unknown Challenge":
+ "FA denial: code 104";
+ break;
+ case REPLY_CODE_FA_NACK_MISSING_CHALLENGE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: Missing Challenge":
+ "FA denial: code 105";
+ break;
+ case REPLY_CODE_FA_NACK_MISSING_MN_FA:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "FA denial: Missing Mobile-Foreign Key Extension":
+ "FA denial: code 106";
+ break;
+ case REPLY_CODE_HA_NACK_UNSPECIFIED:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: unspecified":"HA denial: code 128";
+ break;
+ case REPLY_CODE_HA_NACK_PROHIBITED:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: prohibited":"HA denial: code 129";
+ break;
+ case REPLY_CODE_HA_NACK_RESOURCES:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: no resources":"HA denial: code 130";
+ break;
+ case REPLY_CODE_HA_NACK_MN_AUTH:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: MN auth failed":"HA denial: code 131";
+ break;
+ case REPLY_CODE_HA_NACK_FA_AUTH:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: FA auth failed":"HA denial: code 132";
+ break;
+ case REPLY_CODE_HA_NACK_ID_MISMATCH:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: ID mismatch":"HA denial: code 133";
+ break;
+ case REPLY_CODE_HA_NACK_BAD_REQUEST:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: bad request":"HA denial: code 134";
+ break;
+ case REPLY_CODE_HA_NACK_TOO_MANY_BINDINGS:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: too many bindings":
+ "HA denial: code 135";
+ break;
+ case REPLY_CODE_HA_NACK_BAD_HA_ADDRESS:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: bad HA address":"HA denial: code 136";
+ break;
+ case REPLY_CODE_HA_NACK_BIDIR_TUNNEL_UNAVAILABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: no reverse tunnel":
+ "HA denial: code 137";
+ break;
+ case REPLY_CODE_HA_NACK_BIDIR_TUNNEL_NO_TBIT:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: reverse tunnel: no T-bit":
+ "HA denial: code 138";
+ break;
+ case REPLY_CODE_HA_NACK_BIDIR_ENCAP_UNAVAILABLE:
+ pc = ((flags & F_ALLSUM) || (flags & F_DTAIL))?
+ "HA denial: encapsulation unavailable":
+ "HA denial: code 139";
+ break;
+ default:
+ pc = "?";
+ break;
+ }
+ break;
+
+ default :
+ break;
+ }
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (pc != NULL)
+ (void) sprintf(line, "Mobile IP %s(%s)", pt, pc);
+ else
+ (void) sprintf(line, "Mobile IP %s", pt);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("MIP: ", "Mobile IP Header", fraglen);
+ show_space();
+
+ if (*msg == REG_TYPE_REQ) {
+ (void) sprintf(get_line((char *)&rreq -
+ dlc_header, 1), "Registration header type = %s",
+ pt);
+ (void) sprintf(get_line(
+ (char *)(((uchar_t *)&rreq) + 1) - dlc_header, 1),
+ "%d... .... = %s simultaneous bindings ",
+ (rreq->Simultaneous_registration == 1)? 1 : 0,
+ (rreq->Simultaneous_registration == 1)? "":"no");
+ (void) sprintf(get_line(
+ (char *)(((uchar_t *)&rreq) + 1) - dlc_header, 1),
+ ".%d.. .... = %s broadcast datagrams ",
+ (rreq->Broadcasts_desired == 1) ? 1 : 0,
+ (rreq->Broadcasts_desired == 1) ? "":"no");
+ (void) sprintf(get_line(
+ (char *)(((uchar_t *)&rreq) + 1) - dlc_header, 1),
+ "..%d. .... = %s decapsulation by MN",
+ (rreq->Decapsulation_done_locally == 1) ? 1 : 0,
+ (rreq->Decapsulation_done_locally == 1) ?
+ "" : "no");
+ (void) sprintf(get_line(
+ (char *)(((uchar_t *)&rreq) + 1) - dlc_header, 1),
+ "...%d .... = %s minimum encapsulation ",
+ (rreq->Minimal_encap_desired == 1) ? 1 : 0,
+ (rreq->Minimal_encap_desired == 1) ? "" : "no");
+ (void) sprintf(get_line(
+ (char *)(((uchar_t *)&rreq) + 1) - dlc_header, 1),
+ ".... %d... = %s GRE encapsulation ",
+ (rreq->GRE_encap_desired == 1) ? 1 : 0,
+ (rreq->GRE_encap_desired == 1) ? "" : "no");
+ (void) sprintf(get_line(
+ (char *)(((uchar_t *)&rreq) + 1) - dlc_header, 1),
+ ".... .%d.. = %s VJ hdr Compression ",
+ (rreq->VJ_compression_desired == 1) ? 1 : 0,
+ (rreq->VJ_compression_desired == 1) ? "" : "no");
+ (void) sprintf(get_line(
+ (char *)(((uchar_t *)&rreq) + 1) - dlc_header, 1),
+ ".... ..%d. = %s reverse tunnel",
+ (rreq->BiDirectional_Tunnel_desired == 1) ? 1 : 0,
+ (rreq->BiDirectional_Tunnel_desired == 1) ?
+ "" : "no");
+ if (ntohs(rreq->lifetime) == 0xffff) {
+ (void) sprintf(get_line(
+ (char *)&rreq->lifetime - dlc_header, 1),
+ "Life Time = 0xFFFF (infinity)");
+ } else if (ntohs(rreq->lifetime) == 0) {
+ (void) sprintf(get_line(
+ (char *)&rreq->lifetime - dlc_header, 1),
+ "Life Time = 0 "
+ "(request for de-registration)");
+ } else {
+ (void) sprintf(get_line(
+ (char *)&rreq->lifetime - dlc_header, 1),
+ "Life time = %d seconds",
+ ntohs(rreq->lifetime));
+ }
+ addr_temp.s_addr = rreq->home_addr;
+ (void) sprintf(get_line(
+ (char *)&rreq->home_addr - dlc_header, 1),
+ "Home address = %s, %s",
+ inet_ntoa(addr_temp),
+ addrtoname(AF_INET, &addr_temp));
+ addr_temp.s_addr = rreq->home_agent_addr;
+ (void) sprintf(get_line(
+ (char *)&rreq->home_agent_addr - dlc_header, 1),
+ "Home Agent address = %s, %s",
+ inet_ntoa(addr_temp),
+ addrtoname(AF_INET, &addr_temp));
+ addr_temp.s_addr = rreq->care_of_addr;
+ (void) sprintf(get_line(
+ (char *)&rreq->care_of_addr - dlc_header, 1),
+ "Care of address = %s, %s",
+ inet_ntoa(addr_temp),
+ addrtoname(AF_INET, &addr_temp));
+ (void) sprintf(get_line(
+ (char *)&rreq->identification - dlc_header, 1),
+ "Identification = 0x%x-%x",
+ ntohl(rreq->identification.high_bits),
+ ntohl(rreq->identification.low_bits));
+ } else if (*msg == REG_TYPE_REP) {
+ (void) sprintf(
+ get_line((char *)&rrep->type - dlc_header, 1),
+ "Registration header type = %d (%s)",
+ (int)rrep->type, pt);
+ (void) sprintf(get_line((char *)&rrep - dlc_header, 1),
+ "Code = %d %s", (int)rrep->code, pc);
+ if (ntohs(rrep->lifetime) == 0xffff) {
+ (void) sprintf(get_line(
+ (char *)&rrep->lifetime - dlc_header, 1),
+ "Life time = 0xFFFF (infinity)");
+ } else if (ntohs(rrep->lifetime) == 0) {
+ (void) sprintf(get_line(
+ (char *)&rrep->lifetime - dlc_header, 1),
+ ((rrep->code == REPLY_CODE_ACK) ||
+ (rrep->code ==
+ REPLY_CODE_ACK_NO_SIMULTANEOUS))?
+ "Life time = 0 (de-registeration success)" :
+ "Life time = 0 (de-registration failed)");
+ } else {
+ (void) sprintf(get_line(
+ (char *)&rrep->lifetime - dlc_header, 1),
+ "Life time = %d seconds",
+ ntohs(rrep->lifetime));
+ }
+ addr_temp.s_addr = rrep->home_addr;
+ (void) sprintf(
+ get_line((char *)&rrep->home_addr - dlc_header, 1),
+ "Home address = %s, %s",
+ inet_ntoa(addr_temp),
+ addrtoname(AF_INET, &addr_temp));
+ addr_temp.s_addr = rrep->home_agent_addr;
+ (void) sprintf(get_line(
+ (char *)&rrep->home_agent_addr - dlc_header, 1),
+ "Home Agent address = %s, %s",
+ inet_ntoa(addr_temp),
+ addrtoname(AF_INET, &addr_temp));
+ (void) sprintf(get_line(
+ (char *)&rrep->identification - dlc_header, 1),
+ "Identification = 0x%x-%x",
+ ntohl(rrep->identification.high_bits),
+ ntohl(rrep->identification.low_bits));
+ }
+ fraglen = interpret_extensions(regext_data, regext_size, REG);
+ }
+}
+
+/*ARGSUSED*/
+static void spi_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ uint16_t spi_hi, spi_low;
+ char auth_prn_str[BUFLEN];
+
+ /* SPI */
+ GETSPI(p, spi_hi, spi_low);
+ (void) sprintf(get_line((char *)p - dlc_header, 1),
+ "Security Parameter Index = 0x%x%x",
+ ntohs(spi_hi), ntohs(spi_low));
+ p += sizeof (spi_hi) + sizeof (spi_low);
+ this_ext_len -= sizeof (spi_hi) + sizeof (spi_low);
+
+ /* The rest is the authenticator; dump it in hex */
+ dumphex(p,
+ /* don't write past our string buffer ... */
+ (this_ext_len*3 > BUFLEN ? BUFLEN : this_ext_len),
+ auth_prn_str,
+ "Authenticator = %s");
+}
+
+static void key_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ uint16_t alg, spi_hi, spi_low;
+ char *alg_string;
+ char *hafa = (type == MN_HA_KEY ? "HA" : "FA");
+ char sec_msg[32];
+ char auth_prn_str[BUFLEN];
+
+ /* Algorithm Type */
+ (void) memcpy(&alg, p, sizeof (alg));
+ alg = ntohs(alg);
+ switch (alg) {
+ case KEY_ALG_NONE:
+ alg_string = "None";
+ break;
+ case SA_MD5_MODE_PREF_SUF:
+ alg_string = "MD5/prefix+suffix";
+ break;
+ case SA_HMAC_MD5:
+ alg_string = "HMAC MD5";
+ break;
+ default:
+ alg_string = "Unknown";
+ break;
+ }
+ (void) sprintf(get_line((char *)p-dlc_header, 1),
+ "Algorithm = 0x%x: %s", alg, alg_string);
+ p += sizeof (alg);
+ this_ext_len -= sizeof (alg);
+
+ /* AAA SPI */
+ GETSPI(p, spi_hi, spi_low);
+ (void) sprintf(get_line((char *)p - dlc_header, 1),
+ "AAA Security Parameter Index = 0x%x%x",
+ ntohs(spi_hi), ntohs(spi_low));
+ p += sizeof (spi_hi) + sizeof (spi_low);
+ this_ext_len -= sizeof (spi_hi) + sizeof (spi_low);
+
+ /* HA / FA SPI */
+ GETSPI(p, spi_hi, spi_low);
+ (void) sprintf(get_line((char *)p - dlc_header, 1),
+ "%s Security Parameter Index = 0x%x%x",
+ hafa, ntohs(spi_hi), ntohs(spi_low));
+ p += sizeof (spi_hi) + sizeof (spi_low);
+ this_ext_len -= sizeof (spi_hi) + sizeof (spi_low);
+
+ /* The rest is the security info; dump it in hex */
+ sprintf(sec_msg, "%s Security Info = %%s", hafa);
+ dumphex(p,
+ /* don't write past our string buffer ... */
+ (this_ext_len*3 > BUFLEN ? BUFLEN : this_ext_len),
+ auth_prn_str,
+ sec_msg);
+}
+
+/*ARGSUSED*/
+static void trav_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ struct in_addr addr_temp;
+
+ /* skip reserved */
+ p += 2;
+ this_ext_len -= 2;
+
+ /* Mobile-Home Traversal Address */
+ (void) memcpy(&(addr_temp.s_addr), p, sizeof (addr_temp.s_addr));
+ (void) sprintf(get_line((char *)p-dlc_header, 1),
+ "Mobile-Home Traversal Address= %s, %s",
+ inet_ntoa(addr_temp),
+ addrtoname(AF_INET, &addr_temp));
+ p += sizeof (addr_temp.s_addr);
+ this_ext_len -= sizeof (addr_temp.s_addr);
+
+ /* Home-Mobile Traversal Address */
+ (void) memcpy(&(addr_temp.s_addr), p, sizeof (addr_temp.s_addr));
+ (void) sprintf(get_line((char *)p-dlc_header, 1),
+ "Home-Mobile Traversal Address= %s, %s",
+ inet_ntoa(addr_temp),
+ addrtoname(AF_INET, &addr_temp));
+}
+
+/*ARGSUSED*/
+static void empty_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ /* no payload */
+}
+
+/*ARGSUSED*/
+static void nai_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ /* payload points to the NAI */
+ char *desc = "Network Access Identifier = ";
+ size_t desclen = strlen(desc) + 1 + this_ext_len;
+
+ (void) snprintf(get_line((char *)p-dlc_header, 1),
+ desclen > MAXLINE ? MAXLINE : desclen,
+ "%s%s", desc, p);
+}
+
+/*ARGSUSED*/
+static void chall_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ char auth_prn_str[BUFLEN];
+
+ /* payload points to the challenge */
+ dumphex(p,
+ /* don't write past our string buffer ... */
+ (this_ext_len*3 > BUFLEN ? BUFLEN / 3 : this_ext_len),
+ auth_prn_str,
+ "Challenge = %s");
+}
+
+/*ARGSUSED*/
+static void ma_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ mobagtadvext_t adv_ext[1];
+ int i, len;
+ struct in_addr temp_addr;
+
+ (void) memcpy(adv_ext, p - sizeof (exthdr_t), sizeof (*adv_ext));
+ (void) sprintf(get_line(0, 0), "Sequence number = %d",
+ ntohs(adv_ext->sequence_num));
+ (void) sprintf(get_line(0, 0),
+ "Registration lifetime = %d seconds",
+ ntohs(adv_ext->reg_lifetime));
+ if (adv_ext->reg_bit) {
+ (void) sprintf(get_line(0, 0),
+ "1... .... = registration required "
+ "through FA");
+ } else {
+ (void) sprintf(get_line(0, 0),
+ "0... .... = registration not required "
+ "through FA");
+ }
+ if (adv_ext->busy_bit) {
+ (void) sprintf(get_line(0, 0), ".1.. .... = FA busy");
+ } else {
+ (void) sprintf(get_line(0, 0), ".0.. .... = FA not busy");
+ }
+ if (adv_ext->ha_bit) {
+ (void) sprintf(get_line(0, 0), "..1. .... = node is HA");
+ } else {
+ (void) sprintf(get_line(0, 0), "..0. .... = node not HA");
+ }
+ if (adv_ext->fa_bit) {
+ (void) sprintf(get_line(0, 0), "...1 .... = node is FA ");
+ } else {
+ (void) sprintf(get_line(0, 0), "...0 .... = node not FA ");
+ }
+ if (adv_ext->minencap_bit) {
+ (void) sprintf(get_line(0, 0), ".... 1... = minimal encapsulation "
+ "supported");
+ } else {
+ (void) sprintf(get_line(0, 0),
+ ".... 0... = no minimal encapsulation");
+ }
+ if (adv_ext->greencap_bit) {
+ (void) sprintf(get_line(0, 0),
+ ".... .1.. = GRE encapsulation supported");
+ } else {
+ (void) sprintf(get_line(0, 0),
+ ".... .0.. = no GRE encapsulation");
+ }
+ if (adv_ext->vanjacob_hdr_comp_bit) {
+ (void) sprintf(get_line(0, 0),
+ ".... ..1. = VJ header compression");
+ } else {
+ (void) sprintf(get_line(0, 0),
+ ".... ..0. = no VJ header compression");
+ }
+ if (adv_ext->reverse_tunnel_bit) {
+ (void) sprintf(get_line(0, 0),
+ ".... ...1 = reverse tunneling supported");
+ } else {
+ (void) sprintf(get_line(0, 0),
+ ".... ...0 = no reverse tunneling");
+ }
+ (void) sprintf(get_line(0, 0),
+ "Reserved Byte = 0x%x", adv_ext->reserved);
+
+ /* Parse out COA's */
+ p += sizeof (*adv_ext);
+ len = this_ext_len + sizeof (exthdr_t);
+ /* this_ext_len is unsigned, and here we need a signed number */
+ len -= sizeof (*adv_ext);
+
+ for (i = 0; len >= sizeof (temp_addr.s_addr); i++) {
+ memcpy(&(temp_addr.s_addr), p - sizeof (exthdr_t),
+ sizeof (temp_addr.s_addr));
+
+ (void) sprintf(get_line(0, 0),
+ "Care of address-%d = %s, %s", i,
+ inet_ntoa(temp_addr),
+ addrtoname(AF_INET, &temp_addr));
+
+ p += sizeof (temp_addr);
+ len -= sizeof (temp_addr);
+ }
+}
+
+/*ARGSUSED*/
+static void prefix_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ int i;
+
+ for (i = 0; i < this_ext_len; i++) {
+ (void) sprintf(get_line(0, 0),
+ "Prefix length of router address[%d] "
+ "= %d bits",
+ i, p[i]);
+ }
+}
+
+/*ARGSUSED*/
+static void unk_ext(uint8_t type, uint8_t this_ext_len, uchar_t *p) {
+ char auth_prn_str[BUFLEN];
+
+ /* Unknown extension; just dump the rest of the payload */
+ dumphex(p,
+ /* don't write past our string buffer ... */
+ (this_ext_len*3 > BUFLEN ? BUFLEN : this_ext_len),
+ auth_prn_str,
+ "Payload = %s");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.h
new file mode 100644
index 0000000..ca40f53
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mip.h
@@ -0,0 +1,327 @@
+/*
+ * 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 (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SNOOP_MIP_H
+#define _SNOOP_MIP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ALIGN(ptr) (ptr)
+
+/*
+ * E X T E N S I O N S
+ */
+
+typedef struct {
+ uchar_t type;
+ uchar_t length;
+} exthdr_t;
+
+/* This header is used for Generalized MIP Authentication Extensions */
+typedef struct {
+ uint8_t type;
+ uint8_t subtype;
+ uint16_t length;
+} gen_exthdr_t;
+
+#define MN_HA_AUTH 32
+#define MN_FA_AUTH 33
+#define FA_HA_AUTH 34
+#define GEN_AUTH 36
+#define MN_HA_KEY 126
+#define MN_HA_TRAVERSE 129
+#define ENCAP_DELIV 130
+#define MN_NAI 131
+#define FA_CHALLENGE 132
+#define MN_FA_KEY 133
+
+/* Subtypes for Generalized MIP Authentication Extension (GEN_AUTH) */
+#define GEN_AUTH_MN_AAA 1
+
+#define KEY_ALG_NONE 0
+#define SA_MD5_MODE_PREF_SUF 2 /* ...in prefix+suffix */
+#define SA_HMAC_MD5 3
+
+/*
+ * R E G I S T R A T I O N P R O T O C O L
+ */
+
+#define REG_TYPE_REQ 1
+#define REG_TYPE_REP 3
+
+typedef struct ident_str {
+ uint32_t high_bits; /* generated by the HA */
+ uint32_t low_bits; /* generated by the MN */
+} ident_t;
+
+#ifdef __sparc
+#ifdef _BIT_FIELDS_HTOL
+typedef struct registration_request_str {
+ uchar_t type; /* must be REG_TYPE_REQ */
+ uchar_t
+ Simultaneous_registration : 1,
+ Broadcasts_desired : 1,
+ Decapsulation_done_locally : 1, /* ...by the popup MN */
+ Minimal_encap_desired : 1,
+ GRE_encap_desired : 1,
+ VJ_compression_desired : 1,
+ BiDirectional_Tunnel_desired : 1,
+ reserved : 1;
+ ushort_t lifetime; /* 0 = dereg; 0xffff = infinity */
+ in_addr_t home_addr; /* address of the MN */
+ in_addr_t home_agent_addr; /* address of a HA */
+ in_addr_t care_of_addr; /* address of decap endpoint */
+ ident_t identification; /* for replay protection */
+} regreq_t;
+#endif /* _BIT_FIELDS_HTOL */
+#endif /* __sparc */
+
+#ifdef __i386
+#ifdef _BIT_FIELDS_LTOH
+typedef struct registration_request_str {
+ uchar_t type; /* must be REG_TYPE_REQ */
+ uchar_t
+ reserved : 1,
+ BiDirectional_Tunnel_desired : 1,
+ VJ_compression_desired : 1,
+ GRE_encap_desired : 1,
+ Minimal_encap_desired : 1,
+ Decapsulation_done_locally : 1, /* ...by the popup MN */
+ Broadcasts_desired : 1,
+ Simultaneous_registration : 1;
+ ushort_t lifetime; /* 0 = dereg; 0xffff = infinity */
+ in_addr_t home_addr; /* address of the MN */
+ in_addr_t home_agent_addr; /* address of a HA */
+ in_addr_t care_of_addr; /* address of decap endpoint */
+ ident_t identification; /* for replay protection */
+} regreq_t;
+#endif /* _BIT_FIELDS_LTOH */
+#endif /* __i386 */
+
+/*
+ * Registration Reply sent by a home agent to a mobile node in
+ * response to a registration request.
+ */
+typedef struct registration_reply_str {
+ uchar_t type; /* must be REG_TYPE_REP */
+ uchar_t code; /* refer to draft document */
+ ushort_t lifetime; /* 0 = dereg; 0xffff = infinity */
+ in_addr_t home_addr; /* address of the mobile node */
+ in_addr_t home_agent_addr; /* address of the home agent */
+ ident_t identification; /* derived from request's field */
+} regrep_t;
+
+/* service ok */
+#define REPLY_CODE_ACK 0
+#define REPLY_CODE_ACK_NO_SIMULTANEOUS 1
+
+/* denied by FA */
+#define REPLY_CODE_FA_NACK_UNSPECIFIED 64
+#define REPLY_CODE_FA_NACK_PROHIBITED 65
+#define REPLY_CODE_FA_NACK_RESOURCES 66
+#define REPLY_CODE_FA_NACK_MN_AUTH 67
+#define REPLY_CODE_FA_NACK_HA_AUTH 68
+#define REPLY_CODE_FA_NACK_LIFETIME 69
+#define REPLY_CODE_FA_NACK_BAD_REQUEST 70
+#define REPLY_CODE_FA_NACK_BAD_REPLY 71
+#define REPLY_CODE_FA_NACK_ENCAP_UNAVAILABLE 72
+#define REPLY_CODE_FA_NACK_VJ_UNAVAILABLE 73
+#define REPLY_CODE_FA_NACK_BIDIR_TUNNEL_UNAVAILABLE 74
+#define REPLY_CODE_FA_NACK_BIDIR_TUNNEL_NO_TBIT 75
+#define REPLY_CODE_FA_NACK_BIDIR_TUNNEL_TOO_DISTANT 76
+#define REPLY_CODE_FA_NACK_ICMP_HA_NET_UNREACHABLE 80
+#define REPLY_CODE_FA_NACK_ICMP_HA_HOST_UNREACHABLE 81
+#define REPLY_CODE_FA_NACK_ICMP_HA_PORT_UNREACHABLE 82
+#define REPLY_CODE_FA_NACK_ICMP_HA_UNREACHABLE 88
+#define REPLY_CODE_FA_NACK_UNIQUE_HOMEADDR_REQD 96
+#define REPLY_CODE_FA_NACK_MISSING_NAI 97
+#define REPLY_CODE_FA_NACK_MISSING_HOME_AGENT 98
+#define REPLY_CODE_FA_NACK_MISSING_HOMEADDR 99
+#define REPLY_CODE_FA_NACK_UNKNOWN_CHALLENGE 104
+#define REPLY_CODE_FA_NACK_MISSING_CHALLENGE 105
+#define REPLY_CODE_FA_NACK_MISSING_MN_FA 106
+
+/* denied by HA */
+#define REPLY_CODE_HA_NACK_UNSPECIFIED 128
+#define REPLY_CODE_HA_NACK_PROHIBITED 129
+#define REPLY_CODE_HA_NACK_RESOURCES 130
+#define REPLY_CODE_HA_NACK_MN_AUTH 131
+#define REPLY_CODE_HA_NACK_FA_AUTH 132
+#define REPLY_CODE_HA_NACK_ID_MISMATCH 133
+#define REPLY_CODE_HA_NACK_BAD_REQUEST 134
+#define REPLY_CODE_HA_NACK_TOO_MANY_BINDINGS 135
+#define REPLY_CODE_HA_NACK_BAD_HA_ADDRESS 136
+#define REPLY_CODE_HA_NACK_BIDIR_TUNNEL_UNAVAILABLE 137
+#define REPLY_CODE_HA_NACK_BIDIR_TUNNEL_NO_TBIT 138
+#define REPLY_CODE_HA_NACK_BIDIR_ENCAP_UNAVAILABLE 139
+
+/*
+ * OTHER EXTENSIONS
+ */
+
+/*
+ * The second set consists of those extensions which may appear only
+ * in ICMP Router Discovery messages [4]. Currently, Mobile IP
+ * defines the following Types for Extensions appearing in ICMP
+ * Router Discovery messages:
+ *
+ * 0 One-byte PaddingOne-byte Padding (encoded with no Length nor
+ * Data field)
+ * 16 Mobility Agent Advertisement
+ * 19 Prefix-Lengths
+ */
+#define ICMP_ADV_MSG_PADDING_EXT 0
+#define ICMP_ADV_MSG_MOBILITY_AGT_EXT 16
+#define ICMP_ADV_MSG_PREFIX_LENGTH_EXT 19
+#define ICMP_ADV_MSG_FA_CHALLENGE 24
+#define ICMP_ADV_MSG_FA_NAI 25
+
+
+/*
+ * Mobility Agent Advertisement Extension
+ * The Mobility Agent Adv Extension follows the ICMP Router
+ * Advertisement fields.It is used to indicate that an ICMP Router
+ * Advertisement message is also an Agent Advertisement being sent
+ * by a mobility agent.
+ *
+ * Type 16
+ * Length (6 + 4*N), where N is the number of care-of addresses
+ * advertised.
+ *
+ * Sequence Number
+ * The count of Agent Advertisement messages sent since the
+ * agent was initialized (Section 2.3.2).
+ *
+ * Registration Lifetime
+ * The longest lifetime (measured in seconds) that this
+ * agent is willing to accept in any Registration Request.
+ * A value of 0xffff indicates infinity. This field has no
+ * relation to the "Lifetime" field within the ICMP Router
+ * Advertisement portion of the Agent Advertisement.
+ *
+ * R Registration required. Registration with this foreign
+ * agent (or another foreign agent on this link) is required
+ * rather than using a co-located care-of address.
+ *
+ * B Busy. The foreign agent will not accept registrations
+ * from additional mobile nodes.
+ *
+ * H Home agent. This agent offers service as a home agent
+ * on the link on which this Agent Advertisement message is
+ * sent.
+ *
+ * F Foreign agent. This agent offers service as a foreign
+ * agent on the link on which this Agent Advertisement
+ * message is sent.
+ *
+ * M Minimal encapsulation. This agent implements receiving
+ * tunneled datagrams that use minimal encapsulation [15].
+ *
+ * G GRE encapsulation. This agent implements receiving
+ * tunneled datagrams that use GRE encapsulation [8].
+ *
+ * V Van Jacobson header compression. This agent supports use
+ * of Van Jacobson header compression [10] over the link
+ * with any registered mobile node.
+ *
+ * reserved sent as zero; ignored on reception.
+ *
+ * Care-of Address(es)
+ * The advertised foreign agent care-of address(es) provided
+ * by this foreign agent. An Agent Advertisement MUST
+ * include at least one care-of address if the 'F' bit
+ * is set. The number of care-of addresses present is
+ * determined by the Length field in the Extension.
+ *
+ * A HA must always be prepared to serve the mobile nodes for
+ * which it is the home agent. A FA may at times be too busy
+ * to serve additional MNs; even so, it must continue to send
+ * Agent Advertisements, so that any mobile nodes already registered
+ * with it will know that they have not moved out of range of the
+ * foreign agent and that the has not failed. A foreign
+ * agent may indicate that it is "too busy" to allow new MNs to
+ * register with it, by setting the 'B' bit in its Agent Adv.
+ * An Agent Adv message MUST NOT have the 'B' bit set if the
+ * 'F' bit is not also set, and at least one of the 'F' bit and the
+ * 'H' bit MUST be set in any Agent Advertisement message sent.
+ *
+ * When a FA wishes to require registration even from those
+ * mobile nodes which have acquired a co-located care-of address, it
+ * sets the 'R' bit to one. Because this bit applies only to foreign
+ * agents, an agent MUST NOT set the 'R' bit to one unless the 'F'
+ * bit is also set to one.
+ */
+#ifdef __sparc
+#ifdef _BIT_FIELDS_HTOL
+typedef struct mobility_agt_adv_extension {
+ uchar_t type;
+ uchar_t length;
+ ushort_t sequence_num;
+ ushort_t reg_lifetime;
+ ushort_t reg_bit:1,
+ busy_bit:1,
+ ha_bit:1,
+ fa_bit:1,
+ minencap_bit:1,
+ greencap_bit:1,
+ vanjacob_hdr_comp_bit:1,
+ reverse_tunnel_bit:1,
+ reserved:8;
+} mobagtadvext_t;
+
+#endif /* _BIT_FIELDS_HTOL */
+#endif /* __sparc */
+
+#ifdef __i386
+#ifdef _BIT_FIELDS_LTOH
+typedef struct mobility_agt_adv_extension {
+ uchar_t type;
+ uchar_t length;
+ ushort_t sequence_num;
+ ushort_t reg_lifetime;
+ uchar_t
+ reverse_tunnel_bit:1,
+ vanjacob_hdr_comp_bit:1,
+ greencap_bit:1,
+ minencap_bit:1,
+ fa_bit:1,
+ ha_bit:1,
+ busy_bit:1,
+ reg_bit:1;
+ uchar_t reserved;
+} mobagtadvext_t;
+#endif /* _BIT_FIELDS_LTOH */
+#endif /* __i386 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SNOOP_MIP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mount.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mount.c
new file mode 100644
index 0000000..ce2df1f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_mount.c
@@ -0,0 +1,563 @@
+/*
+ * 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 (c) 1991, 1999-2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <sys/tiuser.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <nfs/nfs.h>
+#include <rpcsvc/mount.h>
+#include <string.h>
+#include "snoop.h"
+#include "snoop_nfs.h"
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+static void mountcall(int, int);
+static void mountreply(int, int);
+
+static void sum_mountstat(char *);
+static void sum_mountstat3(char *);
+static char *sum_mountfh(void);
+static char *sum_mountfh3(void);
+static char *sum_exports(void);
+static char *sum_mounts(void);
+
+static int detail_mountstat(void);
+static void detail_mountstat3(void);
+static void detail_mountfh(void);
+static void detail_mountfh3(void);
+static void detail_exports(void);
+static void detail_mounts(void);
+
+static char *statusmsg3(ulong_t);
+
+static char *procnames_short[] = {
+ "Null", /* 0 */
+ "Mount", /* 1 */
+ "Get mount list", /* 2 */
+ "Unmount", /* 3 */
+ "Unmountall", /* 4 */
+ "Get export list", /* 5 */
+ "Get export list", /* 6 */
+ "PATHCONF", /* 7 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Add mount entry", /* 1 */
+ "Return mount entries", /* 2 */
+ "Remove mount entry", /* 3 */
+ "Remove all mount entries", /* 4 */
+ "Return export list", /* 5 */
+ "Return export list", /* 6 */
+ "Get POSIX Pathconf info", /* 7 */
+};
+
+#define MAXPROC 7
+
+void
+interpret_mount(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[MNTPATHLEN + 1];
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "MOUNT%d C %s",
+ vers, procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case MOUNTPROC_MNT:
+ case MOUNTPROC_UMNT:
+ (void) sprintf(line, " %s",
+ getxdr_string(buff, MNTPATHLEN));
+ break;
+ case MOUNTPROC_DUMP:
+ case MOUNTPROC_UMNTALL:
+ case MOUNTPROC_EXPORT:
+ case MOUNTPROC_EXPORTALL:
+#ifdef MOUNTPROC_PATHCONF
+ case MOUNTPROC_PATHCONF:
+ if (vers != 3)
+ (void) sprintf(line, " %s",
+ getxdr_string(buff,
+ MNTPATHLEN));
+#endif
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "MOUNT%d R %s ",
+ vers, procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case MOUNTPROC_MNT:
+ if (vers == 3)
+ sum_mountstat3(line);
+ else
+ sum_mountstat(line);
+ break;
+ case MOUNTPROC_DUMP:
+ (void) sprintf(line, sum_mounts());
+ break;
+ case MOUNTPROC_UMNT:
+ case MOUNTPROC_UMNTALL:
+ (void) sprintf(line, "reply");
+ break;
+ case MOUNTPROC_EXPORTALL:
+ /*
+ * EXPORTALL is the same as EXPORT in v1
+ * and v2, and it doesn't exist in v3.
+ */
+ if (vers == 3)
+ break;
+ /*FALLTHROUGH*/
+ case MOUNTPROC_EXPORT:
+ (void) sprintf(line, sum_exports());
+ break;
+#ifdef MOUNTPROC_PATHCONF
+ case MOUNTPROC_PATHCONF:
+ if (vers != 2)
+ break;
+#ifdef notyet
+ (void) sprintf(line, sum_ppathcnf());
+#endif
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("MOUNT:", "NFS MOUNT", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+ if (type == CALL)
+ mountcall(proc, vers);
+ else
+ mountreply(proc, vers);
+ show_trailer();
+ }
+}
+
+/*
+ * Interpret call packets in detail
+ */
+
+static void
+mountcall(proc, vers)
+ int proc, vers;
+{
+
+ switch (proc) {
+ case MOUNTPROC_MNT:
+ case MOUNTPROC_UMNT:
+ (void) showxdr_string(MNTPATHLEN, "Directory = %s");
+ break;
+ case MOUNTPROC_DUMP:
+ break;
+ case MOUNTPROC_UMNTALL:
+ break;
+ case MOUNTPROC_EXPORTALL:
+ if (vers == 3)
+ break;
+ break;
+ case MOUNTPROC_EXPORT:
+ break;
+#ifdef MOUNTPROC_PATHCONF
+ case MOUNTPROC_PATHCONF:
+ if (vers != 2)
+ break;
+ (void) showxdr_string(MNTPATHLEN, "File = %s");
+#endif
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Interpret reply packets in detail
+ */
+
+static void
+mountreply(proc, vers)
+ int proc, vers;
+{
+
+ switch (proc) {
+ case MOUNTPROC_MNT:
+ if (vers == 3) {
+ detail_mountstat3();
+ } else {
+ if (detail_mountstat() == 0) {
+ detail_mountfh();
+ }
+ }
+ break;
+ case MOUNTPROC_DUMP:
+ detail_mounts();
+ break;
+ case MOUNTPROC_UMNT:
+ case MOUNTPROC_UMNTALL:
+ (void) detail_mountstat();
+ break;
+ case MOUNTPROC_EXPORTALL:
+ if (vers == 3)
+ break;
+ /*FALLTHROUGH*/
+ case MOUNTPROC_EXPORT:
+ detail_exports();
+ break;
+#ifdef MOUNTPROC_PATHCONF
+ case MOUNTPROC_PATHCONF:
+#ifdef notyet
+ (void) detail_ppathcnf();
+#endif
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+static void
+sum_mountstat(line)
+ char *line;
+{
+ ulong_t status;
+ char *str;
+
+ status = getxdr_u_long();
+ if (status == 0)
+ str = "OK";
+ else if ((str = strerror(status)) == (char *)NULL)
+ str = "";
+ (void) strcpy(line, str);
+ if (status == 0) {
+ (void) strcat(line, sum_mountfh());
+ }
+}
+
+static int
+detail_mountstat()
+{
+ ulong_t status;
+ char *str;
+
+ status = getxdr_u_long();
+ if (status == 0)
+ str = "OK";
+ else if ((str = strerror(status)) == (char *)NULL)
+ str = "";
+
+ (void) sprintf(get_line(0, 0), "Status = %d (%s)", status, str);
+
+ return ((int)status);
+}
+
+char *
+sum_mountfh()
+{
+ int fh;
+ static char buff[8];
+
+ fh = sum_filehandle(NFS_FHSIZE);
+ (void) sprintf(buff, " FH=%04X", fh & 0xFFFF);
+ return (buff);
+}
+
+static void
+detail_mountfh()
+{
+ int pos;
+ int fh;
+
+ pos = getxdr_pos();
+ fh = sum_filehandle(NFS_FHSIZE);
+ setxdr_pos(pos);
+ (void) sprintf(get_line(0, 0), "File handle = [%04X]", fh & 0xFFFF);
+ (void) showxdr_hex(NFS_FHSIZE, " %s");
+}
+
+static char *
+print_auth()
+{
+ int i, auth, flavors;
+ char *p;
+ static char buff[64];
+
+ buff[0] = '\0';
+ flavors = getxdr_long();
+ for (i = 0; i < flavors; i++) {
+ if (i > 0)
+ (void) strlcat(buff, ",", sizeof (buff));
+ switch (auth = getxdr_u_long()) {
+ case AUTH_NONE:
+ (void) strlcat(buff, "none", sizeof (buff));
+ break;
+ case AUTH_UNIX:
+ (void) strlcat(buff, "unix", sizeof (buff));
+ break;
+ case AUTH_SHORT:
+ (void) strlcat(buff, "short", sizeof (buff));
+ break;
+ case AUTH_DES:
+ (void) strlcat(buff, "des", sizeof (buff));
+ break;
+ default:
+ p = buff + strlen(buff);
+ if (p < &buff[sizeof (buff)])
+ (void) snprintf(p, sizeof (buff) - strlen(buff),
+ "%d", auth);
+ break;
+ }
+ }
+ return (buff);
+}
+
+static void
+sum_mountstat3(line)
+ char *line;
+{
+ ulong_t status;
+
+ status = getxdr_u_long();
+ (void) strcpy(line, statusmsg3(status));
+ if (status == 0) {
+ (void) strcat(line, sum_mountfh3());
+ (void) strcat(line, " Auth=");
+ (void) strcat(line, print_auth());
+ }
+}
+
+static void
+detail_mountstat3()
+{
+ ulong_t status;
+
+ status = getxdr_u_long();
+ (void) sprintf(get_line(0, 0), "Status = %d (%s)", status,
+ statusmsg3(status));
+ if (status == 0) {
+ detail_mountfh3();
+ (void) sprintf(get_line(0, 0), "Authentication flavor = %s",
+ print_auth());
+ }
+}
+
+char *
+sum_mountfh3()
+{
+ int len;
+ int fh;
+ static char buff[8];
+
+ len = getxdr_long();
+ fh = sum_filehandle(len);
+ (void) sprintf(buff, " FH=%04X", fh & 0xFFFF);
+ return (buff);
+}
+
+static void
+detail_mountfh3()
+{
+ int pos;
+ int i, l, len;
+ int fh;
+
+ len = getxdr_long();
+ pos = getxdr_pos();
+ fh = sum_filehandle(len);
+ setxdr_pos(pos);
+ (void) sprintf(get_line(0, 0), "File handle = [%04X]", fh & 0xFFFF);
+ i = 0;
+ while (i < len) {
+ l = MIN(len - i, 32);
+ (void) showxdr_hex(l, " %s");
+ i += l;
+ }
+}
+
+static char *
+sum_exports()
+{
+ static char buff[MNTPATHLEN + 1];
+ int entries = 0;
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, "%d+ entries", entries);
+ return (buff);
+ }
+
+ while (getxdr_long()) {
+ (void) getxdr_string(buff, MNTPATHLEN);
+ while (getxdr_long()) {
+ (void) getxdr_string(buff, MNTNAMLEN);
+ }
+ entries++;
+ }
+
+ (void) sprintf(buff, "%d entries", entries);
+ return (buff);
+}
+
+static void
+detail_exports()
+{
+ int entries = 0;
+ char *dirpath, *grpname;
+ char buff[MNTPATHLEN + 1];
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ entries. (Frame is incomplete)",
+ entries);
+ return;
+ }
+
+ while (getxdr_long()) {
+ dirpath = (char *)getxdr_string(buff, MNTPATHLEN);
+ (void) sprintf(get_line(0, 0), "Directory = %s", dirpath);
+ entries++;
+ while (getxdr_long()) {
+ grpname = (char *)getxdr_string(buff, MNTNAMLEN);
+ (void) sprintf(get_line(0, 0), " Group = %s", grpname);
+ }
+ }
+}
+
+static char *
+sum_mounts()
+{
+ int entries = 0;
+ static char buff[MNTPATHLEN + 1];
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, "%d+ entries", entries);
+ return (buff);
+ }
+
+ while (getxdr_long()) {
+ (void) getxdr_string(buff, MNTNAMLEN);
+ (void) getxdr_string(buff, MNTPATHLEN);
+ entries++;
+ }
+
+ (void) sprintf(buff, "%d entries", entries);
+ return (buff);
+}
+
+static void
+detail_mounts()
+{
+ int entries = 0;
+ char *hostname, *directory;
+ char buff1[MNTNAMLEN + 1], buff2[MNTPATHLEN + 1];
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ entries. (Frame is incomplete)",
+ entries);
+ return;
+ }
+
+ (void) sprintf(get_line(0, 0), "Mount list");
+
+ while (getxdr_long()) {
+ hostname = (char *)getxdr_string(buff1, MNTNAMLEN);
+ directory = (char *)getxdr_string(buff2, MNTPATHLEN);
+ (void) sprintf(get_line(0, 0), " %s:%s", hostname, directory);
+ entries++;
+ }
+}
+
+char *
+statusmsg3(status)
+ ulong_t status;
+{
+
+ switch (status) {
+ case MNT_OK:
+ return ("OK");
+ case MNT3ERR_PERM:
+ return ("Not owner");
+ case MNT3ERR_NOENT:
+ return ("No such file or directory");
+ case MNT3ERR_IO:
+ return ("I/O error");
+ case MNT3ERR_ACCES:
+ return ("Permission denied");
+ case MNT3ERR_NOTDIR:
+ return ("Not a directory");
+ case MNT3ERR_INVAL:
+ return ("Invalid argument");
+ case MNT3ERR_NAMETOOLONG:
+ return ("File name too long");
+ case MNT3ERR_NOTSUPP:
+ return ("Operation not supported");
+ case MNT3ERR_SERVERFAULT:
+ return ("Server error");
+ default:
+ return ("(unknown error)");
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nbp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nbp.c
new file mode 100644
index 0000000..fbdaa94
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nbp.c
@@ -0,0 +1,145 @@
+/*
+ * 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 (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <at.h>
+#include <snoop.h>
+
+static void show_nbp_tuples(uint8_t *, int, uint8_t *);
+
+static char *nbp_short[] = {
+ "0", /* 0 */
+ "BRRQ ", /* 1 */
+ "LKUP C ", /* 2 */
+ "LKUP R ", /* 3 */
+ "FWD ", /* 4 */
+ "5 ",
+ "6 ",
+ "7 ",
+ "8 ",
+ "9 ",
+ "10 ",
+ "11 ",
+ "RGSTR ", /* 12 */
+ "UNRGSTR", /* 13 */
+ "OK ", /* 14 */
+ "ERROR ", /* 15 */
+};
+
+void
+interpret_nbp(int flags, struct nbp_hdr *nbp, int len)
+{
+ uint8_t *data;
+ int nbp_cnt = nbp->nbp_fun_cnt & 0xf; /* lower four bits */
+ int nbp_op = (nbp->nbp_fun_cnt >> 4) & 0xf; /* upper four bits */
+
+ data = (uint8_t *)(nbp + 1);
+
+ if (flags & F_SUM) {
+ if (len < sizeof (struct nbp_hdr)) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "NBP (short packet)");
+ return;
+ }
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "NBP F=%s CNT=%d ID=%d", nbp_short[nbp_op],
+ nbp_cnt, nbp->nbp_id);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NBP: ", "NBP Header", len);
+ show_space();
+
+ if (len < sizeof (struct nbp_hdr)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "NBP (short packet)");
+ return;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length = %d", len);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Func = %d (%s)", nbp_op, nbp_short[nbp_op]);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Tuple count = %d", nbp_cnt);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Id = %d", nbp->nbp_id);
+ show_nbp_tuples(data, nbp_cnt, ((uint8_t *)nbp) + len);
+ }
+}
+
+static void
+show_nbp_tuples(uint8_t *p, int tuples, uint8_t *tail)
+{
+ uint16_t net;
+ uint8_t node;
+ uint8_t sock;
+ uint8_t enumer;
+ char obj[100];
+ char *op;
+ char *otail = &obj[sizeof (obj)];
+
+ while (tuples--) {
+ op = obj;
+ if ((p + 5) > tail)
+ goto out;
+ net = get_short(p);
+ p += 2;
+ node = *p++;
+ sock = *p++;
+ enumer = *p++;
+
+ if (p > tail || &p[1]+p[0] > tail)
+ goto out;
+ op += snprintf(op, otail-op, "%.*s", p[0], &p[1]);
+
+ p = &p[1]+p[0];
+ if (p > tail || &p[1]+p[0] > tail)
+ goto out;
+ op += snprintf(op, otail-op, ":%.*s", p[0], &p[1]);
+
+ p = &p[1]+p[0];
+ if (p > tail || &p[1]+p[0] > tail)
+ goto out;
+ (void) snprintf(op, otail-op, "@%.*s", p[0], &p[1]);
+ p = &p[1]+p[0];
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Name = \"%s\"", obj);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Net = %d, node = %d, sock = %d, enum = %d",
+ net, node, sock, enumer);
+ }
+ return;
+out:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "NBP (short tuple)");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_netbios.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_netbios.c
new file mode 100644
index 0000000..2e8f8a9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_netbios.c
@@ -0,0 +1,555 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * References used throughout this code:
+ *
+ * [RFC1001] : PROTOCOL STANDARD FOR A NetBIOS SERVICE
+ * ON A TCP/UDP TRANSPORT:
+ * CONCEPTS AND METHODS
+ * NetBIOS Working Group, March 1987
+ *
+ * [RFC1002] : PROTOCOL STANDARD FOR A NetBIOS SERVICE
+ * ON A TCP/UDP TRANSPORT:
+ * DETAILED SPECIFICATIONS
+ * NetBIOS Working Group, March 1987
+ */
+
+#include <fcntl.h>
+#include "snoop.h"
+#include <stdio.h>
+#include <ctype.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+char *show_type();
+
+/* See snoop_smb.c */
+extern void interpret_smb(int flags, uchar_t *data, int len);
+
+/*
+ * NBT Session Packet Header
+ * [RFC 1002, Sec. 4.3.1]
+ */
+struct nbt_ss {
+ uchar_t type;
+ uchar_t flags;
+ ushort_t length;
+};
+
+/*
+ * NBT Session Request Packet trailer
+ * [RFC 1002, Sec. 4.3.2]
+ */
+struct callnames {
+ uchar_t space; /* padding */
+ uchar_t calledname[32];
+ uchar_t nullchar; /* padding */
+ uchar_t space2; /* padding */
+ uchar_t callingname[32];
+ uchar_t nullchar2; /* padding */
+};
+
+
+static void interpret_netbios_names(int flags, uchar_t *data, int len,
+ char *xtra);
+static void netbiosname2ascii(char *asciiname, uchar_t *netbiosname);
+
+/*
+ * Helpers to read network-order values,
+ * with NO alignment assumed.
+ */
+static ushort_t
+getshort(uchar_t *p) {
+ return (p[1] + (p[0]<<8));
+}
+static uint_t
+getlong(uchar_t *p)
+{
+ return (p[3] + (p[2]<<8) + (p[1]<<16) + (p[0]<<24));
+}
+
+/*
+ * NM_FLAGS fields in the NetBIOS Name Service Packet header.
+ * [RFC 1002, Sec. 4.2.1.1]
+ */
+static void
+print_flag_details(int headerflags)
+{
+ if (headerflags & 1<<4)
+ sprintf(get_line(0, 0), " - Broadcast");
+ if (headerflags & 1<<7)
+ sprintf(get_line(0, 0), " - Recursion Available");
+ if (headerflags & 1<<8)
+ sprintf(get_line(0, 0), " - Recursion Desired");
+ if (headerflags & 1<<9)
+ sprintf(get_line(0, 0), " - Truncation Flag");
+ if (headerflags & 1<<10)
+ sprintf(get_line(0, 0), " - Authoritative Answer");
+}
+
+/*
+ * Possible errors in NetBIOS name service packets.
+ * [RFC 1002, Sec. 4.2.6, 4.2.11, 4.2.14]
+ */
+static void
+getrcodeerr(int headerflags, char *errortype)
+{
+ int error = (headerflags & 0xf);
+
+ switch (error) {
+ case 0:
+ sprintf(errortype, "Success");
+ break;
+ case 1:
+ sprintf(errortype, "Format Error");
+ break;
+ case 2:
+ sprintf(errortype, "Server Failure");
+ break;
+ case 3:
+ sprintf(errortype, "Name Error");
+ break;
+ case 4:
+ sprintf(errortype, "Unsupported Request Error");
+ break;
+ case 5:
+ sprintf(errortype, "Refused Error");
+ break;
+ case 6:
+ sprintf(errortype, "Active Error");
+ break;
+ case 7:
+ sprintf(errortype, "Name in Conflict Error");
+ break;
+ default:
+ sprintf(errortype, "Unknown Error");
+ break;
+ }
+}
+
+/*
+ * OPCODE fields in the NetBIOS Name Service Packet header.
+ * [RFC 1002, Sec. 4.2.1.1]
+ */
+static void
+print_ns_type(int flags, int headerflags, char *xtra)
+{
+ int opcode = (headerflags & 0x7800)>>11;
+ int response = (headerflags & 1<<15);
+ char *resptype = response ? "Response" : "Request";
+ char *optype;
+
+ switch (opcode) {
+ case 0:
+ optype = "Query";
+ break;
+ case 5:
+ optype = "Registration";
+ break;
+ case 6:
+ optype = "Release";
+ break;
+ case 7:
+ optype = "WACK";
+ break;
+ case 8:
+ optype = "Refresh";
+ break;
+ default:
+ optype = "Unknown";
+ break;
+ }
+
+ if (flags & F_DTAIL)
+ sprintf(get_line(0, 0), "Type = %s %s", optype, resptype);
+ else
+ sprintf(xtra, "%s %s", optype, resptype);
+}
+
+
+/*
+ * Interpret Datagram Packets
+ * [RFC 1002, Sec. 4.4]
+ */
+void
+interpret_netbios_datagram(int flags, uchar_t *data, int len)
+{
+ char name[24];
+ int packettype = data[0];
+ int packetlen;
+ data++;
+
+ if (packettype < 0x10 || packettype > 0x11)
+ return;
+
+ if (flags & F_SUM) {
+ data += 14;
+ netbiosname2ascii(name, data);
+ sprintf(get_sum_line(),
+ "NBT Datagram Service Type=%d Source=%s",
+ packettype, name);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NBT: ", "Netbios Datagram Service Header", len);
+ show_space();
+ sprintf(get_line(0, 0), "Datagram Packet Type = 0x%.2x",
+ packettype);
+ sprintf(get_line(0, 0), "Datagram Flags = 0x%.2x",
+ data[0]);
+ data++;
+ sprintf(get_line(0, 0), "Datagram ID = 0x%.4x",
+ getshort(data));
+ data += 2;
+ sprintf(get_line(0, 0), "Source IP = %d.%d.%d.%d",
+ data[0], data[1], data[2], data[3]);
+ data += 4;
+ sprintf(get_line(0, 0), "Source Port = %d",
+ getshort(data));
+ data += 2;
+ packetlen = getshort(data);
+ sprintf(get_line(0, 0), "Datagram Length = 0x%.4x",
+ packetlen);
+ data += 2;
+ sprintf(get_line(0, 0), "Packet Offset = 0x%.4x",
+ getshort(data));
+ data += 3;
+ netbiosname2ascii(name, data);
+ sprintf(get_line(0, 0), "Source Name = %s", name);
+ data += 34;
+ netbiosname2ascii(name, data);
+ sprintf(get_line(0, 0), "Destination Name = %s", name);
+ sprintf(get_line(0, 0), "Number of data bytes remaining = %d",
+ packetlen - 68);
+ show_trailer();
+ }
+}
+
+/*
+ * Interpret NetBIOS Name Service packets.
+ * [RFC 1002, Sec. 4.2]
+ */
+void
+interpret_netbios_ns(int flags, uchar_t *data, int len)
+{
+ int headerflags, qcount, acount, nscount, arcount;
+ int transid;
+ char name[24];
+ char extra[256];
+ char errortype[50];
+ int rdatalen;
+ int rrflags;
+ int nameptr;
+ int nodecode;
+ char *nodetype;
+ uchar_t *data0 = data;
+
+ transid = getshort(data); data += 2;
+ headerflags = getshort(data); data += 2;
+ qcount = getshort(data); data += 2;
+ acount = getshort(data); data += 2;
+ nscount = getshort(data); data += 2;
+ arcount = getshort(data); data += 2;
+ getrcodeerr(headerflags, errortype);
+
+ if (flags & F_SUM) {
+ print_ns_type(flags, headerflags, extra);
+ data++;
+ netbiosname2ascii(name, data);
+ sprintf(get_sum_line(), "NBT NS %s for %s, %s",
+ extra, name, errortype);
+
+ }
+
+
+ if (flags & F_DTAIL) {
+ show_header("NBT: ", "Netbios Name Service Header", len);
+ show_space();
+ print_ns_type(flags, headerflags, 0);
+ sprintf(get_line(0, 0), "Status = %s", errortype);
+ sprintf(get_line(0, 0), "Transaction ID = 0x%.4x", transid);
+ sprintf(get_line(0, 0), "Flags Summary = 0x%.4x",
+ headerflags);
+ print_flag_details(headerflags);
+ sprintf(get_line(0, 0), "Question count = %d", qcount);
+ sprintf(get_line(0, 0), "Answer Count = %d", acount);
+ sprintf(get_line(0, 0), "Name Service Count = %d", nscount);
+ sprintf(get_line(0, 0),
+ "Additional Record Count = %d", arcount);
+
+ /*
+ * Question Section Packet Description from
+ * [RFC 1002, Sec. 4.2.1.2]
+ */
+
+ if (qcount) {
+ data++;
+ netbiosname2ascii(name, data);
+ sprintf(get_line(0, 0), "Question Name = %s", name);
+ data += 33;
+ sprintf(get_line(0, 0), "Question Type = 0x%.4x",
+ getshort(data));
+ data += 2;
+ sprintf(get_line(0, 0), "Question Class = 0x%.4x",
+ getshort(data));
+ data += 2;
+ }
+
+ /*
+ * Resrouce Record Packet Description from
+ * [RFC 1002, Sec. 4.2.1.3]
+ */
+
+ if ((acount || nscount || arcount) ||
+ (qcount+acount+nscount+arcount == 0)) {
+ /* Second level encoding from RFC883 (p.31, 32) */
+ if (data[0] & 0xc0) {
+ nameptr = getshort(data)&0x3fff;
+ netbiosname2ascii(name, (data0+nameptr+1));
+ sprintf(get_line(0, 0),
+ "Resource Record Name = %s", name);
+ data += 2;
+ } else {
+ data++;
+ netbiosname2ascii(name, data);
+ sprintf(get_line(0, 0),
+ "Resource Record Name = %s", name);
+ data += 33;
+ }
+ sprintf(get_line(0, 0),
+ "Resource Record Type = 0x%.4x",
+ getshort(data));
+ data += 2;
+ sprintf(get_line(0, 0),
+ "Resource Record Class = 0x%.4x",
+ getshort(data));
+ data += 2;
+ sprintf(get_line(0, 0),
+ "Time to Live (Milliseconds) = %d",
+ getlong(data));
+ data += 4;
+ rdatalen = getshort(data);
+ sprintf(get_line(0, 0), "RDATA Length = 0x%.4x",
+ rdatalen);
+ data += 2;
+ /* 15.4.2.1.3 */
+ if (rdatalen == 6) {
+ rrflags = getshort(data);
+ data += 2;
+ sprintf(get_line(0, 0),
+ "Resource Record Flags = 0x%.4x",
+ rrflags);
+ nodecode = (rrflags>>13)& 0x11;
+ if (nodecode == 0) nodetype = "B";
+ if (nodecode == 1) nodetype = "P";
+ if (nodecode == 2) nodetype = "M";
+ sprintf(get_line(0, 0), " - %s, %s node",
+ (rrflags & 1<<15) ?
+ "Group NetBIOS Name":
+ "Unique NetBIOS Name", nodetype);
+ sprintf(get_line(0, 0),
+ "Owner IP Address = %d.%d.%d.%d",
+ data[0], data[1], data[2], data[3]);
+ }
+ }
+ show_trailer();
+
+ }
+}
+
+/*
+ * Interpret NetBIOS session packets.
+ * [RFC 1002, Sec. 4.3]
+ */
+void
+interpret_netbios_ses(int flags, uchar_t *data, int len)
+{
+ struct nbt_ss *ss;
+ uchar_t *trailer;
+ int length = len - 4; /* NBT packet length without header */
+ char *type;
+ char extrainfo[300];
+
+ if (len < sizeof (struct nbt_ss))
+ return;
+
+ /*
+ * Packets that are fragments of a large NetBIOS session
+ * message will have no NetBIOS header. (Only the first
+ * TCP segment will have a NetBIOS header.) It turns out
+ * that very often, such fragments start with SMB data, so
+ * we should try to recognize and decode them.
+ */
+ if (data[0] == 0xff &&
+ data[1] == 'S' &&
+ data[2] == 'M' &&
+ data[3] == 'B') {
+ interpret_smb(flags, data, len);
+ return;
+ }
+
+ /* LINTED PTRALIGN */
+ ss = (struct nbt_ss *)data;
+ trailer = data + sizeof (*ss);
+ extrainfo[0] = '\0';
+
+ if (flags & F_SUM) {
+ switch (ss->type) {
+ case 0x00:
+ type = "SESSION MESSAGE";
+ break;
+ case 0x81:
+ type = "SESSION REQUEST";
+ interpret_netbios_names(flags, trailer,
+ length, extrainfo);
+ break;
+ case 0x82:
+ type = "POSITIVE SESSION RESPONSE";
+ break;
+ case 0x83:
+ type = "NEGATIVE SESSION RESPONSE";
+ break;
+ case 0x84:
+ type = "RETARGET SESSION RESPONSE";
+ break;
+ case 0x85:
+ type = "SESSION KEEP ALIVE";
+ break;
+ default:
+ type = "Unknown";
+ break;
+ }
+ (void) sprintf(get_sum_line(),
+ "NBT Type=%s %sLength=%d", type, extrainfo, length);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NBT: ", "NBT Header", len);
+ show_space();
+
+ switch (ss->type) {
+ case 0x00:
+ (void) sprintf(get_line(0, 0),
+ "Type = SESSION MESSAGE");
+ break;
+ case 0x81:
+ (void) sprintf(get_line(0, 0),
+ "Type = SESSION REQUEST");
+ interpret_netbios_names(flags, trailer, length, 0);
+ break;
+ case 0x82:
+ (void) sprintf(get_line(0, 0),
+ "Type = POSITIVE SESSION RESPONSE");
+ break;
+ case 0x83:
+ (void) sprintf(get_line(0, 0),
+ "Type = NEGATIVE SESSION RESPONSE");
+ break;
+ case 0x84:
+ (void) sprintf(get_line(0, 0),
+ "Type = RETARGET SESSION RESPONSE");
+ break;
+ case 0x85:
+ (void) sprintf(get_line(0, 0),
+ "Type = SESSION KEEP ALIVE");
+ break;
+ default:
+ (void) sprintf(get_line(0, 0),
+ "Type = Unknown");
+ break;
+ }
+
+ (void) sprintf(get_line(0, 0), "Length = %d bytes", length);
+ show_trailer();
+ }
+
+ /*
+ * SMB packets have { 0xff, 'S', 'M', 'B' }
+ * in the first four bytes. If we find that,
+ * let snoop_smb.c have a look at it.
+ */
+ if (ss->type == 0x00 &&
+ length > 0 &&
+ trailer[0] == 0xff &&
+ trailer[1] == 'S' &&
+ trailer[2] == 'M' &&
+ trailer[3] == 'B')
+ interpret_smb(flags, trailer, length);
+}
+
+/*
+ * NetBIOS name encoding (First Level Encoding)
+ * [RFC 1001, Sec. 4.1]
+ */
+static void
+netbiosname2ascii(char *aname, uchar_t *nbname)
+{
+ int c, i, j;
+
+ i = j = 0;
+ for (;;) {
+ c = nbname[i++] - 'A';
+ c = (c << 4) +
+ nbname[i++] - 'A';
+ /* 16th char is the "type" */
+ if (i >= 32)
+ break;
+ if (iscntrl(c))
+ c = '.';
+ if (c != ' ')
+ aname[j++] = c;
+ }
+ sprintf(&aname[j], "[%x]", c);
+}
+
+/*
+ * Interpret the names in a Session Request packet.
+ * [RFC 1002, Sec. 4.3.2]
+ */
+static void
+interpret_netbios_names(int flags, uchar_t *data, int len, char *xtra)
+{
+ char calledname[24];
+ char callingname[24];
+ struct callnames *names = (struct callnames *)data;
+
+ if (len < sizeof (*names))
+ return;
+
+ netbiosname2ascii(calledname, names->calledname);
+ netbiosname2ascii(callingname, names->callingname);
+
+ if (flags & F_SUM) {
+ sprintf(xtra, "Dest=%s Source=%s ", calledname, callingname);
+ }
+
+ if (flags & F_DTAIL) {
+ sprintf(get_line(0, 0), "Destination = %s", calledname);
+ sprintf(get_line(0, 0), "Source = %s", callingname);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.c
new file mode 100644
index 0000000..6f52c35
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.c
@@ -0,0 +1,691 @@
+/*
+ * 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-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/tiuser.h>
+#include <setjmp.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <string.h>
+#include "snoop.h"
+#include "snoop_nfs.h"
+
+#include <sys/stat.h>
+#include <rpcsvc/nfs_prot.h>
+
+static char *perms(int);
+static char *filetype(int);
+static char *sum_readdirres(void);
+static void detail_readdirres(void);
+static void detail_diroparg(void);
+static void nfscall2(int);
+static void nfsreply2(int);
+static void detail_mode(int);
+static void detail_sattr(void);
+static void interpret_nfs2(int, int, int, int, int, char *, int);
+
+extern jmp_buf xdr_err;
+
+static char *procnames_short[] = {
+ "NULL2", /* 0 */
+ "GETATTR2", /* 1 */
+ "SETATTR2", /* 2 */
+ "ROOT2", /* 3 */
+ "LOOKUP2", /* 4 */
+ "READLINK2", /* 5 */
+ "READ2", /* 6 */
+ "WRITECACHE2", /* 7 */
+ "WRITE2", /* 8 */
+ "CREATE2", /* 9 */
+ "REMOVE2", /* 10 */
+ "RENAME2", /* 11 */
+ "LINK2", /* 12 */
+ "SYMLINK2", /* 13 */
+ "MKDIR2", /* 14 */
+ "RMDIR2", /* 15 */
+ "READDIR2", /* 16 */
+ "STATFS2", /* 17 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Get file attributes", /* 1 */
+ "Set file attributes", /* 2 */
+ "Get root filehandle", /* 3 */
+ "Look up file name", /* 4 */
+ "Read from symbolic link", /* 5 */
+ "Read from file", /* 6 */
+ "Write to cache", /* 7 */
+ "Write to file", /* 8 */
+ "Create file", /* 9 */
+ "Remove file", /* 10 */
+ "Rename", /* 11 */
+ "Link", /* 12 */
+ "Make symbolic link", /* 13 */
+ "Make directory", /* 14 */
+ "Remove directory", /* 15 */
+ "Read from directory", /* 16 */
+ "Get filesystem attributes", /* 17 */
+};
+
+#define MAXPROC 17
+
+/* ARGSUSED */
+void
+interpret_nfs(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+
+ if (vers == 2) {
+ interpret_nfs2(flags, type, xid, vers, proc, data, len);
+ return;
+ }
+
+ if (vers == 3) {
+ interpret_nfs3(flags, type, xid, vers, proc, data, len);
+ return;
+ }
+
+ if (vers == 4) {
+ interpret_nfs4(flags, type, xid, vers, proc, data, len);
+ return;
+ }
+}
+
+static void
+interpret_nfs2(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[NFS_MAXPATHLEN + 1];
+ int off, sz;
+ char *fh;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "NFS C %s",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NFSPROC_GETATTR:
+ case NFSPROC_READLINK:
+ case NFSPROC_STATFS:
+ case NFSPROC_SETATTR:
+ (void) sprintf(line, sum_nfsfh());
+ break;
+ case NFSPROC_LOOKUP:
+ case NFSPROC_REMOVE:
+ case NFSPROC_RMDIR:
+ case NFSPROC_CREATE:
+ case NFSPROC_MKDIR:
+ fh = sum_nfsfh();
+ (void) sprintf(line, "%s %s",
+ fh,
+ getxdr_string(buff, NFS_MAXNAMLEN));
+ break;
+ case NFSPROC_WRITE:
+ fh = sum_nfsfh();
+ (void) getxdr_long(); /* beginoff */
+ off = getxdr_long();
+ (void) getxdr_long(); /* totalcount */
+ sz = getxdr_long();
+ (void) sprintf(line, "%s at %d for %d",
+ fh, off, sz);
+ break;
+ case NFSPROC_RENAME:
+ fh = sum_nfsfh();
+ (void) sprintf(line, "%s %s",
+ fh,
+ getxdr_string(buff, NFS_MAXNAMLEN));
+ line += strlen(line);
+ fh = sum_nfsfh();
+ (void) sprintf(line, " to%s %s",
+ fh,
+ getxdr_string(buff, NFS_MAXNAMLEN));
+ break;
+ case NFSPROC_LINK:
+ fh = sum_nfsfh();
+ (void) sprintf(line, "%s", fh);
+ line += strlen(line);
+ fh = sum_nfsfh();
+ (void) sprintf(line, " to%s %s",
+ fh,
+ getxdr_string(buff, NFS_MAXNAMLEN));
+ break;
+ case NFSPROC_SYMLINK:
+ fh = sum_nfsfh();
+ (void) sprintf(line, "%s %s",
+ fh,
+ getxdr_string(buff, NFS_MAXNAMLEN));
+ line += strlen(line);
+ (void) sprintf(line, " to %s",
+ getxdr_string(buff, NFS_MAXPATHLEN));
+ break;
+ case NFSPROC_READDIR:
+ fh = sum_nfsfh();
+ (void) sprintf(line, "%s Cookie=%lu",
+ fh, getxdr_u_long());
+ break;
+ case NFSPROC_READ:
+ fh = sum_nfsfh();
+ off = getxdr_long();
+ sz = getxdr_long();
+ (void) sprintf(line, "%s at %d for %d",
+ fh, off, sz);
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, (ulong_t)xid);
+ } else {
+ (void) sprintf(line, "NFS R %s ",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NFSPROC_CREATE:
+ case NFSPROC_MKDIR:
+ case NFSPROC_LOOKUP:
+ if (sum_nfsstat(line) == 0) {
+ line += strlen(line);
+ (void) sprintf(line, sum_nfsfh());
+ }
+ break;
+ case NFSPROC_READLINK:
+ if (sum_nfsstat(line) == 0) {
+ line += strlen(line);
+ (void) sprintf(line, " (Path=%s)",
+ getxdr_string(buff,
+ NFS_MAXPATHLEN));
+ }
+ break;
+ case NFSPROC_GETATTR:
+ case NFSPROC_SYMLINK:
+ case NFSPROC_STATFS:
+ case NFSPROC_SETATTR:
+ case NFSPROC_REMOVE:
+ case NFSPROC_RMDIR:
+ case NFSPROC_WRITE:
+ case NFSPROC_RENAME:
+ case NFSPROC_LINK:
+ (void) sum_nfsstat(line);
+ break;
+ case NFSPROC_READDIR:
+ if (sum_nfsstat(line) == 0) {
+ line += strlen(line);
+ (void) strcat(line, sum_readdirres());
+ }
+ break;
+ case NFSPROC_READ:
+ if (sum_nfsstat(line) == 0) {
+ line += strlen(line);
+ xdr_skip(68); /* fattrs */
+ (void) sprintf(line, " (%ld bytes)",
+ getxdr_long());
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NFS: ", "Sun NFS", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+ if (type == CALL)
+ nfscall2(proc);
+ else
+ nfsreply2(proc);
+ show_trailer();
+ }
+}
+
+/*
+ * Print out version 2 NFS call packets
+ */
+static void
+nfscall2(proc)
+ int proc;
+{
+ switch (proc) {
+ case NFSPROC_GETATTR:
+ case NFSPROC_READLINK:
+ case NFSPROC_STATFS:
+ detail_nfsfh();
+ break;
+ case NFSPROC_SETATTR:
+ detail_nfsfh();
+ detail_sattr();
+ break;
+ case NFSPROC_LOOKUP:
+ case NFSPROC_REMOVE:
+ case NFSPROC_RMDIR:
+ detail_diroparg();
+ break;
+ case NFSPROC_MKDIR:
+ case NFSPROC_CREATE:
+ detail_diroparg();
+ detail_sattr();
+ break;
+ case NFSPROC_WRITE:
+ detail_nfsfh();
+ (void) getxdr_long(); /* begoff */
+ (void) showxdr_long("Offset = %d");
+ (void) getxdr_long(); /* totalcount */
+ (void) showxdr_long("(%d bytes(s) of data)");
+ break;
+ case NFSPROC_RENAME:
+ detail_diroparg();
+ detail_diroparg();
+ break;
+ case NFSPROC_LINK:
+ detail_nfsfh();
+ detail_diroparg();
+ break;
+ case NFSPROC_SYMLINK:
+ detail_diroparg();
+ (void) showxdr_string(NFS_MAXPATHLEN, "Path = %s");
+ detail_sattr();
+ break;
+ case NFSPROC_READDIR:
+ detail_nfsfh();
+ (void) showxdr_u_long("Cookie = %lu");
+ (void) showxdr_long("Count = %d");
+ break;
+ case NFSPROC_READ:
+ detail_nfsfh();
+ (void) showxdr_long("Offset = %d");
+ (void) showxdr_long("Count = %d");
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 2 NFS reply packets
+ */
+static void
+nfsreply2(proc)
+ int proc;
+{
+ switch (proc) {
+ case NFSPROC_GETATTR:
+ case NFSPROC_SETATTR:
+ case NFSPROC_WRITE:
+ /* attrstat */
+ if (detail_nfsstat() == 0) {
+ detail_fattr();
+ }
+ break;
+ case NFSPROC_LOOKUP:
+ case NFSPROC_CREATE:
+ case NFSPROC_MKDIR:
+ /* diropres */
+ if (detail_nfsstat() == 0) {
+ detail_nfsfh();
+ detail_fattr();
+ }
+ break;
+ case NFSPROC_READLINK:
+ /* readlinkres */
+ if (detail_nfsstat() == 0) {
+ (void) showxdr_string(NFS_MAXPATHLEN, "Path = %s");
+ }
+ break;
+ case NFSPROC_READ:
+ /* readres */
+ if (detail_nfsstat() == 0) {
+ detail_fattr();
+ (void) showxdr_long("(%d byte(s) of data)");
+ }
+ break;
+ case NFSPROC_REMOVE:
+ case NFSPROC_RENAME:
+ case NFSPROC_LINK:
+ case NFSPROC_SYMLINK:
+ case NFSPROC_RMDIR:
+ /* stat */
+ detail_nfsstat();
+ break;
+ case NFSPROC_READDIR:
+ /* readdirres */
+ if (detail_nfsstat() == 0)
+ detail_readdirres();
+ break;
+ case NFSPROC_STATFS:
+ /* statfsres */
+ if (detail_nfsstat() == 0) {
+ (void) showxdr_long("Transfer size = %d");
+ (void) showxdr_long("Block size = %d");
+ (void) showxdr_long("Total blocks = %d");
+ (void) showxdr_long("Free blocks = %d");
+ (void) showxdr_long("Available blocks = %d");
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+detail_diroparg()
+{
+ detail_nfsfh();
+ (void) showxdr_string(NFS_MAXPATHLEN, "File name = %s");
+}
+
+/*
+ * V2 NFS protocol was implicitly linked with SunOS errnos.
+ * Some of the errno values changed in SVr4.
+ * Need to map errno value so that SVr4 snoop will interpret
+ * them correctly.
+ */
+static char *
+statusmsg(status)
+ ulong_t status;
+{
+ switch (status) {
+ case NFS_OK: return ("OK");
+ case NFSERR_PERM: return ("Not owner");
+ case NFSERR_NOENT: return ("No such file or directory");
+ case NFSERR_IO: return ("I/O error");
+ case NFSERR_NXIO: return ("No such device or address");
+ case NFSERR_ACCES: return ("Permission denied");
+ case NFSERR_EXIST: return ("File exists");
+ case NFSERR_XDEV: return ("Cross-device link");
+ case NFSERR_NODEV: return ("No such device");
+ case NFSERR_NOTDIR: return ("Not a directory");
+ case NFSERR_ISDIR: return ("Is a directory");
+ case NFSERR_INVAL: return ("Invalid argument");
+ case NFSERR_FBIG: return ("File too large");
+ case NFSERR_NOSPC: return ("No space left on device");
+ case NFSERR_ROFS: return ("Read-only file system");
+ case NFSERR_OPNOTSUPP: return ("Operation not supported");
+ case NFSERR_NAMETOOLONG: return ("File name too long");
+ case NFSERR_NOTEMPTY: return ("Directory not empty");
+ case NFSERR_DQUOT: return ("Disc quota exceeded");
+ case NFSERR_STALE: return ("Stale NFS file handle");
+ case NFSERR_REMOTE: return ("Object is remote");
+ case NFSERR_WFLUSH: return ("write cache flushed");
+ default: return ("(unknown error)");
+ }
+ /* NOTREACHED */
+}
+
+int
+sum_nfsstat(line)
+ char *line;
+{
+ ulong_t status;
+
+ status = getxdr_long();
+ (void) strcpy(line, statusmsg(status));
+ return (status);
+}
+
+int
+detail_nfsstat()
+{
+ ulong_t status;
+ int pos;
+
+ pos = getxdr_pos();
+ status = getxdr_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "Status = %lu (%s)",
+ status, statusmsg(status));
+
+ return ((int)status);
+}
+
+int
+sum_filehandle(len)
+ int len;
+{
+ int i, l;
+ int fh = 0;
+
+ for (i = 0; i < len; i += 4) {
+ l = getxdr_long();
+ fh ^= (l >> 16) ^ l;
+ }
+
+ return (fh);
+}
+
+char *
+sum_nfsfh()
+{
+ int fh;
+ static char buff[16];
+
+ fh = sum_filehandle(NFS_FHSIZE);
+ (void) sprintf(buff, " FH=%04X", fh & 0xFFFF);
+ return (buff);
+}
+
+void
+detail_nfsfh()
+{
+ int pos;
+ int fh;
+
+ pos = getxdr_pos();
+ fh = sum_filehandle(NFS_FHSIZE);
+ setxdr_pos(pos);
+ (void) sprintf(get_line(0, 0), "File handle = [%04X]", fh & 0xFFFF);
+ (void) showxdr_hex(NFS_FHSIZE, " %s");
+}
+
+static void
+detail_mode(mode)
+ int mode;
+{
+ char *str;
+
+ switch (mode & S_IFMT) {
+ case S_IFDIR: str = "Directory"; break;
+ case S_IFCHR: str = "Character"; break;
+ case S_IFBLK: str = "Block"; break;
+ case S_IFREG: str = "Regular file"; break;
+ case S_IFLNK: str = "Link"; break;
+ case S_IFSOCK: str = "Socket"; break;
+ case S_IFIFO: str = "Fifo"; break;
+ default: str = "?"; break;
+ }
+
+ (void) sprintf(get_line(0, 0), "Mode = 0%o", mode);
+ (void) sprintf(get_line(0, 0), " Type = %s", str);
+ (void) sprintf(get_line(0, 0),
+ " Setuid = %d, Setgid = %d, Sticky = %d",
+ (mode & S_ISUID) != 0,
+ (mode & S_ISGID) != 0,
+ (mode & S_ISVTX) != 0);
+ (void) sprintf(get_line(0, 0), " Owner's permissions = %s",
+ perms(mode >> 6 & 0x7));
+ (void) sprintf(get_line(0, 0), " Group's permissions = %s",
+ perms(mode >> 3 & 0x7));
+ (void) sprintf(get_line(0, 0), " Other's permissions = %s",
+ perms(mode & 0x7));
+}
+
+void
+detail_fattr()
+{
+ int fltype, mode, nlinks, uid, gid, size, blksz;
+ int rdev, blocks, fsid, fileid;
+
+ fltype = getxdr_long();
+ mode = getxdr_long();
+ nlinks = getxdr_long();
+ uid = getxdr_long();
+ gid = getxdr_long();
+ size = getxdr_long();
+ blksz = getxdr_long();
+ rdev = getxdr_long();
+ blocks = getxdr_long();
+ fsid = getxdr_long();
+ fileid = getxdr_long();
+
+ (void) sprintf(get_line(0, 0),
+ "File type = %d (%s)",
+ fltype, filetype(fltype));
+ detail_mode(mode);
+ (void) sprintf(get_line(0, 0),
+ "Link count = %d, UID = %d, GID = %d, Rdev = 0x%x",
+ nlinks, uid, gid, rdev);
+ (void) sprintf(get_line(0, 0),
+ "File size = %d, Block size = %d, No. of blocks = %d",
+ size, blksz, blocks);
+ (void) sprintf(get_line(0, 0),
+ "File system id = %d, File id = %d",
+ fsid, fileid);
+ (void) showxdr_date("Access time = %s");
+ (void) showxdr_date("Modification time = %s");
+ (void) showxdr_date("Inode change time = %s");
+}
+
+static void
+detail_sattr()
+{
+ int mode;
+
+ mode = getxdr_long();
+ detail_mode(mode);
+ (void) showxdr_long("UID = %d");
+ (void) showxdr_long("GID = %d");
+ (void) showxdr_long("Size = %d");
+ (void) showxdr_date("Access time = %s");
+ (void) showxdr_date("Modification time = %s");
+}
+
+static char *
+filetype(n)
+ int n;
+{
+ switch (n) {
+ case NFREG: return ("Regular File");
+ case NFDIR: return ("Directory");
+ case NFBLK: return ("Block special");
+ case NFCHR: return ("Character special");
+ case NFLNK: return ("Symbolic Link");
+ default: return ("?");
+ }
+}
+
+static char *
+perms(n)
+ int n;
+{
+ static char buff[4];
+
+ buff[0] = n & 4 ? 'r' : '-';
+ buff[1] = n & 2 ? 'w' : '-';
+ buff[2] = n & 1 ? 'x' : '-';
+ buff[3] = '\0';
+ return (buff);
+}
+
+static char *
+sum_readdirres()
+{
+ static char buff[NFS_MAXNAMLEN + 1];
+ int entries = 0;
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, " %d+ entries (incomplete)", entries);
+ return (buff);
+ }
+ while (getxdr_long()) {
+ entries++;
+ (void) getxdr_long(); /* fileid */
+ (void) getxdr_string(buff, NFS_MAXNAMLEN); /* name */
+ (void) getxdr_u_long(); /* cookie */
+ }
+
+ (void) sprintf(buff, " %d entries (%s)",
+ entries,
+ getxdr_long() ? "No more" : "More");
+ return (buff);
+}
+
+static void
+detail_readdirres()
+{
+ ulong_t fileid, cookie;
+ int entries = 0;
+ char *name;
+ char buff[NFS_MAXNAMLEN + 1];
+
+ (void) sprintf(get_line(0, 0), " File id Cookie Name");
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ entries. (Frame is incomplete)",
+ entries);
+ return;
+ }
+ while (getxdr_long()) {
+ entries++;
+ fileid = getxdr_long();
+ name = (char *)getxdr_string(buff, NFS_MAXNAMLEN);
+ cookie = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ " %7lu %7lu %s",
+ fileid, cookie, name);
+ }
+
+ (void) sprintf(get_line(0, 0), " %d entries", entries);
+ (void) showxdr_long("EOF = %d");
+}
+
+void
+skip_fattr()
+{
+
+ xdr_skip(17 * 4); /* XDR sizeof nfsfattr */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.h
new file mode 100644
index 0000000..576590a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs.h
@@ -0,0 +1,58 @@
+/*
+ * 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 (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SNOOP_NFS_H
+#define _SNOOP_NFS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Definitions that are shared among the NFS-related interpreters.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char *sum_nfsfh(void);
+extern void detail_nfsfh(void);
+extern int sum_nfsstat(char *);
+extern int detail_nfsstat(void);
+extern void detail_fattr(void);
+extern void skip_fattr(void);
+extern int sum_filehandle(int);
+
+extern char *sum_nfsfh3(void);
+extern void detail_nfsfh3(void);
+extern int sum_nfsstat3(char *);
+extern void detail_post_op_attr(char *);
+extern int detail_nfsstat3(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SNOOP_NFS_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs3.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs3.c
new file mode 100644
index 0000000..6355a03
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs3.c
@@ -0,0 +1,1214 @@
+/*
+ * 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 (c) 1991, 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/tiuser.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include "snoop.h"
+#include "snoop_nfs.h"
+
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <rpcsvc/nfs_prot.h>
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+extern jmp_buf xdr_err;
+
+static void nfscall3(int);
+static void nfsreply3(int);
+static char *perms(int);
+static char *filetype(int);
+static char *sum_access(void);
+static char *sum_readdirres(void);
+static char *sum_readdirplusres(void);
+static char *sum_createhow(void);
+static char *sum_stablehow(void);
+static void detail_sattr3(void);
+static void detail_diropargs3(void);
+static void detail_readdirres(void);
+static void detail_readdirplusres(void);
+static void detail_fattr3(void);
+static void detail_access(void);
+static void detail_mode(int);
+static void detail_wcc_attr(void);
+static void detail_pre_op_attr(char *);
+static void detail_wcc_data(char *);
+static void skip_postop(void);
+static void skip_wcc_data(void);
+static void skip_sattr3(void);
+
+#define DONT_CHANGE 0
+#define SET_TO_SERVER_TIME 1
+#define SET_TO_CLIENT_TIME 2
+
+#define UNCHECKED 0
+#define GUARDED 1
+#define EXCLUSIVE 2
+
+#define ACCESS3_READ 0x0001
+#define ACCESS3_LOOKUP 0x0002
+#define ACCESS3_MODIFY 0x0004
+#define ACCESS3_EXTEND 0x0008
+#define ACCESS3_DELETE 0x0010
+#define ACCESS3_EXECUTE 0x0020
+
+#define UNSTABLE 0
+#define DATA_SYNC 1
+#define FILE_SYNC 2
+
+#define NF3REG 1 /* regular file */
+#define NF3DIR 2 /* directory */
+#define NF3BLK 3 /* block special */
+#define NF3CHR 4 /* character special */
+#define NF3LNK 5 /* symbolic link */
+#define NF3SOCK 6 /* unix domain socket */
+#define NF3FIFO 7 /* named pipe */
+
+#define NFS3_FHSIZE 64
+
+static char *procnames_short[] = {
+ "NULL3", /* 0 */
+ "GETATTR3", /* 1 */
+ "SETATTR3", /* 2 */
+ "LOOKUP3", /* 3 */
+ "ACCESS3", /* 4 */
+ "READLINK3", /* 5 */
+ "READ3", /* 6 */
+ "WRITE3", /* 7 */
+ "CREATE3", /* 8 */
+ "MKDIR3", /* 9 */
+ "SYMLINK3", /* 10 */
+ "MKNOD3", /* 11 */
+ "REMOVE3", /* 12 */
+ "RMDIR3", /* 13 */
+ "RENAME3", /* 14 */
+ "LINK3", /* 15 */
+ "READDIR3", /* 16 */
+ "READDIRPLUS3", /* 17 */
+ "FSSTAT3", /* 18 */
+ "FSINFO3", /* 19 */
+ "PATHCONF3", /* 20 */
+ "COMMIT3", /* 21 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Get file attributes", /* 1 */
+ "Set file attributes", /* 2 */
+ "Look up file name", /* 3 */
+ "Check access permission", /* 4 */
+ "Read from symbolic link", /* 5 */
+ "Read from file", /* 6 */
+ "Write to file", /* 7 */
+ "Create file", /* 8 */
+ "Make directory", /* 9 */
+ "Make symbolic link", /* 10 */
+ "Make special file", /* 11 */
+ "Remove file", /* 12 */
+ "Remove directory", /* 13 */
+ "Rename", /* 14 */
+ "Link", /* 15 */
+ "Read from directory", /* 16 */
+ "Read from directory - plus", /* 17 */
+ "Get filesystem statistics", /* 18 */
+ "Get filesystem information", /* 19 */
+ "Get POSIX information", /* 20 */
+ "Commit to stable storage", /* 21 */
+};
+
+#define MAXPROC 21
+
+void
+interpret_nfs3(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[NFS_MAXPATHLEN + 1]; /* protocol allows longer */
+ u_longlong_t off;
+ int sz, how;
+ char *fh, *name;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "NFS C %s",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NFSPROC3_GETATTR:
+ case NFSPROC3_READLINK:
+ case NFSPROC3_FSSTAT:
+ case NFSPROC3_FSINFO:
+ case NFSPROC3_PATHCONF:
+ (void) sprintf(line, sum_nfsfh3());
+ break;
+ case NFSPROC3_SETATTR:
+ (void) sprintf(line, sum_nfsfh3());
+ break;
+ case NFSPROC3_READDIR:
+ fh = sum_nfsfh3();
+ off = getxdr_u_longlong();
+ (void) getxdr_u_longlong();
+ sz = getxdr_u_long();
+ (void) sprintf(line, "%s Cookie=%llu for %lu",
+ fh, off, sz);
+ break;
+ case NFSPROC3_READDIRPLUS:
+ fh = sum_nfsfh3();
+ off = getxdr_u_longlong();
+ (void) getxdr_u_longlong();
+ sz = getxdr_u_long();
+ (void) sprintf(line,
+ "%s Cookie=%llu for %lu/%lu",
+ fh, off, sz, getxdr_u_long());
+ break;
+ case NFSPROC3_ACCESS:
+ fh = sum_nfsfh3();
+ (void) sprintf(line, "%s (%s)",
+ fh, sum_access());
+ break;
+ case NFSPROC3_LOOKUP:
+ case NFSPROC3_REMOVE:
+ case NFSPROC3_RMDIR:
+ case NFSPROC3_MKDIR:
+ fh = sum_nfsfh3();
+ (void) sprintf(line, "%s %s",
+ fh, getxdr_string(buff,
+ NFS_MAXPATHLEN));
+ break;
+ case NFSPROC3_CREATE:
+ fh = sum_nfsfh3();
+ name = getxdr_string(buff, NFS_MAXPATHLEN);
+ (void) sprintf(line, "%s (%s) %s",
+ fh, sum_createhow(), name);
+ break;
+ case NFSPROC3_MKNOD:
+ fh = sum_nfsfh3();
+ name = getxdr_string(buff, NFS_MAXPATHLEN);
+ how = getxdr_long();
+ (void) sprintf(line, "%s (%s) %s",
+ fh, filetype(how), name);
+ break;
+ case NFSPROC3_READ:
+ fh = sum_nfsfh3();
+ off = getxdr_u_longlong();
+ sz = getxdr_u_long();
+ (void) sprintf(line, "%s at %llu for %lu",
+ fh, off, sz);
+ break;
+ case NFSPROC3_WRITE:
+ fh = sum_nfsfh3();
+ off = getxdr_u_longlong();
+ sz = getxdr_u_long();
+ (void) sprintf(line, "%s at %llu for %lu (%s)",
+ fh, off, sz, sum_stablehow());
+ break;
+ case NFSPROC3_SYMLINK:
+ fh = sum_nfsfh3();
+ (void) sprintf(line, "%s %s",
+ fh, getxdr_string(buff,
+ NFS_MAXPATHLEN));
+ skip_sattr3();
+ line += strlen(line);
+ (void) sprintf(line, " to %s",
+ getxdr_string(buff, NFS_MAXPATHLEN));
+ break;
+ case NFSPROC3_RENAME:
+ fh = sum_nfsfh3();
+ (void) sprintf(line, "%s %s",
+ fh, getxdr_string(buff,
+ NFS_MAXPATHLEN));
+ line += strlen(line);
+ fh = sum_nfsfh3();
+ (void) sprintf(line, " to%s %s",
+ fh, getxdr_string(buff,
+ NFS_MAXPATHLEN));
+ break;
+ case NFSPROC3_LINK:
+ fh = sum_nfsfh3();
+ (void) sprintf(line, "%s", fh);
+ line += strlen(line);
+ fh = sum_nfsfh3();
+ (void) sprintf(line, " to%s %s",
+ fh, getxdr_string(buff,
+ NFS_MAXPATHLEN));
+ break;
+ case NFSPROC3_COMMIT:
+ fh = sum_nfsfh3();
+ off = getxdr_u_longlong();
+ sz = getxdr_u_long();
+ (void) sprintf(line, "%s at %llu for %lu",
+ fh, off, sz);
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "NFS R %s ",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NFSPROC3_LOOKUP:
+ if (sum_nfsstat3(line) == NFS3_OK)
+ (void) strcat(line, sum_nfsfh3());
+ break;
+ case NFSPROC3_CREATE:
+ case NFSPROC3_MKDIR:
+ case NFSPROC3_SYMLINK:
+ case NFSPROC3_MKNOD:
+ if (sum_nfsstat3(line) == NFS3_OK) {
+ if (getxdr_bool())
+ (void) strcat(line,
+ sum_nfsfh3());
+ }
+ break;
+ case NFSPROC3_READLINK:
+ if (sum_nfsstat3(line) == NFS3_OK) {
+ line += strlen(line);
+ skip_postop();
+ (void) sprintf(line, " (Path=%s)",
+ getxdr_string(buff,
+ NFS_MAXPATHLEN));
+ }
+ break;
+ case NFSPROC3_GETATTR:
+ case NFSPROC3_SETATTR:
+ case NFSPROC3_REMOVE:
+ case NFSPROC3_RMDIR:
+ case NFSPROC3_RENAME:
+ case NFSPROC3_LINK:
+ case NFSPROC3_FSSTAT:
+ case NFSPROC3_FSINFO:
+ case NFSPROC3_PATHCONF:
+ (void) sum_nfsstat3(line);
+ break;
+ case NFSPROC3_ACCESS:
+ if (sum_nfsstat3(line) == NFS3_OK) {
+ line += strlen(line);
+ skip_postop();
+ (void) sprintf(line, " (%s)",
+ sum_access());
+ }
+ break;
+ case NFSPROC3_WRITE:
+ if (sum_nfsstat3(line) == NFS3_OK) {
+ line += strlen(line);
+ skip_wcc_data();
+ sz = getxdr_u_long();
+ (void) sprintf(line, " %d (%s)",
+ sz, sum_stablehow());
+ }
+ break;
+ case NFSPROC3_READDIR:
+ if (sum_nfsstat3(line) == NFS3_OK)
+ (void) strcat(line, sum_readdirres());
+ break;
+ case NFSPROC3_READ:
+ if (sum_nfsstat3(line) == NFS3_OK) {
+ line += strlen(line);
+ skip_postop();
+ (void) sprintf(line, " (%lu bytes)",
+ getxdr_u_long());
+ if (getxdr_bool())
+ (void) strcat(line, " EOF");
+ }
+ break;
+ case NFSPROC3_READDIRPLUS:
+ if (sum_nfsstat3(line) == NFS3_OK)
+ (void) strcat(line,
+ sum_readdirplusres());
+ break;
+ case NFSPROC3_COMMIT:
+ (void) sum_nfsstat3(line);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NFS: ", "Sun NFS", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+ if (type == CALL)
+ nfscall3(proc);
+ else
+ nfsreply3(proc);
+ show_trailer();
+ }
+}
+
+/*
+ * Print out version 3 NFS call packets
+ */
+static void
+nfscall3(proc)
+ int proc;
+{
+ int h;
+
+ switch (proc) {
+ case NFSPROC3_GETATTR:
+ case NFSPROC3_READLINK:
+ case NFSPROC3_FSINFO:
+ case NFSPROC3_FSSTAT:
+ case NFSPROC3_PATHCONF:
+ detail_nfsfh3();
+ break;
+ case NFSPROC3_SETATTR:
+ detail_nfsfh3();
+ detail_sattr3();
+ if (getxdr_bool())
+ (void) showxdr_date_ns("Guard = %s");
+ break;
+ case NFSPROC3_LOOKUP:
+ case NFSPROC3_REMOVE:
+ case NFSPROC3_RMDIR:
+ detail_diropargs3();
+ break;
+ case NFSPROC3_ACCESS:
+ detail_nfsfh3();
+ detail_access();
+ break;
+ case NFSPROC3_MKDIR:
+ detail_diropargs3();
+ detail_sattr3();
+ break;
+ case NFSPROC3_CREATE:
+ detail_diropargs3();
+ h = getxdr_u_long();
+ if (h == EXCLUSIVE)
+ showxdr_hex(8, "Guard = %s");
+ else {
+ (void) sprintf(get_line(0, 0), "Method = %s",
+ h == UNCHECKED ? "Unchecked" : "Guarded");
+ detail_sattr3();
+ }
+ break;
+ case NFSPROC3_MKNOD:
+ detail_diropargs3();
+ h = getxdr_u_long();
+ (void) sprintf(get_line(0, 0), "File type = %s",
+ filetype(h));
+ switch (h) {
+ case NF3CHR:
+ case NF3BLK:
+ detail_sattr3();
+ showxdr_u_long("Major = %lu");
+ showxdr_u_long("Minor = %lu");
+ break;
+ case NF3SOCK:
+ case NF3FIFO:
+ detail_sattr3();
+ break;
+ }
+ break;
+ case NFSPROC3_WRITE:
+ detail_nfsfh3();
+ (void) showxdr_u_longlong("Offset = %llu");
+ (void) showxdr_u_long("Size = %lu");
+ (void) sprintf(get_line(0, 0), "Stable = %s",
+ sum_stablehow());
+ break;
+ case NFSPROC3_RENAME:
+ detail_diropargs3();
+ detail_diropargs3();
+ break;
+ case NFSPROC3_LINK:
+ detail_nfsfh3();
+ detail_diropargs3();
+ break;
+ case NFSPROC3_SYMLINK:
+ detail_diropargs3();
+ detail_sattr3();
+ (void) showxdr_string(MAXPATHLEN, "Path = %s");
+ break;
+ case NFSPROC3_READDIR:
+ detail_nfsfh3();
+ (void) showxdr_u_longlong("Cookie = %llu");
+ (void) showxdr_hex(8, "Verifier = %s");
+ (void) showxdr_u_long("Count = %lu");
+ break;
+ case NFSPROC3_READDIRPLUS:
+ detail_nfsfh3();
+ (void) showxdr_u_longlong("Cookie = %llu");
+ (void) showxdr_hex(8, "Verifier = %s");
+ (void) showxdr_u_long("Dircount = %lu");
+ (void) showxdr_u_long("Maxcount = %lu");
+ break;
+ case NFSPROC3_READ:
+ case NFSPROC3_COMMIT:
+ detail_nfsfh3();
+ (void) showxdr_u_longlong("Offset = %llu");
+ (void) showxdr_long("Count = %lu");
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 3 NFS reply packets
+ */
+static void
+nfsreply3(proc)
+ int proc;
+{
+ int bits;
+
+ switch (proc) {
+ case NFSPROC3_GETATTR:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_fattr3();
+ }
+ break;
+ case NFSPROC3_SETATTR:
+ (void) detail_nfsstat3();
+ detail_wcc_data("");
+ break;
+ case NFSPROC3_WRITE:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_wcc_data("");
+ (void) showxdr_u_long("Count = %lu bytes written");
+ (void) sprintf(get_line(0, 0), "Stable = %s",
+ sum_stablehow());
+ (void) showxdr_hex(8, "Verifier = %s");
+ } else
+ detail_wcc_data("");
+ break;
+ case NFSPROC3_LOOKUP:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_nfsfh3();
+ detail_post_op_attr("(object)");
+ }
+ detail_post_op_attr("(directory)");
+ break;
+ case NFSPROC3_CREATE:
+ case NFSPROC3_MKDIR:
+ case NFSPROC3_SYMLINK:
+ case NFSPROC3_MKNOD:
+ if (detail_nfsstat3() == NFS3_OK) {
+ if (getxdr_bool())
+ detail_nfsfh3();
+ else
+ (void) sprintf(get_line(0, 0),
+ "(No file handle available)");
+ detail_post_op_attr("");
+ }
+ detail_wcc_data("");
+ break;
+ case NFSPROC3_READLINK:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_post_op_attr("");
+ (void) showxdr_string(MAXPATHLEN, "Path = %s");
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_READ:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_post_op_attr("");
+ (void) showxdr_u_long("Count = %lu bytes read");
+ (void) showxdr_bool("End of file = %s");
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_ACCESS:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_post_op_attr("");
+ (void) sprintf(get_line(0, 0), "Access = %s",
+ sum_access());
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_REMOVE:
+ case NFSPROC3_RMDIR:
+ (void) detail_nfsstat3();
+ detail_wcc_data("");
+ break;
+ case NFSPROC3_RENAME:
+ (void) detail_nfsstat3();
+ detail_wcc_data("(from directory)");
+ detail_wcc_data("(to directory)");
+ break;
+ case NFSPROC3_LINK:
+ (void) detail_nfsstat3();
+ detail_post_op_attr("");
+ detail_wcc_data("");
+ break;
+ case NFSPROC3_READDIR:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_readdirres();
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_READDIRPLUS:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_readdirplusres();
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_FSSTAT:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_post_op_attr("");
+ (void) showxdr_u_longlong(
+ "Total space = %llu bytes");
+ (void) showxdr_u_longlong(
+ "Available space = %llu bytes");
+ (void) showxdr_u_longlong(
+ "Available space - this user = %llu bytes");
+ (void) showxdr_u_longlong(
+ "Total file slots = %llu");
+ (void) showxdr_u_longlong(
+ "Available file slots = %llu");
+ (void) showxdr_u_longlong(
+ "Available file slots - this user = %llu");
+ (void) showxdr_u_long("Invariant time = %lu sec");
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_FSINFO:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_post_op_attr("");
+ (void) show_line("Read transfer sizes:");
+ (void) showxdr_u_long(" Maximum = %lu bytes");
+ (void) showxdr_u_long(" Preferred = %lu bytes");
+ (void) showxdr_u_long(
+ " Suggested multiple = %lu bytes");
+ (void) show_line("Write transfer sizes:");
+ (void) showxdr_u_long(" Maximum = %lu bytes");
+ (void) showxdr_u_long(" Preferred = %lu bytes");
+ (void) showxdr_u_long(
+ " Suggested multiple = %lu bytes");
+ (void) show_line("Directory read size:");
+ (void) showxdr_u_long(" Preferred = %lu bytes");
+ (void) show_line("File system limits:");
+ (void) showxdr_u_longlong(
+ " Max file size = %llu bytes");
+ (void) showxdr_date_ns(
+ " Server minimum time discrimination = %s sec");
+ bits = showxdr_u_long("Properties = 0x%02x");
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, FSF3_LINK,
+ "Hard links supported",
+ "(hard links not supported)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, FSF3_SYMLINK,
+ "Symbolic links supported",
+ "(symbolic links not supported)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, FSF3_HOMOGENEOUS,
+ "Pathconf cannot vary per file",
+ "(pathconf can vary per file)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, FSF3_CANSETTIME,
+ "Server can always set file times",
+ "(server cannot always set file times)"));
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_PATHCONF:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_post_op_attr("");
+ (void) showxdr_u_long("Link max = %lu");
+ (void) showxdr_u_long("Name max = %lu");
+ (void) showxdr_bool("No trunc = %s");
+ (void) showxdr_bool("Chown restricted = %s");
+ (void) showxdr_bool("Case insensitive = %s");
+ (void) showxdr_bool("Case preserving = %s");
+ } else
+ detail_post_op_attr("");
+ break;
+ case NFSPROC3_COMMIT:
+ if (detail_nfsstat3() == NFS3_OK) {
+ detail_wcc_data("");
+ (void) showxdr_hex(8, "Verifier = %s");
+ } else
+ detail_wcc_data("");
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+detail_diropargs3()
+{
+
+ detail_nfsfh3();
+ (void) showxdr_string(MAXPATHLEN, "File name = %s");
+}
+
+int
+sum_nfsstat3(line)
+ char *line;
+{
+ ulong_t status;
+ char *p;
+
+ status = getxdr_long();
+ switch (status) {
+ case NFS3_OK: p = "OK"; break;
+ case NFS3ERR_PERM: p = "Not owner"; break;
+ case NFS3ERR_NOENT: p = "No such file or directory"; break;
+ case NFS3ERR_IO: p = "I/O error"; break;
+ case NFS3ERR_NXIO: p = "No such device or address"; break;
+ case NFS3ERR_ACCES: p = "Permission denied"; break;
+ case NFS3ERR_EXIST: p = "File exists"; break;
+ case NFS3ERR_XDEV: p = "Attempted cross-device link"; break;
+ case NFS3ERR_NODEV: p = "No such device"; break;
+ case NFS3ERR_NOTDIR: p = "Not a directory"; break;
+ case NFS3ERR_ISDIR: p = "Is a directory"; break;
+ case NFS3ERR_INVAL: p = "Invalid argument"; break;
+ case NFS3ERR_FBIG: p = "File too large"; break;
+ case NFS3ERR_NOSPC: p = "No space left on device"; break;
+ case NFS3ERR_ROFS: p = "Read-only file system"; break;
+ case NFS3ERR_MLINK: p = "Too many links"; break;
+ case NFS3ERR_NAMETOOLONG:p = "File name too long"; break;
+ case NFS3ERR_NOTEMPTY: p = "Directory not empty"; break;
+ case NFS3ERR_DQUOT: p = "Disc quota exceeded"; break;
+ case NFS3ERR_STALE: p = "Stale NFS file handle"; break;
+ case NFS3ERR_REMOTE: p = "Too many levels of remote in path"; break;
+ case NFS3ERR_BADHANDLE: p = "Illegal NFS file handle"; break;
+ case NFS3ERR_NOT_SYNC: p = "Update synch mismatch"; break;
+ case NFS3ERR_BAD_COOKIE:p = "Readdir cookie is stale"; break;
+ case NFS3ERR_NOTSUPP: p = "Operation not supported"; break;
+ case NFS3ERR_TOOSMALL: p = "Buffer/request too small"; break;
+ case NFS3ERR_SERVERFAULT:p = "Server fault"; break;
+ case NFS3ERR_BADTYPE: p = "Bad type"; break;
+ case NFS3ERR_JUKEBOX: p = "File is temporarily unavailable"; break;
+ default: p = "(unknown error)"; break;
+ }
+
+ (void) strcpy(line, p);
+ return (status);
+}
+
+int
+detail_nfsstat3()
+{
+ ulong_t status;
+ char buff[64];
+ int pos;
+
+ pos = getxdr_pos();
+ status = sum_nfsstat3(buff);
+
+ (void) sprintf(get_line(pos, getxdr_pos()), "Status = %d (%s)",
+ status, buff);
+
+ return ((int)status);
+}
+
+static void
+skip_postop()
+{
+
+ if (getxdr_bool())
+ xdr_skip(21 * 4); /* XDR size of fattr3 */
+}
+
+static void
+skip_wcc_data()
+{
+
+ if (getxdr_bool() > 0)
+ xdr_skip(3 * 8);
+ skip_postop();
+}
+
+static void
+skip_sattr3()
+{
+
+ if (getxdr_bool() > 0)
+ xdr_skip(4); /* mode */
+ if (getxdr_bool() > 0)
+ xdr_skip(4); /* uid */
+ if (getxdr_bool() > 0)
+ xdr_skip(4); /* gid */
+ if (getxdr_bool() > 0)
+ xdr_skip(8); /* size */
+ if (getxdr_bool() > 0)
+ xdr_skip(8); /* atime */
+ if (getxdr_bool() > 0)
+ xdr_skip(8); /* mtime */
+}
+
+char *
+sum_nfsfh3()
+{
+ int len;
+ int fh;
+ static char buff[16];
+
+ len = getxdr_long();
+ fh = sum_filehandle(len);
+ (void) sprintf(buff, " FH=%04X", fh & 0xFFFF);
+ return (buff);
+}
+
+void
+detail_nfsfh3()
+{
+ int pos;
+ int i, l, len;
+ int fh;
+
+ len = getxdr_long();
+ pos = getxdr_pos();
+ fh = sum_filehandle(len);
+ setxdr_pos(pos);
+ (void) sprintf(get_line(0, 0), "File handle = [%04X]", fh & 0xFFFF);
+ i = 0;
+ while (i < len) {
+ l = MIN(len - i, 32);
+ (void) showxdr_hex(l, " %s");
+ i += l;
+ }
+}
+
+static char *
+sum_access()
+{
+ int bits;
+ static char buff[64];
+
+ bits = getxdr_u_long();
+ buff[0] = '\0';
+
+ if (bits & ACCESS3_READ)
+ (void) strcat(buff, "read,");
+ if (bits & ACCESS3_LOOKUP)
+ (void) strcat(buff, "lookup,");
+ if (bits & ACCESS3_MODIFY)
+ (void) strcat(buff, "modify,");
+ if (bits & ACCESS3_EXTEND)
+ (void) strcat(buff, "extend,");
+ if (bits & ACCESS3_DELETE)
+ (void) strcat(buff, "delete,");
+ if (bits & ACCESS3_EXECUTE)
+ (void) strcat(buff, "execute,");
+ if (buff[0] != '\0')
+ buff[strlen(buff) - 1] = '\0';
+
+ return (buff);
+}
+
+static void
+detail_access()
+{
+ uint_t bits;
+
+ bits = showxdr_u_long("Access bits = 0x%08x");
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS3_READ, "Read", "(no read)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS3_LOOKUP, "Lookup", "(no lookup)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS3_MODIFY, "Modify", "(no modify)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS3_EXTEND, "Extend", "(no extend)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS3_DELETE, "Delete", "(no delete)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS3_EXECUTE, "Execute", "(no execute)"));
+}
+
+static void
+detail_mode(mode)
+ int mode;
+{
+
+ (void) sprintf(get_line(0, 0), " Mode = 0%o", mode);
+ (void) sprintf(get_line(0, 0),
+ " Setuid = %d, Setgid = %d, Sticky = %d",
+ (mode & S_ISUID) != 0,
+ (mode & S_ISGID) != 0,
+ (mode & S_ISVTX) != 0);
+ (void) sprintf(get_line(0, 0), " Owner's permissions = %s",
+ perms(mode >> 6 & 0x7));
+ (void) sprintf(get_line(0, 0), " Group's permissions = %s",
+ perms(mode >> 3 & 0x7));
+ (void) sprintf(get_line(0, 0), " Other's permissions = %s",
+ perms(mode & 0x7));
+}
+
+static void
+detail_fattr3()
+{
+ uint_t fltype, mode, nlinks, uid, gid;
+ uint_t major, minor;
+ u_longlong_t size, used, fsid, fileid;
+
+ fltype = getxdr_u_long();
+ mode = getxdr_u_long();
+ nlinks = getxdr_u_long();
+ uid = getxdr_u_long();
+ gid = getxdr_u_long();
+ size = getxdr_u_longlong();
+ used = getxdr_u_longlong();
+ major = getxdr_u_long();
+ minor = getxdr_u_long();
+ fsid = getxdr_u_longlong();
+ fileid = getxdr_u_longlong();
+
+ (void) sprintf(get_line(0, 0),
+ " File type = %d (%s)",
+ fltype, filetype(fltype));
+ detail_mode(mode);
+ (void) sprintf(get_line(0, 0),
+ " Link count = %u, User ID = %u, Group ID = %u",
+ nlinks, uid, gid);
+ (void) sprintf(get_line(0, 0),
+ " File size = %llu, Used = %llu",
+ size, used);
+ (void) sprintf(get_line(0, 0),
+ " Special: Major = %u, Minor = %u",
+ major, minor);
+ (void) sprintf(get_line(0, 0),
+ " File system id = %llu, File id = %llu",
+ fsid, fileid);
+ (void) showxdr_date_ns(" Last access time = %s");
+ (void) showxdr_date_ns(" Modification time = %s");
+ (void) showxdr_date_ns(" Attribute change time = %s");
+ (void) show_line("");
+}
+
+static void
+detail_sattr3()
+{
+ int t;
+
+ if (getxdr_bool())
+ detail_mode(getxdr_u_long());
+ else
+ (void) sprintf(get_line(0, 0), "Mode = (not set)");
+ if (getxdr_bool())
+ (void) showxdr_long("User ID = %d");
+ else
+ (void) sprintf(get_line(0, 0), "User ID = (not set)");
+ if (getxdr_bool())
+ (void) showxdr_long("Group ID = %d");
+ else
+ (void) sprintf(get_line(0, 0), "Group ID = (not set)");
+ if (getxdr_bool())
+ (void) showxdr_u_longlong("Size = %llu");
+ else
+ (void) sprintf(get_line(0, 0), "Size = (not set)");
+
+ if ((t = getxdr_u_long()) == SET_TO_CLIENT_TIME)
+ (void) showxdr_date("Access time = %s (set to client time)");
+ else if (t == SET_TO_SERVER_TIME)
+ (void) sprintf(get_line(0, 0),
+ "Access time = (set to server time)");
+ else
+ (void) sprintf(get_line(0, 0), "Access time = (do not set)");
+
+ if ((t = getxdr_u_long()) == SET_TO_CLIENT_TIME) {
+ (void) showxdr_date(
+ "Modification time = %s (set to client time)");
+ } else if (t == SET_TO_SERVER_TIME)
+ (void) sprintf(get_line(0, 0),
+ "Modification time = (set to server time)");
+ else
+ (void) sprintf(get_line(0, 0),
+ "Modification time = (do not set)");
+ (void) show_line("");
+}
+
+static char *
+filetype(n)
+ int n;
+{
+
+ switch (n) {
+ case NF3REG:
+ return ("Regular File");
+ case NF3DIR:
+ return ("Directory");
+ case NF3BLK:
+ return ("Block special");
+ case NF3CHR:
+ return ("Character special");
+ case NF3LNK:
+ return ("Symbolic Link");
+ case NF3SOCK:
+ return ("Unix domain socket");
+ case NF3FIFO:
+ return ("Named pipe");
+ default:
+ return ("?");
+ }
+ /* NOTREACHED */
+}
+
+static char *
+perms(n)
+ int n;
+{
+ static char buff[4];
+
+ buff[0] = n & 4 ? 'r' : '-';
+ buff[1] = n & 2 ? 'w' : '-';
+ buff[2] = n & 1 ? 'x' : '-';
+ buff[3] = '\0';
+ return (buff);
+}
+
+static void
+detail_wcc_attr()
+{
+
+ (void) showxdr_u_longlong(" Size = %llu bytes");
+ (void) showxdr_date_ns(" Modification time = %s");
+ (void) showxdr_date_ns(" Attribute change time = %s");
+ (void) show_line("");
+}
+
+static void
+detail_pre_op_attr(str)
+ char *str;
+{
+
+ if (getxdr_bool()) {
+ (void) sprintf(get_line(0, 0),
+ "Pre-operation attributes: %s", str);
+ detail_wcc_attr();
+ } else
+ (void) sprintf(get_line(0, 0),
+ "Pre-operation attributes: %s (not available)", str);
+}
+
+void
+detail_post_op_attr(str)
+ char *str;
+{
+
+ if (getxdr_bool()) {
+ (void) sprintf(get_line(0, 0),
+ "Post-operation attributes: %s", str);
+ detail_fattr3();
+ } else
+ (void) sprintf(get_line(0, 0),
+ "Post-operation attributes: %s (not available)", str);
+}
+
+static void
+detail_wcc_data(str)
+ char *str;
+{
+
+ detail_pre_op_attr(str);
+ detail_post_op_attr(str);
+}
+
+static char *
+sum_readdirres()
+{
+ static char buff[NFS_MAXNAMLEN + 1]; /* protocol allows longer names */
+ static int entries;
+
+ entries = 0;
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, " %d+ entries (incomplete)", entries);
+ return (buff);
+ }
+ skip_postop();
+ xdr_skip(8); /* cookieverf */
+ while (getxdr_bool()) {
+ entries++;
+ xdr_skip(8); /* fileid */
+ (void) getxdr_string(buff, NFS_MAXNAMLEN); /* name */
+ xdr_skip(8); /* cookie */
+ }
+
+ (void) sprintf(buff, " %d entries (%s)",
+ entries, getxdr_bool() ? "No more" : "More");
+ return (buff);
+}
+
+static char *
+sum_readdirplusres()
+{
+ static char buff[NFS_MAXNAMLEN + 1]; /* protocol allows longer */
+ static int entries;
+ int skip;
+
+ entries = 0;
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, " %d+ entries (incomplete)", entries);
+ return (buff);
+ }
+ skip_postop();
+ xdr_skip(8); /* cookieverf */
+ while (getxdr_bool()) {
+ entries++;
+ xdr_skip(8); /* fileid */
+ (void) getxdr_string(buff, NFS_MAXNAMLEN); /* name */
+ xdr_skip(8); /* cookie */
+ skip_postop(); /* post-op */
+ if (getxdr_bool()) {
+ skip = getxdr_long();
+ xdr_skip(RNDUP(skip)); /* fhandle */
+ }
+ }
+
+ (void) sprintf(buff, " %d entries (%s)",
+ entries, getxdr_bool() ? "No more" : "More");
+ return (buff);
+}
+
+static void
+detail_readdirres()
+{
+ static int entries;
+ u_longlong_t fileid, cookie;
+ char *name;
+ char buff[NFS_MAXNAMLEN + 1]; /* protocol allows longer names */
+
+ entries = 0;
+ detail_post_op_attr("");
+ (void) showxdr_hex(8, "Cookie verifier = %s");
+ (void) show_line("");
+ (void) sprintf(get_line(0, 0), " File id Cookie Name");
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ entries. (Frame is incomplete)",
+ entries);
+ return;
+ }
+ while (getxdr_bool()) {
+ entries++;
+ fileid = getxdr_u_longlong();
+ name = (char *)getxdr_string(buff, NFS_MAXNAMLEN);
+ cookie = getxdr_u_longlong();
+ (void) sprintf(get_line(0, 0),
+ " %10llu %10llu %s",
+ fileid, cookie, name);
+ }
+
+ (void) sprintf(get_line(0, 0), " %d entries", entries);
+ (void) showxdr_bool("EOF = %s");
+}
+
+static void
+detail_readdirplusres()
+{
+ static int entries;
+
+ entries = 0;
+ detail_post_op_attr("");
+ (void) showxdr_hex(8, "Cookie verifier = %s");
+ (void) show_line("");
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ entries. (Frame is incomplete)",
+ entries);
+ return;
+ }
+ while (getxdr_bool()) {
+ entries++;
+ (void) sprintf(get_line(0, 0),
+ "------------------ entry #%d",
+ entries);
+ (void) showxdr_u_longlong("File ID = %llu");
+ (void) showxdr_string(NFS_MAXNAMLEN, "Name = %s");
+ (void) showxdr_u_longlong("Cookie = %llu");
+ detail_post_op_attr("");
+ if (getxdr_bool())
+ detail_nfsfh3();
+ else
+ (void) sprintf(get_line(0, 0),
+ "(No file handle available)");
+ }
+
+ (void) show_line("");
+ (void) sprintf(get_line(0, 0), " %d entries", entries);
+ (void) showxdr_bool("EOF = %s");
+}
+
+static char *
+sum_createhow()
+{
+ long how;
+
+ how = getxdr_long();
+ switch (how) {
+ case UNCHECKED:
+ return ("UNCHECKED");
+ case GUARDED:
+ return ("GUARDED");
+ case EXCLUSIVE:
+ return ("EXCLUSIVE");
+ default:
+ return ("?");
+ }
+ /* NOTREACHED */
+}
+
+static char *
+sum_stablehow()
+{
+ long stable;
+
+ stable = getxdr_long();
+ switch (stable) {
+ case UNSTABLE:
+ return ("ASYNC");
+ case DATA_SYNC:
+ return ("DSYNC");
+ case FILE_SYNC:
+ return ("FSYNC");
+ default:
+ return ("?");
+ }
+ /* NOTREACHED */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c
new file mode 100644
index 0000000..bb1db5c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c
@@ -0,0 +1,4900 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/tiuser.h>
+#include <setjmp.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include "snoop.h"
+
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <rpcsvc/nfs_prot.h>
+/* use the same nfs4_prot.h as the xdr code */
+#include "rpcsvc/nfs4_prot.h"
+
+/*
+ * XXX With NFS v2 and v3, we only need to xdr the pieces that we care
+ * about. Anything else we can ignore and just skip to the next packet.
+ * So all the stuff that deals directly with XDR lives in snoop_display.c
+ * With v4, we need to XDR entire structures so that we can skip over
+ * uninteresting bits in a compound array, so we call XDR directly from
+ * here. We need to rethink how we're going to structure XDR access. Do
+ * we continue to hide it all in snoop_display.c, or do we expose it to all
+ * the protocol modules?
+ */
+extern XDR xdrm;
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * Maximum number of characters to display in compound4 summary line.
+ */
+#define SUM_COMPND_MAX 100
+
+/*
+ * Maximum number of recognized attributes.
+ */
+#define MAX_ATTRIBUTES 56
+
+/*
+ * This data structure provides a more convenient way to access an
+ * attribute bitmask. map[N] = value of bit N in a bitmap4.
+ * It's defined as a struct so as to step around all the weird rules in C
+ * about arrays, pointers, passing them as arguments, etc.
+ */
+
+typedef struct {
+ char map[MAX_ATTRIBUTES];
+} unpkd_attrmap_t;
+
+
+static void sumarg_cb_getattr(char *buf, size_t buflen, void *obj);
+static void dtlarg_cb_getattr(void *obj);
+static void sumarg_cb_recall(char *buf, size_t buflen, void *obj);
+static void dtlarg_cb_recall(void *obj);
+
+
+static void sumarg_access(char *buf, size_t buflen, void *obj);
+static void dtlarg_access(void *obj);
+static void sumarg_close(char *buf, size_t buflen, void *obj);
+static void dtlarg_close(void *obj);
+static void sumarg_commit(char *buf, size_t buflen, void *obj);
+static void dtlarg_commit(void *obj);
+static void sumarg_compnt(char *buf, size_t buflen, void *obj);
+static void dtlarg_compnt(void *obj);
+static void sumarg_create(char *buf, size_t buflen, void *obj);
+static void dtlarg_create(void *obj);
+static void sumarg_delprge(char *buf, size_t buflen, void *obj);
+static void dtlarg_delprge(void *obj);
+static void sumarg_delret(char *buf, size_t buflen, void *obj);
+static void dtlarg_delret(void *obj);
+static void sumarg_getattr(char *buf, size_t buflen, void *obj);
+static void dtlarg_getattr(void *obj);
+static void sumarg_link(char *buf, size_t buflen, void *obj);
+static void dtlarg_link(void *obj);
+static void sum_open_to_lock_owner(char *buf, int buflen,
+ open_to_lock_owner4 *own);
+static void sum_exist_lock_owner(char *buf, int buflen,
+ exist_lock_owner4 *own);
+static void sum_locker(char *buf, size_t buflen, locker4 *lk);
+static void sumarg_lock(char *buf, size_t buflen, void *obj);
+static void detail_open_to_lock_owner(open_to_lock_owner4 *own);
+static void detail_exist_lock_owner(exist_lock_owner4 *own);
+static void detail_locker(locker4 *lk);
+static void dtlarg_lock(void *obj);
+static void sumarg_lockt(char *buf, size_t buflen, void *obj);
+static void dtlarg_lockt(void *obj);
+static void sumarg_locku(char *buf, size_t buflen, void *obj);
+static void dtlarg_locku(void *obj);
+static void sumarg_lookup(char *buf, size_t buflen, void *obj);
+static void dtlarg_lookup(void *obj);
+static void sumarg_open(char *buf, size_t buflen, void *obj);
+static void dtlarg_open(void *obj);
+static void sumarg_openattr(char *buf, size_t buflen, void *obj);
+static void dtlarg_openattr(void *obj);
+static void sumarg_open_confirm(char *buf, size_t buflen, void *obj);
+static void dtlarg_open_confirm(void *obj);
+static void sumarg_open_downgrd(char *buf, size_t buflen, void *obj);
+static void dtlarg_open_downgrd(void *obj);
+static void sumarg_putfh(char *buf, size_t buflen, void *obj);
+static void dtlarg_putfh(void *obj);
+static void sumarg_read(char *buf, size_t buflen, void *obj);
+static void dtlarg_read(void *obj);
+static void sumarg_readdir(char *buf, size_t buflen, void *obj);
+static void dtlarg_readdir(void *obj);
+static void sumarg_release_lkown(char *buf, size_t buflen, void *obj);
+static void dtlarg_release_lkown(void *obj);
+static void sumarg_rename(char *buf, size_t buflen, void *obj);
+static void dtlarg_rename(void *obj);
+static void sumarg_renew(char *buf, size_t buflen, void *obj);
+static void dtlarg_renew(void *buf);
+static void sumarg_secinfo(char *buf, size_t buflen, void *obj);
+static void dtlarg_secinfo(void *obj);
+static void sumarg_setattr(char *buf, size_t buflen, void *obj);
+static void dtlarg_setattr(void *obj);
+static void sumarg_setclid(char *buf, size_t buflen, void *obj);
+static void dtlarg_setclid(void *obj);
+static void sumarg_setclid_cfm(char *buf, size_t buflen, void *obj);
+static void dtlarg_setclid_cfm(void *obj);
+static void dtlarg_verify(void *obj);
+static void sumarg_write(char *buf, size_t buflen, void *obj);
+static void dtlarg_write(void *obj);
+
+static void sumres_cb_getattr(char *buf, size_t buflen, void *obj);
+static void dtlres_cb_getattr(void *obj);
+
+static void sumres_access(char *buf, size_t buflen, void *obj);
+static void dtlres_access(void *obj);
+static void sumres_close(char *buf, size_t buflen, void *obj);
+static void dtlres_close(void *obj);
+static void sumres_commit(char *buf, size_t buflen, void *obj);
+static void dtlres_commit(void *obj);
+static void dtlres_create(void *obj);
+static void sumres_getattr(char *buf, size_t buflen, void *obj);
+static void dtlres_getattr(void *obj);
+static void sumres_getfh(char *buf, size_t buflen, void *obj);
+static void dtlres_getfh(void *obj);
+static void dtlres_link(void *obj);
+static void sumres_lock(char *buf, size_t buflen, void *obj);
+static void dtlres_lock(void *obj);
+static void sumres_lockt(char *buf, size_t buflen, void *obj);
+static void dtlres_lockt(void *obj);
+static void sumres_locku(char *buf, size_t buflen, void *obj);
+static void dtlres_locku(void *obj);
+static void sumres_open(char *buf, size_t buflen, void *obj);
+static void dtlres_open(void *obj);
+static void sumres_open_confirm(char *buf, size_t buflen, void *obj);
+static void dtlres_open_confirm(void *obj);
+static void sumres_open_downgrd(char *buf, size_t buflen, void *obj);
+static void dtlres_open_downgrd(void *obj);
+static void sumres_read(char *buf, size_t buflen, void *obj);
+static void dtlres_read(void *obj);
+static void sumres_readdir(char *buf, size_t buflen, void *obj);
+static void dtlres_readdir(void *obj);
+static void sumres_readlnk(char *buf, size_t buflen, void *obj);
+static void dtlres_readlnk(void *obj);
+static void dtlres_remove(void *obj);
+static void dtlres_rename(void *obj);
+static void sumres_secinfo(char *buf, size_t buflen, void *obj);
+static void dtlres_secinfo(void *obj);
+static void sumres_setattr(char *buf, size_t buflen, void *obj);
+static void dtlres_setattr(void *obj);
+static void sumres_setclid(char *buf, size_t buflen, void *obj);
+static void dtlres_setclid(void *obj);
+static void sumres_write(char *buf, size_t buflen, void *obj);
+static void dtlres_write(void *obj);
+static void sum_nfsstat4(char *buf, size_t buflen, void *obj);
+static void dtl_nfsstat4(void *obj);
+static uint32_t adler16(void *, int);
+static void nfs4_xdr_skip(int nbytes);
+static char *sum_lock_type_name(enum nfs_lock_type4 type);
+
+int nfs4_pkt_start;
+int nfs4_pkt_len;
+int nfs4_skip_bytes;
+int nfs4_fragged_rpc;
+char *nfs4err_fragrpc = "<Fragmented RPC>";
+char *nfs4err_xdrfrag = "<XDR Error or Fragmented RPC>";
+
+/*
+ * need a way to enable this if current testcases are parsing snoop
+ * error text. -- maybe an env var would do as temp workaround until
+ * testcases changed to grep for new error text.
+ */
+int nfs4_use_old_error_text = 0;
+
+/*
+ * Information about each operation that can appear in a compound call.
+ * The function pointers are to formatting functions for summary arguments
+ * and results, and detail arguments & results.
+ */
+
+typedef struct {
+ char *name;
+ void (*sumarg)(char *, size_t, void *);
+ void (*sumres)(char *, size_t, void *);
+ void (*dtlarg)(void *);
+ void (*dtlres)(void *);
+} op_info_t;
+
+static op_info_t cb_opcode_info[] = {
+ {"OP_ZERO", NULL, NULL, NULL, NULL}, /* 0 */
+ {"OP_ONE", NULL, NULL, NULL, NULL},
+ {"OP_TWO", NULL, NULL, NULL, NULL}, /* minor vers */
+ {"CB_GETATTR",
+ sumarg_cb_getattr, sumres_cb_getattr,
+ dtlarg_cb_getattr, dtlres_cb_getattr},
+ {"CB_RECALL",
+ sumarg_cb_recall, sum_nfsstat4,
+ dtlarg_cb_recall, dtl_nfsstat4},
+};
+static uint_t cb_num_opcodes = sizeof (cb_opcode_info) / sizeof (op_info_t *);
+
+static op_info_t opcode_info[] = {
+ {"OP_ZERO", NULL, NULL, NULL, NULL}, /* 0 */
+ {"OP_ONE", NULL, NULL, NULL, NULL},
+ {"OP_TWO", NULL, NULL, NULL, NULL}, /* minor vers */
+ {"ACCESS",
+ sumarg_access, sumres_access, dtlarg_access, dtlres_access},
+ {"CLOSE",
+ sumarg_close, sumres_close, dtlarg_close, dtlres_close},
+ {"COMMIT",
+ sumarg_commit, sumres_commit, dtlarg_commit, dtlres_commit},
+ {"CREATE", /* 5 */
+ sumarg_create, sum_nfsstat4, dtlarg_create, dtlres_create},
+ {"DELEGPURGE",
+ sumarg_delprge, sum_nfsstat4, dtlarg_delprge, dtl_nfsstat4},
+ {"DELEGRETURN",
+ sumarg_delret, sum_nfsstat4, dtlarg_delret, dtl_nfsstat4},
+ {"GETATTR",
+ sumarg_getattr, sumres_getattr, dtlarg_getattr, dtlres_getattr},
+ {"GETFH",
+ NULL, sumres_getfh, NULL, dtlres_getfh},
+ {"LINK", /* 10 */
+ sumarg_link, sum_nfsstat4, dtlarg_link, dtlres_link},
+ {"LOCK",
+ sumarg_lock, sumres_lock, dtlarg_lock, dtlres_lock},
+ {"LOCKT",
+ sumarg_lockt, sumres_lockt, dtlarg_lockt, dtlres_lockt},
+ {"LOCKU",
+ sumarg_locku, sumres_locku, dtlarg_locku, dtlres_locku},
+ {"LOOKUP",
+ sumarg_lookup, sum_nfsstat4, dtlarg_lookup, dtl_nfsstat4},
+ {"LOOKUPP", /* 15 */
+ NULL, sum_nfsstat4, NULL, dtl_nfsstat4},
+ {"NVERIFY",
+ NULL, sum_nfsstat4, dtlarg_verify, dtl_nfsstat4},
+ {"OPEN",
+ sumarg_open, sumres_open, dtlarg_open, dtlres_open},
+ {"OPENATTR",
+ sumarg_openattr, sum_nfsstat4, dtlarg_openattr, dtl_nfsstat4},
+ {"OPEN_CONFIRM",
+ sumarg_open_confirm,
+ sumres_open_confirm,
+ dtlarg_open_confirm,
+ dtlres_open_confirm},
+ {"OPEN_DOWNGRADE",
+ sumarg_open_downgrd,
+ sumres_open_downgrd,
+ dtlarg_open_downgrd,
+ dtlres_open_downgrd},
+ {"PUTFH",
+ sumarg_putfh, sum_nfsstat4, dtlarg_putfh, dtl_nfsstat4},
+ {"PUTPUBFH", /* 20 */
+ NULL, sum_nfsstat4, NULL, dtl_nfsstat4},
+ {"PUTROOTFH",
+ NULL, sum_nfsstat4, NULL, dtl_nfsstat4},
+ {"READ",
+ sumarg_read, sumres_read, dtlarg_read, dtlres_read},
+ {"READDIR",
+ sumarg_readdir, sumres_readdir, dtlarg_readdir, dtlres_readdir},
+ {"READLINK",
+ NULL, sumres_readlnk, NULL, dtlres_readlnk},
+ {"REMOVE", /* 25 */
+ sumarg_compnt, sum_nfsstat4, dtlarg_compnt, dtlres_remove},
+ {"RENAME",
+ sumarg_rename, sum_nfsstat4, dtlarg_rename, dtlres_rename},
+ {"RENEW",
+ sumarg_renew, sum_nfsstat4, dtlarg_renew, dtl_nfsstat4},
+ {"RESTOREFH",
+ NULL, sum_nfsstat4, NULL, dtl_nfsstat4},
+ {"SAVEFH",
+ NULL, sum_nfsstat4, NULL, dtl_nfsstat4},
+ {"SECINFO", /* 30 */
+ sumarg_secinfo, sumres_secinfo, dtlarg_secinfo, dtlres_secinfo},
+ {"SETATTR",
+ sumarg_setattr, sumres_setattr, dtlarg_setattr, dtlres_setattr},
+ {"SETCLIENTID",
+ sumarg_setclid, sumres_setclid, dtlarg_setclid, dtlres_setclid},
+ {"SETCLIENTID_CONFIRM",
+ sumarg_setclid_cfm,
+ sum_nfsstat4,
+ dtlarg_setclid_cfm,
+ dtl_nfsstat4},
+ {"VERIFY",
+ NULL, sum_nfsstat4, dtlarg_verify, dtl_nfsstat4},
+ {"WRITE",
+ sumarg_write, sumres_write, dtlarg_write, dtlres_write},
+ {"RELEASE_LOCKOWNER",
+ sumarg_release_lkown, sum_nfsstat4,
+ dtlarg_release_lkown, dtl_nfsstat4},
+};
+static uint_t num_opcodes = sizeof (opcode_info) / sizeof (op_info_t *);
+
+/*
+ * File types.
+ */
+
+typedef struct {
+ char *short_name; /* for summary output */
+ char *long_name; /* for detail output */
+} ftype_names_t;
+
+static ftype_names_t ftype_names[] = {
+ {"Type 0", "Type 0"},
+ {"REG", "Regular File"},
+ {"DIR", "Directory"},
+ {"BLK", "Block Device"},
+ {"CHR", "Character Device"},
+ {"LNK", "Symbolic Link"}, /* 5 */
+ {"SOCK", "Socket"},
+ {"FIFO", "FIFO"},
+ {"ATTRDIR", "Attribute Directory"},
+ {"NAMEDATTR", "Named Attribute"},
+};
+static uint_t num_ftypes = sizeof (ftype_names) / sizeof (ftype_names_t);
+
+static ftype_names_t open_rflags[] = {
+ {"?", "UNKNOWN"}, /* 0 */
+ {"CF", "CONFIRM"}, /* 1 */
+ {"PL", "POSIX LOCK"}, /* 2 */
+ {"?", "UNKNOWN"},
+};
+static uint_t num_open_rflags =
+ sizeof (open_rflags) / sizeof (ftype_names_t) - 1;
+
+static char *get_flags(uint_t, ftype_names_t *, uint_t, int, char *);
+
+#define sum_open_rflags(flag) \
+ get_flags((flag), open_rflags, num_open_rflags, 1, " RF=")
+
+#define detail_open_rflags(flag) \
+ get_flags((flag), open_rflags, num_open_rflags, 0, NULL)
+
+static void prt_supported_attrs(XDR *);
+static void prt_type(XDR *);
+static void prt_fh_expire_type(XDR *);
+static void prt_change(XDR *);
+static void prt_size(XDR *);
+static void prt_link_support(XDR *);
+static void prt_symlink_support(XDR *);
+static void prt_named_attr(XDR *);
+static void prt_fsid(XDR *);
+static void prt_unique_handles(XDR *);
+static void prt_lease_time(XDR *);
+static void prt_rdattr_error(XDR *);
+static void prt_acl(XDR *);
+static void prt_aclsupport(XDR *);
+static void prt_archive(XDR *);
+static void prt_cansettime(XDR *);
+static void prt_case_insensitive(XDR *);
+static void prt_case_preserving(XDR *);
+static void prt_chown_restricted(XDR *);
+static void prt_filehandle(XDR *);
+static void prt_fileid(XDR *);
+static void prt_mounted_on_fileid(XDR *);
+static void prt_files_avail(XDR *);
+static void prt_files_free(XDR *);
+static void prt_files_total(XDR *);
+static void prt_fs_locations(XDR *);
+static void prt_hidden(XDR *);
+static void prt_homogeneous(XDR *);
+static void prt_maxfilesize(XDR *);
+static void prt_maxlink(XDR *);
+static void prt_maxname(XDR *);
+static void prt_maxread(XDR *);
+static void prt_maxwrite(XDR *);
+static void prt_mimetype(XDR *);
+static void prt_mode(XDR *);
+static void prt_no_trunc(XDR *);
+static void prt_numlinks(XDR *);
+static void prt_owner(XDR *);
+static void prt_owner_group(XDR *);
+static void prt_quota_avail_hard(XDR *);
+static void prt_quota_avail_soft(XDR *);
+static void prt_quota_used(XDR *);
+static void prt_rawdev(XDR *);
+static void prt_space_avail(XDR *);
+static void prt_space_free(XDR *);
+static void prt_space_total(XDR *);
+static void prt_space_used(XDR *);
+static void prt_system(XDR *);
+static void prt_time_access(XDR *);
+static void prt_time_access_set(XDR *);
+static void prt_time_backup(XDR *);
+static void prt_time_create(XDR *);
+static void prt_time_delta(XDR *);
+static void prt_time_metadata(XDR *);
+static void prt_time_modify(XDR *);
+static void prt_time_modify_set(XDR *);
+
+
+
+/*
+ * Information for attributes.
+ * name name of the attribute.
+ * prt_details function to XDR decode the attribute and print it.
+ *
+ * XXX If this table ever gets extensively changed (including
+ * reorganization to track changes to the spec), it would probably be a
+ * good idea to change to a scheme where the table is mechanically
+ * generated. Look at $SRC/uts/common/rpcsvc for how this is done in the
+ * kernel.
+ */
+
+typedef struct {
+ char *name;
+ void (*prt_details)(XDR *);
+} attr_info_t;
+
+static attr_info_t attr_info[MAX_ATTRIBUTES] = {
+ {"SUPPORTED_ATTRS", prt_supported_attrs},
+ {"TYPE", prt_type},
+ {"FH_EXPIRE_TYPE", prt_fh_expire_type},
+ {"CHANGE", prt_change},
+ {"SIZE", prt_size},
+ {"LINK_SUPPORT", prt_link_support}, /* 5 */
+ {"SYMLINK_SUPPORT", prt_symlink_support},
+ {"NAMED_ATTR", prt_named_attr},
+ {"FSID", prt_fsid},
+ {"UNIQUE_HANDLES", prt_unique_handles},
+ {"LEASE_TIME", prt_lease_time}, /* 10 */
+ {"RDATTR_ERROR", prt_rdattr_error},
+ {"ACL", prt_acl},
+ {"ACLSUPPORT", prt_aclsupport},
+ {"ARCHIVE", prt_archive},
+ {"CANSETTIME", prt_cansettime}, /* 15 */
+ {"CASE_INSENSITIVE", prt_case_insensitive},
+ {"CASE_PRESERVING", prt_case_preserving},
+ {"CHOWN_RESTRICTED", prt_chown_restricted},
+ {"FILEHANDLE", prt_filehandle},
+ {"FILEID", prt_fileid}, /* 20 */
+ {"FILES_AVAIL", prt_files_avail},
+ {"FILES_FREE", prt_files_free},
+ {"FILES_TOTAL", prt_files_total},
+ {"FS_LOCATIONS", prt_fs_locations},
+ {"HIDDEN", prt_hidden}, /* 25 */
+ {"HOMOGENEOUS", prt_homogeneous},
+ {"MAXFILESIZE", prt_maxfilesize},
+ {"MAXLINK", prt_maxlink},
+ {"MAXNAME", prt_maxname},
+ {"MAXREAD", prt_maxread}, /* 30 */
+ {"MAXWRITE", prt_maxwrite},
+ {"MIMETYPE", prt_mimetype},
+ {"MODE", prt_mode},
+ {"NO_TRUNC", prt_no_trunc},
+ {"NUMLINKS", prt_numlinks}, /* 35 */
+ {"OWNER", prt_owner},
+ {"OWNER_GROUP", prt_owner_group},
+ {"QUOTA_AVAIL_HARD", prt_quota_avail_hard},
+ {"QUOTA_AVAIL_SOFT", prt_quota_avail_soft},
+ {"QUOTA_USED", prt_quota_used}, /* 40 */
+ {"RAWDEV", prt_rawdev},
+ {"SPACE_AVAIL", prt_space_avail},
+ {"SPACE_FREE", prt_space_free},
+ {"SPACE_TOTAL", prt_space_total},
+ {"SPACE_USED", prt_space_used}, /* 45 */
+ {"SYSTEM", prt_system},
+ {"TIME_ACCESS", prt_time_access},
+ {"TIME_ACCESS_SET", prt_time_access_set},
+ {"TIME_BACKUP", prt_time_backup},
+ {"TIME_CREATE", prt_time_create}, /* 50 */
+ {"TIME_DELTA", prt_time_delta},
+ {"TIME_METADATA", prt_time_metadata},
+ {"TIME_MODIFY", prt_time_modify},
+ {"TIME_MODIFY_SET", prt_time_modify_set},
+ {"MOUNTED_ON_FILEID", prt_mounted_on_fileid},
+};
+
+extern char *get_sum_line();
+
+extern jmp_buf xdr_err;
+
+static void sum_comp4res(char *, char *(*)(void));
+static char *sum_compound4args(void);
+static char *sum_compound4res(void);
+static char *sum_operand(nfs_argop4 *opp);
+static char *sum_result(nfs_resop4 *resp);
+
+static char *sum_cb_compound4args(void);
+static char *sum_cb_compound4res(void);
+static char *sum_cb_operand(nfs_cb_argop4 *opp);
+static char *sum_cb_result(nfs_cb_resop4 *resp);
+
+static void detail_acetype4(acetype4);
+static void detail_uint32_bitmap(uint32_t, char *[], int);
+static void detail_aceflag4(aceflag4);
+static void detail_acemask4(acemask4);
+static void detail_nfs_argop4(void);
+static void detail_nfs_resop4(void);
+static void detail_cb_argop4(void);
+static void detail_cb_resop4(void);
+
+static char *attr_name(uint_t);
+static char *claim_name(enum open_claim_type4 claim_type);
+static char *delegation_type_name(enum open_delegation_type4 type);
+static char *flavor_name(uint_t flavor);
+static char *gss_svc_name(rpc_gss_svc_t svc);
+static char *limitby_name(enum limit_by4 limitby);
+static char *lock_type_name(enum nfs_lock_type4);
+static char *opcode_name(uint_t);
+static char *cb_opcode_name(uint_t opnum);
+static char *status_name(int);
+static char *status_name_compat(int);
+static char *status_name_pcol(int);
+static char *sum_type_name(nfs_ftype4);
+static void sum_access4(char *buf, size_t buflen, uint32_t bits);
+static void detail_access4(char *, uint32_t);
+static void sum_claim(char *buf, size_t buflen, open_claim4 *claim);
+static void detail_claim(open_claim4 *claim);
+static char *sum_clientid(clientid4 client);
+static void detail_clientid(clientid4 client);
+static char *_sum_stateid(stateid4 *, char *prefix);
+static void sum_delegation(char *buf, size_t buflen, open_delegation4 *delp);
+static void detail_delegation(open_delegation4 *delp);
+static void detail_lock_owner(lock_owner4 *owner);
+static void detail_open_owner(open_owner4 *owner);
+static void sum_openflag(char *bufp, int buflen, openflag4 *flagp);
+static char *get_deleg_typestr(open_delegation_type4 dt);
+static void detail_openflag(openflag4 *flagp);
+static void sum_name(char *buf, size_t buflen, open_claim4 *claim);
+static void detail_rpcsec_gss(rpcsec_gss_info *);
+static void detail_secinfo4(secinfo4 *infop);
+static char *sum_space_limit(nfs_space_limit4 *limitp);
+static void detail_space_limit(nfs_space_limit4 *limitp);
+static char *detail_type_name(nfs_ftype4);
+static char *createhow4_name(createhow4 *crtp);
+
+
+static void showxdr_utf8string(char *);
+static char *utf8localize(utf8string *);
+static void utf8free(void);
+static void sum_pathname4(char *, size_t, pathname4 *);
+static void detail_pathname4(pathname4 *pathp, char *);
+static void sum_compname4(char *buf, size_t buflen, component4 *comp);
+static void detail_compname4(component4 *comp);
+
+static void detail_fattr4(fattr4 *attrp);
+static void detail_attr_bitmap(char *, bitmap4 *, unpkd_attrmap_t *);
+static void sum_attr_bitmap(char *buf, size_t buflen, bitmap4 *mapp);
+static void detail_fattr4_change(char *msg, fattr4_change chg);
+static char *sum_fh4(nfs_fh4 *fhp);
+static void detail_fh4(nfs_fh4 *fh);
+
+#define fh4_hash(fh) adler16((fh)->nfs_fh4_val, (fh)->nfs_fh4_len)
+#define stateid_hash(st) adler16((st)->other, sizeof ((st)->other))
+#define owner_hash(own) adler16((own)->owner_val, (own)->owner_len)
+
+#define sum_deleg_stateid(st) _sum_stateid((st), "DST=")
+#define sum_open_stateid(st) _sum_stateid((st), "OST=")
+#define sum_lock_stateid(st) _sum_stateid((st), "LST=")
+#define sum_stateid(st) _sum_stateid((st), "ST=")
+
+#define detail_deleg_stateid(st) _detail_stateid((st), "Delegation ")
+#define detail_open_stateid(st) _detail_stateid((st), "Open ")
+#define detail_lock_stateid(st) _detail_stateid((st), "Lock ")
+#define detail_stateid(st) _detail_stateid((st), "")
+
+#define SPECIAL_STATEID0 "SPC0"
+#define SPECIAL_STATEID1 "SPC1"
+
+#define DONT_CHANGE 0
+#define SET_TO_SERVER_TIME 1
+#define SET_TO_CLIENT_TIME 2
+
+static stateid4 spec_stateid_0 =
+ {0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+static stateid4 spec_stateid_1 =
+ {0xFFFFFFFF, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};
+
+static char *procnames_short[] = {
+ "NULL4", /* 0 */
+ "COMPOUND4" /* 1 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Compound", /* 1 */
+};
+
+static char *cb_procnames_short[] = {
+ "CB_NULL", /* 0 */
+ "CB_COMPOUND" /* 1 */
+};
+
+static char *cb_procnames_long[] = {
+ "Null CallBack procedure", /* 0 */
+ "CallBack compound", /* 1 */
+};
+
+static char *acetype4_names[] = {
+ "ACE4_ACCESS_ALLOWED_ACE_TYPE",
+ "ACE4_ACCESS_DENIED_ACE_TYPE",
+ "ACE4_SYSTEM_AUDIT_ACE_TYPE",
+ "ACE4_SYSTEM_ALARM_ACE_TYPE"
+};
+#define ACETYPE4_NAMES_MAX (sizeof (acetype4_names) / sizeof (char *))
+
+static char *aceflag4_names[] = {
+ "ACE4_FILE_INHERIT_ACE",
+ "ACE4_DIRECTORY_INHERIT_ACE",
+ "ACE4_NO_PROPAGATE_INHERIT_ACE",
+ "ACE4_INHERIT_ONLY_ACE",
+ "ACE4_SUCCESSFUL_ACCESS_ACE_FLAG",
+ "ACE4_FAILED_ACCESS_ACE_FLAG",
+ "ACE4_IDENTIFIER_GROUP"
+};
+#define ACEFLAG4_NAMES_MAX (sizeof (aceflag4_names) / sizeof (char *))
+
+static char *acemask4_names[] = {
+ "ACE4_READ_DATA/ACE4_LIST_DIRECTORY",
+ "ACE4_WRITE_DATA/ACE4_ADD_FILE",
+ "ACE4_APPEND_DATA/ACE4_ADD_SUBDIRECTORY",
+ "ACE4_READ_NAMED_ATTRS",
+ "ACE4_WRITE_NAMED_ATTRS",
+ "ACE4_EXECUTE",
+ "ACE4_DELETE_CHILD",
+ "ACE4_READ_ATTRIBUTES",
+ "ACE4_WRITE_ATTRIBUTES",
+ "UNDEFINED", /* 0x00000200 */
+ "UNDEFINED", /* 0x00000400 */
+ "UNDEFINED", /* 0x00000800 */
+ "UNDEFINED", /* 0x00001000 */
+ "UNDEFINED", /* 0x00002000 */
+ "UNDEFINED", /* 0x00004000 */
+ "UNDEFINED", /* 0x00008000 */
+ "ACE4_DELETE",
+ "ACE4_READ_ACL",
+ "ACE4_WRITE_ACL",
+ "ACE4_WRITE_OWNER",
+ "ACE4_SYNCHRONIZE"
+};
+#define ACEMASK4_NAMES_MAX (sizeof (acemask4_names) / sizeof (char *))
+
+#define MAXPROC 1
+
+/*ARGSUSED*/
+void
+interpret_nfs4_cb(int flags, int type, int xid, int vers, int proc,
+ char *data, int len)
+{
+ char *line = NULL;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "NFS C %s",
+ proc == CB_COMPOUND ? "CB4" :
+ cb_procnames_short[proc]);
+ line += strlen(line);
+
+ if (proc == CB_COMPOUND) {
+ static utf8string tag;
+
+ if (!xdr_utf8string(&xdrm, &tag))
+ longjmp(xdr_err, 1);
+ sprintf(line, " (%.20s) %s",
+ utf8localize(&tag),
+ sum_cb_compound4args());
+ xdr_free(xdr_utf8string, (char *)&tag);
+ }
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "NFS R %s ",
+ proc == CB_COMPOUND ? "CB4" :
+ cb_procnames_short[proc]);
+ line += strlen(line);
+ if (proc == CB_COMPOUND)
+ sum_comp4res(line, sum_cb_compound4res);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NFS: ", "Sun NFS4 CallBack", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
+ proc, cb_procnames_long[proc]);
+ if (proc == CB_COMPOUND) {
+ if (type == CALL) {
+ showxdr_utf8string("Tag = %s");
+ detail_cb_argop4();
+ } else {
+ nfsstat4 status;
+
+ status = getxdr_long();
+ showxdr_utf8string("Tag = %s");
+ sprintf(get_line(0, 0), "Status = %d (%s)",
+ status, status_name(status));
+ detail_cb_resop4();
+ }
+ }
+ show_trailer();
+ }
+
+ utf8free(); /* cf. utf8localize() */
+}
+
+
+/*ARGSUSED*/
+void
+interpret_nfs4(int flags, int type, int xid, int vers, int proc,
+ char *data, int len)
+{
+ char *line = NULL;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ nfs4_fragged_rpc = 0;
+ nfs4_pkt_len = len;
+ nfs4_pkt_start = xdr_getpos(&xdrm);
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "NFS C %s",
+ proc == NFSPROC4_COMPOUND ? "4" :
+ procnames_short[proc]);
+ line += strlen(line);
+
+ if (proc == NFSPROC4_COMPOUND) {
+ static utf8string tag;
+
+ if (!xdr_utf8string(&xdrm, &tag))
+ longjmp(xdr_err, 1);
+ sprintf(line, " (%.20s) %s",
+ utf8localize(&tag),
+ sum_compound4args());
+ xdr_free(xdr_utf8string, (char *)&tag);
+ }
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "NFS R %s ",
+ proc == NFSPROC4_COMPOUND ? "4" :
+ procnames_short[proc]);
+ line += strlen(line);
+
+ if (proc == NFSPROC4_COMPOUND)
+ sum_comp4res(line, sum_compound4res);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NFS: ", "Sun NFS", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+ if (proc == NFSPROC4_COMPOUND) {
+ if (type == CALL) {
+ showxdr_utf8string("Tag = %s");
+ detail_nfs_argop4();
+ } else {
+ nfsstat4 status;
+
+ status = getxdr_long();
+ showxdr_utf8string("Tag = %s");
+ sprintf(get_line(0, 0), "Status = %d (%s)",
+ status, status_name(status));
+ detail_nfs_resop4();
+ }
+ }
+ show_trailer();
+ }
+
+ utf8free(); /* cf. utf8localize() */
+}
+
+
+
+/*
+ * Return the names and arguments of the oplist elements, up to
+ * SUM_COMPND_MAX characters. If the elements don't fit, include a "..."
+ * at the end of the string.
+ */
+
+static char *
+sum_compound4args(void)
+{
+ static char buf[SUM_COMPND_MAX + 2]; /* 1 for null, 1 for overflow */
+ int numops;
+ const size_t buflen = sizeof (buf);
+ char *bp;
+ nfs_argop4 one_op;
+ uint32_t minor_version;
+
+ buf[0] = '\0';
+
+ if (setjmp(xdr_err)) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf),
+ nfs4_fragged_rpc ? nfs4err_fragrpc : nfs4err_xdrfrag);
+ return (buf);
+ }
+
+ /*
+ * might be nice to print minor version, but doesn't
+ * seem like very useful info for summary mode
+ */
+ if (!xdr_uint32_t(&xdrm, &minor_version))
+ longjmp(xdr_err, 1);
+
+ numops = getxdr_long();
+ bp = buf;
+ while (numops-- > 0) {
+ char *operand;
+
+ bzero(&one_op, sizeof (one_op));
+
+ if (!xdr_nfs_argop4(&xdrm, &one_op)) {
+ xdr_free(xdr_nfs_argop4, (char *)&one_op);
+ longjmp(xdr_err, 1);
+ }
+ snprintf(bp, buflen - (bp - buf), "%s ",
+ opcode_name(one_op.argop));
+ bp += strlen(bp);
+
+ operand = sum_operand(&one_op);
+ if (strlen(operand) > 0) {
+ snprintf(bp, buflen - (bp - buf), "%s ", operand);
+ bp += strlen(bp);
+ }
+
+ /* nfs4_skip_bytes set by xdr_nfs4_argop4 */
+ if (nfs4_skip_bytes != 0)
+ nfs4_xdr_skip(nfs4_skip_bytes);
+
+ xdr_free(xdr_nfs_argop4, (char *)&one_op);
+
+ /* add "..." if past the "end" of the buffer */
+ if (bp - buf > SUM_COMPND_MAX) {
+ strcpy(buf + SUM_COMPND_MAX - strlen("..."),
+ "...");
+ break;
+ }
+ }
+
+ return (buf);
+}
+
+static void
+nfs4_xdr_skip(int nbytes)
+{
+ int resid, off, len, cur_pos, new_pos;
+
+ len = RNDUP(nbytes);
+ cur_pos = xdr_getpos(&xdrm);
+
+ /*
+ * Time to skip over the rd/wr data. If the
+ * rd/wr data is completely contained in the first
+ * frag, we must skip over it to process the rest of
+ * the packet.
+ *
+ * nfs4_pkt_start: XDR position of start of NFS4 compound
+ * nfs4_pkt_len: number of bytes in pkt relative to
+ * nfs4_pkt_start
+ *
+ * cur_pos: current XDR position
+ * off: current XDR position relative to nfs4_pkt_start
+ * resid: number of unprocessed bytes in current pkt
+ * (relative to cur_pos/off)
+ *
+ * If nbytes <= resid, then we must skip over the rd/wr
+ * bytes so we can read the next op/compound in this
+ * packet. Otherwise, set the fragged flag so we can
+ * display the fragged_rpc message.
+ */
+ off = cur_pos - nfs4_pkt_start;
+ resid = nfs4_pkt_len - off;
+
+ /*
+ * set nfs4_fragged_rpc if the requested number of "skip"
+ * bytes is larger than the bytes remaining in the XDR
+ * stream/current packet. The global is reset to 0 at
+ * start of interpret_nfs4.
+ */
+ new_pos = cur_pos + ((nfs4_fragged_rpc = len > resid) ? resid : len);
+
+ /* there's nothing to do for error case (if it fails pkt is doomed) */
+ xdr_setpos(&xdrm, new_pos);
+}
+
+
+/*
+ * Return the names and arguments of the oplist elements, up to
+ * SUM_COMPND_MAX characters. If the elements don't fit, include a "..."
+ * at the end of the string.
+ */
+static char *
+sum_cb_compound4args(void)
+{
+ static char buf[SUM_COMPND_MAX + 2]; /* 1 for null, 1 for overflow */
+ int numops;
+ const size_t buflen = sizeof (buf);
+ char *bp;
+ nfs_cb_argop4 one_op;
+ uint32_t minor_version, callback_ident;
+
+ buf[0] = '\0';
+ if (setjmp(xdr_err)) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), "<XDR Error or Fragmented"
+ " RPC>");
+ return (buf);
+ }
+
+ /*
+ * might be nice to print minor version, but doesn't
+ * seem like very useful info for summary mode
+ */
+ if (!xdr_uint32_t(&xdrm, &minor_version))
+ longjmp(xdr_err, 1);
+
+ /* print callback_ident */
+ if (!xdr_uint32_t(&xdrm, &callback_ident))
+ longjmp(xdr_err, 1);
+ snprintf(buf, buflen, "CBID=%u ", callback_ident);
+
+ bp = buf + strlen(buf);
+ numops = getxdr_long();
+
+ while (numops-- > 0) {
+ char *operand;
+
+ bzero(&one_op, sizeof (one_op));
+ if (!xdr_nfs_cb_argop4(&xdrm, &one_op)) {
+ xdr_free(xdr_nfs_cb_argop4, (char *)&one_op);
+ longjmp(xdr_err, 1);
+ }
+
+ snprintf(bp, buflen - (bp - buf), "%s ",
+ cb_opcode_name(one_op.argop));
+ bp += strlen(bp);
+ operand = sum_cb_operand(&one_op);
+ if (strlen(operand) > 0) {
+ snprintf(bp, buflen - (bp - buf), "%s ", operand);
+ bp += strlen(bp);
+ }
+
+ xdr_free(xdr_nfs_cb_argop4, (char *)&one_op);
+
+ /* add "..." if past the "end" of the buffer */
+ if (bp - buf > SUM_COMPND_MAX) {
+ strcpy(buf + SUM_COMPND_MAX - strlen("..."),
+ "...");
+ break;
+ }
+ }
+
+ return (buf);
+}
+
+/*
+ * Return the summarized argument list for the given nfs_argop4.
+ */
+
+static char *
+sum_operand(nfs_argop4 *opp)
+{
+ static char buf[1024];
+ void (*fmtproc)(char *, size_t, void *);
+
+ buf[0] = '\0';
+ if (opp->argop < num_opcodes) {
+ fmtproc = opcode_info[opp->argop].sumarg;
+ if (fmtproc != NULL)
+ fmtproc(buf, sizeof (buf), &opp->nfs_argop4_u);
+ }
+
+ return (buf);
+}
+
+/*
+ * Return the summarized argument list for the given nfs_argop4.
+ */
+
+static char *
+sum_cb_operand(nfs_cb_argop4 *opp)
+{
+ static char buf[1024];
+ void (*fmtproc)(char *, size_t, void *);
+
+ buf[0] = '\0';
+ if (opp->argop < cb_num_opcodes) {
+ fmtproc = cb_opcode_info[opp->argop].sumarg;
+ if (fmtproc != NULL)
+ fmtproc(buf, sizeof (buf), &opp->nfs_cb_argop4_u);
+ }
+
+ return (buf);
+}
+
+/*
+ * Print details about the nfs_argop4 that is next in the XDR stream.
+ */
+
+static void
+detail_nfs_argop4(void)
+{
+ int numops;
+ nfs_argop4 one_op;
+ void (*fmtproc)(void *);
+ uint32_t minor_version;
+
+ if (!xdr_uint32_t(&xdrm, &minor_version))
+ longjmp(xdr_err, 1);
+
+ (void) sprintf(get_line(0, 0), "Minor version = %u",
+ minor_version);
+
+ numops = getxdr_long();
+ (void) sprintf(get_line(0, 0), "Number of operations = %d",
+ numops);
+
+ while (numops-- > 0) {
+ bzero(&one_op, sizeof (one_op));
+
+ if (!xdr_nfs_argop4(&xdrm, &one_op)) {
+ xdr_free(xdr_nfs_argop4, (char *)&one_op);
+ longjmp(xdr_err, 1);
+ }
+
+ get_line(0, 0); /* blank line to separate ops */
+ sprintf(get_line(0, 0), "Op = %d (%s)",
+ one_op.argop, opcode_name(one_op.argop));
+ if (one_op.argop < num_opcodes) {
+ fmtproc = opcode_info[one_op.argop].dtlarg;
+ if (fmtproc != NULL)
+ fmtproc(&one_op.nfs_argop4_u);
+ }
+
+ /* nfs4_skip_bytes set by xdr_nfs_argop4() */
+ if (nfs4_skip_bytes)
+ nfs4_xdr_skip(nfs4_skip_bytes);
+
+ xdr_free(xdr_nfs_argop4, (char *)&one_op);
+ }
+}
+
+
+/*
+ * Print details about the nfs_argop4 that is next in the XDR stream.
+ */
+static void
+detail_cb_argop4(void)
+{
+ int numops;
+ nfs_cb_argop4 one_op;
+ void (*fmtproc)(void *);
+ uint32_t minor_version, callback_ident;
+
+ if (!xdr_uint32_t(&xdrm, &minor_version))
+ longjmp(xdr_err, 1);
+ (void) sprintf(get_line(0, 0), "Minor version = %u",
+ minor_version);
+
+ if (!xdr_uint32_t(&xdrm, &callback_ident))
+ longjmp(xdr_err, 1);
+ (void) sprintf(get_line(0, 0), "Callback Ident = %u",
+ callback_ident);
+
+ numops = getxdr_long();
+ (void) sprintf(get_line(0, 0), "Number of operations = %d",
+ numops);
+
+ while (numops-- > 0) {
+ bzero(&one_op, sizeof (one_op));
+ if (!xdr_nfs_cb_argop4(&xdrm, &one_op)) {
+ xdr_free(xdr_nfs_cb_argop4, (char *)&one_op);
+ longjmp(xdr_err, 1);
+ }
+
+ get_line(0, 0); /* blank line to separate ops */
+ sprintf(get_line(0, 0), "Op = %d (%s)",
+ one_op.argop, cb_opcode_name(one_op.argop));
+ if (one_op.argop < cb_num_opcodes) {
+ fmtproc = cb_opcode_info[one_op.argop].dtlarg;
+ if (fmtproc != NULL)
+ fmtproc(&one_op.nfs_cb_argop4_u);
+ }
+
+ xdr_free(xdr_nfs_cb_argop4, (char *)&one_op);
+ }
+}
+
+/*
+ * component_name: return a printable string for the given component4. I'm
+ * leaving this as a separate function (as opposed to having the callers
+ * call utf8localize() directly) in case the definition of component4
+ * changes.
+ */
+
+static char *
+component_name(component4 *cp)
+{
+ return (utf8localize(cp));
+}
+
+/*
+ * linktext_name. cf. component_name().
+ */
+
+static char *
+linktext_name(linktext4 *lp)
+{
+ return (utf8localize(lp));
+}
+
+/*
+ * stable_how4_name: return a string for "how".
+ */
+
+static char *
+stable_how4_name(stable_how4 how)
+{
+ char *result;
+
+ switch (how) {
+ case UNSTABLE4:
+ result = "ASYNC";
+ break;
+ case DATA_SYNC4:
+ result = "DSYNC";
+ break;
+ case FILE_SYNC4:
+ result = "FSYNC";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * sum_open_share_access: return a string corresponding to the
+ * given OPEN share access bitmask.
+ */
+
+static char *
+sum_open_share_access(int32_t mask)
+{
+ char *result;
+
+ switch (mask) {
+ case 0:
+ result = "N";
+ break;
+ case OPEN4_SHARE_ACCESS_READ:
+ result = "R";
+ break;
+ case OPEN4_SHARE_ACCESS_WRITE:
+ result = "W";
+ break;
+ case OPEN4_SHARE_ACCESS_BOTH:
+ result = "RW";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * sum_open_share_deny: return a string corresponding to the
+ * given OPEN share deny bitmask.
+ */
+
+static char *
+sum_open_share_deny(int32_t mask)
+{
+ char *result;
+
+ switch (mask) {
+ case OPEN4_SHARE_DENY_NONE:
+ result = "N";
+ break;
+ case OPEN4_SHARE_DENY_READ:
+ result = "R";
+ break;
+ case OPEN4_SHARE_DENY_WRITE:
+ result = "W";
+ break;
+ case OPEN4_SHARE_DENY_BOTH:
+ result = "RW";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+static int
+special_stateid(stateid4 *stateid)
+{
+
+ if (! memcmp(stateid, &spec_stateid_0, sizeof (*stateid)))
+ return (0);
+
+ if (! memcmp(stateid, &spec_stateid_1, sizeof (*stateid)))
+ return (1);
+
+ return (-1);
+}
+
+static char *
+_sum_stateid(stateid4 *stateid, char *prefix)
+{
+ static char buf[32];
+ int spec;
+
+ if ((spec = special_stateid(stateid)) < 0)
+ snprintf(buf, sizeof (buf), "%s%04X:%u", prefix,
+ stateid_hash(stateid), stateid->seqid);
+ else
+ snprintf(buf, sizeof (buf), "%s%s", prefix,
+ spec == 0 ? "SPC0" : (spec == 1 ? "SPC1" : "SPC?"));
+ return (buf);
+}
+
+static void
+_detail_stateid(stateid4 *stateid, char *prefix)
+{
+ int spec;
+ char seqstr[32] = {0};
+
+ spec = special_stateid(stateid);
+
+ if (spec < 0)
+ sprintf(get_line(0, 0), "%sState ID hash = %04X",
+ prefix, stateid_hash(stateid));
+ else
+ sprintf(get_line(0, 0), "%sState ID hash = %s", prefix,
+ spec == 0 ? "SPECIAL_0" :
+ (spec == 1 ? "SPECIAL_1" : "SPECIAL_?"));
+
+ sprintf(get_line(0, 0), " len = %u val = %s",
+ sizeof (stateid->other),
+ tohex(stateid->other, sizeof (stateid->other)));
+
+ /*
+ * If spec 0/1 stateid, print seqid in hex; otherwise,
+ * use decimal. This makes it more clear how spec stateids
+ * are constructed [obvious that either all bits are 0, or all
+ * bits are 1].
+ */
+ if (spec == -1)
+ sprintf(seqstr, "%d", stateid->seqid);
+ else
+ sprintf(seqstr, "%08X", stateid->seqid);
+
+ sprintf(get_line(0, 0), " %sState ID Sequence ID = %s",
+ prefix, seqstr);
+}
+
+
+static char *
+sum_lock_denied(LOCK4denied *denied)
+{
+ static char buf[64];
+
+ sprintf(buf, "%s %llu %llu LO=%04X",
+ sum_lock_type_name(denied->locktype),
+ denied->offset, denied->length,
+ owner_hash(&denied->owner.owner));
+
+ return (buf);
+}
+
+static void
+detail_lock_denied(LOCK4denied *denied)
+{
+ sprintf(get_line(0, 0), "Type = %s", lock_type_name(denied->locktype));
+ detail_lock_owner(&denied->owner);
+ sprintf(get_line(0, 0), "Offset = %llu", denied->offset);
+ sprintf(get_line(0, 0), "Length = %llu", denied->length);
+}
+
+/*
+ * sum_createhow4: return the string name of "how".
+ */
+
+static char *
+createhow4_name(createhow4 *crtp)
+{
+ char *result;
+
+ switch (crtp->mode) {
+ case UNCHECKED4:
+ result = "UNCHECKED";
+ break;
+ case GUARDED4:
+ result = "GUARDED";
+ break;
+ case EXCLUSIVE4:
+ result = "EXCLUSIVE";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * detail_createhow4: print detail information about "how".
+ */
+
+static void
+detail_createhow4(createhow4 *crtp)
+{
+ sprintf(get_line(0, 0), "Method = %s",
+ createhow4_name(crtp));
+
+ switch (crtp->mode) {
+ case UNCHECKED4:
+ case GUARDED4:
+ detail_fattr4(&crtp->createhow4_u.createattrs);
+ break;
+ case EXCLUSIVE4:
+ sprintf(get_line(0, 0), " Verifier = %s",
+ tohex(crtp->createhow4_u.createverf,
+ NFS4_VERIFIER_SIZE));
+ break;
+ }
+}
+
+static void
+detail_createtype4(createtype4 *crtp)
+{
+ sprintf(get_line(0, 0), "Type = %s",
+ detail_type_name(crtp->type));
+ switch (crtp->type) {
+ case NF4LNK:
+ sprintf(get_line(0, 0), "Linkdata = %s",
+ utf8localize(&crtp->createtype4_u.linkdata));
+ break;
+ case NF4BLK:
+ case NF4CHR:
+ sprintf(get_line(0, 0), "Specdata1 = %04x Specdata2 = %04x",
+ crtp->createtype4_u.devdata.specdata1,
+ crtp->createtype4_u.devdata.specdata2);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+sumarg_access(char *buf, size_t buflen, void *obj)
+{
+ ACCESS4args *args = (ACCESS4args *)obj;
+
+ sum_access4(buf, buflen, args->access);
+}
+
+static void
+dtlarg_access(void *obj)
+{
+ ACCESS4args *args = (ACCESS4args *)obj;
+
+ detail_access4("Access bits", args->access);
+}
+
+static void
+sumarg_close(char *buf, size_t buflen, void *obj)
+{
+ CLOSE4args *args = (CLOSE4args *)obj;
+
+ snprintf(buf, buflen, "SQ=%u %s",
+ args->seqid, sum_open_stateid(&args->open_stateid));
+}
+
+static void
+dtlarg_close(void *obj)
+{
+ CLOSE4args *args = (CLOSE4args *)obj;
+
+ detail_open_stateid(&args->open_stateid);
+ sprintf(get_line(0, 0), "Sequence ID = %u", args->seqid);
+}
+
+static void
+sumarg_commit(char *buf, size_t buflen, void *obj)
+{
+ COMMIT4args *args = (COMMIT4args *)obj;
+
+ snprintf(buf, buflen, "at %llu for %u ", args->offset,
+ args->count);
+}
+
+static void
+dtlarg_commit(void *obj)
+{
+ COMMIT4args *args = (COMMIT4args *)obj;
+
+ sprintf(get_line(0, 0), "Offset = %llu", args->offset);
+ sprintf(get_line(0, 0), "Count = %u", args->count);
+}
+
+static void
+sumarg_compnt(char *buf, size_t buflen, void *obj)
+{
+ component4 *comp = (component4 *)obj;
+
+ snprintf(buf, buflen, "%s", component_name(comp));
+}
+
+static void
+dtlarg_compnt(void *obj)
+{
+ component4 *comp = (component4 *)obj;
+
+ sprintf(get_line(0, 0), "Name = %s", component_name(comp));
+}
+
+static void
+sumarg_create(char *buf, size_t buflen, void *obj)
+{
+ CREATE4args *args = (CREATE4args *)obj;
+
+ snprintf(buf, buflen, "%s %s ", component_name(&args->objname),
+ sum_type_name(args->objtype.type));
+}
+
+static void
+dtlarg_create(void *obj)
+{
+ CREATE4args *args = (CREATE4args *)obj;
+
+ sprintf(get_line(0, 0), "Name = %s", component_name(&args->objname));
+ detail_createtype4(&args->objtype);
+ detail_fattr4(&args->createattrs);
+}
+
+static void
+sumarg_delprge(char *buf, size_t buflen, void *obj)
+{
+ DELEGPURGE4args *args = (DELEGPURGE4args *)obj;
+
+ snprintf(buf, buflen, "%s", sum_clientid(args->clientid));
+}
+
+static void
+dtlarg_delprge(void *obj)
+{
+ DELEGPURGE4args *args = (DELEGPURGE4args *)obj;
+
+ detail_clientid(args->clientid);
+}
+
+static void
+sumarg_delret(char *buf, size_t buflen, void *obj)
+{
+ DELEGRETURN4args *args = (DELEGRETURN4args *)obj;
+
+ snprintf(buf, buflen, "%s", sum_deleg_stateid(&args->deleg_stateid));
+}
+
+static void
+dtlarg_delret(void *obj)
+{
+ DELEGRETURN4args *args = (DELEGRETURN4args *)obj;
+
+ detail_deleg_stateid(&args->deleg_stateid);
+}
+
+static void
+sumarg_getattr(char *buf, size_t buflen, void *obj)
+{
+ GETATTR4args *args = (GETATTR4args *)obj;
+
+ sum_attr_bitmap(buf, buflen, &args->attr_request);
+}
+
+static void
+dtlarg_getattr(void *obj)
+{
+ GETATTR4args *args = (GETATTR4args *)obj;
+
+ detail_attr_bitmap("", &args->attr_request, NULL);
+}
+
+static void
+sumarg_cb_getattr(char *buf, size_t buflen, void *obj)
+{
+ CB_GETATTR4args *args = (CB_GETATTR4args *)obj;
+ char *bp = buf;
+
+ snprintf(bp, buflen, "%s ", sum_fh4(&args->fh));
+ bp += strlen(bp);
+ sum_attr_bitmap(bp, buflen - (bp - buf), &args->attr_request);
+}
+
+static void
+dtlarg_cb_getattr(void *obj)
+{
+ CB_GETATTR4args *args = (CB_GETATTR4args *)obj;
+
+ detail_fh4(&args->fh);
+ detail_attr_bitmap("", &args->attr_request, NULL);
+}
+
+static void
+sumarg_cb_recall(char *buf, size_t buflen, void *obj)
+{
+ CB_RECALL4args *args = (CB_RECALL4args *)obj;
+ char *bp = buf;
+
+ snprintf(bp, buflen, "%s %s TR=%s", sum_fh4(&args->fh),
+ sum_stateid(&args->stateid), args->truncate ? "T" : "F");
+}
+
+static void
+dtlarg_cb_recall(void *obj)
+{
+ CB_RECALL4args *args = (CB_RECALL4args *)obj;
+
+ detail_fh4(&args->fh);
+ detail_stateid(&args->stateid);
+ sprintf(get_line(0, 0), "Truncate = %s",
+ args->truncate ? "True" : "False");
+}
+
+
+/*
+ * name openhow seqid claim access deny owner
+ */
+static void
+sumarg_open(char *buf, size_t buflen, void *obj)
+{
+ OPEN4args *args = (OPEN4args *)obj;
+ char *bp = buf;
+ int blen = buflen, len;
+
+ sum_name(bp, buflen, &args->claim);
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ sum_openflag(bp, blen, &args->openhow);
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ snprintf(bp, blen, " SQ=%u", args->seqid);
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ sum_claim(bp, blen, &args->claim);
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ snprintf(bp, blen, " AC=%s DN=%s OO=%04X",
+ sum_open_share_access(args->share_access),
+ sum_open_share_deny(args->share_deny),
+ owner_hash(&args->owner.owner));
+}
+
+static void
+dtlarg_open(void *obj)
+{
+ OPEN4args *args = (OPEN4args *)obj;
+
+ detail_claim(&args->claim);
+ detail_openflag(&args->openhow);
+ detail_open_owner(&args->owner);
+ sprintf(get_line(0, 0), "Sequence ID = %u", args->seqid);
+ sprintf(get_line(0, 0), "Access = 0x%x (%s)",
+ args->share_access, sum_open_share_access(args->share_access));
+ sprintf(get_line(0, 0), "Deny = 0x%x (%s)",
+ args->share_deny, sum_open_share_access(args->share_deny));
+}
+
+static void
+sumarg_openattr(char *buf, size_t buflen, void *obj)
+{
+ OPENATTR4args *args = (OPENATTR4args *)obj;
+
+ snprintf(buf, buflen, "CD=%s",
+ args->createdir ? "T" : "F");
+}
+
+static void
+dtlarg_openattr(void *obj)
+{
+ OPENATTR4args *args = (OPENATTR4args *)obj;
+
+ sprintf(get_line(0, 0), "CreateDir = %s",
+ args->createdir ? "True" : "False");
+}
+
+static void
+sumarg_open_confirm(char *buf, size_t buflen, void *obj)
+{
+ char *bp = buf;
+ OPEN_CONFIRM4args *args = (OPEN_CONFIRM4args *)obj;
+
+ snprintf(bp, buflen, "SQ=%u %s", args->seqid,
+ sum_open_stateid(&args->open_stateid));
+}
+
+static void
+dtlarg_open_confirm(void *obj)
+{
+ OPEN_CONFIRM4args *args = (OPEN_CONFIRM4args *)obj;
+
+ sprintf(get_line(0, 0), "Sequence ID = %u", args->seqid);
+ detail_open_stateid(&args->open_stateid);
+}
+
+static void
+sumarg_open_downgrd(char *buf, size_t buflen, void *obj)
+{
+ OPEN_DOWNGRADE4args *args = (OPEN_DOWNGRADE4args *)obj;
+
+ snprintf(buf, buflen, "SQ=%u %s AC=%s DN=%s",
+ args->seqid, sum_open_stateid(&args->open_stateid),
+ sum_open_share_access(args->share_access),
+ sum_open_share_deny(args->share_deny));
+}
+
+static void
+dtlarg_open_downgrd(void *obj)
+{
+ OPEN_DOWNGRADE4args *args = (OPEN_DOWNGRADE4args *)obj;
+
+ sprintf(get_line(0, 0), "Open Sequence ID = %u", args->seqid);
+ detail_open_stateid(&args->open_stateid);
+ sprintf(get_line(0, 0), "Access = 0x%x (%s)",
+ args->share_access, sum_open_share_access(args->share_access));
+ sprintf(get_line(0, 0), "Deny = 0x%x (%s)",
+ args->share_deny, sum_open_share_access(args->share_deny));
+}
+
+static void
+sumarg_putfh(char *buf, size_t buflen, void *obj)
+{
+ PUTFH4args *args = (PUTFH4args *)obj;
+
+ snprintf(buf, buflen, "%s", sum_fh4(&args->object));
+}
+
+static void
+dtlarg_putfh(void *obj)
+{
+ PUTFH4args *args = (PUTFH4args *)obj;
+
+ detail_fh4(&args->object);
+}
+
+static void
+sumarg_link(char *buf, size_t buflen, void *obj)
+{
+ LINK4args *args = (LINK4args *)obj;
+
+ snprintf(buf, buflen, "%s", component_name(&args->newname));
+}
+
+static void
+dtlarg_link(void *obj)
+{
+ LINK4args *args = (LINK4args *)obj;
+
+ sprintf(get_line(0, 0), "New name = %s",
+ component_name(&args->newname));
+}
+
+static void
+sum_open_to_lock_owner(char *buf, int buflen, open_to_lock_owner4 *own)
+{
+ snprintf(buf, buflen, " OSQ=%u %s LSQ=%u LO=%04X", own->open_seqid,
+ sum_open_stateid(&own->open_stateid), own->lock_seqid,
+ owner_hash(&own->lock_owner.owner));
+}
+
+static void
+sum_exist_lock_owner(char *buf, int buflen, exist_lock_owner4 *own)
+{
+ snprintf(buf, buflen, " LSQ=%u %s", own->lock_seqid,
+ sum_lock_stateid(&own->lock_stateid));
+}
+
+static void
+sum_locker(char *buf, size_t len, locker4 *lk)
+{
+ if (lk->new_lock_owner == TRUE)
+ sum_open_to_lock_owner(buf, len, &lk->locker4_u.open_owner);
+ else
+ sum_exist_lock_owner(buf, len, &lk->locker4_u.lock_owner);
+}
+
+static char *
+sum_lock_type_name(enum nfs_lock_type4 type)
+{
+ char *result;
+
+ switch (type) {
+ case READ_LT:
+ result = "RD";
+ break;
+ case WRITE_LT:
+ result = "WR";
+ break;
+ case READW_LT:
+ result = "RDW";
+ break;
+ case WRITEW_LT:
+ result = "WRW";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+static void
+sumarg_lock(char *buf, size_t buflen, void *obj)
+{
+ LOCK4args *args = (LOCK4args *)obj;
+ char *bp = buf;
+
+ snprintf(buf, buflen, "%s%s%llu:%llu",
+ sum_lock_type_name(args->locktype),
+ args->reclaim ? " reclaim " : " ",
+ args->offset, args->length);
+
+ bp += strlen(buf);
+ sum_locker(bp, buflen - (bp - buf), &args->locker);
+}
+
+static void
+detail_open_to_lock_owner(open_to_lock_owner4 *own)
+{
+ sprintf(get_line(0, 0), "Open Sequence ID = %u", own->open_seqid);
+ detail_open_stateid(&own->open_stateid);
+ sprintf(get_line(0, 0), "Lock Sequence ID = %u", own->lock_seqid);
+ detail_lock_owner(&own->lock_owner);
+}
+
+static void
+detail_exist_lock_owner(exist_lock_owner4 *own)
+{
+ detail_lock_stateid(&own->lock_stateid);
+ sprintf(get_line(0, 0), "Lock Sequence ID = %u", own->lock_seqid);
+}
+
+static void
+detail_locker(locker4 *lk)
+{
+ if (lk->new_lock_owner == TRUE)
+ detail_open_to_lock_owner(&lk->locker4_u.open_owner);
+ else
+ detail_exist_lock_owner(&lk->locker4_u.lock_owner);
+}
+
+static void
+dtlarg_lock(void *obj)
+{
+ LOCK4args *args = (LOCK4args *)obj;
+
+ sprintf(get_line(0, 0), "Type = %s", lock_type_name(args->locktype));
+ sprintf(get_line(0, 0), "Reclaim = %s",
+ args->reclaim ? "TRUE" : "FALSE");
+ sprintf(get_line(0, 0), "Offset = %llu", args->offset);
+ sprintf(get_line(0, 0), "Length = %llu", args->length);
+ detail_locker(&args->locker);
+}
+
+static void
+sumarg_lockt(char *buf, size_t buflen, void *obj)
+{
+ LOCKT4args *args = (LOCKT4args *)obj;
+
+ snprintf(buf, buflen, "R=%llu:%llu",
+ args->offset, args->length);
+}
+
+static void
+dtlarg_lockt(void *obj)
+{
+ LOCKT4args *args = (LOCKT4args *)obj;
+
+ sprintf(get_line(0, 0), "Type = %s", lock_type_name(args->locktype));
+ detail_lock_owner(&args->owner);
+ sprintf(get_line(0, 0), "Offset = %llu", args->offset);
+ sprintf(get_line(0, 0), "Length = %llu", args->length);
+}
+
+static void
+sumarg_locku(char *buf, size_t buflen, void *obj)
+{
+ LOCKU4args *args = (LOCKU4args *)obj;
+
+ snprintf(buf, buflen, "R=%llu:%llu LSQ=%u %s",
+ args->offset, args->length, args->seqid,
+ sum_lock_stateid(&args->lock_stateid));
+}
+
+
+static void
+dtlarg_locku(void *obj)
+{
+ LOCKU4args *args = (LOCKU4args *)obj;
+
+ sprintf(get_line(0, 0), "Type = %s", lock_type_name(args->locktype));
+ sprintf(get_line(0, 0), "Sequence ID = %u", args->seqid);
+ detail_lock_stateid(&args->lock_stateid);
+ sprintf(get_line(0, 0), "Offset = %llu", args->offset);
+ sprintf(get_line(0, 0), "Length = %llu", args->length);
+}
+
+static void
+sumarg_lookup(char *buf, size_t buflen, void *obj)
+{
+ LOOKUP4args *args = (LOOKUP4args *)obj;
+
+ sum_compname4(buf, buflen, &args->objname);
+}
+
+static void
+dtlarg_lookup(void *obj)
+{
+ LOOKUP4args *args = (LOOKUP4args *)obj;
+
+ detail_compname4(&args->objname);
+}
+
+static void
+sumarg_read(char *buf, size_t buflen, void *obj)
+{
+ READ4args *args = (READ4args *)obj;
+
+ snprintf(buf, buflen, "%s at %llu for %u",
+ sum_stateid(&args->stateid), args->offset, args->count);
+}
+
+static void
+dtlarg_read(void *obj)
+{
+ READ4args *args = (READ4args *)obj;
+
+ sprintf(get_line(0, 0), "Offset = %llu", args->offset);
+ sprintf(get_line(0, 0), "Count = %u", args->count);
+ detail_stateid(&args->stateid);
+}
+
+static void
+sumarg_readdir(char *buf, size_t buflen, void *obj)
+{
+ READDIR4args *args = (READDIR4args *)obj;
+
+ snprintf(buf, buflen, "Cookie=%llu (%s) for %u/%u",
+ args->cookie, tohex(args->cookieverf, NFS4_VERIFIER_SIZE),
+ args->dircount, args->maxcount);
+}
+
+static void
+dtlarg_readdir(void *obj)
+{
+ READDIR4args *args = (READDIR4args *)obj;
+
+ sprintf(get_line(0, 0), "Cookie = %llu", args->cookie);
+ sprintf(get_line(0, 0), "Verifier = %s",
+ tohex(args->cookieverf, NFS4_VERIFIER_SIZE));
+ sprintf(get_line(0, 0), "Dircount = %u", args->dircount);
+ sprintf(get_line(0, 0), "Maxcount = %u", args->maxcount);
+ detail_attr_bitmap("", &args->attr_request, NULL);
+}
+
+static void
+dtlarg_release_lkown(void *obj)
+{
+ RELEASE_LOCKOWNER4args *args = (RELEASE_LOCKOWNER4args *)obj;
+
+ detail_lock_owner(&args->lock_owner);
+}
+
+static void
+sumarg_release_lkown(char *buf, size_t buflen, void *obj)
+
+{
+ RELEASE_LOCKOWNER4args *args = (RELEASE_LOCKOWNER4args *)obj;
+
+ snprintf(buf, buflen, "LO=%04X", owner_hash(&args->lock_owner.owner));
+}
+
+static void
+sumarg_rename(char *buf, size_t buflen, void *obj)
+{
+ RENAME4args *args = (RENAME4args *)obj;
+
+ snprintf(buf, buflen, "%s to %s",
+ component_name(&args->oldname),
+ component_name(&args->newname));
+}
+
+static void
+dtlarg_rename(void *obj)
+{
+ RENAME4args *args = (RENAME4args *)obj;
+
+ sprintf(get_line(0, 0), "Old name = %s",
+ component_name(&args->oldname));
+ sprintf(get_line(0, 0), "New name = %s",
+ component_name(&args->newname));
+}
+
+static void
+sumarg_renew(char *buf, size_t buflen, void *obj)
+{
+ RENEW4args *args = (RENEW4args *)obj;
+
+ snprintf(buf, buflen, "%s", sum_clientid(args->clientid));
+}
+static void
+dtlarg_renew(void *obj)
+{
+ RENEW4args *args = (RENEW4args *)obj;
+
+ detail_clientid(args->clientid);
+}
+
+static void
+sumarg_secinfo(char *buf, size_t buflen, void *obj)
+{
+ SECINFO4args *args = (SECINFO4args *)obj;
+
+ snprintf(buf, buflen, "%s",
+ component_name(&args->name));
+}
+
+static void
+dtlarg_secinfo(void *obj)
+{
+ SECINFO4args *args = (SECINFO4args *)obj;
+
+ sprintf(get_line(0, 0), "Name = %s",
+ component_name(&args->name));
+}
+
+static void
+sumarg_setattr(char *buf, size_t buflen, void *obj)
+{
+ SETATTR4args *args = (SETATTR4args *)obj;
+
+ snprintf(buf, buflen, "%s", sum_stateid(&args->stateid));
+}
+
+static void
+dtlarg_setattr(void *obj)
+{
+ SETATTR4args *args = (SETATTR4args *)obj;
+
+ detail_stateid(&args->stateid);
+ detail_fattr4(&args->obj_attributes);
+}
+
+static void
+sumarg_setclid(char *buf, size_t buflen, void *obj)
+{
+ SETCLIENTID4args *args = (SETCLIENTID4args *)obj;
+
+ snprintf(buf, buflen, "Prog=%u ID=%s Addr=%s CBID=%u",
+ args->callback.cb_program,
+ args->callback.cb_location.r_netid,
+ args->callback.cb_location.r_addr, args->callback_ident);
+}
+
+static void
+dtlarg_setclid(void *obj)
+{
+ SETCLIENTID4args *args = (SETCLIENTID4args *)obj;
+
+ sprintf(get_line(0, 0), "Verifier=%s",
+ tohex(args->client.verifier, NFS4_VERIFIER_SIZE));
+ sprintf(get_line(0, 0), "ID = (%d) %s",
+ args->client.id.id_len,
+ tohex(args->client.id.id_val, args->client.id.id_len));
+
+ sprintf(get_line(0, 0), "Callback Program = %u",
+ args->callback.cb_program);
+ sprintf(get_line(0, 0), "Callback Net ID = %s",
+ args->callback.cb_location.r_netid);
+ sprintf(get_line(0, 0), "Callback Addr = %s",
+ args->callback.cb_location.r_addr);
+ sprintf(get_line(0, 0), "Callback Ident = %u", args->callback_ident);
+}
+
+static void
+sumarg_setclid_cfm(char *buf, size_t buflen, void *obj)
+{
+ SETCLIENTID_CONFIRM4args *args = (SETCLIENTID_CONFIRM4args *)obj;
+
+ snprintf(buf, buflen, "%s CFV=%s", sum_clientid(args->clientid),
+ tohex(args->setclientid_confirm, NFS4_VERIFIER_SIZE));
+}
+
+static void
+dtlarg_setclid_cfm(void *obj)
+{
+ SETCLIENTID_CONFIRM4args *args = (SETCLIENTID_CONFIRM4args *)obj;
+
+ detail_clientid(args->clientid);
+ sprintf(get_line(0, 0), "Set Client ID Confirm Verifier = %s",
+ tohex(args->setclientid_confirm, NFS4_VERIFIER_SIZE));
+}
+
+
+static void
+dtlarg_verify(void *obj)
+{
+ NVERIFY4args *args = (NVERIFY4args *)obj;
+
+ detail_fattr4(&args->obj_attributes);
+}
+
+static void
+sumarg_write(char *buf, size_t buflen, void *obj)
+{
+ WRITE4args *args = (WRITE4args *)obj;
+
+ snprintf(buf, buflen, "%s at %llu for %u",
+ sum_stateid(&args->stateid), args->offset, args->data.data_len);
+}
+
+static void
+dtlarg_write(void *obj)
+{
+ WRITE4args *args = (WRITE4args *)obj;
+
+ sprintf(get_line(0, 0), "Offset = %llu", args->offset);
+ sprintf(get_line(0, 0), "Count = %u", args->data.data_len);
+ sprintf(get_line(0, 0), "Stable = %s", stable_how4_name(args->stable));
+ detail_stateid(&args->stateid);
+}
+
+static char *
+sum_fh4(nfs_fh4 *fh)
+{
+ static char buf[20];
+
+ sprintf(buf, "FH=%04X", fh4_hash(fh));
+
+ return (buf);
+}
+
+static void
+detail_fh4(nfs_fh4 *fh)
+{
+ int i;
+ uchar_t *cp;
+ char *bufp;
+
+ sprintf(get_line(0, 0), "File handle = [%04X]", fh4_hash(fh));
+ bufp = get_line(0, 0);
+ sprintf(bufp, "(%d) ", fh->nfs_fh4_len);
+ bufp += strlen(bufp);
+ /* XXX use tohex()? */
+ for (i = 0, cp = (uchar_t *)fh->nfs_fh4_val;
+ i < fh->nfs_fh4_len;
+ i++, cp++) {
+ if (i != 0 && i % 32 == 0)
+ bufp = get_line(0, 0);
+ sprintf(bufp, "%02x", *cp);
+ bufp += strlen(bufp);
+ }
+}
+
+static void
+detail_fattr4(fattr4 *attrp)
+{
+ unpkd_attrmap_t provided;
+ uint_t attrnum;
+ XDR attrxdr;
+ jmp_buf old_errbuf;
+
+ xdrmem_create(&attrxdr, attrp->attr_vals.attrlist4_val,
+ attrp->attr_vals.attrlist4_len, XDR_DECODE);
+
+ bcopy(xdr_err, old_errbuf, sizeof (old_errbuf));
+ if (setjmp(xdr_err)) {
+ sprintf(get_line(0, 0), "<attr_vals too short>");
+ goto done;
+ }
+
+ detail_attr_bitmap("", &attrp->attrmask, &provided);
+ for (attrnum = 0; attrnum < MAX_ATTRIBUTES; attrnum++) {
+ if (provided.map[attrnum]) {
+ attr_info[attrnum].prt_details(&attrxdr);
+ }
+ }
+
+done:
+ bcopy(old_errbuf, xdr_err, sizeof (old_errbuf));
+}
+
+static void
+sum_attr_bitmap(char *buf, size_t buflen, bitmap4 *mapp)
+{
+ uint_t num_words;
+ char *bp;
+ size_t curlen, remaining;
+
+ buf[0] = '\0';
+ for (num_words = 0; num_words < mapp->bitmap4_len; num_words++) {
+ curlen = strlen(buf);
+ if (curlen + sizeof ("<Too Long>") >= buflen) {
+ strcpy(buf + buflen - sizeof ("<Too Long>"),
+ "<Too Long>");
+ return;
+ }
+ bp = buf + curlen;
+ remaining = buflen - curlen;
+ snprintf(bp, remaining,
+ num_words == 0 ? "%x" : " %x",
+ mapp->bitmap4_val[num_words]);
+ }
+}
+
+/*
+ * Print detail information for the given attribute bitmap, and fill in the
+ * unpacked version of the map if "unpacked" is non-null. Returns the
+ * number of bytes in the bitmap. "prefix" is an initial string that is
+ * printed at the front of each line.
+ */
+
+static void
+detail_attr_bitmap(char *prefix, bitmap4 *bitp, unpkd_attrmap_t *unpacked)
+{
+ uint_t num_words;
+ uint32_t *wp;
+ uint_t byte_num;
+
+ if (unpacked != NULL)
+ memset(unpacked, 0, sizeof (unpkd_attrmap_t));
+
+ /*
+ * Break the bitmap into octets, then print in hex and
+ * symbolically.
+ */
+
+ for (num_words = 0, wp = bitp->bitmap4_val;
+ num_words < bitp->bitmap4_len;
+ num_words++, wp++) {
+ for (byte_num = 0; byte_num < 4; byte_num++) {
+ uchar_t val = (*wp) >> (byte_num * 8);
+ char *buf = get_line(0, 0);
+ uint_t attrnum;
+ int bit;
+
+ sprintf(buf, "%s 0x%02x ", prefix, val);
+ attrnum = num_words * 32 + byte_num * 8;
+ for (bit = 7; bit >= 0; bit--) {
+ if (val & (1 << bit)) {
+ strcat(buf, " ");
+ strcat(buf,
+ attr_name(attrnum + bit));
+ if (unpacked != NULL)
+ unpacked->map[attrnum + bit] =
+ 1;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Format the summary line results from a COMPOUND4 call.
+ */
+
+static void
+sum_comp4res(char *line, char *(*sumres_fn)(void))
+{
+ nfsstat4 status;
+ static utf8string tag;
+
+ status = getxdr_long();
+ if (!xdr_utf8string(&xdrm, &tag))
+ longjmp(xdr_err, 1);
+
+ sprintf(line, "(%.20s) %s %s", utf8localize(&tag),
+ status_name(status), sumres_fn());
+
+ xdr_free(xdr_utf8string, (char *)&tag);
+}
+
+
+/*
+ * Return a set of summary strings for the result data that's next in the
+ * XDR stream, up to SUM_COMPND_MAX characters. If the strings don't fit,
+ * include a "..." at the end of the string.
+ */
+
+static char *
+sum_compound4res(void)
+{
+ static char buf[SUM_COMPND_MAX + 2]; /* 1 for null, 1 for overflow */
+ int numres;
+ const size_t buflen = sizeof (buf);
+ char *bp;
+ nfs_resop4 one_res;
+
+ buf[0] = '\0';
+ if (setjmp(xdr_err)) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf),
+ nfs4_fragged_rpc ? nfs4err_fragrpc : nfs4err_xdrfrag);
+ return (buf);
+ }
+
+ numres = getxdr_long();
+ bp = buf;
+ while (numres-- > 0) {
+ char *result;
+
+ bzero(&one_res, sizeof (one_res));
+
+ if (!xdr_nfs_resop4(&xdrm, &one_res)) {
+ xdr_free(xdr_nfs_resop4, (char *)&one_res);
+ longjmp(xdr_err, 1);
+ }
+
+ snprintf(bp, buflen - (bp - buf), "%s ",
+ opcode_name(one_res.resop));
+ bp += strlen(bp);
+
+ result = sum_result(&one_res);
+ if (strlen(result) > 0) {
+ snprintf(bp, buflen - (bp - buf), "%s ", result);
+ bp += strlen(bp);
+ }
+
+ /* nfs4_skip_bytes set by xdr_nfs4_argop4() */
+ if (nfs4_skip_bytes != 0)
+ nfs4_xdr_skip(nfs4_skip_bytes);
+
+ xdr_free(xdr_nfs_resop4, (char *)&one_res);
+ /* add "..." if past the "end" of the buffer */
+ if (bp - buf > SUM_COMPND_MAX) {
+ strcpy(buf + SUM_COMPND_MAX - strlen("..."),
+ "...");
+ break;
+ }
+ }
+
+ return (buf);
+}
+
+
+/*
+ * Return a set of summary strings for the result data that's next in the
+ * XDR stream, up to SUM_COMPND_MAX characters. If the strings don't fit,
+ * include a "..." at the end of the string.
+ */
+
+static char *
+sum_cb_compound4res(void)
+{
+ static char buf[SUM_COMPND_MAX + 2]; /* 1 for null, 1 for overflow */
+ int numres;
+ const size_t buflen = sizeof (buf);
+ char *bp;
+ nfs_cb_resop4 one_res;
+
+ buf[0] = '\0';
+ if (setjmp(xdr_err)) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), "<XDR Error or Fragmented"
+ " RPC>");
+ return (buf);
+ }
+
+ numres = getxdr_long();
+ bp = buf;
+ while (numres-- > 0) {
+ bzero(&one_res, sizeof (one_res));
+ if (!xdr_nfs_cb_resop4(&xdrm, &one_res)) {
+ xdr_free(xdr_nfs_cb_resop4, (char *)&one_res);
+ longjmp(xdr_err, 1);
+ }
+ snprintf(bp, buflen - (bp - buf), "%s %s ",
+ cb_opcode_name(one_res.resop),
+ sum_cb_result(&one_res));
+ bp += strlen(bp);
+
+ xdr_free(xdr_nfs_cb_resop4, (char *)&one_res);
+
+ /* add "..." if past the "end" of the buffer */
+ if (bp - buf > SUM_COMPND_MAX) {
+ strcpy(buf + SUM_COMPND_MAX - strlen("..."),
+ "...");
+ break;
+ }
+ }
+
+ return (buf);
+}
+
+
+/*
+ * Return the summarized results for the given resultdata.
+ */
+
+static char *
+sum_result(nfs_resop4 *resp)
+{
+ static char buf[1024];
+ void (*fmtproc)(char *, size_t, void *);
+
+ buf[0] = '\0';
+ if (resp->resop < num_opcodes)
+ fmtproc = opcode_info[resp->resop].sumres;
+ else if (resp->resop == OP_ILLEGAL)
+ fmtproc = sum_nfsstat4;
+ else
+ fmtproc = NULL;
+
+ if (fmtproc != NULL)
+ fmtproc(buf, sizeof (buf), &resp->nfs_resop4_u);
+
+ return (buf);
+}
+
+/*
+ * Return the summarized results for the given resultdata.
+ */
+
+static char *
+sum_cb_result(nfs_cb_resop4 *resp)
+{
+ static char buf[1024];
+ void (*fmtproc)(char *, size_t, void *);
+
+ buf[0] = '\0';
+ if (resp->resop < cb_num_opcodes)
+ fmtproc = cb_opcode_info[resp->resop].sumres;
+ else if (resp->resop == OP_CB_ILLEGAL)
+ fmtproc = sum_nfsstat4;
+ else
+ fmtproc = NULL;
+
+ if (fmtproc != NULL)
+ fmtproc(buf, sizeof (buf), &resp->nfs_cb_resop4_u);
+
+ return (buf);
+}
+
+
+static void
+dtl_change_info(char *msg, change_info4 *infop)
+{
+ sprintf(get_line(0, 0), "%s:", msg);
+ sprintf(get_line(0, 0), " Atomic = %s",
+ infop->atomic ? "TRUE" : "FALSE");
+ detail_fattr4_change(" Before", infop->before);
+ detail_fattr4_change(" After", infop->after);
+}
+
+static void
+detail_fattr4_change(char *msg, fattr4_change chg)
+{
+ sprintf(get_line(0, 0), "%s: 0x%llx", msg, chg);
+ /* XXX print as time_t, too? */
+}
+
+static void
+sum_nfsstat4(char *buf, size_t buflen, void *obj)
+{
+ nfsstat4 status = *(nfsstat4 *)obj;
+
+ strncpy(buf, status_name(status), buflen);
+}
+
+static void
+dtl_nfsstat4(void *obj)
+{
+ nfsstat4 status = *(nfsstat4 *)obj;
+
+ sprintf(get_line(0, 0), "Status = %d (%s)", status,
+ status_name(status));
+}
+
+static void
+sumres_access(char *buf, size_t buflen, void *obj)
+{
+ ACCESS4res *res = (ACCESS4res *)obj;
+ char *bp = buf;
+ int len, blen = buflen;
+
+ strcpy(bp, status_name(res->status));
+ if (res->status == NFS4_OK) {
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ snprintf(bp, blen, " Supp=");
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ sum_access4(bp, blen, res->ACCESS4res_u.resok4.supported);
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ snprintf(bp, blen, " Allow=");
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ sum_access4(bp, blen, res->ACCESS4res_u.resok4.access);
+ }
+}
+
+static void
+dtlres_access(void *obj)
+{
+ ACCESS4res *res = (ACCESS4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_access4("Supported Attributes",
+ res->ACCESS4res_u.resok4.supported);
+ detail_access4("Allowed Attributes",
+ res->ACCESS4res_u.resok4.access);
+ }
+}
+
+static void
+sumres_close(char *buf, size_t buflen, void *obj)
+{
+ CLOSE4res *res = (CLOSE4res *)obj;
+
+ if (res->status == NFS4_OK)
+ snprintf(buf, buflen, "%s",
+ sum_open_stateid(&res->CLOSE4res_u.open_stateid));
+}
+
+static void
+dtlres_close(void *obj)
+{
+ CLOSE4res *res = (CLOSE4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_open_stateid(&res->CLOSE4res_u.open_stateid);
+ }
+}
+
+static void
+sumres_commit(char *buf, size_t buflen, void *obj)
+{
+ COMMIT4res *res = (COMMIT4res *)obj;
+
+ if (res->status == NFS4_OK)
+ snprintf(buf, buflen, "Verf=%s",
+ tohex(res->COMMIT4res_u.resok4.writeverf,
+ NFS4_VERIFIER_SIZE));
+}
+
+static void
+dtlres_commit(void *obj)
+{
+ COMMIT4res *res = (COMMIT4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ sprintf(get_line(0, 0), "Verifier = %s",
+ tohex(res->COMMIT4res_u.resok4.writeverf,
+ NFS4_VERIFIER_SIZE));
+ }
+}
+
+static void
+dtlres_create(void *obj)
+{
+ CREATE4res *res = (CREATE4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ dtl_change_info("Change Information",
+ &res->CREATE4res_u.resok4.cinfo);
+ detail_attr_bitmap("", &res->CREATE4res_u.resok4.attrset,
+ NULL);
+ }
+}
+
+static void
+sumres_getattr(char *buf, size_t buflen, void *obj)
+{
+ GETATTR4res *res = (GETATTR4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+}
+
+static void
+dtlres_getattr(void *obj)
+{
+ GETATTR4res *res = (GETATTR4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_fattr4(&res->GETATTR4res_u.resok4.obj_attributes);
+ }
+}
+
+static void
+sumres_cb_getattr(char *buf, size_t buflen, void *obj)
+{
+ CB_GETATTR4res *res = (CB_GETATTR4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+}
+
+static void
+dtlres_cb_getattr(void *obj)
+{
+ CB_GETATTR4res *res = (CB_GETATTR4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_fattr4(&res->CB_GETATTR4res_u.resok4.obj_attributes);
+ }
+}
+
+
+static void
+sumres_getfh(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ GETFH4res *res = (GETFH4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s",
+ sum_fh4(&res->GETFH4res_u.resok4.object));
+ }
+}
+
+static void
+dtlres_getfh(void *obj)
+{
+ GETFH4res *res = (GETFH4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_fh4(&res->GETFH4res_u.resok4.object);
+ }
+}
+
+static void
+dtlres_link(void *obj)
+{
+ LINK4res *res = (LINK4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ dtl_change_info("Change Information",
+ &res->LINK4res_u.resok4.cinfo);
+ }
+}
+
+static void
+sumres_lock(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ LOCK4res *res = (LOCK4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s",
+ sum_lock_stateid(&res->LOCK4res_u.resok4.lock_stateid));
+ }
+ if (res->status == NFS4ERR_DENIED) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s",
+ sum_lock_denied(&res->LOCK4res_u.denied));
+ }
+}
+
+static void
+dtlres_lock(void *obj)
+{
+ LOCK4res *res = (LOCK4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_lock_stateid(&res->LOCK4res_u.resok4.lock_stateid);
+ }
+ if (res->status == NFS4ERR_DENIED) {
+ detail_lock_denied(&res->LOCK4res_u.denied);
+ }
+}
+
+static void
+sumres_lockt(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ LOCKT4res *res = (LOCKT4res *)obj;
+
+ strcpy(buf, status_name(res->status));
+ if (res->status == NFS4ERR_DENIED) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s",
+ sum_lock_denied(&res->LOCKT4res_u.denied));
+ }
+}
+
+static void
+dtlres_lockt(void *obj)
+{
+ LOCKT4res *res = (LOCKT4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4ERR_DENIED) {
+ detail_lock_denied(&res->LOCKT4res_u.denied);
+ }
+}
+
+static void
+sumres_locku(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ LOCKU4res *res = (LOCKU4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ bp = buf + strlen(buf);
+ if (res->status == NFS4_OK)
+ snprintf(bp, buflen - (bp - buf), " %s",
+ sum_lock_stateid(&res->LOCKU4res_u.lock_stateid));
+}
+
+static void
+dtlres_locku(void *obj)
+{
+ LOCKU4res *res = (LOCKU4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK)
+ detail_lock_stateid(&res->LOCKU4res_u.lock_stateid);
+}
+
+static void
+sumres_open(char *buf, size_t buflen, void *obj)
+{
+ char *bp = buf;
+ OPEN4res *res = (OPEN4res *)obj;
+ uint_t rflags;
+ int len, blen = buflen;
+
+ strncpy(bp, status_name(res->status), blen);
+
+ if (res->status == NFS4_OK) {
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ snprintf(bp, blen, " %s",
+ sum_stateid(&res->OPEN4res_u.resok4.stateid));
+ bp += (len = strlen(bp));
+ blen -= len;
+
+ if ((rflags = res->OPEN4res_u.resok4.rflags) != 0) {
+ snprintf(bp, blen, "%s", sum_open_rflags(rflags));
+ bp += (len = strlen(bp));
+ blen -= len;
+ }
+
+ sum_delegation(bp, blen, &res->OPEN4res_u.resok4.delegation);
+ }
+}
+
+static void
+dtlres_open(void *obj)
+{
+ OPEN4res *res = (OPEN4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_stateid(&res->OPEN4res_u.resok4.stateid);
+ dtl_change_info("Change Information",
+ &res->OPEN4res_u.resok4.cinfo);
+ sprintf(get_line(0, 0), "Flags = 0x%x (%s)",
+ res->OPEN4res_u.resok4.rflags,
+ detail_open_rflags(res->OPEN4res_u.resok4.rflags));
+ detail_attr_bitmap("", &res->OPEN4res_u.resok4.attrset,
+ NULL);
+ detail_delegation(&res->OPEN4res_u.resok4.delegation);
+ }
+}
+
+static void
+sumres_open_confirm(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ OPEN_CONFIRM4res *res = (OPEN_CONFIRM4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s",
+ sum_open_stateid(&res->OPEN_CONFIRM4res_u.resok4.
+ open_stateid));
+ }
+}
+
+static void
+dtlres_open_confirm(void *obj)
+{
+ OPEN_CONFIRM4res *res = (OPEN_CONFIRM4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_open_stateid(&res->OPEN_CONFIRM4res_u.resok4.
+ open_stateid);
+ }
+}
+
+static void
+sumres_open_downgrd(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ OPEN_DOWNGRADE4res *res = (OPEN_DOWNGRADE4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s",
+ sum_open_stateid(&res->OPEN_DOWNGRADE4res_u.resok4.
+ open_stateid));
+ }
+}
+
+static void
+dtlres_open_downgrd(void *obj)
+{
+ OPEN_DOWNGRADE4res *res = (OPEN_DOWNGRADE4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ detail_open_stateid(&res->OPEN_DOWNGRADE4res_u.resok4.
+ open_stateid);
+ }
+}
+
+static void
+sumres_read(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ READ4res *res = (READ4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " (%u bytes) %s",
+ res->READ4res_u.resok4.data.data_len,
+ res->READ4res_u.resok4.eof ? "EOF" : "");
+ }
+}
+
+static void
+dtlres_read(void *obj)
+{
+ READ4res *res = (READ4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ sprintf(get_line(0, 0), "Count = %u bytes read",
+ res->READ4res_u.resok4.data.data_len);
+ sprintf(get_line(0, 0), "End of file = %s",
+ res->READ4res_u.resok4.eof ? "TRUE" : "FALSE");
+ }
+}
+
+static void
+sumres_readdir(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ READDIR4res *res = (READDIR4res *)obj;
+ int num_entries = 0;
+ entry4 *ep;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ for (ep = res->READDIR4res_u.resok4.reply.entries;
+ ep != NULL;
+ ep = ep->nextentry)
+ num_entries++;
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %d entries (%s)",
+ num_entries,
+ res->READDIR4res_u.resok4.reply.eof
+ ? "No more" : "More");
+ }
+}
+
+static void
+dtlres_readdir(void *obj)
+{
+ READDIR4res *res = (READDIR4res *)obj;
+ int num_entries = 0;
+ entry4 *ep;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ for (ep = res->READDIR4res_u.resok4.reply.entries;
+ ep != NULL;
+ ep = ep->nextentry) {
+ num_entries++;
+ sprintf(get_line(0, 0),
+ "------------------ entry #%d",
+ num_entries);
+ sprintf(get_line(0, 0), "Cookie = %llu",
+ ep->cookie);
+ sprintf(get_line(0, 0), "Name = %s",
+ component_name(&ep->name));
+ detail_fattr4(&ep->attrs);
+ }
+ if (num_entries == 0)
+ sprintf(get_line(0, 0), "(No entries)");
+ sprintf(get_line(0, 0), "EOF = %s",
+ res->READDIR4res_u.resok4.reply.eof ? "TRUE" : "FALSE");
+ sprintf(get_line(0, 0), "Verifer = %s",
+ tohex(res->READDIR4res_u.resok4.cookieverf,
+ NFS4_VERIFIER_SIZE));
+ }
+}
+
+static void
+sumres_readlnk(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ READLINK4res *res = (READLINK4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s",
+ linktext_name(&res->READLINK4res_u.resok4.link));
+ }
+}
+
+static void
+dtlres_readlnk(void *obj)
+{
+ READLINK4res *res = (READLINK4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ sprintf(get_line(0, 0), "Link = %s",
+ linktext_name(&res->READLINK4res_u.resok4.link));
+ }
+}
+
+static void
+dtlres_remove(void *obj)
+{
+ REMOVE4res *res = (REMOVE4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ dtl_change_info("Change Information",
+ &res->REMOVE4res_u.resok4.cinfo);
+ }
+}
+
+static void
+dtlres_rename(void *obj)
+{
+ RENAME4res *res = (RENAME4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ dtl_change_info("Source Change Information",
+ &res->RENAME4res_u.resok4.source_cinfo);
+ dtl_change_info("Target Change Information",
+ &res->RENAME4res_u.resok4.target_cinfo);
+ }
+}
+
+static void
+sumres_secinfo(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ SECINFO4res *res = (SECINFO4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ bp = buf + strlen(buf);
+ if (res->status == NFS4_OK) {
+ uint_t numinfo = res->SECINFO4res_u.resok4.SECINFO4resok_len;
+ secinfo4 *infop;
+
+ for (infop = res->SECINFO4res_u.resok4.SECINFO4resok_val;
+ numinfo != 0;
+ infop++, numinfo--) {
+ snprintf(bp, buflen - (bp - buf), " %s",
+ flavor_name(infop->flavor));
+ bp += strlen(bp);
+ }
+ }
+}
+
+static void
+dtlres_secinfo(void *obj)
+{
+ SECINFO4res *res = (SECINFO4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ uint_t numinfo =
+ res->SECINFO4res_u.resok4.SECINFO4resok_len;
+ secinfo4 *infop;
+
+ for (infop = res->SECINFO4res_u.resok4.SECINFO4resok_val;
+ numinfo != 0;
+ infop++, numinfo--) {
+ detail_secinfo4(infop);
+ }
+ }
+}
+
+static void
+sumres_setattr(char *buf, size_t buflen, void *obj)
+{
+ SETATTR4res *res = (SETATTR4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ sum_attr_bitmap(buf, buflen, &res->attrsset);
+}
+
+static void
+dtlres_setattr(void *obj)
+{
+ SETATTR4res *res = (SETATTR4res *)obj;
+
+ dtl_nfsstat4(obj);
+ detail_attr_bitmap("", &res->attrsset, NULL);
+}
+
+static void
+sumres_setclid(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ SETCLIENTID4res *res = (SETCLIENTID4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ switch (res->status) {
+ case NFS_OK:
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %s CFV=%s",
+ sum_clientid(res->SETCLIENTID4res_u.resok4.clientid),
+ tohex(res->SETCLIENTID4res_u.resok4.setclientid_confirm,
+ NFS4_VERIFIER_SIZE));
+ break;
+ case NFS4ERR_CLID_INUSE:
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " ID=%s Addr=%s",
+ res->SETCLIENTID4res_u.client_using.r_netid,
+ res->SETCLIENTID4res_u.client_using.r_addr);
+ break;
+ }
+}
+
+static void
+dtlres_setclid(void *obj)
+{
+ SETCLIENTID4res *res = (SETCLIENTID4res *)obj;
+
+ dtl_nfsstat4(obj);
+ switch (res->status) {
+ case NFS_OK:
+ detail_clientid(res->SETCLIENTID4res_u.resok4.clientid);
+ sprintf(get_line(0, 0), "Set Client ID Confirm Verifier = %s",
+ tohex(res->SETCLIENTID4res_u.resok4.setclientid_confirm,
+ NFS4_VERIFIER_SIZE));
+ break;
+ case NFS4ERR_CLID_INUSE:
+ sprintf(get_line(0, 0), "Used by Net ID = %s",
+ res->SETCLIENTID4res_u.client_using.r_netid);
+ sprintf(get_line(0, 0), "Used by Addr = %s",
+ res->SETCLIENTID4res_u.client_using.r_addr);
+ break;
+ }
+}
+
+static void
+sumres_write(char *buf, size_t buflen, void *obj)
+{
+ char *bp;
+ WRITE4res *res = (WRITE4res *)obj;
+
+ strncpy(buf, status_name(res->status), buflen);
+ if (res->status == NFS4_OK) {
+ bp = buf + strlen(buf);
+ snprintf(bp, buflen - (bp - buf), " %u (%s)",
+ res->WRITE4res_u.resok4.count,
+ stable_how4_name(res->WRITE4res_u.resok4.committed));
+ }
+}
+
+static void
+dtlres_write(void *obj)
+{
+ WRITE4res *res = (WRITE4res *)obj;
+
+ dtl_nfsstat4(obj);
+ if (res->status == NFS4_OK) {
+ sprintf(get_line(0, 0), "Count = %u bytes written",
+ res->WRITE4res_u.resok4.count);
+ sprintf(get_line(0, 0), "Stable = %s",
+ stable_how4_name(res->WRITE4res_u.resok4.committed));
+ sprintf(get_line(0, 0), "Verifier = %s",
+ tohex(res->WRITE4res_u.resok4.writeverf,
+ NFS4_VERIFIER_SIZE));
+ }
+}
+
+/*
+ * Print details about the nfs_resop4 that is next in the XDR stream.
+ */
+
+static void
+detail_nfs_resop4(void)
+{
+ int numres;
+ nfs_resop4 one_res;
+ void (*fmtproc)(void *);
+
+ numres = getxdr_long();
+ (void) sprintf(get_line(0, 0), "Number of results = %d",
+ numres);
+
+ while (numres-- > 0) {
+ bzero(&one_res, sizeof (one_res));
+
+ if (!xdr_nfs_resop4(&xdrm, &one_res)) {
+ xdr_free(xdr_nfs_resop4, (char *)&one_res);
+ longjmp(xdr_err, 1);
+ }
+
+ get_line(0, 0); /* blank line to separate ops */
+ sprintf(get_line(0, 0), "Op = %d (%s)",
+ one_res.resop, opcode_name(one_res.resop));
+ if (one_res.resop < num_opcodes)
+ fmtproc = opcode_info[one_res.resop].dtlres;
+ else if (one_res.resop == OP_ILLEGAL)
+ fmtproc = dtl_nfsstat4;
+ else
+ fmtproc = NULL;
+
+ if (fmtproc != NULL)
+ fmtproc(&one_res.nfs_resop4_u);
+
+ /* nfs4_skip_bytes set by xdr_nfs_resop4()() */
+ if (nfs4_skip_bytes)
+ nfs4_xdr_skip(nfs4_skip_bytes);
+
+ xdr_free(xdr_nfs_resop4, (char *)&one_res);
+ }
+}
+
+
+/*
+ * Print details about the nfs_cb_resop4 that is next in the XDR stream.
+ */
+
+static void
+detail_cb_resop4(void)
+{
+ int numres;
+ nfs_cb_resop4 one_res;
+ void (*fmtproc)(void *);
+
+ numres = getxdr_long();
+ (void) sprintf(get_line(0, 0), "Number of results = %d",
+ numres);
+
+ while (numres-- > 0) {
+ bzero(&one_res, sizeof (one_res));
+ if (!xdr_nfs_cb_resop4(&xdrm, &one_res))
+ longjmp(xdr_err, 1);
+
+ get_line(0, 0); /* blank line to separate ops */
+ sprintf(get_line(0, 0), "Op = %d (%s)",
+ one_res.resop, cb_opcode_name(one_res.resop));
+ if (one_res.resop < cb_num_opcodes)
+ fmtproc = cb_opcode_info[one_res.resop].dtlres;
+ else if (one_res.resop == OP_CB_ILLEGAL)
+ fmtproc = dtl_nfsstat4;
+ else
+ fmtproc = NULL;
+
+ if (fmtproc != NULL)
+ fmtproc(&one_res.nfs_cb_resop4_u);
+
+ xdr_free(xdr_nfs_cb_resop4, (char *)&one_res);
+ }
+}
+
+
+/*
+ * Return the name of a lock type.
+ */
+static char *
+lock_type_name(enum nfs_lock_type4 type)
+{
+ char *result;
+
+ switch (type) {
+ case READ_LT:
+ result = "READ";
+ break;
+ case WRITE_LT:
+ result = "WRITE";
+ break;
+ case READW_LT:
+ result = "READW";
+ break;
+ case WRITEW_LT:
+ result = "WRITEW";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * Return the name of an opcode.
+ */
+
+static char *
+opcode_name(uint_t opnum)
+{
+ static char buf[20];
+
+ if (opnum < num_opcodes)
+ return (opcode_info[opnum].name);
+
+ if (opnum == OP_ILLEGAL)
+ return ("ILLEGAL");
+
+ sprintf(buf, "op %d", opnum);
+ return (buf);
+}
+
+/*
+ * Return the name of an opcode.
+ */
+static char *
+cb_opcode_name(uint_t opnum)
+{
+ static char buf[20];
+
+ if (opnum < cb_num_opcodes)
+ return (cb_opcode_info[opnum].name);
+
+ if (opnum == OP_CB_ILLEGAL)
+ return ("CB_ILLEGAL");
+
+ sprintf(buf, "op %d", opnum);
+ return (buf);
+}
+
+
+/*
+ * Fill in a summary string for the given access bitmask.
+ */
+
+static void
+sum_access4(char *buf, size_t buflen, uint32_t bits)
+{
+ buf[0] = '\0';
+
+ if (bits & ACCESS4_READ)
+ (void) strncat(buf, "rd,", buflen);
+ if (bits & ACCESS4_LOOKUP)
+ (void) strncat(buf, "lk,", buflen);
+ if (bits & ACCESS4_MODIFY)
+ (void) strncat(buf, "mo,", buflen);
+ if (bits & ACCESS4_EXTEND)
+ (void) strncat(buf, "ext,", buflen);
+ if (bits & ACCESS4_DELETE)
+ (void) strncat(buf, "dl,", buflen);
+ if (bits & ACCESS4_EXECUTE)
+ (void) strncat(buf, "exc,", buflen);
+ if (buf[0] != '\0')
+ buf[strlen(buf) - 1] = '\0';
+}
+
+/*
+ * Print detail information about the given access bitmask.
+ */
+
+static void
+detail_access4(char *descrip, uint32_t bits)
+{
+ sprintf(get_line(0, 0), "%s = 0x%08x", descrip, bits);
+
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS4_READ, "Read", "(no read)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS4_LOOKUP, "Lookup", "(no lookup)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS4_MODIFY, "Modify", "(no modify)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS4_EXTEND, "Extend", "(no extend)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS4_DELETE, "Delete", "(no delete)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS4_EXECUTE, "Execute", "(no execute)"));
+}
+
+
+/*
+ * Fill in a summary string for the given open_claim4.
+ */
+static void
+sum_name(char *buf, size_t buflen, open_claim4 *claim)
+{
+ char *bp = buf;
+
+ switch (claim->claim) {
+ case CLAIM_NULL:
+ snprintf(bp, buflen, "%s ",
+ component_name(&claim->open_claim4_u.file));
+ break;
+ case CLAIM_PREVIOUS:
+ break;
+ case CLAIM_DELEGATE_CUR:
+ snprintf(bp, buflen, "%s ",
+ component_name(&claim->open_claim4_u.
+ delegate_cur_info.file));
+ break;
+ case CLAIM_DELEGATE_PREV:
+ snprintf(bp, buflen, "%s ",
+ component_name(&claim->open_claim4_u.
+ file_delegate_prev));
+ break;
+ }
+}
+
+/*
+ * Fill in a summary string for the given open_claim4.
+ */
+static void
+sum_claim(char *buf, size_t buflen, open_claim4 *claim)
+{
+ char *bp = buf;
+
+ switch (claim->claim) {
+ case CLAIM_NULL:
+ snprintf(bp, buflen, " CT=N");
+ break;
+ case CLAIM_PREVIOUS:
+ snprintf(bp, buflen, " CT=P DT=%s",
+ get_deleg_typestr(claim->open_claim4_u.delegate_type));
+ break;
+ case CLAIM_DELEGATE_CUR:
+ snprintf(bp, buflen, " CT=DC %s",
+ sum_deleg_stateid(&claim->open_claim4_u.
+ delegate_cur_info.delegate_stateid));
+ break;
+ case CLAIM_DELEGATE_PREV:
+ snprintf(bp, buflen, " CT=DP");
+ break;
+ default:
+ snprintf(bp, buflen, " CT=?");
+ break;
+ }
+}
+
+static char *
+get_deleg_typestr(open_delegation_type4 dt)
+{
+ char *str = "";
+
+ switch (dt) {
+ case OPEN_DELEGATE_NONE:
+ str = "N";
+ break;
+ case OPEN_DELEGATE_READ:
+ str = "R";
+ break;
+ case OPEN_DELEGATE_WRITE:
+ str = "W";
+ break;
+ default:
+ str = "?";
+ }
+
+ return (str);
+}
+
+/*
+ * Print detail information for the given open_claim4.
+ */
+
+static void
+detail_claim(open_claim4 *claim)
+{
+ sprintf(get_line(0, 0), "Claim Type = %d (%s)",
+ claim->claim, claim_name(claim->claim));
+
+ switch (claim->claim) {
+ case CLAIM_NULL:
+ detail_compname4(&claim->open_claim4_u.file);
+ break;
+ case CLAIM_PREVIOUS:
+ sprintf(get_line(0, 0), "Delegate Type = %s (val = %d)",
+ get_deleg_typestr(claim->open_claim4_u.delegate_type),
+ claim->open_claim4_u.delegate_type);
+ break;
+ case CLAIM_DELEGATE_CUR:
+ detail_compname4(&claim->open_claim4_u.delegate_cur_info.file);
+ detail_deleg_stateid(&claim->open_claim4_u.delegate_cur_info.
+ delegate_stateid);
+ break;
+ case CLAIM_DELEGATE_PREV:
+ detail_compname4(&claim->open_claim4_u.file_delegate_prev);
+ break;
+ }
+}
+
+/*
+ * Return a summary string for the given clientid4.
+ */
+static char *
+sum_clientid(clientid4 client)
+{
+ static char buf[50];
+
+ snprintf(buf, sizeof (buf), "CL=%llx", client);
+
+ return (buf);
+}
+
+/*
+ * Print a detail string for the given clientid4.
+ */
+static void
+detail_clientid(clientid4 client)
+{
+ sprintf(get_line(0, 0), "Client ID = %llx", client);
+}
+
+/*
+ * Write a summary string for the given delegation into buf.
+ */
+
+static void
+sum_delegation(char *buf, size_t buflen, open_delegation4 *delp)
+{
+ switch (delp->delegation_type) {
+ case OPEN_DELEGATE_NONE:
+ snprintf(buf, buflen, " DT=N");
+ break;
+ case OPEN_DELEGATE_READ:
+ snprintf(buf, buflen, " DT=R %s",
+ sum_deleg_stateid(&delp->open_delegation4_u.write.
+ stateid));
+ break;
+ case OPEN_DELEGATE_WRITE:
+ snprintf(buf, buflen, " DT=W %s %s",
+ sum_deleg_stateid(&delp->open_delegation4_u.write.
+ stateid),
+ sum_space_limit(&delp->open_delegation4_u.write.
+ space_limit));
+ break;
+ default:
+ snprintf(buf, buflen, " DT=?");
+ break;
+ }
+}
+
+static void
+detail_delegation(open_delegation4 *delp)
+{
+ sprintf(get_line(0, 0), "Delegation Type = %d (%s)",
+ delp->delegation_type,
+ delegation_type_name(delp->delegation_type));
+
+ switch (delp->delegation_type) {
+ case OPEN_DELEGATE_NONE:
+ /* no-op */
+ break;
+ case OPEN_DELEGATE_READ:
+ detail_deleg_stateid(&delp->open_delegation4_u.read.stateid);
+ sprintf(get_line(0, 0), "Recall = %s",
+ delp->open_delegation4_u.read.recall ?
+ "TRUE" : "FALSE");
+ sprintf(get_line(0, 0), "[nfsacl4]");
+ break;
+ case OPEN_DELEGATE_WRITE:
+ detail_deleg_stateid(&delp->open_delegation4_u.write.stateid);
+ sprintf(get_line(0, 0), "Recall = %s",
+ delp->open_delegation4_u.write.recall ?
+ "TRUE" : "FALSE");
+ detail_space_limit(&delp->open_delegation4_u.write.
+ space_limit);
+ sprintf(get_line(0, 0), "[nfsacl4]");
+ break;
+ }
+}
+
+
+static void
+detail_open_owner(open_owner4 *owner)
+{
+ sprintf(get_line(0, 0), "Open Owner hash = [%04X] ",
+ owner_hash(&owner->owner));
+ sprintf(get_line(0, 0), " len = %u val = %s ",
+ owner->owner.owner_len,
+ tohex(owner->owner.owner_val, owner->owner.owner_len));
+ detail_clientid(owner->clientid);
+}
+
+static void
+detail_lock_owner(lock_owner4 *owner)
+{
+ sprintf(get_line(0, 0), "Lock Owner hash = [%04X] ",
+ owner_hash(&owner->owner));
+ sprintf(get_line(0, 0), " len = %u val = %s ",
+ owner->owner.owner_len,
+ tohex(owner->owner.owner_val, owner->owner.owner_len));
+ detail_clientid(owner->clientid);
+}
+
+static void
+sum_openflag(char *bufp, int buflen, openflag4 *flagp)
+{
+ if (flagp->opentype == OPEN4_CREATE) {
+ switch (flagp->openflag4_u.how.mode) {
+ case UNCHECKED4:
+ snprintf(bufp, buflen, "OT=CR(U)");
+ break;
+ case GUARDED4:
+ snprintf(bufp, buflen, "OT=CR(G)");
+ break;
+ case EXCLUSIVE4:
+ snprintf(bufp, buflen, "OT=CR(E)");
+ break;
+ default:
+ snprintf(bufp, buflen, "OT=CR(?:%d)",
+ flagp->openflag4_u.how.mode);
+ break;
+ }
+ } else
+ snprintf(bufp, buflen, "OT=NC");
+}
+
+static void
+detail_openflag(openflag4 *flagp)
+{
+ sprintf(get_line(0, 0), "Open Type = %s",
+ flagp->opentype == OPEN4_CREATE ? "CREATE" : "NOCREATE");
+ if (flagp->opentype == OPEN4_CREATE)
+ detail_createhow4(&flagp->openflag4_u.how);
+}
+
+/*
+ * Fill in buf with the given path.
+ */
+static void
+sum_pathname4(char *buf, size_t buflen, pathname4 *pathp)
+{
+ char *bp = buf;
+ uint_t component;
+
+ for (component = 0; component < pathp->pathname4_len;
+ component++) {
+ snprintf(bp, buflen - (bp - buf),
+ component == 0 ? "%s" : "/%s",
+ component_name(&pathp->pathname4_val[component]));
+ bp += strlen(bp);
+ }
+}
+
+static void
+sum_compname4(char *buf, size_t buflen, component4 *comp)
+{
+ snprintf(buf, buflen, "%s", component_name(comp));
+}
+
+static void
+detail_compname4(component4 *comp)
+{
+ sprintf(get_line(0, 0), "%s", component_name(comp));
+}
+
+static void
+detail_pathname4(pathname4 *pathp, char *what)
+{
+ char *bp = get_line(0, 0);
+ uint_t component;
+
+ sprintf(bp, what);
+ bp += strlen(bp);
+
+ for (component = 0; component < pathp->pathname4_len; component++) {
+ sprintf(bp, component == 0 ? "%s" : "/%s",
+ component_name(&pathp->pathname4_val[component]));
+ bp += strlen(bp);
+ }
+}
+
+/*
+ * Print detail information about the rpcsec_gss_info that is XDR-encoded
+ * at mem.
+ */
+
+static void
+detail_rpcsec_gss(rpcsec_gss_info *info)
+{
+ sprintf(get_line(0, 0), "OID = %s",
+ tohex(info->oid.sec_oid4_val, info->oid.sec_oid4_len));
+ sprintf(get_line(0, 0), "QOP = %u", info->qop);
+ sprintf(get_line(0, 0), "Service = %d (%s)",
+ info->service, gss_svc_name(info->service));
+}
+
+/*
+ * Print detail information about the given secinfo4.
+ */
+
+static void
+detail_secinfo4(secinfo4 *infop)
+{
+ sprintf(get_line(0, 0), "Flavor = %d (%s)",
+ infop->flavor, flavor_name(infop->flavor));
+ switch (infop->flavor) {
+ case RPCSEC_GSS:
+ detail_rpcsec_gss(&infop->secinfo4_u.flavor_info);
+ break;
+ }
+}
+
+
+/*
+ * Return a summary string corresponding to the given nfs_space_limit4.
+ */
+
+static char *
+sum_space_limit(nfs_space_limit4 *limitp)
+{
+ static char buf[64];
+ int buflen = sizeof (buf);
+
+ buf[0] = '\0';
+ switch (limitp->limitby) {
+ case NFS_LIMIT_SIZE:
+ snprintf(buf, buflen, "LB=SZ(%llu)",
+ limitp->nfs_space_limit4_u.filesize);
+ break;
+ case NFS_LIMIT_BLOCKS:
+ snprintf(buf, buflen, "LB=BL(%u*%u)",
+ limitp->nfs_space_limit4_u.mod_blocks.num_blocks,
+ limitp->nfs_space_limit4_u.mod_blocks.bytes_per_block);
+ break;
+ default:
+ snprintf(buf, buflen, "LB=?(%d)", limitp->limitby);
+ break;
+ }
+
+ return (buf);
+}
+
+/*
+ * Print detail information about the given nfs_space_limit4.
+ */
+
+static void
+detail_space_limit(nfs_space_limit4 *limitp)
+{
+ sprintf(get_line(0, 0), "LimitBy = %d (%s)",
+ limitp->limitby,
+ limitby_name(limitp->limitby));
+
+ switch (limitp->limitby) {
+ case NFS_LIMIT_SIZE:
+ sprintf(get_line(0, 0), "Bytes = %llu",
+ limitp->nfs_space_limit4_u.filesize);
+ break;
+ case NFS_LIMIT_BLOCKS:
+ sprintf(get_line(0, 0), "Blocks = %u",
+ limitp->nfs_space_limit4_u.mod_blocks.num_blocks);
+ sprintf(get_line(0, 0), "Bytes Per Block = %u",
+ limitp->nfs_space_limit4_u.mod_blocks.bytes_per_block);
+ break;
+ }
+}
+
+
+/*
+ * Return the short name of a file type.
+ */
+
+static char *
+sum_type_name(nfs_ftype4 type)
+{
+ static char buf[20];
+
+ if (type < num_ftypes)
+ return (ftype_names[type].short_name);
+ else {
+ sprintf(buf, "type %d", type);
+ return (buf);
+ }
+}
+
+
+/*
+ * Return string with long/short flag names
+ */
+
+static char *
+get_flags(uint_t flag, ftype_names_t *names, uint_t num_flags, int shortname,
+ char *prefix)
+{
+ static char buf[200];
+ char *bp = buf, *str;
+ int i, len, blen = sizeof (buf);
+ ftype_names_t *fn = NULL;
+
+ *bp = '\0';
+
+ if (prefix) {
+ snprintf(bp, blen, "%s", prefix);
+ bp += (len = sizeof (bp));
+ blen -= len;
+ }
+
+ for (i = 0; i < 32; i++)
+ if (flag & (1 << i)) {
+ fn = names + (i < num_flags ? i : num_flags);
+ str = (shortname ? fn->short_name : fn->long_name);
+
+ snprintf(bp, blen, "%s,", str);
+ bp += (len = strlen(bp));
+ blen -= len;
+ }
+
+ if (fn)
+ *(bp - 1) = '\0';
+ else
+ *buf = '\0';
+
+ return (buf);
+}
+
+
+/*
+ * Return the long name of a file type.
+ */
+
+static char *
+detail_type_name(nfs_ftype4 type)
+{
+ static char buf[20];
+
+ if (type < num_ftypes)
+ return (ftype_names[type].long_name);
+ else {
+ sprintf(buf, "type %d", type);
+ return (buf);
+ }
+}
+
+/*
+ * Return the name of an attribute.
+ */
+
+static char *
+attr_name(uint_t attrnum)
+{
+ static char buf[20];
+
+ if (attrnum < MAX_ATTRIBUTES)
+ return (attr_info[attrnum].name);
+ else {
+ sprintf(buf, "attr #%d", attrnum);
+ return (buf);
+ }
+}
+
+/*
+ * Return the name of the given open_claim_type4.
+ */
+
+static char *
+claim_name(enum open_claim_type4 claim_type)
+{
+ char *result;
+
+ switch (claim_type) {
+ case CLAIM_NULL:
+ result = "NULL";
+ break;
+ case CLAIM_PREVIOUS:
+ result = "PREVIOUS";
+ break;
+ case CLAIM_DELEGATE_CUR:
+ result = "DELEGATE CURRENT";
+ break;
+ case CLAIM_DELEGATE_PREV:
+ result = "DELEGATE PREVIOUS";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * Return a string naming the given delegation.
+ */
+
+static char *
+delegation_type_name(enum open_delegation_type4 type)
+{
+ char *result;
+
+ switch (type) {
+ case OPEN_DELEGATE_NONE:
+ result = "NONE";
+ break;
+ case OPEN_DELEGATE_READ:
+ result = "READ";
+ break;
+ case OPEN_DELEGATE_WRITE:
+ result = "WRITE";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * Return the name of the given authentication flavor.
+ */
+
+static char *
+flavor_name(uint_t flavor)
+{
+ char *result;
+ static char buf[50];
+
+ switch (flavor) {
+ case AUTH_SYS:
+ result = "AUTH_SYS";
+ break;
+ case AUTH_NONE:
+ result = "AUTH_NONE";
+ break;
+ case AUTH_DH:
+ result = "AUTH_DH";
+ break;
+ case RPCSEC_GSS:
+ result = "RPCSEC_GSS";
+ break;
+ default:
+ sprintf(buf, "[flavor %d]", flavor);
+ result = buf;
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * Return the name of the given rpc_gss_svc_t.
+ */
+
+static char *
+gss_svc_name(rpc_gss_svc_t svc)
+{
+ char *result;
+ static char buf[50];
+
+ switch (svc) {
+ case RPC_GSS_SVC_NONE:
+ result = "NONE";
+ break;
+ case RPC_GSS_SVC_INTEGRITY:
+ result = "INTEGRITY";
+ break;
+ case RPC_GSS_SVC_PRIVACY:
+ result = "PRIVACY";
+ break;
+ default:
+ sprintf(buf, "Service %d", svc);
+ result = buf;
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * Return a string name for the given limit_by4.
+ */
+
+static char *
+limitby_name(enum limit_by4 limitby)
+{
+ char *result;
+
+ switch (limitby) {
+ case NFS_LIMIT_SIZE:
+ result = "SIZE";
+ break;
+ case NFS_LIMIT_BLOCKS:
+ result = "BLOCKS";
+ break;
+ default:
+ result = "?";
+ break;
+ }
+
+ return (result);
+}
+
+static char *
+status_name(int status)
+{
+ char *p;
+
+ switch (status) {
+ case NFS4_OK: p = "NFS4_OK"; break;
+ case NFS4ERR_PERM: p = "NFS4ERR_PERM"; break;
+ case NFS4ERR_NOENT: p = "NFS4ERR_NOENT"; break;
+ case NFS4ERR_IO: p = "NFS4ERR_IO"; break;
+ case NFS4ERR_NXIO: p = "NFS4ERR_NXIO"; break;
+ case NFS4ERR_ACCESS: p = "NFS4ERR_ACCESS"; break;
+ case NFS4ERR_EXIST: p = "NFS4ERR_EXIST"; break;
+ case NFS4ERR_XDEV: p = "NFS4ERR_XDEV"; break;
+ case NFS4ERR_NOTDIR: p = "NFS4ERR_NOTDIR"; break;
+ case NFS4ERR_ISDIR: p = "NFS4ERR_ISDIR"; break;
+ case NFS4ERR_INVAL: p = "NFS4ERR_INVAL"; break;
+ case NFS4ERR_FBIG: p = "NFS4ERR_FBIG"; break;
+ case NFS4ERR_NOSPC: p = "NFS4ERR_NOSPC"; break;
+ case NFS4ERR_ROFS: p = "NFS4ERR_ROFS"; break;
+ case NFS4ERR_MLINK: p = "NFS4ERR_MLINK"; break;
+ case NFS4ERR_NAMETOOLONG:p = "NFS4ERR_NAMETOOLONG"; break;
+ case NFS4ERR_NOTEMPTY: p = "NFS4ERR_NOTEMPTY"; break;
+ case NFS4ERR_DQUOT: p = "NFS4ERR_DQUOT"; break;
+ case NFS4ERR_STALE: p = "NFS4ERR_STALE"; break;
+ case NFS4ERR_BADHANDLE: p = "NFS4ERR_BADHANDLE"; break;
+ case NFS4ERR_BAD_COOKIE:p = "NFS4ERR_BAD_COOKIE"; break;
+ case NFS4ERR_NOTSUPP: p = "NFS4ERR_NOTSUPP"; break;
+ case NFS4ERR_TOOSMALL: p = "NFS4ERR_TOOSMALL"; break;
+ case NFS4ERR_SERVERFAULT:p = "NFS4ERR_SERVERFAULT"; break;
+ case NFS4ERR_BADTYPE: p = "NFS4ERR_BADTYPE"; break;
+ case NFS4ERR_DELAY: p = "NFS4ERR_DELAY"; break;
+ case NFS4ERR_SAME: p = "NFS4ERR_SAME"; break;
+ case NFS4ERR_DENIED: p = "NFS4ERR_DENIED"; break;
+ case NFS4ERR_EXPIRED: p = "NFS4ERR_EXPIRED"; break;
+ case NFS4ERR_LOCKED: p = "NFS4ERR_LOCKED"; break;
+ case NFS4ERR_GRACE: p = "NFS4ERR_GRACE"; break;
+ case NFS4ERR_FHEXPIRED: p = "NFS4ERR_FHEXPIRED"; break;
+ case NFS4ERR_SHARE_DENIED: p = "NFS4ERR_SHARE_DENIED"; break;
+ case NFS4ERR_WRONGSEC: p = "NFS4ERR_WRONGSEC"; break;
+ case NFS4ERR_CLID_INUSE: p = "NFS4ERR_CLID_INUSE"; break;
+ case NFS4ERR_RESOURCE: p = "NFS4ERR_RESOURCE"; break;
+ case NFS4ERR_MOVED: p = "NFS4ERR_MOVED"; break;
+ case NFS4ERR_NOFILEHANDLE: p = "NFS4ERR_NOFILEHANDLE"; break;
+ case NFS4ERR_MINOR_VERS_MISMATCH: p = "NFS4ERR_MINOR_VERS_MISMATCH";
+ break;
+ case NFS4ERR_STALE_CLIENTID: p = "NFS4ERR_STALE_CLIENTID"; break;
+ case NFS4ERR_STALE_STATEID: p = "NFS4ERR_STALE_STATEID"; break;
+ case NFS4ERR_OLD_STATEID: p = "NFS4ERR_OLD_STATEID"; break;
+ case NFS4ERR_BAD_STATEID: p = "NFS4ERR_BAD_STATEID"; break;
+ case NFS4ERR_BAD_SEQID: p = "NFS4ERR_BAD_SEQID"; break;
+ case NFS4ERR_NOT_SAME: p = "NFS4ERR_NOT_SAME"; break;
+ case NFS4ERR_LOCK_RANGE: p = "NFS4ERR_LOCK_RANGE"; break;
+ case NFS4ERR_SYMLINK: p = "NFS4ERR_SYMLINK"; break;
+ case NFS4ERR_RESTOREFH: p = "NFS4ERR_RESTOREFH"; break;
+ case NFS4ERR_LEASE_MOVED: p = "NFS4ERR_LEASE_MOVED"; break;
+ case NFS4ERR_ATTRNOTSUPP: p = "NFS4ERR_ATTRNOTSUPP"; break;
+ case NFS4ERR_NO_GRACE: p = "NFS4ERR_NO_GRACE"; break;
+ case NFS4ERR_RECLAIM_BAD: p = "NFS4ERR_RECLAIM_BAD"; break;
+ case NFS4ERR_RECLAIM_CONFLICT: p = "NFS4ERR_RECLAIM_CONFLICT"; break;
+ case NFS4ERR_BADXDR: p = "NFS4ERR_BADXDR"; break;
+ case NFS4ERR_LOCKS_HELD: p = "NFS4ERR_LOCKS_HELD"; break;
+ case NFS4ERR_OPENMODE: p = "NFS4ERR_OPENMODE"; break;
+ case NFS4ERR_BADOWNER: p = "NFS4ERR_BADOWNER"; break;
+ case NFS4ERR_BADCHAR: p = "NFS4ERR_BADCHAR"; break;
+ case NFS4ERR_BADNAME: p = "NFS4ERR_BADNAME"; break;
+ case NFS4ERR_BAD_RANGE: p = "NFS4ERR_BAD_RANGE"; break;
+ case NFS4ERR_LOCK_NOTSUPP: p = "NFS4ERR_LOCK_NOTSUPP"; break;
+ case NFS4ERR_OP_ILLEGAL: p = "NFS4ERR_OP_ILLEGAL"; break;
+ case NFS4ERR_DEADLOCK: p = "NFS4ERR_DEADLOCK"; break;
+ case NFS4ERR_FILE_OPEN: p = "NFS4ERR_FILE_OPEN"; break;
+ case NFS4ERR_ADMIN_REVOKED: p = "NFS4ERR_ADMIN_REVOKED"; break;
+ case NFS4ERR_CB_PATH_DOWN: p = "NFS4ERR_CB_PATH_DOWN"; break;
+ default: p = "(unknown error)"; break;
+ }
+
+ return (p);
+}
+
+char *
+nfsstat4_to_name(int status)
+{
+ return (status_name(status));
+}
+
+/*
+ * Attribute print functions. See attr_info_t.
+ */
+
+static void
+prt_supported_attrs(XDR *xdr)
+{
+ static bitmap4 val;
+
+ if (!xdr_bitmap4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Supported Attributes:");
+ detail_attr_bitmap("\t", &val, NULL);
+ xdr_free(xdr_bitmap4, (char *)&val);
+}
+
+static void
+prt_type(XDR *xdr)
+{
+ nfs_ftype4 val;
+
+ if (!xdr_nfs_ftype4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Type = %s", sum_type_name(val));
+}
+
+static void
+prt_fh_expire_type(XDR *xdr)
+{
+ fattr4_fh_expire_type val;
+ char *buf;
+ bool_t first = TRUE;
+
+ if (!xdr_fattr4_fh_expire_type(xdr, &val))
+ longjmp(xdr_err, 1);
+ buf = get_line(0, 0);
+
+ sprintf(buf, "Filehandle expire type = ");
+ if ((val & (FH4_NOEXPIRE_WITH_OPEN | FH4_VOLATILE_ANY |
+ FH4_VOL_MIGRATION | FH4_VOL_RENAME)) == 0) {
+ strcat(buf, "Persistent");
+ return;
+ }
+ if (val & FH4_NOEXPIRE_WITH_OPEN) {
+ strcat(buf, "No Expire With OPEN");
+ first = FALSE;
+ }
+ if (val & FH4_VOLATILE_ANY) {
+ if (first)
+ first = FALSE;
+ else
+ strcat(buf, ", ");
+ strcat(buf, "Volatile at any time");
+ }
+ if (val & FH4_VOL_MIGRATION) {
+ if (first)
+ first = FALSE;
+ else
+ strcat(buf, ", ");
+ strcat(buf, "Volatile at Migration");
+ }
+ if (val & FH4_VOL_RENAME) {
+ if (first)
+ first = FALSE;
+ else
+ strcat(buf, ", ");
+ strcat(buf, "Volatile at Rename");
+ }
+}
+
+static void
+prt_change(XDR *xdr)
+{
+ changeid4 val;
+
+ if (!xdr_changeid4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Change ID = 0x%llx", val);
+ /* XXX print as time_t, too? */
+}
+
+static void
+prt_size(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Size = %llu", val);
+}
+
+static void
+prt_link_support(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Link Support = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_symlink_support(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Symlink Support = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_named_attr(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Has Named Attributes = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_fsid(XDR *xdr)
+{
+ fsid4 val;
+
+ if (!xdr_fsid4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "FS ID: Major = %llx, Minor = %llx",
+ val.major, val.minor);
+}
+
+static void
+prt_unique_handles(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Unique Handles = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_lease_time(XDR *xdr)
+{
+ uint32_t val;
+
+ if (!xdr_uint32_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Lease Time = %u", val);
+}
+
+static void
+prt_rdattr_error(XDR *xdr)
+{
+ nfsstat4 val;
+
+ if (!xdr_nfsstat4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Rdattr Error = %u (%s)",
+ val, status_name(val));
+}
+
+static void
+prt_acl(XDR *xdr)
+{
+ static fattr4_acl val;
+ char buffy[NFS4_OPAQUE_LIMIT];
+ int i, len;
+
+ if (!xdr_fattr4_acl(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "ACL of %d entries", val.fattr4_acl_len);
+ for (i = 0; i < val.fattr4_acl_len; i++) {
+ sprintf(get_line(0, 0), "nfsace4[%d]", i);
+
+ sprintf(get_line(0, 0), " type = %x",
+ val.fattr4_acl_val[i].type);
+ detail_acetype4(val.fattr4_acl_val[i].type);
+
+ sprintf(get_line(0, 0), " flags = %x",
+ val.fattr4_acl_val[i].flag);
+ detail_aceflag4(val.fattr4_acl_val[i].flag);
+
+ sprintf(get_line(0, 0), " mask = %x",
+ val.fattr4_acl_val[i].access_mask);
+ detail_acemask4(val.fattr4_acl_val[i].access_mask);
+
+ len = val.fattr4_acl_val[i].who.utf8string_len;
+ if (len >= NFS4_OPAQUE_LIMIT)
+ len = NFS4_OPAQUE_LIMIT - 1;
+ (void) strncpy(buffy, val.fattr4_acl_val[i].who.utf8string_val,
+ len);
+ buffy[len] = '\0';
+ sprintf(get_line(0, 0), " who = %s", buffy);
+ }
+ xdr_free(xdr_fattr4_acl, (char *)&val);
+}
+
+static void
+detail_acetype4(acetype4 type)
+{
+ if (type >= ACETYPE4_NAMES_MAX) {
+ sprintf(get_line(0, 0), " unknown type");
+ } else {
+ sprintf(get_line(0, 0), " %s", acetype4_names[type]);
+ }
+}
+
+static void
+detail_uint32_bitmap(uint32_t mask, char *mask_names[], int names_max)
+{
+ char buffy[BUFSIZ], *name;
+ char *indent = " ";
+ char *spacer = " ";
+ int pending = 0;
+ int bit;
+ int len, namelen, spacelen;
+
+ strcpy(buffy, indent);
+ len = strlen(buffy);
+ spacelen = strlen(spacer);
+
+ for (bit = 0; bit < names_max; bit++) {
+ if (mask & (1 << bit)) {
+ name = mask_names[bit];
+ namelen = strlen(name);
+ /* 80 - 6 for "NFS: " = 74 */
+ if ((len + spacelen + namelen) >= 74) {
+ sprintf(get_line(0, 0), "%s", buffy);
+ strcpy(buffy, indent);
+ len = strlen(buffy);
+ pending = 0;
+ }
+ (void) strlcat(buffy, spacer, sizeof (buffy));
+ (void) strlcat(buffy, name, sizeof (buffy));
+ pending = 1;
+ len += spacelen + namelen;
+ }
+ }
+ if (pending)
+ sprintf(get_line(0, 0), "%s", buffy);
+}
+
+static void
+detail_aceflag4(aceflag4 flag)
+{
+ detail_uint32_bitmap(flag, aceflag4_names, ACEFLAG4_NAMES_MAX);
+}
+
+static void
+detail_acemask4(acemask4 mask)
+{
+ detail_uint32_bitmap(mask, acemask4_names, ACEMASK4_NAMES_MAX);
+}
+
+static void
+prt_aclsupport(XDR *xdr)
+{
+ fattr4_aclsupport val;
+
+ if (!xdr_fattr4_aclsupport(xdr, &val))
+ longjmp(xdr_err, 1);
+ if (val & ACL4_SUPPORT_ALLOW_ACL)
+ sprintf(get_line(0, 0), "ALLOW ACL Supported");
+ if (val & ACL4_SUPPORT_DENY_ACL)
+ sprintf(get_line(0, 0), "DENY ACL Supported");
+ if (val & ACL4_SUPPORT_AUDIT_ACL)
+ sprintf(get_line(0, 0), "AUDIT ACL Supported");
+ if (val & ACL4_SUPPORT_ALARM_ACL)
+ sprintf(get_line(0, 0), "ALARM ACL Supported");
+}
+
+static void
+prt_archive(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Archived = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_cansettime(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Server Can Set Time = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_case_insensitive(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Case Insensitive Lookups = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_case_preserving(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Case Preserving = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_chown_restricted(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Chown Is Restricted = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_filehandle(XDR *xdr)
+{
+ static nfs_fh4 val;
+
+ if (!xdr_nfs_fh4(xdr, &val))
+ longjmp(xdr_err, 1);
+ detail_fh4(&val);
+ xdr_free(xdr_nfs_fh4, (char *)&val);
+}
+
+static void
+prt_fileid(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "File ID = %llu", val);
+}
+
+static void
+prt_mounted_on_fileid(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Mounted On File ID = %llu", val);
+}
+
+static void
+prt_files_avail(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Files Available = %llu", val);
+}
+
+static void
+prt_files_free(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Files Free = %llu", val);
+}
+
+static void
+prt_files_total(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Files Total = %llu", val);
+}
+
+static void
+prt_fs_location(fs_location4 *fsl)
+{
+ int i;
+
+ for (i = 0; i < fsl->server.server_len; i++)
+ sprintf(get_line(0, 0), "server: %s",
+ utf8localize(&fsl->server.server_val[i]));
+
+ detail_pathname4(&fsl->rootpath, "rootpath: ");
+}
+
+static void
+prt_fs_locations(XDR *xdr)
+{
+ static fs_locations4 val;
+ int i;
+
+ if (!xdr_fs_locations4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "[fs_locations]");
+ detail_pathname4(&val.fs_root, "fs_root: ");
+ for (i = 0; i < val.locations.locations_len; i++)
+ prt_fs_location(&val.locations.locations_val[i]);
+ xdr_free(xdr_fs_locations4, (char *)&val);
+}
+
+static void
+prt_hidden(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Hidden = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_homogeneous(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "FS Is Homogeneous = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_maxfilesize(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Maximum File Size = %llu", val);
+}
+
+static void
+prt_maxlink(XDR *xdr)
+{
+ uint32_t val;
+
+ if (!xdr_uint32_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Maximum Number of Links = %u", val);
+}
+
+static void
+prt_maxname(XDR *xdr)
+{
+ uint32_t val;
+
+ if (!xdr_uint32_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Maximum File Name Length = %u", val);
+}
+
+static void
+prt_maxread(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Maximum Read Size = %llu", val);
+}
+
+static void
+prt_maxwrite(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+
+ sprintf(get_line(0, 0), "Maximum Write Size = %llu", val);
+}
+
+static void
+prt_mimetype(XDR *xdr)
+{
+ static utf8string val;
+
+ if (!xdr_utf8string(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "MIME Type = %s", utf8localize(&val));
+ xdr_free(xdr_utf8string, (char *)&val);
+}
+
+static void
+prt_mode(XDR *xdr)
+{
+ mode4 val;
+
+ if (!xdr_mode4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Mode = 0%03o", val);
+}
+
+static void
+prt_no_trunc(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Long Names Are Error (no_trunc) = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_numlinks(XDR *xdr)
+{
+ uint32_t val;
+
+ if (!xdr_uint32_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Number of Links = %u", val);
+}
+
+static void
+prt_owner(XDR *xdr)
+{
+ static utf8string val;
+
+ if (!xdr_utf8string(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Owner = %s", utf8localize(&val));
+ xdr_free(xdr_utf8string, (char *)&val);
+}
+
+static void
+prt_owner_group(XDR *xdr)
+{
+ static utf8string val;
+
+ if (!xdr_utf8string(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Group = %s", utf8localize(&val));
+ xdr_free(xdr_utf8string, (char *)&val);
+}
+
+static void
+prt_quota_avail_hard(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Quota Hard Limit = %llu", val);
+}
+
+static void
+prt_quota_avail_soft(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Quota Soft Limit = %llu", val);
+}
+
+static void
+prt_quota_used(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Quota Used = %llu", val);
+}
+
+static void
+prt_rawdev(XDR *xdr)
+{
+ specdata4 val;
+
+ if (!xdr_specdata4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Raw Device ID = %u, %u",
+ val.specdata1, val.specdata2);
+}
+
+static void
+prt_space_avail(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Space Available = %llu", val);
+}
+
+static void
+prt_space_free(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Space Free = %llu", val);
+}
+
+static void
+prt_space_total(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Total Disk Space = %llu", val);
+}
+
+static void
+prt_space_used(XDR *xdr)
+{
+ uint64_t val;
+
+ if (!xdr_uint64_t(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Space Used (this object) = %llu", val);
+}
+
+static void
+prt_system(XDR *xdr)
+{
+ bool_t val;
+
+ if (!xdr_bool(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "System File = %s",
+ val ? "TRUE" : "FALSE");
+}
+
+static void
+prt_time_access(XDR *xdr)
+{
+ nfstime4 val;
+
+ if (!xdr_nfstime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Last Access Time = %s",
+ format_time(val.seconds, val.nseconds));
+}
+
+static void
+prt_time_access_set(XDR *xdr)
+{
+ settime4 val;
+
+ if (!xdr_settime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ if (val.set_it == SET_TO_CLIENT_TIME4) {
+ sprintf(get_line(0, 0), "Access Time = %s (set to client time)",
+ format_time(val.settime4_u.time.seconds,
+ val.settime4_u.time.nseconds));
+ } else if (val.set_it == SET_TO_SERVER_TIME4) {
+ sprintf(get_line(0, 0), "Access Time (set to server time)");
+ } else
+ longjmp(xdr_err, 1);
+}
+
+static void
+prt_time_backup(XDR *xdr)
+{
+ nfstime4 val;
+
+ if (!xdr_nfstime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Last Backup Time = %s",
+ format_time(val.seconds, val.nseconds));
+}
+
+static void
+prt_time_create(XDR *xdr)
+{
+ nfstime4 val;
+
+ if (!xdr_nfstime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Creation Time = %s",
+ format_time(val.seconds, val.nseconds));
+}
+
+static void
+prt_time_delta(XDR *xdr)
+{
+ nfstime4 val;
+
+ if (!xdr_nfstime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Server Time Granularity = %lld.%09d sec",
+ val.seconds, val.nseconds);
+}
+
+static void
+prt_time_metadata(XDR *xdr)
+{
+ nfstime4 val;
+
+ if (!xdr_nfstime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Last Metadata Change Time = %s",
+ format_time(val.seconds, val.nseconds));
+}
+
+static void
+prt_time_modify(XDR *xdr)
+{
+ nfstime4 val;
+
+ if (!xdr_nfstime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), "Last Modification Time = %s",
+ format_time(val.seconds, val.nseconds));
+}
+
+static void
+prt_time_modify_set(XDR *xdr)
+{
+ settime4 val;
+
+ if (!xdr_settime4(xdr, &val))
+ longjmp(xdr_err, 1);
+ if (val.set_it == SET_TO_CLIENT_TIME4) {
+ sprintf(get_line(0, 0),
+ "Modification Time = %s (set to client time)",
+ format_time(val.settime4_u.time.seconds,
+ val.settime4_u.time.nseconds));
+ } else if (val.set_it == SET_TO_SERVER_TIME4) {
+ sprintf(get_line(0, 0),
+ "Modification Time (set to server time)");
+ } else
+ longjmp(xdr_err, 1);
+}
+
+/*
+ * Display the UTF8 string that is next in the XDR stream.
+ */
+
+static void
+showxdr_utf8string(char *fmt)
+{
+ static utf8string string;
+
+ if (!xdr_utf8string(&xdrm, &string))
+ longjmp(xdr_err, 1);
+ sprintf(get_line(0, 0), fmt, utf8localize(&string));
+ xdr_free(xdr_utf8string, (char *)&string);
+}
+
+/*
+ * utf8string is defined in nfs4_prot.x as an opaque array, which means
+ * when it is decoded into a string, the string might not have a trailing
+ * null. Also, the string will still be encoded in UTF-8, rather than
+ * whatever character encoding is associated with the current locale. This
+ * routine converts a utf8string into a (null-terminated) C string. One day
+ * it will convert into the current character encoding, too. To avoid
+ * dealing with storage management issues, it allocates storage for each
+ * new string, then this storage is "freed" when the packet has been
+ * processed.
+ */
+
+#define MAX_UTF8_STRINGS 512
+
+static char *utf_buf[MAX_UTF8_STRINGS];
+static size_t utf_buflen[MAX_UTF8_STRINGS];
+static uint_t cur_utf_buf = 0;
+
+static char *
+utf8localize(utf8string *utf8str)
+{
+ size_t newsize, oldsize, len;
+ char *result, *cp;
+
+ len = utf8str->utf8string_len;
+ if (len == 0)
+ return ("");
+ if (cur_utf_buf >= MAX_UTF8_STRINGS)
+ return ("[Too Many UTF-8 Strings]");
+
+ newsize = oldsize = utf_buflen[cur_utf_buf];
+
+
+ if (oldsize < len + 1) {
+ /* truncate opaques at NFS4_OPAQUE_LIMIT */
+ if (len > NFS4_OPAQUE_LIMIT)
+ len = NFS4_OPAQUE_LIMIT;
+ newsize = len + 1;
+ }
+ if (newsize != oldsize) {
+ utf_buf[cur_utf_buf] = realloc(utf_buf[cur_utf_buf],
+ newsize);
+ if (utf_buf[cur_utf_buf] == NULL) {
+ pr_err("out of memory\n");
+ utf_buflen[cur_utf_buf] = 0;
+ return ("");
+ }
+ utf_buflen[cur_utf_buf] = newsize;
+ }
+
+ result = utf_buf[cur_utf_buf];
+ strncpy(result, utf8str->utf8string_val, len);
+ result[len] = '\0';
+ for (cp = result; cp < result + len; cp++) {
+ if (!isprint(*cp)) {
+ *cp = '.';
+ }
+ }
+
+ cur_utf_buf++;
+
+ return (result);
+}
+
+static void
+utf8free()
+{
+ cur_utf_buf = 0;
+}
+
+
+/*
+ * adler16(): adler32 hash code shamelessly copied and mutiliated from
+ * usr/src/uts/common/io/ppp/spppcomp/zlib.[ch]
+ *
+ * The alg was originally created to provide a running
+ * checksum, but we don't need that -- we just want to
+ * chksum data described by buf,len; therefore, the first
+ * parameter was removed (held the running checksum),
+ * and s1/s2 are always set to their required initial
+ * values (1 and 0). I also ripped out code which only
+ * applied to large data sets (bufs larger than 5k). All
+ * I wanted was their core checksum alg (which is supposed
+ * to do really well). The v2/v3 hash alg didn't work well
+ * at all for v4 stuff -- it produced too many collisions.
+ *
+ * The copyright info from uts/common/io/ppp/spppcomp/zlib.[ch]
+ * is included below.
+ */
+
+/* -----zlib.c copyright info below */
+/*
+ * Copyright 2000 Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Updated from zlib-1.0.4 to zlib-1.1.3 by James Carlson.
+ *
+ * This file is derived from various .h and .c files from the zlib-1.0.4
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets. See zlib.h for conditions of
+ * distribution and use.
+ *
+ * Changes that have been made include:
+ * - added Z_PACKET_FLUSH (see zlib.h for details)
+ * - added inflateIncomp and deflateOutputPending
+ * - allow strm->next_out to be NULL, meaning discard the output
+ *
+ * $Id: zlib.c,v 1.11 1998/09/13 23:37:12 paulus Exp $
+ */
+/* +++ adler32.c */
+/*
+ * adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+/* From: adler32.c,v 1.10 1996/05/22 11:52:18 me Exp $ */
+/* -----zlib.c copyright info above */
+
+/* -----zlib.h copyright info below */
+/*
+ * Copyright 2000 Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation is hereby granted, provided that the above
+ * copyright notice appears in all copies.
+ *
+ * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
+ * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
+ * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
+ *
+ * This file has been altered from its original by Sun Microsystems to
+ * fit local coding style.
+ */
+/* -----zlib.h copyright info above */
+
+#define DO1(buf, i) {s1 += buf[i]; s2 += s1; }
+#define DO2(buf, i) DO1(buf, i); DO1(buf, i+1);
+#define DO4(buf, i) DO2(buf, i); DO2(buf, i+2);
+#define DO8(buf, i) DO4(buf, i); DO4(buf, i+4);
+#define DO16(buf) DO8(buf, 0); DO8(buf, 8);
+
+static uint32_t
+adler16(void *p, int len)
+{
+ uint32_t s1 = 1;
+ uint32_t s2 = 0;
+ uchar_t *buf = p;
+
+ while (len >= 16) {
+ DO16(buf);
+ buf += 16;
+ len -= 16;
+ }
+
+ while (len > 0) {
+ s1 += *buf++;
+ s2 += s1;
+ len--;
+ }
+
+ return ((uint32_t)(s2 ^ s1) & 0xFFFFU);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs_acl.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs_acl.c
new file mode 100644
index 0000000..4883ed5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs_acl.c
@@ -0,0 +1,797 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/tiuser.h>
+#include <setjmp.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <string.h>
+#include "snoop.h"
+
+#include <sys/stat.h>
+
+extern char *get_sum_line();
+extern void check_retransmit();
+extern char *sum_nfsfh();
+extern int sum_nfsstat();
+extern int detail_nfsstat();
+extern void detail_nfsfh();
+extern void detail_fattr();
+extern void skip_fattr();
+extern char *sum_nfsfh3();
+extern int sum_nfsstat3();
+extern int detail_nfsstat3();
+extern void detail_post_op_attr();
+extern void detail_nfsfh3();
+extern int sum_nfsstat4();
+extern int detail_nfsstat4();
+
+extern jmp_buf xdr_err;
+
+static void aclcall2();
+static void aclreply2();
+static void aclcall3();
+static void aclreply3();
+static void aclcall4();
+static void aclreply4();
+static void detail_access2();
+static char *sum_access2();
+static void detail_mask();
+static void detail_secattr();
+static void detail_aclent();
+static char *detail_uname();
+static char *detail_gname();
+static char *detail_perm(ushort_t);
+static void interpret_nfs_acl2(int, int, int, int, int, char *, int);
+static void interpret_nfs_acl3(int, int, int, int, int, char *, int);
+static void interpret_nfs_acl4(int, int, int, int, int, char *, int);
+
+#define ACLPROC2_NULL ((unsigned long)(0))
+#define ACLPROC2_GETACL ((unsigned long)(1))
+#define ACLPROC2_SETACL ((unsigned long)(2))
+#define ACLPROC2_GETATTR ((unsigned long)(3))
+#define ACLPROC2_ACCESS ((unsigned long)(4))
+#define ACLPROC2_GETXATTRDIR ((unsigned long)(5))
+
+#define ACLPROC3_NULL ((unsigned long)(0))
+#define ACLPROC3_GETACL ((unsigned long)(1))
+#define ACLPROC3_SETACL ((unsigned long)(2))
+#define ACLPROC3_GETXATTRDIR ((unsigned long)(3))
+
+#define ACLPROC4_NULL ((unsigned long)(0))
+#define ACLPROC4_GETACL ((unsigned long)(1))
+#define ACLPROC4_SETACL ((unsigned long)(2))
+
+#define NA_USER_OBJ 0x1
+#define NA_USER 0x2
+#define NA_GROUP_OBJ 0x4
+#define NA_GROUP 0x8
+#define NA_CLASS_OBJ 0x10
+#define NA_OTHER_OBJ 0x20
+#define NA_ACL_DEFAULT 0x1000
+
+#define NA_DEF_USER_OBJ (NA_USER_OBJ | NA_ACL_DEFAULT)
+#define NA_DEF_USER (NA_USER | NA_ACL_DEFAULT)
+#define NA_DEF_GROUP_OBJ (NA_GROUP_OBJ | NA_ACL_DEFAULT)
+#define NA_DEF_GROUP (NA_GROUP | NA_ACL_DEFAULT)
+#define NA_DEF_CLASS_OBJ (NA_CLASS_OBJ | NA_ACL_DEFAULT)
+#define NA_DEF_OTHER_OBJ (NA_OTHER_OBJ | NA_ACL_DEFAULT)
+
+#define NA_ACL 0x1
+#define NA_ACLCNT 0x2
+#define NA_DFACL 0x4
+#define NA_DFACLCNT 0x8
+
+#define ACCESS2_READ 0x0001
+#define ACCESS2_LOOKUP 0x0002
+#define ACCESS2_MODIFY 0x0004
+#define ACCESS2_EXTEND 0x0008
+#define ACCESS2_DELETE 0x0010
+#define ACCESS2_EXECUTE 0x0020
+
+static char *procnames_short_v2[] = {
+ "NULL2", /* 0 */
+ "GETACL2", /* 1 */
+ "SETACL2", /* 2 */
+ "GETATTR2", /* 3 */
+ "ACCESS2", /* 4 */
+ "GETXATTRDIR2", /* 5 */
+};
+static char *procnames_short_v3[] = {
+ "NULL3", /* 0 */
+ "GETACL3", /* 1 */
+ "SETACL3", /* 2 */
+ "GETXATTRDIR3", /* 3 */
+};
+static char *procnames_short_v4[] = {
+ "NULL4", /* 0 */
+ "GETACL4", /* 1 */
+ "SETACL4", /* 2 */
+};
+
+static char *procnames_long_v2[] = {
+ "Null procedure", /* 0 */
+ "Get file access control list", /* 1 */
+ "Set file access control list", /* 2 */
+ "Get file attributes", /* 3 */
+ "Check access permission", /* 4 */
+ "Get extended attribute directory", /* 5 */
+};
+static char *procnames_long_v3[] = {
+ "Null procedure", /* 0 */
+ "Get file access control list", /* 1 */
+ "Set file access control list", /* 2 */
+ "Get extended attribute directory", /* 3 */
+};
+static char *procnames_long_v4[] = {
+ "Null procedure", /* 0 */
+ "Get file access control list", /* 1 */
+ "Set file access control list", /* 2 */
+};
+
+#define MAXPROC_V2 5
+#define MAXPROC_V3 3
+#define MAXPROC_V4 2
+
+/* ARGSUSED */
+void
+interpret_nfs_acl(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+
+ if (vers == 2) {
+ interpret_nfs_acl2(flags, type, xid, vers, proc, data, len);
+ return;
+ }
+
+ if (vers == 3) {
+ interpret_nfs_acl3(flags, type, xid, vers, proc, data, len);
+ return;
+ }
+
+ if (vers == 4) {
+ interpret_nfs_acl4(flags, type, xid, vers, proc, data, len);
+ return;
+ }
+}
+
+static void
+interpret_nfs_acl2(int flags, int type, int xid, int vers, int proc,
+ char *data, int len)
+{
+ char *line;
+ char buff[2048];
+ int off, sz;
+ char *fh;
+ ulong_t mask;
+
+ if (proc < 0 || proc > MAXPROC_V2)
+ return;
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "NFS_ACL C %s",
+ procnames_short_v2[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case ACLPROC2_GETACL:
+ fh = sum_nfsfh();
+ mask = getxdr_u_long();
+ (void) sprintf(line, "%s mask=0x%lx", fh, mask);
+ break;
+ case ACLPROC2_SETACL:
+ (void) sprintf(line, sum_nfsfh());
+ break;
+ case ACLPROC2_GETATTR:
+ (void) sprintf(line, sum_nfsfh());
+ break;
+ case ACLPROC2_ACCESS:
+ fh = sum_nfsfh();
+ (void) sprintf(line, "%s (%s)", fh,
+ sum_access2());
+ break;
+ case ACLPROC2_GETXATTRDIR:
+ fh = sum_nfsfh();
+ (void) sprintf(line, "%s create=%s", fh,
+ getxdr_bool() ? "true" : "false");
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, (ulong_t)xid);
+ } else {
+ (void) sprintf(line, "NFS_ACL R %s ",
+ procnames_short_v2[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case ACLPROC2_GETACL:
+ (void) sum_nfsstat(line);
+ break;
+ case ACLPROC2_SETACL:
+ (void) sum_nfsstat(line);
+ break;
+ case ACLPROC2_GETATTR:
+ (void) sum_nfsstat(line);
+ break;
+ case ACLPROC2_ACCESS:
+ if (sum_nfsstat(line) == 0) {
+ skip_fattr();
+ line += strlen(line);
+ (void) sprintf(line, " (%s)",
+ sum_access2());
+ }
+ break;
+ case ACLPROC2_GETXATTRDIR:
+ if (sum_nfsstat(line) == 0) {
+ line += strlen(line);
+ (void) sprintf(line, sum_nfsfh());
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NFS_ACL: ", "Sun NFS_ACL", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
+ proc, procnames_long_v2[proc]);
+ if (type == CALL)
+ aclcall2(proc);
+ else
+ aclreply2(proc);
+ show_trailer();
+ }
+}
+
+static void
+interpret_nfs_acl3(int flags, int type, int xid, int vers, int proc,
+ char *data, int len)
+{
+ char *line;
+ char buff[2048];
+ int off, sz;
+ char *fh;
+ ulong_t mask;
+
+ if (proc < 0 || proc > MAXPROC_V3)
+ return;
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "NFS_ACL C %s",
+ procnames_short_v3[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case ACLPROC3_GETACL:
+ fh = sum_nfsfh3();
+ mask = getxdr_u_long();
+ (void) sprintf(line, "%s mask=0x%lx", fh, mask);
+ break;
+ case ACLPROC3_SETACL:
+ (void) sprintf(line, sum_nfsfh3());
+ break;
+ case ACLPROC3_GETXATTRDIR:
+ fh = sum_nfsfh3();
+ (void) sprintf(line, "%s create=%s", fh,
+ getxdr_bool() ? "true" : "false");
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, (ulong_t)xid);
+ } else {
+ (void) sprintf(line, "NFS_ACL R %s ",
+ procnames_short_v3[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case ACLPROC3_GETACL:
+ (void) sum_nfsstat3(line);
+ break;
+ case ACLPROC3_SETACL:
+ (void) sum_nfsstat3(line);
+ break;
+ case ACLPROC3_GETXATTRDIR:
+ if (sum_nfsstat3(line) == 0) {
+ line += strlen(line);
+ (void) sprintf(line, sum_nfsfh3());
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NFS_ACL: ", "Sun NFS_ACL", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
+ proc, procnames_long_v3[proc]);
+ if (type == CALL)
+ aclcall3(proc);
+ else
+ aclreply3(proc);
+ show_trailer();
+ }
+}
+
+static void
+interpret_nfs_acl4(int flags, int type, int xid, int vers, int proc,
+ char *data, int len)
+{
+ char *line;
+ char buff[2048];
+ int off, sz;
+ char *fh;
+ ulong_t mask;
+
+ if (proc < 0 || proc > MAXPROC_V4)
+ return;
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "NFS_ACL C %s",
+ procnames_short_v4[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case ACLPROC4_GETACL:
+ fh = sum_nfsfh3();
+ mask = getxdr_u_long();
+ (void) sprintf(line, "%s mask=0x%lx", fh, mask);
+ break;
+ case ACLPROC4_SETACL:
+ (void) sprintf(line, sum_nfsfh3());
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, (ulong_t)xid);
+ } else {
+ (void) sprintf(line, "NFS_ACL R %s ",
+ procnames_short_v4[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case ACLPROC4_GETACL:
+ (void) sum_nfsstat4(line);
+ break;
+ case ACLPROC4_SETACL:
+ (void) sum_nfsstat4(line);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NFS_ACL: ", "Sun NFS_ACL", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
+ proc, procnames_long_v4[proc]);
+ if (type == CALL)
+ aclcall4(proc);
+ else
+ aclreply4(proc);
+ show_trailer();
+ }
+}
+
+int
+sum_nfsstat4(char *line)
+{
+ ulong_t status;
+ char *p, *nfsstat4_to_name(int);
+
+ status = getxdr_long();
+ p = nfsstat4_to_name(status);
+ (void) strcpy(line, p);
+ return (status);
+}
+
+int
+detail_nfsstat4()
+{
+ ulong_t status;
+ char buff[64];
+ int pos;
+
+ pos = getxdr_pos();
+ status = sum_nfsstat4(buff);
+
+ (void) sprintf(get_line(pos, getxdr_pos()), "Status = %d (%s)",
+ status, buff);
+
+ return ((int)status);
+}
+
+/*
+ * Print out version 2 NFS_ACL call packets
+ */
+static void
+aclcall2(proc)
+ int proc;
+{
+
+ switch (proc) {
+ case ACLPROC2_GETACL:
+ detail_nfsfh();
+ detail_mask();
+ break;
+ case ACLPROC2_SETACL:
+ detail_nfsfh();
+ detail_secattr();
+ break;
+ case ACLPROC2_GETATTR:
+ detail_nfsfh();
+ break;
+ case ACLPROC2_ACCESS:
+ detail_nfsfh();
+ detail_access2();
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 2 NFS_ACL reply packets
+ */
+static void
+aclreply2(proc)
+ int proc;
+{
+
+ switch (proc) {
+ case ACLPROC2_GETACL:
+ if (detail_nfsstat() == 0) {
+ detail_fattr();
+ detail_secattr();
+ }
+ break;
+ case ACLPROC2_SETACL:
+ if (detail_nfsstat() == 0)
+ detail_fattr();
+ break;
+ case ACLPROC2_GETATTR:
+ if (detail_nfsstat() == 0)
+ detail_fattr();
+ break;
+ case ACLPROC2_ACCESS:
+ if (detail_nfsstat() == 0) {
+ detail_fattr();
+ detail_access2();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 3 NFS_ACL call packets
+ */
+static void
+aclcall3(proc)
+ int proc;
+{
+
+ switch (proc) {
+ case ACLPROC3_GETACL:
+ detail_nfsfh3();
+ detail_mask();
+ break;
+ case ACLPROC3_SETACL:
+ detail_nfsfh3();
+ detail_secattr();
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 3 NFS_ACL reply packets
+ */
+static void
+aclreply3(proc)
+ int proc;
+{
+
+ switch (proc) {
+ case ACLPROC3_GETACL:
+ if (detail_nfsstat3() == 0) {
+ detail_post_op_attr("");
+ detail_secattr();
+ }
+ break;
+ case ACLPROC3_SETACL:
+ if (detail_nfsstat3() == 0)
+ detail_post_op_attr("");
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 4 NFS_ACL call packets
+ */
+static void
+aclcall4(proc)
+ int proc;
+{
+
+ switch (proc) {
+ case ACLPROC4_GETACL:
+ detail_nfsfh3();
+ detail_mask();
+ break;
+ case ACLPROC4_SETACL:
+ detail_nfsfh3();
+ detail_secattr();
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 4 NFS_ACL reply packets
+ */
+static void
+aclreply4(proc)
+ int proc;
+{
+
+ switch (proc) {
+ case ACLPROC4_GETACL:
+ if (detail_nfsstat4() == 0) {
+ detail_post_op_attr("");
+ detail_secattr();
+ }
+ break;
+ case ACLPROC4_SETACL:
+ if (detail_nfsstat4() == 0)
+ detail_post_op_attr("");
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+detail_access2()
+{
+ uint_t bits;
+
+ bits = showxdr_u_long("Access bits = 0x%08x");
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS2_READ, "Read", "(no read)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS2_LOOKUP, "Lookup", "(no lookup)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS2_MODIFY, "Modify", "(no modify)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS2_EXTEND, "Extend", "(no extend)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS2_DELETE, "Delete", "(no delete)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(bits, ACCESS2_EXECUTE, "Execute", "(no execute)"));
+}
+
+static char *
+sum_access2()
+{
+ int bits;
+ static char buff[22];
+
+ bits = getxdr_u_long();
+ buff[0] = '\0';
+
+ if (bits & ACCESS2_READ)
+ (void) strcat(buff, "read,");
+ if (bits & ACCESS2_LOOKUP)
+ (void) strcat(buff, "lookup,");
+ if (bits & ACCESS2_MODIFY)
+ (void) strcat(buff, "modify,");
+ if (bits & ACCESS2_EXTEND)
+ (void) strcat(buff, "extend,");
+ if (bits & ACCESS2_DELETE)
+ (void) strcat(buff, "delete,");
+ if (bits & ACCESS2_EXECUTE)
+ (void) strcat(buff, "execute,");
+ if (buff[0] != '\0')
+ buff[strlen(buff) - 1] = '\0';
+
+ return (buff);
+}
+
+static void
+detail_mask()
+{
+ ulong_t mask;
+
+ mask = showxdr_u_long("Mask = 0x%lx");
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(mask, NA_ACL, "aclent", "(no aclent)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(mask, NA_ACLCNT, "aclcnt", "(no aclcnt)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(mask, NA_DFACL, "dfaclent", "(no dfaclent)"));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(mask, NA_DFACLCNT, "dfaclcnt", "(no dfaclcnt)"));
+}
+
+static void
+detail_secattr()
+{
+
+ detail_mask();
+ showxdr_long("Aclcnt = %d");
+ detail_aclent();
+ showxdr_long("Dfaclcnt = %d");
+ detail_aclent();
+}
+
+static void
+detail_aclent()
+{
+ int count;
+ int type;
+ int id;
+ ushort_t perm;
+
+ count = getxdr_long();
+ while (count-- > 0) {
+ type = getxdr_long();
+ id = getxdr_long();
+ perm = getxdr_u_short();
+ switch (type) {
+ case NA_USER:
+ (void) sprintf(get_line(0, 0), "\tuser:%s:%s",
+ detail_uname(id), detail_perm(perm));
+ break;
+ case NA_USER_OBJ:
+ (void) sprintf(get_line(0, 0), "\tuser::%s",
+ detail_perm(perm));
+ break;
+ case NA_GROUP:
+ (void) sprintf(get_line(0, 0), "\tgroup:%s:%s",
+ detail_gname(id), detail_perm(perm));
+ break;
+ case NA_GROUP_OBJ:
+ (void) sprintf(get_line(0, 0), "\tgroup::%s",
+ detail_perm(perm));
+ break;
+ case NA_CLASS_OBJ:
+ (void) sprintf(get_line(0, 0), "\tmask:%s",
+ detail_perm(perm));
+ break;
+ case NA_OTHER_OBJ:
+ (void) sprintf(get_line(0, 0), "\tother:%s",
+ detail_perm(perm));
+ break;
+ case NA_DEF_USER:
+ (void) sprintf(get_line(0, 0), "\tdefault:user:%s:%s",
+ detail_uname(id), detail_perm(perm));
+ break;
+ case NA_DEF_USER_OBJ:
+ (void) sprintf(get_line(0, 0), "\tdefault:user::%s",
+ detail_perm(perm));
+ break;
+ case NA_DEF_GROUP:
+ (void) sprintf(get_line(0, 0), "\tdefault:group:%s:%s",
+ detail_gname(id), detail_perm(perm));
+ break;
+ case NA_DEF_GROUP_OBJ:
+ (void) sprintf(get_line(0, 0), "\tdefault:group::%s",
+ detail_perm(perm));
+ break;
+ case NA_DEF_CLASS_OBJ:
+ (void) sprintf(get_line(0, 0), "\tdefault:mask:%s",
+ detail_perm(perm));
+ break;
+ case NA_DEF_OTHER_OBJ:
+ (void) sprintf(get_line(0, 0), "\tdefault:other:%s",
+ detail_perm(perm));
+ break;
+ default:
+ (void) sprintf(get_line(0, 0), "\tunrecognized entry");
+ break;
+ }
+ }
+}
+
+static char *
+detail_uname(uid_t uid)
+{
+ struct passwd *pwd;
+ static char uidp[10];
+
+ pwd = getpwuid(uid);
+ if (pwd == NULL) {
+ sprintf(uidp, "%d", uid);
+ return (uidp);
+ }
+ return (pwd->pw_name);
+}
+
+static char *
+detail_gname(gid_t gid)
+{
+ struct group *grp;
+ static char gidp[10];
+
+ grp = getgrgid(gid);
+ if (grp == NULL) {
+ sprintf(gidp, "%d", gid);
+ return (gidp);
+ }
+ return (grp->gr_name);
+}
+
+static char *perms[] = {
+ "---",
+ "--x",
+ "-w-",
+ "-wx",
+ "r--",
+ "r-x",
+ "rw-",
+ "rwx"
+};
+static char *
+detail_perm(ushort_t perm)
+{
+
+ if (perm >= sizeof (perms) / sizeof (perms[0]))
+ return ("?");
+ return (perms[perm]);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nis.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nis.c
new file mode 100644
index 0000000..a78c418
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nis.c
@@ -0,0 +1,676 @@
+/*
+ * 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 (c) 1991, 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/tiuser.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <rpcsvc/yp_prot.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+char *ypbind_error();
+char *sum_ypxfrstat();
+char *sum_ypmaplist();
+void detail_ypmaplist();
+
+static void niscall(int);
+static void nisreply(int);
+static int detail_ypstat(void);
+static int sum_ypstat(char *);
+
+/*
+ * Defines missing from 5.0 yp_prot.h
+ */
+#define YPBINDPROG ((ulong_t)100007)
+#define YPBINDVERS ((ulong_t)2)
+#define YPBINDVERS_ORIG ((ulong_t)1)
+
+/* Procedure symbols */
+
+#define YPBINDPROC_NULL ((ulong_t)0)
+#define YPBINDPROC_DOMAIN ((ulong_t)1)
+#define YPBINDPROC_SETDOM ((ulong_t)2)
+
+#define YPBIND_ERR_ERR 1 /* Internal error */
+#define YPBIND_ERR_NOSERV 2 /* No bound server for passed domain */
+#define YPBIND_ERR_RESC 3 /* System resource allocation failure */
+
+
+static char *procnames_bind_short[] = {
+ "NULL", /* 0 */
+ "DOMAIN", /* 1 */
+ "SETDOMAIN", /* 2 */
+};
+
+static char *procnames_bind_long[] = {
+ "Null procedure", /* 0 */
+ "Get domain name", /* 1 */
+ "Set domain name", /* 2 */
+};
+
+static char *procnames_short[] = {
+ "NULL", /* 0 */
+ "DOMAIN", /* 1 */
+ "DOMAIN_NONACK", /* 2 */
+ "MATCH", /* 3 */
+ "FIRST", /* 4 */
+ "NEXT", /* 5 */
+ "XFR", /* 6 */
+ "CLEAR", /* 7 */
+ "ALL", /* 8 */
+ "MASTER", /* 9 */
+ "ORDER", /* 10 */
+ "MAPLIST", /* 11 */
+ "NEWXFR", /* 12 */
+};
+
+#define MAXPROC_BIND 2
+#define MAXPROC 12
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Verify domain support", /* 1 */
+ "Verify domain support (broadcast)", /* 2 */
+ "Return value of a key", /* 3 */
+ "Return first key-value pair in map", /* 4 */
+ "Return next key-value pair in map", /* 5 */
+ "Request map update (old)", /* 6 */
+ "Close current map on server", /* 7 */
+ "Get all key-value pairs in map", /* 8 */
+ "Get master server", /* 9 */
+ "Get order", /* 10 */
+ "Return list of supported maps", /* 11 */
+ "Request map update", /* 12 */
+};
+
+void
+interpret_nisbind(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[YPMAXDOMAIN + 1];
+ unsigned int status;
+
+ if (proc < 0 || proc > MAXPROC_BIND)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "NISBIND C %s",
+ procnames_bind_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case YPBINDPROC_NULL:
+ break;
+ case YPBINDPROC_DOMAIN:
+ (void) sprintf(line, " %s",
+ getxdr_string(buff, YPMAXDOMAIN));
+ break;
+ case YPBINDPROC_SETDOM:
+ (void) sprintf(line, " %s",
+ getxdr_string(buff, YPMAXDOMAIN));
+ break;
+ default:
+ break;
+ }
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "NISBIND R %s ",
+ procnames_bind_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case YPBINDPROC_NULL:
+ break;
+ case YPBINDPROC_DOMAIN:
+ status = getxdr_long();
+ if (status == 1) { /* success */
+ (void) strcat(line, "OK");
+ } else { /* failure */
+ status = getxdr_long();
+ (void) sprintf(line, "ERROR=%s",
+ ypbind_error(status));
+ }
+ break;
+ case YPBINDPROC_SETDOM:
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NISBIND:",
+ "Network Information Service Bind", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_bind_long[proc]);
+ if (type == CALL) {
+ switch (proc) {
+ case YPBINDPROC_NULL:
+ break;
+ case YPBINDPROC_DOMAIN:
+ (void) showxdr_string(YPMAXDOMAIN,
+ "Domain = %s");
+ break;
+ case YPBINDPROC_SETDOM:
+ (void) showxdr_string(YPMAXDOMAIN,
+ "Domain = %s");
+ (void) showxdr_hex(4, "Address=%s");
+ (void) showxdr_hex(2, "Port=%s");
+ (void) showxdr_u_long("Version=%lu");
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (proc) {
+ case YPBINDPROC_NULL:
+ break;
+ case YPBINDPROC_DOMAIN:
+ status = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Status = %lu (%s)",
+ status,
+ status == 1 ? "OK":"Fail");
+ if (status == 1) {
+ (void) showxdr_hex(4,
+ "Address=%s");
+ (void) showxdr_hex(2,
+ "Port=%s");
+ } else {
+ status = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Error = %lu (%s)",
+ status,
+ ypbind_error(status));
+ }
+ break;
+ case YPBINDPROC_SETDOM:
+ break;
+ default:
+ break;
+ }
+ }
+ show_trailer();
+ }
+}
+
+void
+interpret_nis(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char *dom, *map, *key;
+ int transid, status;
+ /* buffers are all the same size so we don't have to keep track */
+ char buff1[YPMAXRECORD + 1], buff2[YPMAXRECORD + 1];
+ char buff3[YPMAXRECORD + 1];
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ if (proc > MAXPROC)
+ (void) sprintf(line, "NIS C %d", proc);
+ else
+ (void) sprintf(line,
+ "NIS C %s",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case YPPROC_NULL:
+ break;
+ case YPPROC_DOMAIN:
+ case YPPROC_DOMAIN_NONACK:
+ case YPPROC_MAPLIST:
+ /* YPMAXDOMAIN > YPMAXMAP */
+ (void) sprintf(line, " %s",
+ getxdr_string(buff1, YPMAXDOMAIN));
+ break;
+ case YPPROC_FIRST:
+ dom = getxdr_string(buff1, YPMAXDOMAIN);
+ map = getxdr_string(buff2, YPMAXMAP);
+ (void) sprintf(line, " %s", map);
+ break;
+ case YPPROC_MATCH:
+ case YPPROC_NEXT:
+ dom = getxdr_string(buff1, YPMAXDOMAIN);
+ map = getxdr_string(buff2, YPMAXMAP);
+ key = getxdr_string(buff3, YPMAXRECORD);
+ (void) sprintf(line,
+ " %s in %s",
+ key, map);
+ break;
+ case YPPROC_NEWXFR:
+ case YPPROC_XFR:
+ dom = getxdr_string(buff1, YPMAXDOMAIN);
+ map = getxdr_string(buff2, YPMAXMAP);
+ (void) sprintf(line,
+ " map %s in %s",
+ map, dom);
+ break;
+ case YPPROC_CLEAR:
+ break;
+ case YPPROC_ALL:
+ case YPPROC_MASTER:
+ case YPPROC_ORDER:
+ dom = getxdr_string(buff1, YPMAXDOMAIN);
+ map = getxdr_string(buff2, YPMAXMAP);
+ (void) sprintf(line,
+ " map %s in %s",
+ map, dom);
+ break;
+ default:
+ break;
+ }
+ check_retransmit(line, xid);
+ } else {
+ if (proc > MAXPROC)
+ (void) sprintf(line, "NIS R %d ", proc);
+ else
+ (void) sprintf(line, "NIS R %s ",
+ procnames_short[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case YPPROC_NULL:
+ break;
+ case YPPROC_DOMAIN:
+ case YPPROC_DOMAIN_NONACK:
+ (void) sprintf(line, "%s",
+ getxdr_long() ? "OK":"Fail");
+ break;
+ case YPPROC_MATCH:
+ (void) sum_ypstat(line);
+ break;
+ case YPPROC_FIRST:
+ case YPPROC_NEXT:
+ if (sum_ypstat(line) == YP_TRUE) {
+ line += strlen(line);
+ (void) getxdr_string(buff1,
+ YPMAXRECORD);
+ (void) sprintf(line, " key=%s",
+ getxdr_string(buff1,
+ YPMAXRECORD));
+ }
+ break;
+ case YPPROC_NEWXFR:
+ case YPPROC_XFR:
+ transid = getxdr_u_long();
+ status = getxdr_long();
+ (void) sprintf(line, "transid=%lu %s",
+ transid,
+ sum_ypxfrstat(status));
+ break;
+ case YPPROC_CLEAR:
+ break;
+ case YPPROC_ALL:
+ if (getxdr_u_long()) {
+ (void) sum_ypstat(line);
+ line += strlen(line);
+ (void) sprintf(line, " key=%s",
+ getxdr_string(buff1, YPMAXRECORD));
+ } else {
+ (void) sprintf(line,
+ "No more");
+ }
+ break;
+ case YPPROC_MASTER:
+ if (sum_ypstat(line) == YP_TRUE) {
+ line += strlen(line);
+ (void) sprintf(line, " peer=%s",
+ getxdr_string(buff1,
+ YPMAXPEER));
+ }
+ break;
+ case YPPROC_ORDER:
+ if (sum_ypstat(line) == YP_TRUE) {
+ line += strlen(line);
+ (void) sprintf(line, " order=%lu",
+ getxdr_u_long());
+ }
+ break;
+ case YPPROC_MAPLIST:
+ if (sum_ypstat(line) == YP_TRUE) {
+ line += strlen(line);
+ (void) sprintf(line, " %s",
+ sum_ypmaplist());
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NIS: ", "Network Information Service", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc,
+ proc > MAXPROC ? "unknown" : procnames_long[proc]);
+ if (type == CALL)
+ niscall(proc);
+ else
+ nisreply(proc);
+ show_trailer();
+ }
+}
+
+/*
+ * Print out version 2 NIS call packets
+ */
+
+static void
+niscall(proc)
+ int proc;
+{
+ switch (proc) {
+ case YPPROC_NULL:
+ break;
+ case YPPROC_DOMAIN:
+ case YPPROC_DOMAIN_NONACK:
+ case YPPROC_MAPLIST:
+ (void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
+ break;
+ case YPPROC_FIRST:
+ (void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
+ (void) showxdr_string(YPMAXMAP, "Map = %s");
+ break;
+ case YPPROC_MATCH:
+ case YPPROC_NEXT:
+ (void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
+ (void) showxdr_string(YPMAXMAP, "Map = %s");
+ (void) showxdr_string(YPMAXRECORD, "Key = %s");
+ break;
+ case YPPROC_NEWXFR:
+ (void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
+ (void) showxdr_string(YPMAXMAP, "Map = %s");
+ (void) showxdr_u_long("Order = %lu");
+ (void) showxdr_string(YPMAXPEER, "Peer = %s");
+ (void) showxdr_u_long("Transid = %lu");
+ (void) showxdr_u_long("Prog = %lu");
+ (void) showxdr_string(YPMAXPEER, "Name = %s");
+ break;
+ case YPPROC_XFR:
+ (void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
+ (void) showxdr_string(YPMAXMAP, "Map = %s");
+ (void) showxdr_u_long("Order = %lu");
+ (void) showxdr_string(YPMAXPEER, "Peer = %s");
+ (void) showxdr_u_long("Transid = %lu");
+ (void) showxdr_u_long("Prog = %lu");
+ (void) showxdr_u_long("Port = %lu");
+ break;
+ case YPPROC_CLEAR:
+ break;
+ case YPPROC_ALL:
+ case YPPROC_MASTER:
+ case YPPROC_ORDER:
+ (void) showxdr_string(YPMAXDOMAIN, "Domain = %s");
+ (void) showxdr_string(YPMAXMAP, "Map = %s");
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print out version 2 NIS reply packets
+ */
+
+void
+nisreply(proc)
+ int proc;
+{
+ unsigned int xfrstat, more;
+
+ switch (proc) {
+ case YPPROC_NULL:
+ break;
+ case YPPROC_DOMAIN:
+ case YPPROC_DOMAIN_NONACK:
+ (void) sprintf(get_line(0, 0),
+ "Result=%s",
+ getxdr_u_long() ? "OK":"Fail");
+ break;
+ case YPPROC_MATCH:
+ (void) detail_ypstat();
+ (void) showxdr_string(YPMAXRECORD, "Value = %s");
+ break;
+ case YPPROC_FIRST:
+ case YPPROC_NEXT:
+ (void) detail_ypstat();
+ (void) showxdr_string(YPMAXRECORD, "Value = %s");
+ (void) showxdr_string(YPMAXRECORD, "Key = %s");
+ break;
+ case YPPROC_NEWXFR:
+ case YPPROC_XFR:
+ (void) showxdr_u_long("Transid = %lu");
+ xfrstat = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Transfer status = %lu (%s)",
+ xfrstat, sum_ypxfrstat(xfrstat));
+ break;
+ case YPPROC_CLEAR:
+ break;
+ case YPPROC_ALL:
+ more = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "More = %s",
+ more ? "true" : "false");
+ if (more) {
+ (void) detail_ypstat();
+ (void) showxdr_string(YPMAXRECORD, "Value = %s");
+ (void) showxdr_string(YPMAXRECORD, "Key = %s");
+ }
+ break;
+ case YPPROC_MASTER:
+ (void) detail_ypstat();
+ (void) showxdr_string(YPMAXPEER, "Peer = %s");
+ case YPPROC_ORDER:
+ (void) detail_ypstat();
+ (void) showxdr_u_long("Order=%lu");
+ break;
+ case YPPROC_MAPLIST:
+ (void) detail_ypstat();
+ detail_ypmaplist();
+ break;
+ default:
+ break;
+ }
+}
+
+char *
+sum_ypxfrstat(status)
+ int status;
+{
+ static char buff [16];
+
+ switch (status) {
+ case 1: return ("Success");
+ case 2: return ("Master's version not newer");
+ case -1: return ("Can't find server for map");
+ case -2: return ("No such domain");
+ case -3: return ("Resource allocation failure");
+ case -4: return ("RPC failure talking to server");
+ case -5: return ("Can't get master address");
+ case -6: return ("NIS server/map db error");
+ case -7: return ("Bad arguments");
+ case -8: return ("Local dbm operation failed");
+ case -9: return ("Local file I/O operation failed");
+ case -10: return ("Map version skew during transfer");
+ case -11: return ("Can't send clear req to local ypserv");
+ case -12: return ("No local order number in map");
+ case -13: return ("Transfer error");
+ case -14: return ("Transfer request refused");
+ default:
+ (void) sprintf(buff, "(%d)", status);
+ return (buff);
+ }
+ /* NOTREACHED */
+}
+
+static int
+sum_ypstat(line)
+ char *line;
+{
+ ulong_t status;
+ char *str;
+ char buff[16];
+
+ status = getxdr_u_long();
+ switch (status) {
+ case YP_TRUE: str = "OK"; break;
+ case YP_NOMORE: str = "No more entries"; break;
+ case YP_FALSE: str = "Fail"; break;
+ case YP_NOMAP: str = "No such map"; break;
+ case YP_NODOM: str = "No such domain"; break;
+ case YP_NOKEY: str = "No such key"; break;
+ case YP_BADOP: str = "Invalid operation"; break;
+ case YP_BADDB: str = "Bad database"; break;
+ case YP_YPERR: str = "Server error"; break;
+ case YP_BADARGS:str = "Bad args"; break;
+ case YP_VERS: str = "Version mismatch"; break;
+ default: (void) sprintf(buff, "(%lu)", status);
+ str = buff;
+ break;
+ }
+ (void) strcpy(line, str);
+ return ((int)status);
+}
+
+static int
+detail_ypstat()
+{
+ ulong_t status;
+ char buff[32];
+
+
+ status = sum_ypstat(buff);
+ (void) sprintf(get_line(0, 0),
+ "Status = %d (%s)",
+ status, buff);
+
+ return ((int)status);
+}
+
+char *
+sum_ypmaplist()
+{
+ static char buff[YPMAXMAP + 1];
+ int maps = 0;
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, "%d+ maps", maps);
+ return (buff);
+ }
+
+ while (getxdr_long()) {
+ (void) getxdr_string(buff, YPMAXMAP);
+ maps++;
+ }
+
+ (void) sprintf(buff, "%d maps", maps);
+ return (buff);
+}
+
+void
+detail_ypmaplist()
+{
+ int maps = 0;
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ maps. (Frame is incomplete)",
+ maps);
+ return;
+ }
+
+ (void) sprintf(get_line(0, 0), "Map list");
+
+ while (getxdr_long()) {
+ (void) showxdr_string(YPMAXMAP, " %s");
+ maps++;
+ }
+
+ (void) sprintf(get_line(0, 0), "%d maps", maps);
+}
+
+char *
+ypbind_error(err)
+ int err;
+{
+ static char buff[16];
+
+ switch (err) {
+ case YPBIND_ERR_ERR: return ("Internal error");
+ case YPBIND_ERR_NOSERV: return ("Internal error");
+ case YPBIND_ERR_RESC: return ("Resource allocation fail");
+ default:
+ (void) sprintf(buff, "(%d)", err);
+ return (buff);
+ }
+ /* NOTREACHED */
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nlm.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nlm.c
new file mode 100644
index 0000000..45c23ee
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nlm.c
@@ -0,0 +1,1136 @@
+/*
+ * 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 (c) 1991, 1998, 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <setjmp.h>
+#include <string.h>
+
+#ifdef notdef
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/rpc_msg.h>
+#endif /* notdef */
+#include <rpcsvc/nlm_prot.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+extern void check_retransmit();
+static void interpret_nlm_1();
+static void interpret_nlm_3();
+static void interpret_nlm_4();
+static char *nameof_access();
+static char *nameof_mode();
+static char *nameof_stat();
+static char *nameof_stat4();
+static void show_cancargs();
+static void show_cancargs4();
+static void show_lock();
+static void show_lock4();
+static void show_lockargs();
+static void show_lockargs4();
+static void show_netobj();
+static void show_nlm_access();
+static void show_nlm_mode();
+static void show_notify();
+static void show_res();
+static void show_res4();
+static void show_share();
+static void show_shareargs();
+static void show_shareres();
+static void show_shareres4();
+static enum nlm_stats show_stat();
+static enum nlm4_stats show_stat4();
+static void show_testargs();
+static void show_testargs4();
+static void show_testres();
+static void show_testres4();
+static void show_unlockargs();
+static void show_unlockargs4();
+static void skip_netobj();
+static char *sum_lock();
+static char *sum_lock4();
+static char *sum_netobj();
+static char *sum_notify();
+static char *sum_share();
+
+void
+interpret_nlm(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ switch (vers) {
+ case 1: interpret_nlm_1(flags, type, xid, vers, proc, data, len);
+ break;
+ case 3: interpret_nlm_3(flags, type, xid, vers, proc, data, len);
+ break;
+ case 4: interpret_nlm_4(flags, type, xid, vers, proc, data, len);
+ break;
+ }
+}
+
+
+/* ------------ V E R S I O N 1 ---------------------------------- */
+
+static char *procnames_short_1[] = {
+ "Null1", /* 0 */
+ "TEST1", /* 1 */
+ "LOCK1", /* 2 */
+ "CANCEL1", /* 3 */
+ "UNLOCK1", /* 4 */
+ "GRANTED1", /* 5 */
+ "TEST MSG1", /* 6 */
+ "LOCK MSG1", /* 7 */
+ "CANCEL MSG1", /* 8 */
+ "UNLOCK MSG1", /* 9 */
+ "GRANTED MSG1", /* 10 */
+ "TEST RES1", /* 11 */
+ "LOCK RES1", /* 12 */
+ "CANCEL RES1", /* 13 */
+ "UNLOCK RES1", /* 14 */
+ "GRANTED RES1", /* 15 */
+};
+
+static char *procnames_long_1[] = {
+ "Null procedure", /* 0 */
+ "Test", /* 1 */
+ "Lock", /* 2 */
+ "Cancel", /* 3 */
+ "Unlock", /* 4 */
+ "Granted", /* 5 */
+ "Test message", /* 6 */
+ "Lock message", /* 7 */
+ "Cancel message", /* 8 */
+ "Unlock message", /* 9 */
+ "Granted message", /* 10 */
+ "Test result", /* 11 */
+ "Lock result", /* 12 */
+ "Cancel result", /* 13 */
+ "Unlock result", /* 14 */
+ "Granted result", /* 15 */
+};
+
+/* Highest procedure number that officially belongs to version 1. */
+#define MAXPROC_1 15
+
+/* ARGSUSED */
+static void
+interpret_nlm_1(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+
+ if (proc < 0 || proc > MAXPROC_1)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "NLM C %s",
+ procnames_short_1[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NLM_TEST:
+ case NLM_GRANTED:
+ case NLM_TEST_MSG:
+ case NLM_GRANTED_MSG:
+ /* testargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) getxdr_bool(); /* Excl */
+ (void) strcat(line, sum_lock());
+ break;
+ case NLM_LOCK:
+ case NLM_LOCK_MSG:
+ /* lockargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) getxdr_bool(); /* Block */
+ (void) getxdr_bool(); /* Excl */
+ (void) strcat(line, sum_lock());
+ break;
+ case NLM_CANCEL:
+ case NLM_CANCEL_MSG:
+ /* cancargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) getxdr_bool(); /* Block */
+ (void) getxdr_bool(); /* Excl */
+ (void) strcat(line, sum_lock());
+ break;
+ case NLM_UNLOCK:
+ case NLM_UNLOCK_MSG:
+ /* unlockargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, sum_lock());
+ break;
+ case NLM_TEST_RES:
+ /* testres */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat(getxdr_u_long()));
+ break;
+ case NLM_LOCK_RES:
+ case NLM_CANCEL_RES:
+ case NLM_UNLOCK_RES:
+ case NLM_GRANTED_RES:
+ /* res */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat(getxdr_u_long()));
+ break;
+ }
+ check_retransmit(line, (ulong_t)xid);
+ } else {
+ (void) sprintf(line, "NLM R %s",
+ procnames_short_1[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NLM_TEST:
+ /* testres */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat(getxdr_u_long()));
+ break;
+ case NLM_LOCK:
+ case NLM_CANCEL:
+ case NLM_UNLOCK:
+ case NLM_GRANTED:
+ /* res */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat(getxdr_u_long()));
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NLM: ", "Network Lock Manager", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long_1[proc]);
+ if (type == CALL) {
+ switch (proc) {
+ case NLM_TEST:
+ case NLM_GRANTED:
+ case NLM_TEST_MSG:
+ case NLM_GRANTED_MSG:
+ show_testargs();
+ break;
+ case NLM_LOCK:
+ case NLM_LOCK_MSG:
+ show_lockargs();
+ break;
+ case NLM_CANCEL:
+ case NLM_CANCEL_MSG:
+ show_cancargs();
+ break;
+ case NLM_UNLOCK:
+ case NLM_UNLOCK_MSG:
+ show_unlockargs();
+ break;
+ case NLM_TEST_RES:
+ show_testres();
+ break;
+ case NLM_LOCK_RES:
+ case NLM_CANCEL_RES:
+ case NLM_UNLOCK_RES:
+ case NLM_GRANTED_RES:
+ show_res();
+ break;
+ }
+ } else {
+ switch (proc) {
+ case NLM_TEST:
+ show_testres();
+ break;
+ case NLM_LOCK:
+ case NLM_CANCEL:
+ case NLM_UNLOCK:
+ case NLM_GRANTED:
+ show_res();
+ break;
+ case NLM_TEST_MSG:
+ case NLM_LOCK_MSG:
+ case NLM_CANCEL_MSG:
+ case NLM_UNLOCK_MSG:
+ case NLM_GRANTED_MSG:
+ case NLM_TEST_RES:
+ case NLM_LOCK_RES:
+ case NLM_CANCEL_RES:
+ case NLM_UNLOCK_RES:
+ case NLM_GRANTED_RES:
+ break;
+ }
+ }
+ show_trailer();
+ }
+}
+
+#define roundup(sz) ((sz / 4 + (sz % 4 > 0)) * 4)
+
+/*
+ * Skip a netobj.
+ * Make sure an integral number of words
+ * are skipped.
+ */
+static void
+skip_netobj()
+{
+ int sz = getxdr_u_long();
+
+ xdr_skip(roundup(sz));
+}
+
+static char *
+sum_netobj(handle)
+ char *handle;
+{
+ int i, l, sz;
+ int sum = 0;
+ static char buff[32];
+
+ sz = getxdr_u_long();
+ for (i = 0; i < sz; i += 4) {
+ l = getxdr_long();
+ sum ^= (l >> 16) ^ l;
+ }
+ (void) sprintf(buff, " %s=%04X", handle, sum & 0xFFFF);
+ return (buff);
+}
+
+static void
+show_netobj(fmt)
+ char *fmt;
+{
+ int sz, chunk;
+ char *p;
+ char buff[64];
+ int needspace;
+
+ sz = getxdr_u_long(); /* size of the netobj */
+
+ if (sz == 0) {
+ (void) sprintf(get_line(0, 0), fmt, "<null>");
+ } else {
+ needspace = sz > 16;
+ (void) strcpy(buff, fmt);
+ while (sz > 0) {
+ chunk = sz > 16 ? 16 : sz;
+ sz -= 16;
+ (void) showxdr_hex(chunk, buff);
+ /*
+ * For every line after the first, blank out
+ * everything in the format string before the "%s".
+ */
+ for (p = buff; *p != '%'; p++)
+ *p = ' ';
+ }
+ if (needspace)
+ show_space();
+ }
+}
+
+static char *
+sum_lock()
+{
+ static char buff[LM_MAXSTRLEN + 1];
+ char *cp = buff;
+ long id;
+ ulong_t off, len;
+
+ (void) getxdr_string(buff, LM_MAXSTRLEN); /* Caller */
+ (void) strcpy(buff, sum_netobj("FH")); /* Fh */
+ cp += strlen(buff);
+ skip_netobj(); /* Owner */
+ id = getxdr_long();
+ off = getxdr_u_long();
+ len = getxdr_u_long();
+ (void) sprintf(cp, " PID=%ld Region=%lu:%lu", id, off, len);
+ return (buff);
+}
+
+static void
+show_lock()
+{
+ showxdr_string(LM_MAXSTRLEN, "Caller = %s");
+ show_netobj("Filehandle = %s");
+ show_netobj("Lock owner = %s");
+ showxdr_long("Svid = %ld (process id)");
+ showxdr_u_long("Offset = %lu bytes");
+ showxdr_u_long("Length = %lu bytes");
+}
+
+static void
+show_cancargs()
+{
+ show_netobj("Cookie = %s");
+ showxdr_bool("Block = %s");
+ showxdr_bool("Exclusive = %s");
+ show_lock();
+}
+
+static void
+show_lockargs()
+{
+ show_netobj("Cookie = %s");
+ showxdr_bool("Block = %s");
+ showxdr_bool("Exclusive = %s");
+ show_lock();
+ showxdr_bool("Reclaim = %s");
+ showxdr_long("State = %ld");
+}
+
+static void
+show_unlockargs()
+{
+ show_netobj("Cookie = %s");
+ show_lock();
+}
+
+static void
+show_testargs()
+{
+ show_netobj("Cookie = %s");
+ showxdr_bool("Exclusive = %s");
+ show_lock();
+}
+
+static void
+show_res()
+{
+ show_netobj("Cookie = %s");
+ (void) show_stat();
+}
+
+static char *
+nameof_stat(s)
+ ulong_t s;
+{
+ switch ((enum nlm_stats) s) {
+ case nlm_granted: return ("granted");
+ case nlm_denied: return ("denied");
+ case nlm_denied_nolocks:return ("denied (no locks)");
+ case nlm_blocked: return ("blocked");
+ case nlm_denied_grace_period: return ("denied (grace period)");
+ case nlm_deadlck: return ("deadlock");
+ default: return ("?");
+ }
+}
+
+static enum nlm_stats
+show_stat()
+{
+ enum nlm_stats s;
+
+ s = (enum nlm_stats) getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Status = %d (%s)",
+ s, nameof_stat((ulong_t)s));
+
+ return (s);
+}
+
+static void
+show_testres()
+{
+ show_netobj("Cookie = %s");
+ if (show_stat() == nlm_denied) {
+ showxdr_bool("Exclusive = %s");
+ showxdr_long("Svid = %ld (process id)");
+ show_netobj("Owner handle = %s");
+ showxdr_u_long("Offset = %lu bytes");
+ showxdr_u_long("Length = %lu bytes");
+ }
+}
+
+
+/* ------------ V E R S I O N 3 ---------------------------------- */
+
+static char *procnames_short_3[] = {
+ "SHARE3", /* 20 */
+ "UNSHARE3", /* 21 */
+ "NM_LOCK3", /* 22 */
+ "FREEALL3", /* 23 */
+};
+
+static char *procnames_long_3[] = {
+ "Share", /* 20 */
+ "Unshare", /* 21 */
+ "Unmonitored lock", /* 22 */
+ "Free all", /* 23 */
+};
+
+/* Maximum procedure number for version 3. */
+#define MAXPROC_3 23
+
+static void
+interpret_nlm_3(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line, *pl;
+ ulong_t i;
+
+ if (proc < 0 || proc > MAXPROC_3)
+ return;
+
+ /*
+ * Version 3 is a superset of version 1
+ */
+ if (proc >= 0 && proc <= MAXPROC_1) {
+ interpret_nlm_1(flags, type, xid, vers, proc, data, len);
+ return;
+ }
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "NLM C %s",
+ procnames_short_3[proc-20]);
+ line += strlen(line);
+ switch (proc) {
+ case NLM_SHARE:
+ case NLM_UNSHARE:
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, sum_share());
+ break;
+ case NLM_NM_LOCK:
+ /* lockargs */
+ skip_netobj();
+ (void) getxdr_u_long(); /* Block */
+ (void) getxdr_u_long(); /* Excl */
+ (void) strcat(line, sum_lock());
+ break;
+ case NLM_FREE_ALL:
+ (void) sprintf(line,
+ " %s", sum_notify());
+ break;
+ }
+ check_retransmit(line, (ulong_t)xid);
+ } else {
+ (void) sprintf(line, "NLM R %s",
+ procnames_short_3[proc-20]);
+ line += strlen(line);
+ switch (proc) {
+ case NLM_SHARE:
+ case NLM_UNSHARE:
+ pl = sum_netobj("OH");
+ i = getxdr_u_long();
+ sprintf(line, "%s %s %ld",
+ pl, nameof_stat(i), getxdr_long());
+ break;
+ case NLM_NM_LOCK:
+ /* res */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat(getxdr_u_long()));
+ break;
+ case NLM_FREE_ALL:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NLM: ", "Network Lock Manager", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long_3[proc-20]);
+ if (type == CALL) {
+ switch (proc) {
+ case NLM_SHARE:
+ case NLM_UNSHARE:
+ show_shareargs();
+ break;
+ case NLM_NM_LOCK:
+ show_lockargs();
+ break;
+ case NLM_FREE_ALL:
+ show_notify();
+ break;
+ }
+ } else {
+ switch (proc) {
+ case NLM_SHARE:
+ case NLM_UNSHARE:
+ show_shareres();
+ break;
+ case NLM_NM_LOCK:
+ show_res();
+ break;
+ case NLM_FREE_ALL:
+ break;
+ }
+ }
+ show_trailer();
+ }
+}
+
+static char *
+nameof_mode(m)
+ uint_t m;
+{
+ switch ((enum fsh_mode) m) {
+ case fsm_DN: return ("deny none");
+ case fsm_DR: return ("deny read");
+ case fsm_DW: return ("deny write");
+ case fsm_DRW: return ("deny read/write");
+ default: return ("?");
+ }
+}
+
+static char *
+nameof_access(a)
+ uint_t a;
+{
+ switch ((enum fsh_access) a) {
+ case fsa_NONE: return ("?");
+ case fsa_R: return ("read only");
+ case fsa_W: return ("write only");
+ case fsa_RW: return ("read/write");
+ default: return ("?");
+ }
+}
+
+static void
+show_nlm_mode()
+{
+ enum fsh_mode m;
+
+ m = (enum fsh_mode) getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Mode = %d (%s)",
+ m, nameof_mode((uint_t)m));
+}
+
+static void
+show_nlm_access()
+{
+ enum fsh_access a;
+
+ a = (enum fsh_access) getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Access = %d (%s)",
+ a, nameof_access((uint_t)a));
+}
+
+static char *
+sum_share()
+{
+ static char buff[LM_MAXSTRLEN + 1];
+ char *cp = buff;
+ ulong_t mode, access;
+
+ (void) getxdr_string(buff, LM_MAXSTRLEN); /* Caller */
+ (void) strcpy(buff, sum_netobj("FH")); /* Fh */
+ cp += strlen(buff);
+ skip_netobj(); /* Owner */
+ mode = getxdr_u_long();
+ access = getxdr_u_long();
+ (void) sprintf(cp, " Mode=%lu Access=%lu", mode, access);
+ return (buff);
+}
+
+static void
+show_share()
+{
+ showxdr_string(LM_MAXSTRLEN, "Caller = %s");
+ show_netobj("Filehandle = %s");
+ show_netobj("Lock owner = %s");
+ show_nlm_mode();
+ show_nlm_access();
+}
+
+static void
+show_shareargs()
+{
+ show_netobj("Cookie = %s");
+ show_share();
+ showxdr_bool("Reclaim = %s");
+}
+
+static void
+show_shareres()
+{
+ show_netobj("Cookie = %s");
+ (void) show_stat();
+ showxdr_long("Sequence = %d");
+}
+
+static void
+show_notify()
+{
+ showxdr_string(LM_MAXNAMELEN, "Name = %s");
+ showxdr_long("State = %d");
+}
+
+#define NOTIFY_PAD (sizeof (" State=-2147483648") + 1)
+
+static char *
+sum_notify()
+{
+ static char buff[LM_MAXNAMELEN + NOTIFY_PAD];
+ char *cp = buff;
+ long state;
+
+ (void) getxdr_string(buff, LM_MAXNAMELEN);
+ cp += strlen(buff);
+ state = getxdr_long();
+ (void) sprintf(cp, " State=%ld", state);
+ return (buff);
+}
+
+/* ------------ V E R S I O N 4 ---------------------------------- */
+
+static char *procnames_short_4[] = {
+ "Null4", /* 0 */
+ "TEST4", /* 1 */
+ "LOCK4", /* 2 */
+ "CANCEL4", /* 3 */
+ "UNLOCK4", /* 4 */
+ "GRANTED4", /* 5 */
+ "TEST MSG4", /* 6 */
+ "LOCK MSG4", /* 7 */
+ "CANCEL MSG4", /* 8 */
+ "UNLOCK MSG4", /* 9 */
+ "GRANTED MSG4", /* 10 */
+ "TEST RES4", /* 11 */
+ "LOCK RES4", /* 12 */
+ "CANCEL RES4", /* 13 */
+ "UNLOCK RES4", /* 14 */
+ "GRANTED RES4", /* 15 */
+ "PROC 16 v4", /* 16 */
+ "PROC 17 v4", /* 17 */
+ "PROC 18 v4", /* 18 */
+ "PROC 19 v4", /* 19 */
+ "SHARE4", /* 20 */
+ "UNSHARE4", /* 21 */
+ "NM_LOCK4", /* 22 */
+ "FREEALL4", /* 23 */
+};
+
+static char *procnames_long_4[] = {
+ "Null procedure", /* 0 */
+ "Test", /* 1 */
+ "Lock", /* 2 */
+ "Cancel", /* 3 */
+ "Unlock", /* 4 */
+ "Granted", /* 5 */
+ "Test message", /* 6 */
+ "Lock message", /* 7 */
+ "Cancel message", /* 8 */
+ "Unlock message", /* 9 */
+ "Granted message", /* 10 */
+ "Test result", /* 11 */
+ "Lock result", /* 12 */
+ "Cancel result", /* 13 */
+ "Unlock result", /* 14 */
+ "Granted result", /* 15 */
+ "Procedure 16", /* 16 */
+ "Procedure 17", /* 17 */
+ "Procedure 18", /* 18 */
+ "Procedure 19", /* 19 */
+ "Share", /* 20 */
+ "Unshare", /* 21 */
+ "Unmonitored lock", /* 22 */
+ "Free all", /* 23 */
+};
+
+/* Maximum procedure number for version 4. */
+#define MAXPROC_4 23
+
+/* ARGSUSED */
+static void
+interpret_nlm_4(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char *pl;
+ ulong_t i;
+
+ if (proc < 0 || proc > MAXPROC_4)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "NLM C %s",
+ procnames_short_4[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NLM4_TEST:
+ case NLM4_GRANTED:
+ case NLM4_TEST_MSG:
+ case NLM4_GRANTED_MSG:
+ /* testargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) getxdr_bool(); /* Excl */
+ (void) strcat(line, sum_lock4());
+ break;
+ case NLM4_LOCK:
+ case NLM4_LOCK_MSG:
+ /* lockargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) getxdr_bool(); /* Block */
+ (void) getxdr_bool(); /* Excl */
+ (void) strcat(line, sum_lock4());
+ /* ignore reclaim, state fields */
+ break;
+ case NLM4_CANCEL:
+ case NLM4_CANCEL_MSG:
+ /* cancargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) getxdr_bool(); /* Block */
+ (void) getxdr_bool(); /* Excl */
+ (void) strcat(line, sum_lock4());
+ break;
+ case NLM4_UNLOCK:
+ case NLM4_UNLOCK_MSG:
+ /* unlockargs */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, sum_lock4());
+ break;
+ case NLM4_TEST_RES:
+ /* testres */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat4(getxdr_u_long()));
+ break;
+ case NLM4_LOCK_RES:
+ case NLM4_CANCEL_RES:
+ case NLM4_UNLOCK_RES:
+ case NLM4_GRANTED_RES:
+ /* res */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat4(getxdr_u_long()));
+ break;
+ case NLM4_SHARE:
+ case NLM4_UNSHARE:
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, sum_share());
+ break;
+ case NLM4_NM_LOCK:
+ /* lockargs */
+ skip_netobj(); /* Cookie */
+ (void) getxdr_bool(); /* Block */
+ (void) getxdr_bool(); /* Excl */
+ (void) strcat(line, sum_lock4());
+ /* skip reclaim & state fields */
+ break;
+ case NLM4_FREE_ALL:
+ (void) sprintf(line,
+ " %s", sum_notify());
+ break;
+ }
+ check_retransmit(line, (ulong_t)xid);
+ } else {
+ (void) sprintf(line, "NLM R %s",
+ procnames_short_4[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case NLM4_TEST:
+ /* testres */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat4(getxdr_u_long()));
+ break;
+ case NLM4_LOCK:
+ case NLM4_CANCEL:
+ case NLM4_UNLOCK:
+ case NLM4_GRANTED:
+ case NLM4_NM_LOCK:
+ /* res */
+ (void) strcat(line, sum_netobj("OH"));
+ (void) strcat(line, " ");
+ (void) strcat(line,
+ nameof_stat4(getxdr_u_long()));
+ break;
+ case NLM4_SHARE:
+ case NLM4_UNSHARE:
+ /* shareres */
+ pl = sum_netobj("OH");
+ i = getxdr_u_long();
+ sprintf(line, "%s %s %ld",
+ pl, nameof_stat4(i), getxdr_long());
+ break;
+ case NLM4_FREE_ALL:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("NLM: ", "Network Lock Manager", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long_4[proc]);
+ if (type == CALL) {
+ switch (proc) {
+ case NLM4_TEST:
+ case NLM4_GRANTED:
+ case NLM4_TEST_MSG:
+ case NLM4_GRANTED_MSG:
+ show_testargs4();
+ break;
+ case NLM4_LOCK:
+ case NLM4_LOCK_MSG:
+ case NLM4_NM_LOCK:
+ show_lockargs4();
+ break;
+ case NLM4_CANCEL:
+ case NLM4_CANCEL_MSG:
+ show_cancargs4();
+ break;
+ case NLM4_UNLOCK:
+ case NLM4_UNLOCK_MSG:
+ show_unlockargs4();
+ break;
+ case NLM4_TEST_RES:
+ show_testres4();
+ break;
+ case NLM4_LOCK_RES:
+ case NLM4_CANCEL_RES:
+ case NLM4_UNLOCK_RES:
+ case NLM4_GRANTED_RES:
+ show_res4();
+ break;
+ case NLM4_SHARE:
+ case NLM4_UNSHARE:
+ show_shareargs();
+ break;
+ case NLM4_FREE_ALL:
+ show_notify();
+ break;
+ }
+ } else {
+ switch (proc) {
+ case NLM4_TEST:
+ show_testres4();
+ break;
+ case NLM4_LOCK:
+ case NLM4_CANCEL:
+ case NLM4_UNLOCK:
+ case NLM4_GRANTED:
+ case NLM_NM_LOCK:
+ show_res4();
+ break;
+ case NLM4_TEST_MSG:
+ case NLM4_LOCK_MSG:
+ case NLM4_CANCEL_MSG:
+ case NLM4_UNLOCK_MSG:
+ case NLM4_GRANTED_MSG:
+ case NLM4_TEST_RES:
+ case NLM4_LOCK_RES:
+ case NLM4_CANCEL_RES:
+ case NLM4_UNLOCK_RES:
+ case NLM4_GRANTED_RES:
+ break;
+ case NLM_SHARE:
+ case NLM_UNSHARE:
+ show_shareres4();
+ break;
+ case NLM_FREE_ALL:
+ break;
+ }
+ }
+ show_trailer();
+ }
+}
+
+static char *
+sum_lock4()
+{
+ static char buff[LM_MAXSTRLEN + 1];
+ char *cp = buff;
+ long id;
+ u_longlong_t off, len;
+
+ (void) getxdr_string(buff, LM_MAXSTRLEN); /* Caller */
+ (void) strcpy(buff, sum_netobj("FH")); /* Fh */
+ cp += strlen(buff);
+ skip_netobj(); /* Owner */
+ id = getxdr_long();
+ off = getxdr_u_longlong();
+ len = getxdr_u_longlong();
+ (void) sprintf(cp, " PID=%ld Region=%llu:%llu", id, off, len);
+ return (buff);
+}
+
+static void
+show_lock4()
+{
+ showxdr_string(LM_MAXSTRLEN, "Caller = %s");
+ show_netobj("Filehandle = %s");
+ show_netobj("Lock owner = %s");
+ showxdr_long("Svid = %ld (process id)");
+ showxdr_u_longlong("Offset = %llu bytes");
+ showxdr_u_longlong("Length = %llu bytes");
+}
+
+static void
+show_cancargs4()
+{
+ show_netobj("Cookie = %s");
+ showxdr_bool("Block = %s");
+ showxdr_bool("Exclusive = %s");
+ show_lock4();
+}
+
+static void
+show_lockargs4()
+{
+ show_netobj("Cookie = %s");
+ showxdr_bool("Block = %s");
+ showxdr_bool("Exclusive = %s");
+ show_lock4();
+ showxdr_bool("Reclaim = %s");
+ showxdr_long("State = %ld");
+}
+
+static void
+show_unlockargs4()
+{
+ show_netobj("Cookie = %s");
+ show_lock4();
+}
+
+static void
+show_testargs4()
+{
+ show_netobj("Cookie = %s");
+ showxdr_bool("Exclusive = %s");
+ show_lock4();
+}
+
+static void
+show_res4()
+{
+ show_netobj("Cookie = %s");
+ (void) show_stat4();
+}
+
+static char *
+nameof_stat4(s)
+ ulong_t s;
+{
+ switch ((enum nlm4_stats) s) {
+ case nlm4_granted: return ("granted");
+ case nlm4_denied: return ("denied");
+ case nlm4_denied_nolocks:return ("denied (no locks)");
+ case nlm4_blocked: return ("blocked");
+ case nlm4_denied_grace_period: return ("denied (grace period)");
+ case nlm4_deadlck: return ("deadlock");
+ case nlm4_rofs: return ("read-only fs");
+ case nlm4_stale_fh: return ("stale fh");
+ case nlm4_fbig: return ("file too big");
+ case nlm4_failed: return ("failed");
+ default: return ("?");
+ }
+}
+
+static enum nlm4_stats
+show_stat4()
+{
+ enum nlm4_stats s;
+
+ s = (enum nlm4_stats) getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Status = %d (%s)",
+ s, nameof_stat4((ulong_t)s));
+
+ return (s);
+}
+
+static void
+show_testres4()
+{
+ show_netobj("Cookie = %s");
+ if (show_stat() == nlm_denied) {
+ showxdr_bool("Exclusive = %s");
+ showxdr_long("Svid = %ld (process id)");
+ show_netobj("Owner handle = %s");
+ showxdr_u_longlong("Offset = %llu bytes");
+ showxdr_u_longlong("Length = %llu bytes");
+ }
+}
+
+static void
+show_shareres4()
+{
+ show_netobj("Cookie = %s");
+ (void) show_stat4();
+ showxdr_long("Sequence = %d");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ntp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ntp.c
new file mode 100644
index 0000000..4f6fae3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ntp.c
@@ -0,0 +1,505 @@
+/*
+ * 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");
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.c
new file mode 100644
index 0000000..84234dd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.c
@@ -0,0 +1,772 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+#include "snoop.h"
+#include "snoop_ospf.h"
+#include "snoop_ospf6.h"
+
+extern char *dlc_header;
+static char *sum_line;
+
+char *ospf_types[] = {
+ "umd", /* 0 */
+ "Hello", /* 1 */
+ "DD", /* 2 */
+ "LSReq", /* 3 */
+ "LSUpd", /* 4 */
+ "LSAck", /* 5 */
+};
+
+static char *ospf_authtypes[] = {
+ "None", /* 0 */
+ "simple", /* 1 */
+ "md5", /* 2 */
+};
+
+const struct bits ospf_rla_flag_bits[] = {
+ { RLA_FLAG_B, "B" },
+ { RLA_FLAG_E, "E" },
+ { RLA_FLAG_V, "V" },
+ { RLA_FLAG_W, "W" },
+ { 0, NULL }
+};
+
+const struct bits ospf_db_flags_bits[] = {
+ { OSPF_DB_INIT, "I" },
+ { OSPF_DB_MORE, "M" },
+ { OSPF_DB_MASTER, "MS" },
+ { 0, NULL }
+};
+
+const struct bits ospf_option_bits[] = {
+ { OSPF_OPTION_T, "T" },
+ { OSPF_OPTION_E, "E" },
+ { OSPF_OPTION_MC, "MC" },
+ { 0, NULL }
+};
+
+static int interpret_ospf_hello(int, struct ospfhdr *, int);
+static void ospf_print_ls_type(int, uint32_t, struct in_addr, struct in_addr);
+static void interpret_ospf_lsa_hdr(int, struct lsa_hdr *);
+static int interpret_ospf_lsa(int flags, struct lsa *lsa, uchar_t *);
+
+char *
+ospf_print_bits(const struct bits *bp, uchar_t options)
+{
+ static char bitstring[32];
+
+ bitstring[0] = '\0';
+ do {
+ if (options & bp->bit) {
+ strcat(bitstring, bp->str);
+ strcat(bitstring, "/");
+ }
+ } while ((++bp)->bit);
+
+ /* wipe out the trailing "/" */
+ bitstring[strlen(bitstring) - 1] = '\0';
+ return (bitstring);
+}
+
+char *
+ospf_print_lsa_age(long age)
+{
+ long sec, mins, hour;
+ static char lsa_age[16];
+
+ sec = age % 60;
+ mins = (age / 60) % 60;
+ hour = age / 3600;
+ if (hour != 0)
+ snprintf(lsa_age, sizeof (lsa_age), "%u:%02u:%02u",
+ hour, mins, sec);
+ else if (mins != 0)
+ snprintf(lsa_age, sizeof (lsa_age), "%u:%02u", mins, sec);
+ else
+ snprintf(lsa_age, sizeof (lsa_age), "%u", sec);
+ return (lsa_age);
+}
+
+static int
+interpret_ospf_hello(int flags, struct ospfhdr *op, int fraglen)
+{
+ struct in_addr *nbr;
+ int j;
+
+ if (fraglen < OSPF_MIN_HEADER_SIZE + OSPF_MIN_HELLO_HEADER_SIZE)
+ return (-1); /* truncated packet */
+
+ if (flags & F_SUM) {
+ if (op->ospf_hello.hello_dr.s_addr != 0) {
+ (void) sprintf(sum_line, "DR=%s ",
+ inet_ntoa(op->ospf_hello.hello_dr));
+ }
+ sum_line += strlen(sum_line);
+ if (op->ospf_hello.hello_bdr.s_addr != 0) {
+ (void) sprintf(sum_line, "BDR=%s ",
+ inet_ntoa(op->ospf_hello.hello_bdr));
+ }
+ sum_line += strlen(sum_line);
+ nbr = op->ospf_hello.hello_neighbor;
+ j = 0;
+ while ((uchar_t *)nbr < ((uchar_t *)op + fraglen)) {
+ if ((uchar_t *)nbr + sizeof (struct in_addr) >
+ ((uchar_t *)op + fraglen))
+ return (-1); /* truncated */
+ j++;
+ ++nbr;
+ }
+ (void) sprintf(sum_line, "%d nbrs", j);
+ sum_line += strlen(sum_line);
+
+ }
+ if (flags & F_DTAIL) {
+ show_header("OSPF HELLO: ", "Hello Packet",
+ ntohs(op->ospf_len));
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Options = %s", ospf_print_bits(ospf_option_bits,
+ op->ospf_hello.hello_options));
+ (void) snprintf(get_line(0, 0), get_line_remain(), "Mask = %s",
+ inet_ntoa(op->ospf_hello.hello_mask));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Hello interval = %d",
+ ntohs(op->ospf_hello.hello_helloint));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Priority = %d", op->ospf_hello.hello_priority);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Dead interval = %u", ntohl(op->ospf_hello.hello_deadint));
+ if (op->ospf_hello.hello_dr.s_addr != 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Designated Router = %s",
+ inet_ntoa(op->ospf_hello.hello_dr));
+ }
+ if (op->ospf_hello.hello_bdr.s_addr != 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Backup Designated Router = %s",
+ inet_ntoa(op->ospf_hello.hello_bdr));
+ }
+ nbr = op->ospf_hello.hello_neighbor;
+ while ((uchar_t *)nbr < ((uchar_t *)op + fraglen)) {
+ if ((uchar_t *)nbr + sizeof (struct in_addr) >
+ ((uchar_t *)op + fraglen))
+ return (-1); /* truncated */
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Neighbor: %s", inet_ntoa(*nbr));
+ ++nbr;
+ }
+ }
+ return (fraglen);
+}
+
+static void
+ospf_print_ls_type(int flags, uint32_t ls_type, struct in_addr ls_stateid,
+ struct in_addr ls_router)
+{
+ switch (ls_type) {
+ case LS_TYPE_ROUTER:
+ if (flags & F_SUM) {
+ sprintf(sum_line, " rtr %s ", inet_ntoa(ls_router));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Router LSA; Router = %s ", inet_ntoa(ls_router));
+ }
+ break;
+ case LS_TYPE_NETWORK:
+ if (flags & F_SUM) {
+ sprintf(sum_line, " net dr %s ", inet_ntoa(ls_router));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, "if %s ", inet_ntoa(ls_stateid));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Network LSA Router = %s ", inet_ntoa(ls_router));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Interface = %s ",
+ inet_ntoa(ls_stateid));
+ }
+ break;
+ case LS_TYPE_SUM_IP:
+ if (flags & F_SUM) {
+ sprintf(sum_line, " sum %s ", inet_ntoa(ls_stateid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, "abr %s ", inet_ntoa(ls_router));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Summary LSA IP = %s ", inet_ntoa(ls_stateid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Area Border Router = %s ",
+ inet_ntoa(ls_router));
+ }
+ break;
+ case LS_TYPE_SUM_ABR:
+ if (flags & F_SUM) {
+ sprintf(sum_line, "abr %s ", inet_ntoa(ls_stateid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, "asbr %s ", inet_ntoa(ls_router));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "ASBR Summary abr = %s ", inet_ntoa(ls_stateid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " asbr = %s ", inet_ntoa(ls_router));
+ }
+ break;
+ case LS_TYPE_ASE:
+ if (flags & F_SUM) {
+ sprintf(sum_line, " ase %s", inet_ntoa(ls_stateid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, " asbr %s", inet_ntoa(ls_router));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "AS External LSA ase = %s ", inet_ntoa(ls_stateid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " asbr = %s ", inet_ntoa(ls_router));
+ }
+
+ break;
+ case LS_TYPE_GROUP:
+ if (flags & F_SUM) {
+ sprintf(sum_line, " group %s", inet_ntoa(ls_stateid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, " rtr %s", inet_ntoa(ls_router));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Group LSA %s ", inet_ntoa(ls_stateid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " rtr = %s ", inet_ntoa(ls_router));
+ }
+ break;
+ default:
+ if (flags & F_SUM) {
+ sprintf(sum_line, " unknown LSA type %d", ls_type);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Unknown LSA type %d", ls_type);
+ }
+ break;
+ }
+}
+
+static void
+interpret_ospf_lsa_hdr(int flags, struct lsa_hdr *lsah)
+{
+ if (flags & F_SUM)
+ return;
+
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Options = %s",
+ ospf_print_bits(ospf_option_bits, lsah->ls_options));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Sequence = %X ", ntohl(lsah->ls_seq));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Age = %X ", ospf_print_lsa_age(ntohs(lsah->ls_age)));
+ }
+
+ ospf_print_ls_type(flags, lsah->ls_type, lsah->ls_stateid,
+ lsah->ls_router);
+
+}
+
+#define TRUNC(addr) ((uchar_t *)(addr) > fragend)
+static int
+interpret_ospf_lsa(int flags, struct lsa *lsa, uchar_t *fragend)
+{
+ uchar_t *ls_end;
+ int rla_count, k;
+ struct rlalink *rl;
+ struct tos_metric *tosp;
+ struct in_addr *addr;
+ uint32_t *tosmetric;
+ struct aslametric *am;
+ uint32_t tm;
+ int tos, metric;
+
+ interpret_ospf_lsa_hdr(flags, &lsa->ls_hdr);
+
+ ls_end = (uchar_t *)lsa + ntohs(lsa->ls_hdr.ls_length);
+
+ if (TRUNC(ls_end))
+ return (-1);
+
+ switch (lsa->ls_hdr.ls_type) {
+
+ case LS_TYPE_ROUTER:
+ if (TRUNC(&lsa->lsa_un.un_rla.rla_flags))
+ return (-1);
+
+ if (flags & F_DTAIL) {
+ (void) ospf_print_bits(ospf_rla_flag_bits,
+ lsa->lsa_un.un_rla.rla_flags);
+ }
+
+ if (TRUNC(&lsa->lsa_un.un_rla.rla_count))
+ return (-1);
+ rla_count = ntohs(lsa->lsa_un.un_rla.rla_count);
+
+ rl = lsa->lsa_un.un_rla.rla_link;
+ if (TRUNC(rl))
+ return (-1);
+
+ while (rla_count-- != 0) {
+ if (TRUNC((uchar_t *)rl + sizeof (*rl)))
+ return (-1);
+ switch (rl->link_type) {
+ case RLA_TYPE_VIRTUAL:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Virtual Link");
+ }
+ /* fall through */
+ case RLA_TYPE_ROUTER:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Neighbor = %s",
+ inet_ntoa(rl->link_id));
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Interface = %s",
+ inet_ntoa(rl->link_data));
+ }
+ break;
+ case RLA_TYPE_TRANSIT:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Designated Router = %s",
+ inet_ntoa(rl->link_id));
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Interface = %s",
+ inet_ntoa(rl->link_data));
+ }
+ break;
+ case RLA_TYPE_STUB:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Network = %s",
+ inet_ntoa(rl->link_id));
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Mask = %s",
+ inet_ntoa(rl->link_data));
+ }
+ break;
+ default:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Unknown link type %d",
+ rl->link_type);
+ }
+
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "TOS 0 metric = %d",
+ ntohs(rl->link_tos0metric));
+ }
+ tosp = (struct tos_metric *)(
+ (uchar_t *)rl + sizeof (rl->link_tos0metric));
+ for (k = 0; k > (int)rl->link_toscount; ++k, ++tosp) {
+ if (TRUNC(tosp))
+ return (-1);
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "TOS %d metric = %d",
+ tosp->tos_type,
+ ntohs(tosp->tos_metric));
+ }
+
+ }
+ rl = (struct rlalink *)((uchar_t *)(rl + 1) +
+ ((rl->link_toscount) * sizeof (*tosp)));
+ if (TRUNC(rl))
+ return (-1); /* truncated */
+ }
+ break;
+ case LS_TYPE_NETWORK:
+
+ if (TRUNC(&lsa->lsa_un.un_nla.nla_mask))
+ return (-1);
+
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Mask = %s",
+ inet_ntoa(lsa->lsa_un.un_nla.nla_mask));
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Routers:");
+ }
+ addr = lsa->lsa_un.un_nla.nla_router;
+ while ((uchar_t *)addr < ls_end) {
+ if ((uchar_t *)addr + sizeof (struct in_addr) > ls_end)
+ return (-1); /* truncated */
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ "\t%s", inet_ntoa(*addr));
+ }
+ ++addr;
+ }
+ break;
+ case LS_TYPE_SUM_IP:
+
+ if (TRUNC((uchar_t *)&lsa->lsa_un.un_sla.sla_mask +
+ sizeof (struct in_addr)))
+ return (-1);
+
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(), "Mask = %s",
+ inet_ntoa(lsa->lsa_un.un_sla.sla_mask));
+ }
+ /* FALLTHROUGH */
+ case LS_TYPE_SUM_ABR:
+ if (TRUNC(&lsa->lsa_un.un_sla.sla_tosmetric))
+ return (-1);
+ tosmetric = lsa->lsa_un.un_sla.sla_tosmetric;
+ while ((uchar_t *)tosmetric < ls_end) {
+ if ((uchar_t *)tosmetric + sizeof (tm) > fragend)
+ return (-1); /* truncated */
+ tm = ntohl(*tosmetric);
+ tos = (tm & SLA_MASK_TOS) >> SLA_SHIFT_TOS;
+ metric = tm & SLA_MASK_METRIC;
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ " tos %d metric %d", tos, metric);
+ }
+ ++tosmetric;
+ }
+ break;
+ case LS_TYPE_ASE:
+ if (TRUNC(&lsa->lsa_un.un_asla.asla_mask))
+ return (-1);
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(), "Mask = %s",
+ inet_ntoa(lsa->lsa_un.un_asla.asla_mask));
+ }
+ am = lsa->lsa_un.un_asla.asla_metric;
+ while ((uchar_t *)am < ls_end) {
+ if ((uchar_t *)am + sizeof (tm) > fragend)
+ return (-1); /* truncated */
+ tm = ntohl(am->asla_tosmetric);
+ tos = (tm & ASLA_MASK_TOS) >> ASLA_SHIFT_TOS;
+ metric = tm & ASLA_MASK_METRIC;
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ " type %d tos %d metric %d",
+ (tm & ASLA_FLAG_EXTERNAL) ? 2 : 1,
+ tos, metric);
+ }
+ if (am->asla_forward.s_addr != 0) {
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0),
+ get_line_remain(), " Forward %s",
+ inet_ntoa(am->asla_forward));
+ }
+ }
+ if (am->asla_tag.s_addr != 0) {
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0),
+ get_line_remain(), " Tag %s",
+ inet_ntoa(am->asla_tag));
+ }
+ }
+ ++am;
+ }
+ break;
+ default:
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ " Unknown LSA type %d", lsa->ls_hdr.ls_type);
+
+ }
+ break;
+ }
+ return (0);
+}
+#undef TRUNC
+
+int
+interpret_ospf(int flags, struct ospfhdr *ospf, int iplen, int fraglen)
+{
+ int nlsa, nlsah = 0;
+ struct lsa_hdr *lsah;
+ struct lsr *lsr;
+ struct lsa *lsa;
+ boolean_t trunc = B_FALSE;
+
+ if ((fraglen < OSPF_MIN_HEADER_SIZE) ||
+ (fraglen < ntohs(ospf->ospf_len)))
+ return (fraglen); /* incomplete header */
+
+ if (fraglen > ntohs(ospf->ospf_len))
+ fraglen = ntohs(ospf->ospf_len);
+
+
+ if (ospf->ospf_type > OSPF_TYPE_MAX) {
+ if (flags & F_SUM) {
+ (void) sprintf(sum_line, "Unknown OSPF TYPE %d \n",
+ ospf->ospf_type);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_SUM) {
+ show_header("OSPF: ", "OSPF Header", fraglen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Unknown OSPF Type = %d", ospf->ospf_type);
+ }
+ return (fraglen);
+ }
+
+ if (flags & F_SUM) {
+ sum_line = (char *)get_sum_line();
+ (void) sprintf(sum_line, "OSPF %s RTRID=%s ",
+ ospf_types[ospf->ospf_type],
+ inet_ntoa(ospf->ospf_routerid));
+ sum_line += strlen(sum_line);
+ (void) sprintf(sum_line, "AREA=%s LEN=%d ",
+ inet_ntoa(ospf->ospf_areaid),
+ ntohs((ushort_t)ospf->ospf_len));
+ sum_line += strlen(sum_line);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("OSPF: ", "OSPF Header", fraglen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Version = %d", ospf->ospf_version);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Type = %s", ospf_types[ospf->ospf_type]);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Router ID = %s", inet_ntoa(ospf->ospf_routerid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Area ID = %s", inet_ntoa(ospf->ospf_areaid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Checksum = 0x%x", ospf->ospf_chksum);
+
+ if (ospf->ospf_authtype > OSPF_AUTH_TYPE_MAX) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Auth = %d (unknown auth type)",
+ ospf->ospf_authtype);
+ } else {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Auth = %s", ospf_authtypes[ospf->ospf_authtype]);
+ }
+ }
+
+ if (ospf->ospf_version != 2) {
+ if (ospf->ospf_version == 3) {
+ if (flags & F_DTAIL)
+ snprintf(get_line(0, 0), get_line_remain(),
+ "ospfv3 packet in ipv4 header");
+ return (interpret_ospf6(flags, ospf, iplen, fraglen));
+ } else {
+ return (fraglen);
+ }
+ }
+
+ switch (ospf->ospf_type) {
+ case OSPF_TYPE_HELLO:
+ if (interpret_ospf_hello(flags, ospf, fraglen) < 0)
+ trunc = B_TRUE;
+ break;
+
+ case OSPF_TYPE_DB:
+ if (fraglen < OSPF_MIN_HEADER_SIZE + OSPF_MIN_DB_HEADER_SIZE) {
+ trunc = B_TRUE;
+ break;
+ }
+ if (flags & F_SUM) {
+ sprintf(sum_line, " %s %s S %X", ospf_print_bits(
+ ospf_option_bits, ospf->ospf_db.db_options),
+ ospf_print_bits(ospf_db_flags_bits,
+ ospf->ospf_db.db_flags),
+ ntohl(ospf->ospf_db.db_seq));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ show_header("OSPF DB: ", "Database Description Packet",
+ fraglen);
+ show_space();
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Options = %s", ospf_print_bits(
+ ospf_option_bits, ospf->ospf_db.db_options));
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Flags = %s", ospf_print_bits(
+ ospf_db_flags_bits, ospf->ospf_db.db_flags));
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Sequence = 0x%X", ntohl(ospf->ospf_db.db_seq));
+ /* Print all the LS advs */
+ lsah = ospf->ospf_db.db_lshdr;
+ while ((uchar_t *)lsah < ((uchar_t *)ospf + fraglen)) {
+ if ((uchar_t *)lsah + sizeof (struct lsa_hdr) >
+ ((uchar_t *)ospf + fraglen)) {
+ trunc = B_TRUE;
+ break;
+ }
+ interpret_ospf_lsa_hdr(flags, lsah);
+ ++lsah;
+ }
+ }
+ break;
+
+ case OSPF_TYPE_LSR:
+ if (fraglen < OSPF_MIN_HEADER_SIZE + OSPF_MIN_LSR_HEADER_SIZE) {
+ trunc = B_TRUE;
+ break;
+ }
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Link State Request Packet");
+ }
+ lsr = ospf->ospf_lsr;
+ while ((uchar_t *)lsr < ((uchar_t *)ospf + fraglen)) {
+ if ((uchar_t *)lsr + sizeof (struct lsr) >
+ ((uchar_t *)ospf + fraglen)) {
+ trunc = B_TRUE;
+ break;
+ }
+ if (flags & F_SUM) {
+ nlsah++;
+ }
+ if (flags & F_DTAIL) {
+ ospf_print_ls_type(flags, ntohl(lsr->ls_type),
+ lsr->ls_stateid, lsr->ls_router);
+ }
+ ++lsr;
+ }
+ if (flags & F_SUM) {
+ sprintf(sum_line, " %d LSAs", nlsah);
+ sum_line += strlen(sum_line);
+ }
+ break;
+
+ case OSPF_TYPE_LSU:
+ if (fraglen < OSPF_MIN_HEADER_SIZE + OSPF_MIN_LSU_HEADER_SIZE) {
+ trunc = B_TRUE;
+ break;
+ }
+ if (flags & F_DTAIL) {
+ show_header("OSPF LSU: ", "Link State Update Packet",
+ fraglen);
+ show_space();
+ }
+ lsa = ospf->ospf_lsu.lsu_lsa;
+ nlsa = ntohl(ospf->ospf_lsu.lsu_count);
+ if (flags & F_SUM) {
+ sprintf(sum_line, "%d LSAs", nlsa);
+ sum_line += strlen(sum_line);
+ break;
+ }
+ while (nlsa-- != 0) {
+ uchar_t *fragend = (uchar_t *)ospf + fraglen;
+ if (((uchar_t *)lsa >= fragend) ||
+ ((uchar_t *)lsa + sizeof (struct lsa_hdr) >
+ fragend) ||
+ ((uchar_t *)lsa + ntohs(lsa->ls_hdr.ls_length) >
+ fragend)) {
+ trunc = B_TRUE;
+ break;
+ }
+
+ if (interpret_ospf_lsa(flags, lsa, fragend) < 0) {
+ trunc = B_TRUE;
+ break;
+ }
+ lsa = (struct lsa *)((uchar_t *)lsa +
+ ntohs(lsa->ls_hdr.ls_length));
+ }
+
+ break;
+
+ case OSPF_TYPE_LSA:
+ if (flags & F_DTAIL) {
+ show_header("OSPF LSA: ", "Link State Ack Packet",
+ fraglen);
+ show_space();
+ }
+ lsah = ospf->ospf_lsa.lsa_lshdr;
+ nlsah = 0;
+ while ((uchar_t *)lsah < ((uchar_t *)ospf + fraglen)) {
+ if ((uchar_t *)lsah + sizeof (struct lsa_hdr) >
+ ((uchar_t *)ospf + fraglen)) {
+ trunc = B_TRUE;
+ break;
+ }
+ nlsah++;
+ if (flags & F_DTAIL)
+ interpret_ospf_lsa_hdr(flags, lsah);
+ ++lsah;
+ }
+ if (flags & F_SUM) {
+ sprintf(sum_line, "%d LSAs", nlsah);
+ sum_line += strlen(sum_line);
+ }
+ break;
+
+ default:
+ /* NOTREACHED */
+ break;
+ }
+ if (trunc) {
+ if (flags & F_SUM) {
+ sprintf(sum_line, "--truncated");
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL)
+ snprintf(get_line(0, 0), get_line_remain(),
+ "--truncated");
+ }
+
+ return (fraglen);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.h
new file mode 100644
index 0000000..8bf4d3d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf.h
@@ -0,0 +1,280 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _OSPF_H
+#define _OSPF_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Definitions for parsing OSPF packets (RFC 2328 and RFC 2740)
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define OSPF_TYPE_UMD 0 /* UMD's special monitoring packets */
+#define OSPF_TYPE_HELLO 1 /* Hello */
+#define OSPF_TYPE_DB 2 /* Database Description */
+#define OSPF_TYPE_LSR 3 /* Link State Request */
+#define OSPF_TYPE_LSU 4 /* Link State Update */
+#define OSPF_TYPE_LSA 5 /* Link State Ack */
+#define OSPF_TYPE_MAX 6
+
+extern char *ospf_types[];
+struct bits {
+ uint32_t bit;
+ const char *str;
+};
+char *ospf_print_bits(const struct bits *, uchar_t);
+char *ospf_print_lsa_age(long);
+
+/* Options *_options */
+#define OSPF_OPTION_T 0x01 /* RFC 2328 T bit: TOS support */
+#define OSPF_OPTION_E 0x02 /* E bit: External routes advertised */
+#define OSPF_OPTION_MC 0x04 /* MC bit: Multicast capable */
+#define OSPF_OPTION_N 0x08 /* N bit: For type-7 LSA */
+#define OSPF_OPTION_R 0x10 /* R bit: Router bit */
+#define OSPF_OPTION_DC 0x20 /* DC bit: Demand circuits */
+
+#define OSPF_OPTION_V6 0x01 /* RFC 2740 V6 bit */
+
+/* ospf_authtype */
+#define OSPF_AUTH_NONE 0 /* No auth-data */
+#define OSPF_AUTH_SIMPLE 1 /* Simple password */
+#define OSPF_AUTH_MD5 2 /* MD5 authentication */
+#define OSPF_AUTH_MD5_LEN 16 /* length of MD5 authentication */
+
+#define OSPF_AUTH_TYPE_MAX 3
+
+/* db_flags */
+#define OSPF_DB_INIT 0x04 /* "I" */
+#define OSPF_DB_MORE 0x02 /* "M" */
+#define OSPF_DB_MASTER 0x01 /* "MS" */
+
+
+/* ls_type */
+#define LS_TYPE_ROUTER 1 /* router link */
+#define LS_TYPE_NETWORK 2 /* network link */
+#define LS_TYPE_SUM_IP 3 /* summary link */
+#define LS_TYPE_SUM_ABR 4 /* summary area link */
+#define LS_TYPE_ASE 5 /* ASE */
+#define LS_TYPE_GROUP 6 /* Group membership (multicast */
+ /* extensions 23 July 1991) */
+#define LS_TYPE_TYPE7 7 /* Type 7 LSA */
+#define LS_TYPE_LINK 8 /* Link LSA */
+#define LS_TYPE_INTRA_AP 9 /* Intra-Area-Prefix */
+#define LS_TYPE_MAX 10
+#define LS_TYPE_MASK 0x1fff
+
+#define LS_TYPE_INTER_AP 3 /* RFC 2740 Inter-Area-Prefix */
+#define LS_TYPE_INTER_AR 4 /* RFC 2740 Inter-Area-Router */
+
+#define LS6_SCOPE_LINKLOCAL 0x0000
+#define LS6_SCOPE_AREA 0x2000
+#define LS6_SCOPE_AS 0x4000
+#define LS6_SCOPE_MASK 0x6000
+
+/* rla_link.link_type */
+#define RLA_TYPE_ROUTER 1 /* point-to-point to another router */
+#define RLA_TYPE_TRANSIT 2 /* connection to transit network */
+#define RLA_TYPE_STUB 3 /* connection to stub network */
+#define RLA_TYPE_VIRTUAL 4 /* virtual link */
+
+/* rla_flags */
+#define RLA_FLAG_B 0x01
+#define RLA_FLAG_E 0x02
+#define RLA_FLAG_V 0x04
+#define RLA_FLAG_W 0x08
+
+
+/* sla_tosmetric breakdown */
+#define SLA_MASK_TOS 0x7f000000
+#define SLA_MASK_METRIC 0x00ffffff
+#define SLA_SHIFT_TOS 24
+
+/* asla_tosmetric breakdown */
+#define ASLA_FLAG_EXTERNAL 0x80000000
+#define ASLA_MASK_TOS 0x7f000000
+#define ASLA_SHIFT_TOS 24
+#define ASLA_MASK_METRIC 0x00ffffff
+
+/* multicast vertex type */
+#define MCLA_VERTEX_ROUTER 1
+#define MCLA_VERTEX_NETWORK 2
+
+/* link state advertisement header */
+struct lsa_hdr {
+ ushort_t ls_age;
+ uchar_t ls_options;
+ uchar_t ls_type;
+ struct in_addr ls_stateid;
+ struct in_addr ls_router;
+ uint32_t ls_seq;
+ ushort_t ls_chksum;
+ ushort_t ls_length;
+};
+
+/* link state advertisement */
+struct lsa {
+ struct lsa_hdr ls_hdr;
+
+ /* Link state types */
+ union {
+ /* Router links advertisements */
+ struct {
+ uchar_t rla_flags;
+ uchar_t rla_zero[1];
+ ushort_t rla_count;
+ struct rlalink {
+ struct in_addr link_id;
+ struct in_addr link_data;
+ uchar_t link_type;
+ uchar_t link_toscount;
+ ushort_t link_tos0metric;
+ } rla_link[1]; /* may repeat */
+ } un_rla;
+
+ /* Network links advertisements */
+ struct {
+ struct in_addr nla_mask;
+ struct in_addr nla_router[1]; /* may repeat */
+ } un_nla;
+
+ /* Summary links advertisements */
+ struct {
+ struct in_addr sla_mask;
+ uint32_t sla_tosmetric[1]; /* may repeat */
+ } un_sla;
+
+ /* AS external links advertisements */
+ struct {
+ struct in_addr asla_mask;
+ struct aslametric {
+ uint32_t asla_tosmetric;
+ struct in_addr asla_forward;
+ struct in_addr asla_tag;
+ } asla_metric[1]; /* may repeat */
+ } un_asla;
+
+ /* Multicast group membership */
+ struct mcla {
+ uint32_t mcla_vtype;
+ struct in_addr mcla_vid;
+ } un_mcla[1];
+ } lsa_un;
+};
+
+/*
+ * TOS metric struct (will be 0 or more in router links update)
+ */
+struct tos_metric {
+ uchar_t tos_type;
+ uchar_t tos_zero;
+ ushort_t tos_metric;
+};
+
+/*
+ * OSPF minimum header sizes
+ */
+#define OSPF_AUTH_SIZE 8
+#define OSPF_MIN_HEADER_SIZE 24
+#define OSPF6_MIN_HEADER_SIZE 16
+#define OSPF_MIN_HELLO_HEADER_SIZE 20
+#define OSPF_MIN_DB_HEADER_SIZE 8
+#define OSPF6_MIN_DB_HEADER_SIZE 12
+#define OSPF_MIN_LSR_HEADER_SIZE 12
+#define OSPF_MIN_LSU_HEADER_SIZE 4
+
+/*
+ * ospf packet header
+ */
+struct ospfhdr {
+ uchar_t ospf_version;
+ uchar_t ospf_type;
+ ushort_t ospf_len;
+ struct in_addr ospf_routerid;
+ struct in_addr ospf_areaid;
+ ushort_t ospf_chksum;
+ ushort_t ospf_authtype;
+ uchar_t ospf_authdata[OSPF_AUTH_SIZE];
+ union {
+
+ /* Hello packet */
+ struct {
+ struct in_addr hello_mask;
+ ushort_t hello_helloint;
+ uchar_t hello_options;
+ uchar_t hello_priority;
+ uint32_t hello_deadint;
+ struct in_addr hello_dr;
+ struct in_addr hello_bdr;
+ struct in_addr hello_neighbor[1]; /* may repeat */
+ } un_hello;
+
+ /* Database Description packet */
+ struct {
+ uchar_t db_zero[2];
+ uchar_t db_options;
+ uchar_t db_flags;
+ uint32_t db_seq;
+ struct lsa_hdr db_lshdr[1]; /* may repeat */
+ } un_db;
+
+ /* Link State Request */
+ struct lsr {
+ uint32_t ls_type;
+ struct in_addr ls_stateid;
+ struct in_addr ls_router;
+ } un_lsr[1]; /* may repeat */
+
+ /* Link State Update */
+ struct {
+ uint32_t lsu_count;
+ struct lsa lsu_lsa[1]; /* may repeat */
+ } un_lsu;
+
+ /* Link State Acknowledgement */
+ struct {
+ struct lsa_hdr lsa_lshdr[1]; /* may repeat */
+ } un_lsa;
+ } ospf_un;
+};
+
+#define ospf_hello ospf_un.un_hello
+#define ospf_db ospf_un.un_db
+#define ospf_lsr ospf_un.un_lsr
+#define ospf_lsu ospf_un.un_lsu
+#define ospf_lsa ospf_un.un_lsa
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _OSPF_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.c
new file mode 100644
index 0000000..6394c8e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.c
@@ -0,0 +1,815 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+#include "snoop.h"
+#include "snoop_ospf.h"
+#include "snoop_ospf6.h"
+
+extern char *dlc_header;
+static char *sum_line;
+extern const struct bits ospf_db_flags_bits[];
+extern const struct bits ospf_rla_flag_bits[];
+extern const struct bits ospf_option_bits[];
+
+const struct bits ospf6_option_bits[] = {
+ { OSPF_OPTION_V6, "V6" },
+ { OSPF_OPTION_E, "E" },
+ { OSPF_OPTION_MC, "MC" },
+ { OSPF_OPTION_N, "N" },
+ { OSPF_OPTION_R, "R" },
+ { OSPF_OPTION_DC, "DC" },
+ { 0, NULL }
+};
+
+/*
+ * return a printable string in dotted-decimal notation
+ * for id.
+ */
+static char *
+print_ipaddr(uint32_t id)
+{
+ struct in_addr tmp;
+
+ tmp.s_addr = id;
+ return (inet_ntoa(tmp));
+}
+
+static int
+interpret_ospf6_hello(int flags, struct ospf6hdr *op, int fraglen)
+{
+ uint32_t *nbr;
+ int j;
+
+ if (fraglen < OSPF6_MIN_HEADER_SIZE + OSPF_MIN_HELLO_HEADER_SIZE)
+ return (-1); /* truncated packet */
+
+ if (flags & F_SUM) {
+ if (op->ospf6_hello.hello_dr != 0) {
+ (void) sprintf(sum_line, "DR=%s ",
+ print_ipaddr(op->ospf6_hello.hello_dr));
+ }
+ sum_line += strlen(sum_line);
+ if (op->ospf6_hello.hello_bdr != 0) {
+ (void) sprintf(sum_line, "BDR=%s ",
+ print_ipaddr(op->ospf6_hello.hello_bdr));
+ }
+ sum_line += strlen(sum_line);
+ j = 0;
+ nbr = op->ospf6_hello.hello_neighbor;
+ while ((uchar_t *)nbr < ((uchar_t *)op + fraglen)) {
+ if ((uchar_t *)nbr + sizeof (struct in_addr) >
+ ((uchar_t *)op + fraglen))
+ return (-1); /* truncated */
+ ++nbr;
+ j++;
+ }
+ (void) sprintf(sum_line, "%d nbrs", j);
+ sum_line += strlen(sum_line);
+
+ }
+ if (flags & F_DTAIL) {
+ show_header("OSPF HELLO: ", "Hello Packet",
+ ntohs(op->ospf6_len));
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Options = %s", ospf_print_bits(ospf6_option_bits,
+ op->ospf6_hello.hello6_options));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Interface ID = %s",
+ print_ipaddr(op->ospf6_hello.hello_ifid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Hello interval = %d",
+ ntohs(op->ospf6_hello.hello_helloint));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Priority = %d", op->ospf6_hello.hello6_priority);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Dead interval = %u", ntohl(op->ospf6_hello.hello_deadint));
+ if (op->ospf6_hello.hello_dr != 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Designated Router = %s",
+ print_ipaddr(op->ospf6_hello.hello_dr));
+ }
+ if (op->ospf6_hello.hello_bdr != 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Backup Designated Router = %s",
+ print_ipaddr(op->ospf6_hello.hello_bdr));
+ }
+ nbr = op->ospf6_hello.hello_neighbor;
+ while ((uchar_t *)nbr < ((uchar_t *)op + fraglen)) {
+ if ((uchar_t *)nbr + sizeof (struct in_addr) >
+ ((uchar_t *)op + fraglen))
+ return (-1); /* truncated */
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Neigbor: %s", print_ipaddr(*nbr));
+ ++nbr;
+ }
+ }
+ return (fraglen);
+}
+
+static void
+ospf6_print_ls_type(int flags, uint_t ls6_type, uint32_t ls6_stateid,
+ uint32_t ls6_router)
+{
+ char scope[15];
+
+ if (flags & F_SUM)
+ return;
+
+ switch (ls6_type & LS6_SCOPE_MASK) {
+ case LS6_SCOPE_LINKLOCAL:
+ snprintf(scope, sizeof (scope), "linklocal");
+ break;
+ case LS6_SCOPE_AREA:
+ snprintf(scope, sizeof (scope), "area");
+ break;
+ case LS6_SCOPE_AS:
+ snprintf(scope, sizeof (scope), "AS");
+ break;
+ default:
+ snprintf(scope, sizeof (scope), "");
+ break;
+ }
+ switch (ls6_type & LS_TYPE_MASK) {
+ case LS_TYPE_ROUTER:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s Router = %s", scope, print_ipaddr(ls6_router));
+ }
+ break;
+ case LS_TYPE_NETWORK:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s Net DR %s IF %s", scope,
+ print_ipaddr(ls6_router),
+ print_ipaddr(ls6_stateid));
+ }
+ break;
+ case LS_TYPE_INTER_AP:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s Inter-area-prefix = %s ABR %s", scope,
+ print_ipaddr(ls6_stateid),
+ print_ipaddr(ls6_router));
+ }
+ break;
+ case LS_TYPE_INTER_AR:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s Inter-area-router = %s Router %s", scope,
+ print_ipaddr(ls6_router),
+ print_ipaddr(ls6_stateid));
+ }
+ break;
+ case LS_TYPE_ASE:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s ASE = %s ASBR %s", scope,
+ print_ipaddr(ls6_stateid),
+ print_ipaddr(ls6_router));
+ }
+ break;
+ case LS_TYPE_GROUP:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s group = %s Router %s", scope,
+ print_ipaddr(ls6_stateid),
+ print_ipaddr(ls6_router));
+ }
+ break;
+ case LS_TYPE_TYPE7:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s Type 7 = %s Router %s", scope,
+ print_ipaddr(ls6_stateid),
+ print_ipaddr(ls6_router));
+ }
+ break;
+ case LS_TYPE_LINK:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s link = %s Router %s", scope,
+ print_ipaddr(ls6_stateid),
+ print_ipaddr(ls6_router));
+ }
+ break;
+ case LS_TYPE_INTRA_AP:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s Inter-area-prefix = %s Router %s", scope,
+ print_ipaddr(ls6_stateid),
+ print_ipaddr(ls6_router));
+ }
+ break;
+ default:
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s Unknown type = 0x%x", ls6_type);
+ }
+ break;
+ }
+}
+
+static int
+ospf6_print_lsaprefix(int flags, struct lsa6_prefix *lpfx)
+{
+ int k;
+ struct in6_addr prefix;
+ char prefixstr[INET6_ADDRSTRLEN];
+
+ k = (lpfx->lsa6_plen + 31)/32;
+ if (k * 4 > sizeof (struct in6_addr)) {
+ if (flags & F_SUM) {
+ sprintf(sum_line, "Unknown prefix len %d",
+ lpfx->lsa6_plen);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Unknown prefix len %d", lpfx->lsa6_plen);
+ }
+ }
+ memset((void *)&prefix, 0, sizeof (prefix));
+ memcpy((void *)&prefix, lpfx->lsa6_pfx, k * 4);
+ (void) inet_ntop(AF_INET6, (char *)&prefix, prefixstr,
+ INET6_ADDRSTRLEN);
+ if (flags & F_SUM) {
+ sprintf(sum_line, "%s/%d", prefixstr, lpfx->lsa6_plen);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "%s/%d", prefixstr, lpfx->lsa6_plen);
+ }
+ if (lpfx->lsa6_popt != 0) {
+ if (flags & F_SUM) {
+ sprintf(sum_line, "(opt = %x)", lpfx->lsa6_popt);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "(opt = %x)", lpfx->lsa6_popt);
+ }
+ }
+ return (sizeof (*lpfx) - 4 + k * 4);
+}
+
+static void
+interpret_ospf6_lsa_hdr(int flags, struct lsa6_hdr *lsah)
+{
+ if (flags & F_SUM)
+ return;
+
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Sequence = %X ", ntohl(lsah->ls6_seq));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Age = %X ", ospf_print_lsa_age(ntohl(lsah->ls6_age)));
+ }
+
+ ospf6_print_ls_type(flags, lsah->ls6_type, lsah->ls6_stateid,
+ lsah->ls6_router);
+
+}
+
+#define TRUNC(addr) ((uchar_t *)(addr) > fragend)
+static int
+interpret_ospf6_lsa(int flags, struct lsa6 *lsa, uchar_t *fragend)
+{
+ uchar_t *ls_end;
+ int k, j;
+ struct rla6link *rl;
+ uint32_t *addr;
+ struct lsa6_prefix *lpfx;
+ struct llsa *llsa;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ interpret_ospf6_lsa_hdr(flags, &lsa->ls6_hdr);
+
+ ls_end = (uchar_t *)lsa + ntohs(lsa->ls6_hdr.ls6_length);
+
+ if (TRUNC(ls_end))
+ return (-1);
+
+ switch (ntohs(lsa->ls6_hdr.ls6_type)) {
+
+ case LS_TYPE_ROUTER|LS6_SCOPE_AREA:
+ if (TRUNC(&lsa->lsa_un.un_rla.rla6_flags))
+ return (-1);
+
+ (void) ospf_print_bits(ospf_rla_flag_bits,
+ lsa->lsa_un.un_rla.rla6_flags);
+
+ if (TRUNC(&lsa->lsa_un.un_rla.rla6_options))
+ return (-1);
+ (void) ospf_print_bits(ospf_option_bits,
+ ntohl(lsa->lsa_un.un_rla.rla6_options));
+
+ rl = lsa->lsa_un.un_rla.rla_link;
+ if (TRUNC(rl))
+ return (-1);
+
+ while (rl + sizeof (*rl) <= (struct rla6link *)ls_end) {
+ if (TRUNC((uchar_t *)rl + sizeof (*rl)))
+ return (-1);
+ if (flags & F_SUM) {
+ sprintf(sum_line, "{"); /* } (ctags) */
+ sum_line += strlen(sum_line);
+ }
+ switch (rl->link_type) {
+ case RLA_TYPE_VIRTUAL:
+ if (flags & F_SUM) {
+ sprintf(sum_line, "virt ");
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Virtual Link");
+ }
+ /* FALLTHROUGH */
+ case RLA_TYPE_ROUTER:
+ if (flags & F_SUM) {
+ sprintf(sum_line, "nbrid %s",
+ print_ipaddr(rl->link_nrtid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, " nbrif %s",
+ print_ipaddr(rl->link_nifid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, " if %s",
+ print_ipaddr(rl->link_ifid));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Neighbor = %s",
+ print_ipaddr(rl->link_nrtid));
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Interface = %s id %s",
+ print_ipaddr(rl->link_nifid),
+ print_ipaddr(rl->link_ifid));
+ }
+ break;
+ case RLA_TYPE_TRANSIT:
+ if (flags & F_SUM) {
+ sprintf(sum_line, "dr %s",
+ print_ipaddr(rl->link_nrtid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, " drif %s",
+ print_ipaddr(rl->link_nifid));
+ sum_line += strlen(sum_line);
+ sprintf(sum_line, " if %s",
+ print_ipaddr(rl->link_ifid));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Designated Router = %s",
+ print_ipaddr(rl->link_nrtid));
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "DR Interface = %s id %s",
+ print_ipaddr(rl->link_nifid),
+ print_ipaddr(rl->link_ifid));
+ }
+ break;
+ default:
+ if (flags & F_SUM) {
+ sprintf(sum_line,
+ "Unknown link type %d",
+ rl->link_type);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Unknown link type %d",
+ rl->link_type);
+ }
+
+ }
+ if (flags & F_SUM) {
+ sprintf(sum_line, " metric %d",
+ ntohs(rl->link_metric));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), " metric = %d",
+ ntohs(rl->link_metric));
+ }
+ if (flags & F_SUM) { /* { (ctags) */
+ sprintf(sum_line, " }");
+ sum_line += strlen(sum_line);
+ }
+ rl++;
+ if ((uchar_t *)rl > fragend)
+ return (-1); /* truncated */
+ }
+ break;
+ case LS_TYPE_NETWORK | LS6_SCOPE_AREA:
+
+ if (TRUNC(&lsa->lsa_un.un_nla.nla_options))
+ return (-1);
+
+ (void) ospf_print_bits(ospf6_option_bits,
+ ntohl(lsa->lsa_un.un_nla.nla_options));
+
+ if (flags & F_SUM) {
+ sprintf(sum_line, " rtrs");
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Routers:");
+ }
+ addr = lsa->lsa_un.un_nla.nla_router;
+ while ((uchar_t *)addr < ls_end) {
+ if ((uchar_t *)addr + sizeof (struct in_addr) > ls_end)
+ return (-1); /* truncated */
+ if (flags & F_SUM) {
+ sprintf(sum_line, " %s", print_ipaddr(*addr));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ "\t%s", print_ipaddr(*addr));
+ }
+ ++addr;
+ }
+ break;
+ case LS_TYPE_INTER_AP | LS6_SCOPE_AREA:
+
+ if (TRUNC(&lsa->lsa_un.un_inter_ap.inter_ap_metric))
+ return (-1);
+
+ if (flags & F_SUM) {
+ sprintf(sum_line, " metric %s",
+ ntohl(lsa->lsa_un.un_inter_ap.inter_ap_metric) &
+ SLA_MASK_METRIC);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Metric = %s",
+ ntohl(lsa->lsa_un.un_inter_ap.inter_ap_metric) &
+ SLA_MASK_METRIC);
+ }
+ lpfx = lsa->lsa_un.un_inter_ap.inter_ap_prefix;
+ if (lpfx > (struct lsa6_prefix *)ls_end)
+ return (-1);
+ while (lpfx + sizeof (*lpfx) <= (struct lsa6_prefix *)ls_end) {
+ k = ospf6_print_lsaprefix(flags, lpfx);
+ lpfx = (struct lsa6_prefix *)(((uchar_t *)lpfx) + k);
+ if (lpfx > (struct lsa6_prefix *)ls_end)
+ return (-1);
+ }
+ break;
+ case LS_TYPE_LINK:
+ llsa = &lsa->lsa_un.un_llsa;
+ if (TRUNC(llsa->llsa_options))
+ return (-1);
+ ospf_print_bits(ospf6_option_bits, ntohl(llsa->llsa_options));
+ if (TRUNC(llsa->llsa_nprefix))
+ return (-1);
+ (void) inet_ntop(AF_INET6, &llsa->llsa_lladdr,
+ addrstr, INET6_ADDRSTRLEN);
+ if (flags & F_SUM) {
+ sprintf(sum_line, " pri %d lladdr %s npref %d",
+ ntohl(llsa->llsa_priority), addrstr,
+ ntohl(llsa->llsa_nprefix));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Priority %d", ntohl(llsa->llsa_priority));
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Link Local addr %d", addrstr);
+ snprintf(get_line(0, 0), get_line_remain(),
+ "npref %d", ntohl(llsa->llsa_nprefix));
+ }
+ lpfx = llsa->llsa_prefix;
+ for (j = 0; j < ntohl(llsa->llsa_nprefix); j++) {
+ if (TRUNC(lpfx))
+ return (-1);
+ k = ospf6_print_lsaprefix(flags, lpfx);
+ lpfx = (struct lsa6_prefix *)(((uchar_t *)lpfx) + k);
+ }
+ break;
+
+ case LS_TYPE_INTRA_AP | LS6_SCOPE_AREA:
+ if (TRUNC(&lsa->lsa_un.un_intra_ap.intra_ap_rtid))
+ return (-1);
+ ospf6_print_ls_type(flags,
+ ntohs(lsa->lsa_un.un_intra_ap.intra_ap_lstype),
+ lsa->lsa_un.un_intra_ap.intra_ap_lsid,
+ lsa->lsa_un.un_intra_ap.intra_ap_rtid);
+ if (TRUNC(&lsa->lsa_un.un_intra_ap.intra_ap_nprefix))
+ return (-1);
+ if (flags & F_SUM) {
+ sprintf(sum_line, " npref %d",
+ ntohs(lsa->lsa_un.un_intra_ap.intra_ap_nprefix));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(), "NPref %d",
+ ntohs(lsa->lsa_un.un_intra_ap.intra_ap_nprefix));
+ }
+
+ lpfx = lsa->lsa_un.un_intra_ap.intra_ap_prefix;
+ for (j = 0;
+ j < ntohs(lsa->lsa_un.un_intra_ap.intra_ap_nprefix); j++) {
+ if (TRUNC(lpfx))
+ return (-1);
+ k = ospf6_print_lsaprefix(flags, lpfx);
+ lpfx = (struct lsa6_prefix *)(((uchar_t *)lpfx) + k);
+ }
+ break;
+
+ default:
+ if (flags & F_SUM) {
+ sprintf(sum_line, " Unknown LSA type (%d)",
+ lsa->ls6_hdr.ls6_type);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ snprintf(get_line(0, 0), get_line_remain(),
+ " Unknown LSA type %d", lsa->ls6_hdr.ls6_type);
+
+ }
+ break;
+ }
+ return (0);
+}
+#undef TRUNC
+int
+interpret_ospf6(int flags, struct ospf6hdr *ospf, int iplen, int fraglen)
+{
+ boolean_t trunc = B_FALSE;
+ struct lsa6_hdr *lsah;
+ struct lsr6 *lsr;
+ struct lsa6 *lsa;
+ int nlsa, nlsah;
+
+ if ((fraglen < OSPF6_MIN_HEADER_SIZE) ||
+ (fraglen < ntohs(ospf->ospf6_len)))
+ return (fraglen); /* incomplete header */
+
+ if (ospf->ospf6_version != 3) {
+ if (ospf->ospf6_version == 2) {
+ if (flags & F_DTAIL)
+ snprintf(get_line(0, 0), get_line_remain(),
+ "ospfv2 packet in ipv6 header");
+ return (interpret_ospf(flags, ospf, iplen, fraglen));
+ } else {
+ return (fraglen);
+ }
+ }
+
+ if (fraglen > ntohs(ospf->ospf6_len))
+ fraglen = ntohs(ospf->ospf6_len);
+
+ if (ospf->ospf6_type > OSPF_TYPE_MAX) {
+ if (flags & F_SUM) {
+ (void) sprintf(sum_line, "Unknown OSPF TYPE %d \n",
+ ospf->ospf6_type);
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_SUM) {
+ show_header("OSPFv3: ", "OSPFv3 Header", fraglen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Unknown OSPF Type = %d", ospf->ospf6_type);
+ }
+ return (fraglen);
+ }
+
+ if (flags & F_SUM) {
+ sum_line = (char *)get_sum_line();
+ (void) sprintf(sum_line, "OSPFv3 %s RTRID=%s ",
+ ospf_types[ospf->ospf6_type],
+ print_ipaddr(ospf->ospf6_routerid));
+ sum_line += strlen(sum_line);
+ (void) sprintf(sum_line, "AREA=%s LEN=%d instance %u ",
+ print_ipaddr(ospf->ospf6_areaid),
+ ntohs((ushort_t)ospf->ospf6_len), ospf->ospf6_instanceid);
+ sum_line += strlen(sum_line);
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("OSPFv3: ", "OSPF Header", fraglen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Version = %d", ospf->ospf6_version);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Type = %s", ospf_types[ospf->ospf6_type]);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Router ID = %s", print_ipaddr(ospf->ospf6_routerid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Area ID = %s", print_ipaddr(ospf->ospf6_areaid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Checksum = 0x%x", ospf->ospf6_chksum);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Instance = %u", ospf->ospf6_instanceid);
+ }
+
+ switch (ospf->ospf6_type) {
+ case OSPF_TYPE_HELLO:
+ if (interpret_ospf6_hello(flags, ospf, fraglen) < 0)
+ trunc = B_TRUE;
+ break;
+
+ case OSPF_TYPE_DB:
+ if (fraglen < OSPF6_MIN_HEADER_SIZE +
+ OSPF6_MIN_DB_HEADER_SIZE) {
+ trunc = B_TRUE;
+ break;
+ }
+ if (flags & F_SUM) {
+ sprintf(sum_line, " %s %s mtu %u S %X", ospf_print_bits(
+ ospf6_option_bits,
+ ntohl(ospf->ospf6_db.db_options)),
+ ospf_print_bits(ospf_db_flags_bits,
+ ospf->ospf6_db.db_flags),
+ ntohs(ospf->ospf6_db.db_mtu),
+ ntohl(ospf->ospf6_db.db_seq));
+ sum_line += strlen(sum_line);
+ }
+ if (flags & F_DTAIL) {
+ show_header("OSPF DB: ", "Database Description Packet",
+ fraglen);
+ show_space();
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Options = %s", ospf_print_bits(
+ ospf6_option_bits, ospf->ospf6_db.db_options));
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Flags = %s", ospf_print_bits(
+ ospf_db_flags_bits, ospf->ospf6_db.db_flags));
+ snprintf(get_line(0, 0), get_line_remain(),
+ "MTU = %u", ntohl(ospf->ospf6_db.db_seq));
+ snprintf(get_line(0, 0), get_line_remain(),
+ "Sequence = 0x%X", ntohl(ospf->ospf6_db.db_seq));
+ /* Print all the LS advs */
+ lsah = ospf->ospf6_db.db_lshdr;
+ while ((uchar_t *)lsah < ((uchar_t *)ospf + fraglen)) {
+ if ((uchar_t *)lsah + sizeof (struct lsa6_hdr) >
+ ((uchar_t *)ospf + fraglen)) {
+ trunc = B_TRUE;
+ break;
+ }
+ interpret_ospf6_lsa_hdr(flags, lsah);
+ ++lsah;
+ }
+ }
+ break;
+
+ case OSPF_TYPE_LSR:
+ if (fraglen < OSPF6_MIN_HEADER_SIZE +
+ OSPF_MIN_LSR_HEADER_SIZE) {
+ trunc = B_TRUE;
+ break;
+ }
+ if (flags & F_DTAIL) {
+ show_header("OSPF LSR: ", "Link State Request Packet",
+ fraglen);
+ show_space();
+ }
+ lsr = ospf->ospf6_lsr;
+ nlsah = 0;
+ while ((uchar_t *)lsr < ((uchar_t *)ospf + fraglen)) {
+ if ((uchar_t *)lsr + sizeof (struct lsr6) >
+ ((uchar_t *)ospf + fraglen)) {
+ trunc = B_TRUE;
+ break;
+ }
+ nlsah++;
+ if (flags & F_DTAIL) {
+ ospf6_print_ls_type(flags, ntohl(lsr->ls_type),
+ lsr->ls_stateid, lsr->ls_router);
+ }
+ ++lsr;
+ }
+ if (flags & F_SUM) {
+ sprintf(sum_line, "%d LSAs", nlsah);
+ sum_line += strlen(sum_line);
+ }
+ break;
+
+ case OSPF_TYPE_LSU:
+ if (fraglen < OSPF6_MIN_HEADER_SIZE +
+ OSPF_MIN_LSU_HEADER_SIZE) {
+ trunc = B_TRUE;
+ break;
+ }
+ if (flags & F_DTAIL) {
+ show_header("OSPF LSU: ", "Link State Update Packet",
+ fraglen);
+ show_space();
+ }
+ lsa = ospf->ospf6_lsu.lsu_lsa;
+ nlsa = ntohl(ospf->ospf6_lsu.lsu_count);
+ if (flags & F_SUM) {
+ sprintf(sum_line, "%d LSAs", nlsa);
+ sum_line += strlen(sum_line);
+ break;
+ }
+ while (nlsa-- != 0) {
+ uchar_t *fragend = (uchar_t *)ospf + fraglen;
+ if (((uchar_t *)lsa >= fragend) ||
+ ((uchar_t *)lsa + sizeof (struct lsa_hdr) >
+ fragend) ||
+ ((uchar_t *)lsa + ntohs(lsa->ls6_hdr.ls6_length) >
+ fragend)) {
+ trunc = B_TRUE;
+ break;
+ }
+
+ if (interpret_ospf6_lsa(flags, lsa, fragend) < 0) {
+ trunc = B_TRUE;
+ break;
+ }
+ lsa = (struct lsa6 *)((uchar_t *)lsa +
+ ntohs(lsa->ls6_hdr.ls6_length));
+ }
+ break;
+
+ case OSPF_TYPE_LSA:
+ if (flags & F_DTAIL) {
+ show_header("OSPF LSA: ", "Link State Ack Packet",
+ fraglen);
+ show_space();
+ }
+ lsah = ospf->ospf6_lsa.lsa_lshdr;
+ nlsah = 0;
+ while ((uchar_t *)lsah < ((uchar_t *)ospf + fraglen)) {
+ if ((uchar_t *)lsah + sizeof (struct lsa6_hdr) >
+ ((uchar_t *)ospf + fraglen)) {
+ trunc = B_TRUE;
+ break;
+ }
+ nlsah++;
+ if (flags & F_DTAIL)
+ interpret_ospf6_lsa_hdr(flags, lsah);
+ ++lsah;
+ }
+ if (flags & F_SUM) {
+ sprintf(sum_line, "%d LSAs", nlsah);
+ sum_line += strlen(sum_line);
+ }
+ break;
+
+ default:
+ /* NOTREACHED */
+ break;
+ }
+ if (trunc) {
+ if (flags & F_SUM)
+ sprintf(sum_line, "--truncated");
+ if (flags & F_DTAIL)
+ snprintf(get_line(0, 0), get_line_remain(),
+ "--truncated");
+ }
+
+ return (fraglen);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.h
new file mode 100644
index 0000000..2c71fca
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ospf6.h
@@ -0,0 +1,185 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _OSPF6_H
+#define _OSPF6_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Definitions for parsing OSPF packets (RFC 2328)
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct lsa6_hdr {
+ uint16_t ls6_age;
+ uint16_t ls6_type;
+ uint32_t ls6_stateid;
+ uint32_t ls6_router;
+ uint32_t ls6_seq;
+ uint16_t ls6_chksum;
+ uint16_t ls6_length;
+};
+
+struct lsa6_prefix {
+ uint8_t lsa6_plen;
+ uint8_t lsa6_popt;
+ uint16_t lsa6_pmbz;
+ uint8_t lsa6_pfx[4];
+};
+
+/* link state advertisement */
+struct lsa6 {
+ struct lsa6_hdr ls6_hdr;
+
+ /* Link state types */
+ union {
+ /* Router links advertisements */
+ struct {
+ union {
+ uint8_t rla_flg;
+ uint32_t rla_opt;
+ } un_rla_flgopt;
+#define rla6_flags un_rla_flgopt.rla_flg
+#define rla6_options un_rla_flgopt.rla_opt
+ struct rla6link {
+ uint8_t link_type;
+ uint8_t link_zero[1];
+ uint16_t link_metric;
+ uint32_t link_ifid;
+ uint32_t link_nifid;
+ uint32_t link_nrtid;
+ } rla_link[1]; /* may repeat */
+ } un_rla;
+
+ /* Network links advertisements */
+ struct {
+ uint32_t nla_options;
+ uint32_t nla_router[1]; /* may repeat */
+ } un_nla;
+
+ /* Inter Area Prefix LSA */
+ struct {
+ uint32_t inter_ap_metric;
+ struct lsa6_prefix inter_ap_prefix[1];
+ } un_inter_ap;
+
+ /* Link LSA */
+ struct llsa {
+ union {
+ uint8_t pri;
+ uint32_t opt;
+ } llsa_priandopt;
+#define llsa_priority llsa_priandopt.pri
+#define llsa_options llsa_priandopt.opt
+ struct in6_addr llsa_lladdr;
+ uint32_t llsa_nprefix;
+ struct lsa6_prefix llsa_prefix[1];
+ } un_llsa;
+
+ /* Intra-Area-Prefix */
+ struct {
+ uint16_t intra_ap_nprefix;
+ uint16_t intra_ap_lstype;
+ uint32_t intra_ap_lsid;
+ uint32_t intra_ap_rtid;
+ struct lsa6_prefix intra_ap_prefix[1];
+ } un_intra_ap;
+ } lsa_un;
+};
+
+struct ospf6hdr {
+ uint8_t ospf6_version;
+ uint8_t ospf6_type;
+ uint16_t ospf6_len;
+ uint32_t ospf6_routerid;
+ uint32_t ospf6_areaid;
+ uint16_t ospf6_chksum;
+ uint8_t ospf6_instanceid;
+ uint8_t ospf6_rsvd;
+ union {
+
+ /* Hello packet */
+ struct {
+ uint32_t hello_ifid;
+ union {
+ uint8_t pri;
+ uint32_t opt;
+ } hello_priandopt;
+#define hello6_priority hello_priandopt.pri
+#define hello6_options hello_priandopt.opt
+ uint16_t hello_helloint;
+ uint16_t hello_deadint;
+ uint32_t hello_dr;
+ uint32_t hello_bdr;
+ uint32_t hello_neighbor[1]; /* may repeat */
+ } un_hello;
+
+ /* Database Description packet */
+ struct {
+ uint32_t db_options;
+ uint16_t db_mtu;
+ uint8_t db_mbz;
+ uint8_t db_flags;
+ uint32_t db_seq;
+ struct lsa6_hdr db_lshdr[1]; /* may repeat */
+ } un_db;
+
+ /* Link State Request */
+ struct lsr6 {
+ uint16_t ls_mbz;
+ uint16_t ls_type;
+ uint32_t ls_stateid;
+ uint32_t ls_router;
+ } un_lsr[1]; /* may repeat */
+
+ /* Link State Update */
+ struct {
+ uint32_t lsu_count;
+ struct lsa6 lsu_lsa[1]; /* may repeat */
+ } un_lsu;
+
+ /* Link State Acknowledgement */
+ struct {
+ struct lsa6_hdr lsa_lshdr[1]; /* may repeat */
+ } un_lsa;
+ } ospf6_un;
+};
+
+#define ospf6_hello ospf6_un.un_hello
+#define ospf6_db ospf6_un.un_db
+#define ospf6_lsr ospf6_un.un_lsr
+#define ospf6_lsu ospf6_un.un_lsu
+#define ospf6_lsa ospf6_un.un_lsa
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _OSPF6_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pf.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pf.c
new file mode 100644
index 0000000..40faeb5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pf.c
@@ -0,0 +1,1664 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2012 Milan Jurik. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/isa_defs.h>
+
+#include <sys/socket.h>
+#include <sys/vlan.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <netdb.h>
+#include <rpc/rpc.h>
+#include <setjmp.h>
+
+#include <sys/pfmod.h>
+#include "snoop.h"
+#include "snoop_vlan.h"
+
+/*
+ * This module generates code for the kernel packet filter.
+ * The kernel packet filter is more efficient since it
+ * operates without context switching or moving data into
+ * the capture buffer. On the other hand, it is limited
+ * in its filtering ability i.e. can't cope with variable
+ * length headers, can't compare the packet size, 1 and 4 octet
+ * comparisons are awkward, code space is limited to ENMAXFILTERS
+ * halfwords, etc.
+ * The parser is the same for the user-level packet filter though
+ * more limited in the variety of expressions it can generate
+ * code for. If the pf compiler finds an expression it can't
+ * handle, it tries to set up a split filter in kernel and do the
+ * remaining filtering in userland. If that also fails, it resorts
+ * to userland filter. (See additional comment in pf_compile)
+ */
+
+extern struct Pf_ext_packetfilt pf;
+static ushort_t *pfp;
+jmp_buf env;
+
+int eaddr; /* need ethernet addr */
+
+int opstack; /* operand stack depth */
+
+#define EQ(val) (strcmp(token, val) == 0)
+#define IPV4_ONLY 0
+#define IPV6_ONLY 1
+#define IPV4_AND_IPV6 2
+
+typedef struct {
+ int transport_protocol;
+ int network_protocol;
+ /*
+ * offset is the offset in bytes from the beginning
+ * of the network protocol header to where the transport
+ * protocol type is.
+ */
+ int offset;
+} transport_table_t;
+
+typedef struct network_table {
+ char *nmt_name;
+ int nmt_val;
+} network_table_t;
+
+static network_table_t ether_network_mapping_table[] = {
+ { "pup", ETHERTYPE_PUP },
+ { "ip", ETHERTYPE_IP },
+ { "arp", ETHERTYPE_ARP },
+ { "revarp", ETHERTYPE_REVARP },
+ { "at", ETHERTYPE_AT },
+ { "aarp", ETHERTYPE_AARP },
+ { "vlan", ETHERTYPE_VLAN },
+ { "ip6", ETHERTYPE_IPV6 },
+ { "slow", ETHERTYPE_SLOW },
+ { "ppoed", ETHERTYPE_PPPOED },
+ { "ppoes", ETHERTYPE_PPPOES },
+ { "NULL", -1 }
+
+};
+
+static network_table_t ib_network_mapping_table[] = {
+ { "pup", ETHERTYPE_PUP },
+ { "ip", ETHERTYPE_IP },
+ { "arp", ETHERTYPE_ARP },
+ { "revarp", ETHERTYPE_REVARP },
+ { "at", ETHERTYPE_AT },
+ { "aarp", ETHERTYPE_AARP },
+ { "vlan", ETHERTYPE_VLAN },
+ { "ip6", ETHERTYPE_IPV6 },
+ { "slow", ETHERTYPE_SLOW },
+ { "ppoed", ETHERTYPE_PPPOED },
+ { "ppoes", ETHERTYPE_PPPOES },
+ { "NULL", -1 }
+
+};
+
+static network_table_t ipnet_network_mapping_table[] = {
+ { "ip", (DL_IPNETINFO_VERSION << 8 | AF_INET) },
+ { "ip6", (DL_IPNETINFO_VERSION << 8 | AF_INET6) },
+ { "NULL", -1 }
+
+};
+
+static transport_table_t ether_transport_mapping_table[] = {
+ {IPPROTO_TCP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_TCP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_UDP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_UDP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_OSPF, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_OSPF, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_SCTP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_SCTP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_ICMP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_ICMPV6, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_ENCAP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_ESP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_ESP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_AH, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_AH, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
+ {-1, 0, 0} /* must be the final entry */
+};
+
+static transport_table_t ipnet_transport_mapping_table[] = {
+ {IPPROTO_TCP, (DL_IPNETINFO_VERSION << 8 | AF_INET),
+ IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_TCP, (DL_IPNETINFO_VERSION << 8 | AF_INET6),
+ IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_UDP, (DL_IPNETINFO_VERSION << 8 | AF_INET),
+ IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_UDP, (DL_IPNETINFO_VERSION << 8 | AF_INET6),
+ IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_OSPF, (DL_IPNETINFO_VERSION << 8 | AF_INET),
+ IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_OSPF, (DL_IPNETINFO_VERSION << 8 | AF_INET6),
+ IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_SCTP, (DL_IPNETINFO_VERSION << 8 | AF_INET),
+ IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_SCTP, (DL_IPNETINFO_VERSION << 8 | AF_INET6),
+ IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_ICMP, (DL_IPNETINFO_VERSION << 8 | AF_INET),
+ IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_ICMPV6, (DL_IPNETINFO_VERSION << 8 | AF_INET6),
+ IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_ENCAP, (DL_IPNETINFO_VERSION << 8 | AF_INET),
+ IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_ESP, (DL_IPNETINFO_VERSION << 8 | AF_INET),
+ IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_ESP, (DL_IPNETINFO_VERSION << 8 | AF_INET6),
+ IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_AH, (DL_IPNETINFO_VERSION << 8 | AF_INET),
+ IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_AH, (DL_IPNETINFO_VERSION << 8 | AF_INET6),
+ IPV6_TYPE_HEADER_OFFSET},
+ {-1, 0, 0} /* must be the final entry */
+};
+
+static transport_table_t ib_transport_mapping_table[] = {
+ {IPPROTO_TCP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_TCP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_UDP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_UDP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_OSPF, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_OSPF, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_SCTP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_SCTP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_ICMP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_ICMPV6, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_ENCAP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_ESP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_ESP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
+ {IPPROTO_AH, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
+ {IPPROTO_AH, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
+ {-1, 0, 0} /* must be the final entry */
+};
+
+typedef struct datalink {
+ uint_t dl_type;
+ void (*dl_match_fn)(uint_t datatype);
+ transport_table_t *dl_trans_map_tbl;
+ network_table_t *dl_net_map_tbl;
+ int dl_link_header_len;
+ int dl_link_type_offset;
+ int dl_link_dest_offset;
+ int dl_link_src_offset;
+ int dl_link_addr_len;
+} datalink_t;
+
+datalink_t dl;
+
+#define IPV4_SRCADDR_OFFSET (dl.dl_link_header_len + 12)
+#define IPV4_DSTADDR_OFFSET (dl.dl_link_header_len + 16)
+#define IPV6_SRCADDR_OFFSET (dl.dl_link_header_len + 8)
+#define IPV6_DSTADDR_OFFSET (dl.dl_link_header_len + 24)
+
+#define IPNET_SRCZONE_OFFSET 16
+#define IPNET_DSTZONE_OFFSET 20
+
+static int inBrace = 0, inBraceOR = 0;
+static int foundOR = 0;
+char *tkp, *sav_tkp;
+char *token;
+enum { EOL, ALPHA, NUMBER, FIELD, ADDR_IP, ADDR_ETHER, SPECIAL,
+ ADDR_IP6 } tokentype;
+uint_t tokenval;
+
+enum direction { ANY, TO, FROM };
+enum direction dir;
+
+extern void next();
+
+static void pf_expression();
+static void pf_check_vlan_tag(uint_t offset);
+static void pf_clear_offset_register();
+static void pf_emit_load_offset(uint_t offset);
+static void pf_match_ethertype(uint_t ethertype);
+static void pf_match_ipnettype(uint_t type);
+static void pf_match_ibtype(uint_t type);
+static void pf_check_transport_protocol(uint_t transport_protocol);
+static void pf_compare_value_mask_generic(int offset, uint_t len,
+ uint_t val, int mask, uint_t op);
+static void pf_matchfn(const char *name);
+
+/*
+ * This pointer points to the function that last generated
+ * instructions to change the offset register. It's used
+ * for comparisons to see if we need to issue more instructions
+ * to change the register.
+ *
+ * It's initialized to pf_clear_offset_register because the offset
+ * register in pfmod is initialized to zero, similar to the state
+ * it would be in after executing the instructions issued by
+ * pf_clear_offset_register.
+ */
+static void *last_offset_operation = (void*)pf_clear_offset_register;
+
+static void
+pf_emit(x)
+ ushort_t x;
+{
+ if (pfp > &pf.Pf_Filter[PF_MAXFILTERS - 1])
+ longjmp(env, 1);
+ *pfp++ = x;
+}
+
+static void
+pf_codeprint(code, len)
+ ushort_t *code;
+ int len;
+{
+ ushort_t *pc;
+ ushort_t *plast = code + len;
+ int op, action;
+
+ if (len > 0) {
+ printf("Kernel Filter:\n");
+ }
+
+ for (pc = code; pc < plast; pc++) {
+ printf("\t%3d: ", pc - code);
+
+ op = *pc & 0xfc00; /* high 10 bits */
+ action = *pc & 0x3ff; /* low 6 bits */
+
+ switch (action) {
+ case ENF_PUSHLIT:
+ printf("PUSHLIT ");
+ break;
+ case ENF_PUSHZERO:
+ printf("PUSHZERO ");
+ break;
+#ifdef ENF_PUSHONE
+ case ENF_PUSHONE:
+ printf("PUSHONE ");
+ break;
+#endif
+#ifdef ENF_PUSHFFFF
+ case ENF_PUSHFFFF:
+ printf("PUSHFFFF ");
+ break;
+#endif
+#ifdef ENF_PUSHFF00
+ case ENF_PUSHFF00:
+ printf("PUSHFF00 ");
+ break;
+#endif
+#ifdef ENF_PUSH00FF
+ case ENF_PUSH00FF:
+ printf("PUSH00FF ");
+ break;
+#endif
+ case ENF_LOAD_OFFSET:
+ printf("LOAD_OFFSET ");
+ break;
+ case ENF_BRTR:
+ printf("BRTR ");
+ break;
+ case ENF_BRFL:
+ printf("BRFL ");
+ break;
+ case ENF_POP:
+ printf("POP ");
+ break;
+ }
+
+ if (action >= ENF_PUSHWORD)
+ printf("PUSHWORD %d ", action - ENF_PUSHWORD);
+
+ switch (op) {
+ case ENF_EQ:
+ printf("EQ ");
+ break;
+ case ENF_LT:
+ printf("LT ");
+ break;
+ case ENF_LE:
+ printf("LE ");
+ break;
+ case ENF_GT:
+ printf("GT ");
+ break;
+ case ENF_GE:
+ printf("GE ");
+ break;
+ case ENF_AND:
+ printf("AND ");
+ break;
+ case ENF_OR:
+ printf("OR ");
+ break;
+ case ENF_XOR:
+ printf("XOR ");
+ break;
+ case ENF_COR:
+ printf("COR ");
+ break;
+ case ENF_CAND:
+ printf("CAND ");
+ break;
+ case ENF_CNOR:
+ printf("CNOR ");
+ break;
+ case ENF_CNAND:
+ printf("CNAND ");
+ break;
+ case ENF_NEQ:
+ printf("NEQ ");
+ break;
+ }
+
+ if (action == ENF_PUSHLIT ||
+ action == ENF_LOAD_OFFSET ||
+ action == ENF_BRTR ||
+ action == ENF_BRFL) {
+ pc++;
+ printf("\n\t%3d: %d (0x%04x)", pc - code, *pc, *pc);
+ }
+
+ printf("\n");
+ }
+}
+
+/*
+ * Emit packet filter code to check a
+ * field in the packet for a particular value.
+ * Need different code for each field size.
+ * Since the pf can only compare 16 bit quantities
+ * we have to use masking to compare byte values.
+ * Long word (32 bit) quantities have to be done
+ * as two 16 bit comparisons.
+ */
+static void
+pf_compare_value(int offset, uint_t len, uint_t val)
+{
+ /*
+ * If the property being filtered on is absent in the media
+ * packet, error out.
+ */
+ if (offset == -1)
+ pr_err("filter option unsupported on media");
+
+ switch (len) {
+ case 1:
+ pf_emit(ENF_PUSHWORD + offset / 2);
+#if defined(_BIG_ENDIAN)
+ if (offset % 2)
+#else
+ if (!(offset % 2))
+#endif
+ {
+#ifdef ENF_PUSH00FF
+ pf_emit(ENF_PUSH00FF | ENF_AND);
+#else
+ pf_emit(ENF_PUSHLIT | ENF_AND);
+ pf_emit(0x00FF);
+#endif
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+ pf_emit(val);
+ } else {
+#ifdef ENF_PUSHFF00
+ pf_emit(ENF_PUSHFF00 | ENF_AND);
+#else
+ pf_emit(ENF_PUSHLIT | ENF_AND);
+ pf_emit(0xFF00);
+#endif
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+ pf_emit(val << 8);
+ }
+ break;
+
+ case 2:
+ pf_emit(ENF_PUSHWORD + offset / 2);
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+ pf_emit((ushort_t)val);
+ break;
+
+ case 4:
+ pf_emit(ENF_PUSHWORD + offset / 2);
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+#if defined(_BIG_ENDIAN)
+ pf_emit(val >> 16);
+#elif defined(_LITTLE_ENDIAN)
+ pf_emit(val & 0xffff);
+#else
+#error One of _BIG_ENDIAN and _LITTLE_ENDIAN must be defined
+#endif
+ pf_emit(ENF_PUSHWORD + (offset / 2) + 1);
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+#if defined(_BIG_ENDIAN)
+ pf_emit(val & 0xffff);
+#else
+ pf_emit(val >> 16);
+#endif
+ pf_emit(ENF_AND);
+ break;
+ }
+}
+
+/*
+ * same as pf_compare_value, but only for emiting code to
+ * compare ipv6 addresses.
+ */
+static void
+pf_compare_value_v6(int offset, uint_t len, struct in6_addr val)
+{
+ int i;
+
+ for (i = 0; i < len; i += 2) {
+ pf_emit(ENF_PUSHWORD + offset / 2 + i / 2);
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+ pf_emit(*(uint16_t *)&val.s6_addr[i]);
+ if (i != 0)
+ pf_emit(ENF_AND);
+ }
+}
+
+
+/*
+ * Same as above except mask the field value
+ * before doing the comparison. The comparison checks
+ * to make sure the values are equal.
+ */
+static void
+pf_compare_value_mask(int offset, uint_t len, uint_t val, int mask)
+{
+ pf_compare_value_mask_generic(offset, len, val, mask, ENF_EQ);
+}
+
+/*
+ * Same as above except the values are compared to see if they are not
+ * equal.
+ */
+static void
+pf_compare_value_mask_neq(int offset, uint_t len, uint_t val, int mask)
+{
+ pf_compare_value_mask_generic(offset, len, val, mask, ENF_NEQ);
+}
+
+/*
+ * Similar to pf_compare_value.
+ *
+ * This is the utility function that does the actual work to compare
+ * two values using a mask. The comparison operation is passed into
+ * the function.
+ */
+static void
+pf_compare_value_mask_generic(int offset, uint_t len, uint_t val, int mask,
+ uint_t op)
+{
+ /*
+ * If the property being filtered on is absent in the media
+ * packet, error out.
+ */
+ if (offset == -1)
+ pr_err("filter option unsupported on media");
+
+ switch (len) {
+ case 1:
+ pf_emit(ENF_PUSHWORD + offset / 2);
+#if defined(_BIG_ENDIAN)
+ if (offset % 2)
+#else
+ if (!offset % 2)
+#endif
+ {
+ pf_emit(ENF_PUSHLIT | ENF_AND);
+ pf_emit(mask & 0x00ff);
+ pf_emit(ENF_PUSHLIT | op);
+ pf_emit(val);
+ } else {
+ pf_emit(ENF_PUSHLIT | ENF_AND);
+ pf_emit((mask << 8) & 0xff00);
+ pf_emit(ENF_PUSHLIT | op);
+ pf_emit(val << 8);
+ }
+ break;
+
+ case 2:
+ pf_emit(ENF_PUSHWORD + offset / 2);
+ pf_emit(ENF_PUSHLIT | ENF_AND);
+ pf_emit(htons((ushort_t)mask));
+ pf_emit(ENF_PUSHLIT | op);
+ pf_emit(htons((ushort_t)val));
+ break;
+
+ case 4:
+ pf_emit(ENF_PUSHWORD + offset / 2);
+ pf_emit(ENF_PUSHLIT | ENF_AND);
+ pf_emit(htons((ushort_t)((mask >> 16) & 0xffff)));
+ pf_emit(ENF_PUSHLIT | op);
+ pf_emit(htons((ushort_t)((val >> 16) & 0xffff)));
+
+ pf_emit(ENF_PUSHWORD + (offset / 2) + 1);
+ pf_emit(ENF_PUSHLIT | ENF_AND);
+ pf_emit(htons((ushort_t)(mask & 0xffff)));
+ pf_emit(ENF_PUSHLIT | op);
+ pf_emit(htons((ushort_t)(val & 0xffff)));
+
+ pf_emit(ENF_AND);
+ break;
+ }
+}
+
+/*
+ * Like pf_compare_value() but compare on a 32-bit zoneid value.
+ * The argument val passed in is in network byte order.
+ */
+static void
+pf_compare_zoneid(int offset, uint32_t val)
+{
+ int i;
+
+ for (i = 0; i < sizeof (uint32_t) / 2; i ++) {
+ pf_emit(ENF_PUSHWORD + offset / 2 + i);
+ pf_emit(ENF_PUSHLIT | ENF_EQ);
+ pf_emit(((uint16_t *)&val)[i]);
+ if (i != 0)
+ pf_emit(ENF_AND);
+ }
+}
+
+/*
+ * Generate pf code to match an IPv4 or IPv6 address.
+ */
+static void
+pf_ipaddr_match(which, hostname, inet_type)
+ enum direction which;
+ char *hostname;
+ int inet_type;
+{
+ bool_t found_host;
+ uint_t *addr4ptr;
+ uint_t addr4;
+ struct in6_addr *addr6ptr;
+ int h_addr_index;
+ struct hostent *hp = NULL;
+ int error_num = 0;
+ boolean_t first = B_TRUE;
+ int pass = 0;
+ int i;
+
+ /*
+ * The addr4offset and addr6offset variables simplify the code which
+ * generates the address comparison filter. With these two variables,
+ * duplicate code need not exist for the TO and FROM case.
+ * A value of -1 describes the ANY case (TO and FROM).
+ */
+ int addr4offset;
+ int addr6offset;
+
+ found_host = 0;
+
+ if (tokentype == ADDR_IP) {
+ hp = getipnodebyname(hostname, AF_INET, 0, &error_num);
+ if (hp == NULL) {
+ if (error_num == TRY_AGAIN) {
+ pr_err("could not resolve %s (try again later)",
+ hostname);
+ } else {
+ pr_err("could not resolve %s", hostname);
+ }
+ }
+ inet_type = IPV4_ONLY;
+ } else if (tokentype == ADDR_IP6) {
+ hp = getipnodebyname(hostname, AF_INET6, 0, &error_num);
+ if (hp == NULL) {
+ if (error_num == TRY_AGAIN) {
+ pr_err("could not resolve %s (try again later)",
+ hostname);
+ } else {
+ pr_err("could not resolve %s", hostname);
+ }
+ }
+ inet_type = IPV6_ONLY;
+ } else if (tokentype == ALPHA) {
+ /* Some hostname i.e. tokentype is ALPHA */
+ switch (inet_type) {
+ case IPV4_ONLY:
+ /* Only IPv4 address is needed */
+ hp = getipnodebyname(hostname, AF_INET, 0, &error_num);
+ if (hp != NULL) {
+ found_host = 1;
+ }
+ break;
+ case IPV6_ONLY:
+ /* Only IPv6 address is needed */
+ hp = getipnodebyname(hostname, AF_INET6, 0, &error_num);
+ if (hp != NULL) {
+ found_host = 1;
+ }
+ break;
+ case IPV4_AND_IPV6:
+ /* Both IPv4 and IPv6 are needed */
+ hp = getipnodebyname(hostname, AF_INET6,
+ AI_ALL | AI_V4MAPPED, &error_num);
+ if (hp != NULL) {
+ found_host = 1;
+ }
+ break;
+ default:
+ found_host = 0;
+ }
+
+ if (!found_host) {
+ if (error_num == TRY_AGAIN) {
+ pr_err("could not resolve %s (try again later)",
+ hostname);
+ } else {
+ pr_err("could not resolve %s", hostname);
+ }
+ }
+ } else {
+ pr_err("unknown token type: %s", hostname);
+ }
+
+ if (hp == NULL)
+ return;
+
+ switch (which) {
+ case TO:
+ addr4offset = IPV4_DSTADDR_OFFSET;
+ addr6offset = IPV6_DSTADDR_OFFSET;
+ break;
+ case FROM:
+ addr4offset = IPV4_SRCADDR_OFFSET;
+ addr6offset = IPV6_SRCADDR_OFFSET;
+ break;
+ case ANY:
+ addr4offset = -1;
+ addr6offset = -1;
+ break;
+ }
+
+ if (hp->h_addrtype == AF_INET) {
+ pf_matchfn("ip");
+ if (dl.dl_type == DL_ETHER)
+ pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
+ h_addr_index = 0;
+ addr4ptr = (uint_t *)hp->h_addr_list[h_addr_index];
+ while (addr4ptr != NULL) {
+ if (addr4offset == -1) {
+ pf_compare_value(IPV4_SRCADDR_OFFSET, 4,
+ *addr4ptr);
+ if (h_addr_index != 0)
+ pf_emit(ENF_OR);
+ pf_compare_value(IPV4_DSTADDR_OFFSET, 4,
+ *addr4ptr);
+ pf_emit(ENF_OR);
+ } else {
+ pf_compare_value(addr4offset, 4,
+ *addr4ptr);
+ if (h_addr_index != 0)
+ pf_emit(ENF_OR);
+ }
+ addr4ptr = (uint_t *)hp->h_addr_list[++h_addr_index];
+ }
+ pf_emit(ENF_AND);
+ } else {
+ /* first pass: IPv4 addresses */
+ h_addr_index = 0;
+ addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index];
+ first = B_TRUE;
+ while (addr6ptr != NULL) {
+ if (IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
+ if (first) {
+ pf_matchfn("ip");
+ if (dl.dl_type == DL_ETHER) {
+ pf_check_vlan_tag(
+ ENCAP_ETHERTYPE_OFF/2);
+ }
+ pass++;
+ }
+ IN6_V4MAPPED_TO_INADDR(addr6ptr,
+ (struct in_addr *)&addr4);
+ if (addr4offset == -1) {
+ pf_compare_value(IPV4_SRCADDR_OFFSET, 4,
+ addr4);
+ if (!first)
+ pf_emit(ENF_OR);
+ pf_compare_value(IPV4_DSTADDR_OFFSET, 4,
+ addr4);
+ pf_emit(ENF_OR);
+ } else {
+ pf_compare_value(addr4offset, 4,
+ addr4);
+ if (!first)
+ pf_emit(ENF_OR);
+ }
+ if (first)
+ first = B_FALSE;
+ }
+ addr6ptr = (struct in6_addr *)
+ hp->h_addr_list[++h_addr_index];
+ }
+ if (!first) {
+ pf_emit(ENF_AND);
+ }
+ /* second pass: IPv6 addresses */
+ h_addr_index = 0;
+ addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index];
+ first = B_TRUE;
+ while (addr6ptr != NULL) {
+ if (!IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
+ if (first) {
+ pf_matchfn("ip6");
+ if (dl.dl_type == DL_ETHER) {
+ pf_check_vlan_tag(
+ ENCAP_ETHERTYPE_OFF/2);
+ }
+ pass++;
+ }
+ if (addr6offset == -1) {
+ pf_compare_value_v6(IPV6_SRCADDR_OFFSET,
+ 16, *addr6ptr);
+ if (!first)
+ pf_emit(ENF_OR);
+ pf_compare_value_v6(IPV6_DSTADDR_OFFSET,
+ 16, *addr6ptr);
+ pf_emit(ENF_OR);
+ } else {
+ pf_compare_value_v6(addr6offset, 16,
+ *addr6ptr);
+ if (!first)
+ pf_emit(ENF_OR);
+ }
+ if (first)
+ first = B_FALSE;
+ }
+ addr6ptr = (struct in6_addr *)
+ hp->h_addr_list[++h_addr_index];
+ }
+ if (!first) {
+ pf_emit(ENF_AND);
+ }
+ if (pass == 2) {
+ pf_emit(ENF_OR);
+ }
+ }
+
+ freehostent(hp);
+}
+
+
+static void
+pf_compare_address(int offset, uint_t len, uchar_t *addr)
+{
+ uint32_t val;
+ uint16_t sval;
+ boolean_t didone = B_FALSE;
+
+ /*
+ * If the property being filtered on is absent in the media
+ * packet, error out.
+ */
+ if (offset == -1)
+ pr_err("filter option unsupported on media");
+
+ while (len > 0) {
+ if (len >= 4) {
+ (void) memcpy(&val, addr, 4);
+ pf_compare_value(offset, 4, val);
+ addr += 4;
+ offset += 4;
+ len -= 4;
+ } else if (len >= 2) {
+ (void) memcpy(&sval, addr, 2);
+ pf_compare_value(offset, 2, sval);
+ addr += 2;
+ offset += 2;
+ len -= 2;
+ } else {
+ pf_compare_value(offset++, 1, *addr++);
+ len--;
+ }
+ if (didone)
+ pf_emit(ENF_AND);
+ didone = B_TRUE;
+ }
+}
+
+/*
+ * Compare ethernet addresses.
+ */
+static void
+pf_etheraddr_match(which, hostname)
+ enum direction which;
+ char *hostname;
+{
+ struct ether_addr e, *ep = NULL;
+
+ if (isxdigit(*hostname))
+ ep = ether_aton(hostname);
+ if (ep == NULL) {
+ if (ether_hostton(hostname, &e))
+ if (!arp_for_ether(hostname, &e))
+ pr_err("cannot obtain ether addr for %s",
+ hostname);
+ ep = &e;
+ }
+
+ pf_clear_offset_register();
+
+ switch (which) {
+ case TO:
+ pf_compare_address(dl.dl_link_dest_offset, dl.dl_link_addr_len,
+ (uchar_t *)ep);
+ break;
+ case FROM:
+ pf_compare_address(dl.dl_link_src_offset, dl.dl_link_addr_len,
+ (uchar_t *)ep);
+ break;
+ case ANY:
+ pf_compare_address(dl.dl_link_dest_offset, dl.dl_link_addr_len,
+ (uchar_t *)ep);
+ pf_compare_address(dl.dl_link_src_offset, dl.dl_link_addr_len,
+ (uchar_t *)ep);
+ pf_emit(ENF_OR);
+ break;
+ }
+}
+
+/*
+ * Emit code to compare the network part of
+ * an IP address.
+ */
+static void
+pf_netaddr_match(which, netname)
+ enum direction which;
+ char *netname;
+{
+ uint_t addr;
+ uint_t mask = 0xff000000;
+ struct netent *np;
+
+ if (isdigit(*netname)) {
+ addr = inet_network(netname);
+ } else {
+ np = getnetbyname(netname);
+ if (np == NULL)
+ pr_err("net %s not known", netname);
+ addr = np->n_net;
+ }
+
+ /*
+ * Left justify the address and figure
+ * out a mask based on the supplied address.
+ * Set the mask according to the number of zero
+ * low-order bytes.
+ * Note: this works only for whole octet masks.
+ */
+ if (addr) {
+ while ((addr & ~mask) != 0) {
+ mask |= (mask >> 8);
+ }
+ }
+
+ pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
+
+ switch (which) {
+ case TO:
+ pf_compare_value_mask(IPV4_DSTADDR_OFFSET, 4, addr, mask);
+ break;
+ case FROM:
+ pf_compare_value_mask(IPV4_SRCADDR_OFFSET, 4, addr, mask);
+ break;
+ case ANY:
+ pf_compare_value_mask(IPV4_SRCADDR_OFFSET, 4, addr, mask);
+ pf_compare_value_mask(IPV4_DSTADDR_OFFSET, 4, addr, mask);
+ pf_emit(ENF_OR);
+ break;
+ }
+}
+
+/*
+ * Emit code to match on src or destination zoneid.
+ * The zoneid passed in is in network byte order.
+ */
+static void
+pf_match_zone(enum direction which, uint32_t zoneid)
+{
+ if (dl.dl_type != DL_IPNET)
+ pr_err("zone filter option unsupported on media");
+
+ switch (which) {
+ case TO:
+ pf_compare_zoneid(IPNET_DSTZONE_OFFSET, zoneid);
+ break;
+ case FROM:
+ pf_compare_zoneid(IPNET_SRCZONE_OFFSET, zoneid);
+ break;
+ case ANY:
+ pf_compare_zoneid(IPNET_SRCZONE_OFFSET, zoneid);
+ pf_compare_zoneid(IPNET_DSTZONE_OFFSET, zoneid);
+ pf_emit(ENF_OR);
+ break;
+ }
+}
+
+/*
+ * A helper function to keep the code to emit instructions
+ * to change the offset register in one place.
+ *
+ * INPUTS: offset - An value representing an offset in 16-bit
+ * words.
+ * OUTPUTS: If there is enough room in the storage for the
+ * packet filtering program, instructions to load
+ * a constant to the offset register. Otherwise,
+ * nothing.
+ */
+static void
+pf_emit_load_offset(uint_t offset)
+{
+ pf_emit(ENF_LOAD_OFFSET | ENF_NOP);
+ pf_emit(offset);
+}
+
+/*
+ * Clear pfmod's offset register.
+ *
+ * INPUTS: none
+ * OUTPUTS: Instructions to clear the offset register if
+ * there is enough space remaining in the packet
+ * filtering program structure's storage, and
+ * the last thing done to the offset register was
+ * not clearing the offset register. Otherwise,
+ * nothing.
+ */
+static void
+pf_clear_offset_register()
+{
+ if (last_offset_operation != (void*)pf_clear_offset_register) {
+ pf_emit_load_offset(0);
+ last_offset_operation = (void*)pf_clear_offset_register;
+ }
+}
+
+/*
+ * This function will issue opcodes to check if a packet
+ * is VLAN tagged, and if so, update the offset register
+ * with the appropriate offset.
+ *
+ * Note that if the packet is not VLAN tagged, then the offset
+ * register will be cleared.
+ *
+ * If the interface type is not an ethernet type, then this
+ * function returns without doing anything.
+ *
+ * If the last attempt to change the offset register occured because
+ * of a call to this function that was called with the same offset,
+ * then we don't issue packet filtering instructions.
+ *
+ * INPUTS: offset - an offset in 16 bit words. The function
+ * will set the offset register to this
+ * value if the packet is VLAN tagged.
+ * OUTPUTS: If the conditions are met, packet filtering instructions.
+ */
+static void
+pf_check_vlan_tag(uint_t offset)
+{
+ static uint_t last_offset = 0;
+
+ if ((interface->mac_type == DL_ETHER ||
+ interface->mac_type == DL_CSMACD) &&
+ (last_offset_operation != (void*)pf_check_vlan_tag ||
+ last_offset != offset)) {
+ /*
+ * First thing is to clear the offset register.
+ * We don't know what state it is in, and if it
+ * is not zero, then we have no idea what we load
+ * when we execute ENF_PUSHWORD.
+ */
+ pf_clear_offset_register();
+
+ /*
+ * Check the ethertype.
+ */
+ pf_compare_value(dl.dl_link_type_offset, 2,
+ htons(ETHERTYPE_VLAN));
+
+ /*
+ * And if it's not VLAN, don't load offset to the offset
+ * register.
+ */
+ pf_emit(ENF_BRFL | ENF_NOP);
+ pf_emit(3);
+
+ /*
+ * Otherwise, load offset to the offset register.
+ */
+ pf_emit_load_offset(offset);
+
+ /*
+ * Now get rid of the results of the comparison,
+ * we don't want the results of the comparison to affect
+ * other logic in the packet filtering program.
+ */
+ pf_emit(ENF_POP | ENF_NOP);
+
+ /*
+ * Set the last operation at the end, or any time
+ * after the call to pf_clear_offset because
+ * pf_clear_offset uses it.
+ */
+ last_offset_operation = (void*)pf_check_vlan_tag;
+ last_offset = offset;
+ }
+}
+
+/*
+ * Utility function used to emit packet filtering code
+ * to match an ethertype.
+ *
+ * INPUTS: ethertype - The ethertype we want to check for.
+ * Don't call htons on the ethertype before
+ * calling this function.
+ * OUTPUTS: If there is sufficient storage available, packet
+ * filtering code to check an ethertype. Otherwise,
+ * nothing.
+ */
+static void
+pf_match_ethertype(uint_t ethertype)
+{
+ /*
+ * If the user wants to filter on ethertype VLAN,
+ * then clear the offset register so that the offset
+ * for ENF_PUSHWORD points to the right place in the
+ * packet.
+ *
+ * Otherwise, call pf_check_vlan_tag to set the offset
+ * register such that the contents of the offset register
+ * plus the argument for ENF_PUSHWORD point to the right
+ * part of the packet, whether or not the packet is VLAN
+ * tagged. We call pf_check_vlan_tag with an offset of
+ * two words because if the packet is VLAN tagged, we have
+ * to move past the ethertype in the ethernet header, and
+ * past the lower two octets of the VLAN header to get to
+ * the ethertype in the VLAN header.
+ */
+ if (ethertype == ETHERTYPE_VLAN)
+ pf_clear_offset_register();
+ else
+ pf_check_vlan_tag(2);
+
+ pf_compare_value(dl.dl_link_type_offset, 2, htons(ethertype));
+}
+
+static void
+pf_match_ipnettype(uint_t type)
+{
+ pf_compare_value(dl.dl_link_type_offset, 2, htons(type));
+}
+
+static void
+pf_match_ibtype(uint_t type)
+{
+ pf_compare_value(dl.dl_link_type_offset, 2, htons(type));
+}
+
+/*
+ * This function uses the table above to generate a
+ * piece of a packet filtering program to check a transport
+ * protocol type.
+ *
+ * INPUTS: tranport_protocol - the transport protocol we're
+ * interested in.
+ * OUTPUTS: If there is sufficient storage, then packet filtering
+ * code to check a transport protocol type. Otherwise,
+ * nothing.
+ */
+static void
+pf_check_transport_protocol(uint_t transport_protocol)
+{
+ int i;
+ uint_t number_of_matches = 0;
+
+ for (i = 0; dl.dl_trans_map_tbl[i].transport_protocol != -1; i++) {
+ if (transport_protocol ==
+ (uint_t)dl.dl_trans_map_tbl[i].transport_protocol) {
+ number_of_matches++;
+ dl.dl_match_fn(dl.dl_trans_map_tbl[i].network_protocol);
+ pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
+ pf_compare_value(dl.dl_trans_map_tbl[i].offset +
+ dl.dl_link_header_len, 1,
+ transport_protocol);
+ pf_emit(ENF_AND);
+ if (number_of_matches > 1) {
+ /*
+ * Since we have two or more matches, in
+ * order to have a correct and complete
+ * program we need to OR the result of
+ * each block of comparisons together.
+ */
+ pf_emit(ENF_OR);
+ }
+ }
+ }
+}
+
+static void
+pf_matchfn(const char *proto)
+{
+ int i;
+
+ for (i = 0; dl.dl_net_map_tbl[i].nmt_val != -1; i++) {
+ if (strcmp(proto, dl.dl_net_map_tbl[i].nmt_name) == 0) {
+ dl.dl_match_fn(dl.dl_net_map_tbl[i].nmt_val);
+ break;
+ }
+ }
+}
+
+static void
+pf_primary()
+{
+ for (;;) {
+ if (tokentype == FIELD)
+ break;
+
+ if (EQ("ip")) {
+ pf_matchfn("ip");
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("ip6")) {
+ pf_matchfn("ip6");
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("pppoe")) {
+ pf_matchfn("pppoe");
+ pf_match_ethertype(ETHERTYPE_PPPOES);
+ pf_emit(ENF_OR);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("pppoed")) {
+ pf_matchfn("pppoed");
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("pppoes")) {
+ pf_matchfn("pppoes");
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("arp")) {
+ pf_matchfn("arp");
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("vlan")) {
+ pf_matchfn("vlan");
+ pf_compare_value_mask_neq(VLAN_ID_OFFSET, 2,
+ 0, VLAN_ID_MASK);
+ pf_emit(ENF_AND);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("vlan-id")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("VLAN ID expected");
+ pf_matchfn("vlan-id");
+ pf_compare_value_mask(VLAN_ID_OFFSET, 2, tokenval,
+ VLAN_ID_MASK);
+ pf_emit(ENF_AND);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("rarp")) {
+ pf_matchfn("rarp");
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("tcp")) {
+ pf_check_transport_protocol(IPPROTO_TCP);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("udp")) {
+ pf_check_transport_protocol(IPPROTO_UDP);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("ospf")) {
+ pf_check_transport_protocol(IPPROTO_OSPF);
+ opstack++;
+ next();
+ break;
+ }
+
+
+ if (EQ("sctp")) {
+ pf_check_transport_protocol(IPPROTO_SCTP);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("icmp")) {
+ pf_check_transport_protocol(IPPROTO_ICMP);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("icmp6")) {
+ pf_check_transport_protocol(IPPROTO_ICMPV6);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("ip-in-ip")) {
+ pf_check_transport_protocol(IPPROTO_ENCAP);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("esp")) {
+ pf_check_transport_protocol(IPPROTO_ESP);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("ah")) {
+ pf_check_transport_protocol(IPPROTO_AH);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("(")) {
+ inBrace++;
+ next();
+ pf_expression();
+ if (EQ(")")) {
+ if (inBrace)
+ inBraceOR--;
+ inBrace--;
+ next();
+ }
+ break;
+ }
+
+ if (EQ("to") || EQ("dst")) {
+ dir = TO;
+ next();
+ continue;
+ }
+
+ if (EQ("from") || EQ("src")) {
+ dir = FROM;
+ next();
+ continue;
+ }
+
+ if (EQ("ether")) {
+ eaddr = 1;
+ next();
+ continue;
+ }
+
+ if (EQ("inet")) {
+ next();
+ if (EQ("host"))
+ next();
+ if (tokentype != ALPHA && tokentype != ADDR_IP)
+ pr_err("host/IPv4 addr expected after inet");
+ pf_ipaddr_match(dir, token, IPV4_ONLY);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("inet6")) {
+ next();
+ if (EQ("host"))
+ next();
+ if (tokentype != ALPHA && tokentype != ADDR_IP6)
+ pr_err("host/IPv6 addr expected after inet6");
+ pf_ipaddr_match(dir, token, IPV6_ONLY);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("proto")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("IP proto type expected");
+ pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
+ pf_compare_value(
+ IPV4_TYPE_HEADER_OFFSET + dl.dl_link_header_len, 1,
+ tokenval);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("broadcast")) {
+ pf_clear_offset_register();
+ pf_compare_value(dl.dl_link_dest_offset, 4, 0xffffffff);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("multicast")) {
+ pf_clear_offset_register();
+ pf_compare_value_mask(
+ dl.dl_link_dest_offset, 1, 0x01, 0x01);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("ethertype")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("ether type expected");
+ pf_match_ethertype(tokenval);
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("net") || EQ("dstnet") || EQ("srcnet")) {
+ if (EQ("dstnet"))
+ dir = TO;
+ else if (EQ("srcnet"))
+ dir = FROM;
+ next();
+ pf_netaddr_match(dir, token);
+ dir = ANY;
+ opstack++;
+ next();
+ break;
+ }
+
+ if (EQ("zone")) {
+ next();
+ if (tokentype != NUMBER)
+ pr_err("zoneid expected after inet");
+ pf_match_zone(dir, BE_32((uint32_t)(tokenval)));
+ opstack++;
+ next();
+ break;
+ }
+
+ /*
+ * Give up on anything that's obviously
+ * not a primary.
+ */
+ if (EQ("and") || EQ("or") ||
+ EQ("not") || EQ("decnet") || EQ("apple") ||
+ EQ("length") || EQ("less") || EQ("greater") ||
+ EQ("port") || EQ("srcport") || EQ("dstport") ||
+ EQ("rpc") || EQ("gateway") || EQ("nofrag") ||
+ EQ("bootp") || EQ("dhcp") || EQ("dhcp6") ||
+ EQ("slp") || EQ("ldap")) {
+ break;
+ }
+
+ if (EQ("host") || EQ("between") ||
+ tokentype == ALPHA || /* assume its a hostname */
+ tokentype == ADDR_IP ||
+ tokentype == ADDR_IP6 ||
+ tokentype == ADDR_ETHER) {
+ if (EQ("host") || EQ("between"))
+ next();
+ if (eaddr || tokentype == ADDR_ETHER) {
+ pf_etheraddr_match(dir, token);
+ } else if (tokentype == ALPHA) {
+ pf_ipaddr_match(dir, token, IPV4_AND_IPV6);
+ } else if (tokentype == ADDR_IP) {
+ pf_ipaddr_match(dir, token, IPV4_ONLY);
+ } else {
+ pf_ipaddr_match(dir, token, IPV6_ONLY);
+ }
+ dir = ANY;
+ eaddr = 0;
+ opstack++;
+ next();
+ break;
+ }
+
+ break; /* unknown token */
+ }
+}
+
+static void
+pf_alternation()
+{
+ int s = opstack;
+
+ pf_primary();
+ for (;;) {
+ if (EQ("and"))
+ next();
+ pf_primary();
+ if (opstack != s + 2)
+ break;
+ pf_emit(ENF_AND);
+ opstack--;
+ }
+}
+
+static void
+pf_expression()
+{
+ pf_alternation();
+ while (EQ("or") || EQ(",")) {
+ if (inBrace)
+ inBraceOR++;
+ else
+ foundOR++;
+ next();
+ pf_alternation();
+ pf_emit(ENF_OR);
+ opstack--;
+ }
+}
+
+/*
+ * Attempt to compile the expression
+ * in the string "e". If we can generate
+ * pf code for it then return 1 - otherwise
+ * return 0 and leave it up to the user-level
+ * filter.
+ */
+int
+pf_compile(e, print)
+ char *e;
+ int print;
+{
+ char *argstr;
+ char *sav_str, *ptr, *sav_ptr;
+ int inBr = 0, aheadOR = 0;
+
+ argstr = strdup(e);
+ sav_str = e;
+ tkp = argstr;
+ dir = ANY;
+
+ pfp = &pf.Pf_Filter[0];
+ if (setjmp(env)) {
+ return (0);
+ }
+
+ /*
+ * Set media specific packet offsets that this code uses.
+ */
+ if (interface->mac_type == DL_ETHER) {
+ dl.dl_type = DL_ETHER;
+ dl.dl_match_fn = pf_match_ethertype;
+ dl.dl_trans_map_tbl = ether_transport_mapping_table;
+ dl.dl_net_map_tbl = ether_network_mapping_table;
+ dl.dl_link_header_len = 14;
+ dl.dl_link_type_offset = 12;
+ dl.dl_link_dest_offset = 0;
+ dl.dl_link_src_offset = 6;
+ dl.dl_link_addr_len = 6;
+ }
+
+ if (interface->mac_type == DL_IB) {
+ dl.dl_type = DL_IB;
+ dl.dl_link_header_len = 4;
+ dl.dl_link_type_offset = 0;
+ dl.dl_link_dest_offset = dl.dl_link_src_offset = -1;
+ dl.dl_link_addr_len = 20;
+ dl.dl_match_fn = pf_match_ibtype;
+ dl.dl_trans_map_tbl = ib_transport_mapping_table;
+ dl.dl_net_map_tbl = ib_network_mapping_table;
+ }
+
+ if (interface->mac_type == DL_IPNET) {
+ dl.dl_type = DL_IPNET;
+ dl.dl_link_header_len = 24;
+ dl.dl_link_type_offset = 0;
+ dl.dl_link_dest_offset = dl.dl_link_src_offset = -1;
+ dl.dl_link_addr_len = -1;
+ dl.dl_match_fn = pf_match_ipnettype;
+ dl.dl_trans_map_tbl = ipnet_transport_mapping_table;
+ dl.dl_net_map_tbl = ipnet_network_mapping_table;
+ }
+
+ next();
+ pf_expression();
+
+ if (tokentype != EOL) {
+ /*
+ * The idea here is to do as much filtering as possible in
+ * the kernel. So even if we find a token we don't understand,
+ * we try to see if we can still set up a portion of the filter
+ * in the kernel and use the userland filter to filter the
+ * remaining stuff. Obviously, if our filter expression is of
+ * type A AND B, we can filter A in kernel and then apply B
+ * to the packets that got through. The same is not true for
+ * a filter of type A OR B. We can't apply A first and then B
+ * on the packets filtered through A.
+ *
+ * (We need to keep track of the fact when we find an OR,
+ * and the fact that we are inside brackets when we find OR.
+ * The variable 'foundOR' tells us if there was an OR behind,
+ * 'inBraceOR' tells us if we found an OR before we could find
+ * the end brace i.e. ')', and variable 'aheadOR' checks if
+ * there is an OR in the expression ahead. if either of these
+ * cases become true, we can't split the filtering)
+ */
+
+ if (foundOR || inBraceOR) {
+ /* FORGET IN KERNEL FILTERING */
+ return (0);
+ } else {
+
+ /* CHECK IF NO OR AHEAD */
+ sav_ptr = (char *)((uintptr_t)sav_str +
+ (uintptr_t)sav_tkp -
+ (uintptr_t)argstr);
+ ptr = sav_ptr;
+ while (*ptr != '\0') {
+ switch (*ptr) {
+ case '(':
+ inBr++;
+ break;
+ case ')':
+ inBr--;
+ break;
+ case 'o':
+ case 'O':
+ if ((*(ptr + 1) == 'R' ||
+ *(ptr + 1) == 'r') && !inBr)
+ aheadOR = 1;
+ break;
+ case ',':
+ if (!inBr)
+ aheadOR = 1;
+ break;
+ }
+ ptr++;
+ }
+ if (!aheadOR) {
+ /* NO OR AHEAD, SPLIT UP THE FILTERING */
+ pf.Pf_FilterLen = pfp - &pf.Pf_Filter[0];
+ pf.Pf_Priority = 5;
+ if (print) {
+ pf_codeprint(&pf.Pf_Filter[0],
+ pf.Pf_FilterLen);
+ }
+ compile(sav_ptr, print);
+ return (2);
+ } else
+ return (0);
+ }
+ }
+
+ pf.Pf_FilterLen = pfp - &pf.Pf_Filter[0];
+ pf.Pf_Priority = 5; /* unimportant, so long as > 2 */
+ if (print) {
+ pf_codeprint(&pf.Pf_Filter[0], pf.Pf_FilterLen);
+ }
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pmap.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pmap.c
new file mode 100644
index 0000000..f31fb4c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pmap.c
@@ -0,0 +1,772 @@
+/*
+ * 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 (c) 1991, 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+/*
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <sys/tiuser.h>
+#include <string.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <rpc/pmap_prot.h>
+#include "snoop.h"
+
+/*
+ * Number of bytes to display from a string (address, netid, etc.).
+ */
+#define MAXSTRINGLEN 64
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+static void interpret_pmap_2(int, int, int, int, int, char *, int);
+static void interpret_pmap_4(int, int, int, int, int, char *, int);
+static void stash_callit(ulong_t, int, int, int, int);
+
+void
+interpret_pmap(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ switch (vers) {
+ case 2: interpret_pmap_2(flags, type, xid, vers, proc, data, len);
+ break;
+
+ /* Version 3 is a subset of version 4 */
+ case 3:
+ case 4: interpret_pmap_4(flags, type, xid, vers, proc, data, len);
+ break;
+ }
+}
+
+void show_pmap();
+char *sum_pmaplist();
+void show_pmaplist();
+
+static char *procnames_short_2[] = {
+ "Null", /* 0 */
+ "SET", /* 1 */
+ "UNSET", /* 2 */
+ "GETPORT", /* 3 */
+ "DUMP", /* 4 */
+ "CALLIT", /* 5 */
+};
+
+static char *procnames_long_2[] = {
+ "Null procedure", /* 0 */
+ "Set port", /* 1 */
+ "Unset port", /* 2 */
+ "Get port number", /* 3 */
+ "Dump the mappings", /* 4 */
+ "Indirect call", /* 5 */
+};
+
+#define MAXPROC_2 5
+
+void
+interpret_pmap_2(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ unsigned port, proto;
+ unsigned iprog, ivers, iproc, ilen;
+ extern int pi_frame;
+ struct cache_struct *x, *find_callit();
+ int trailer_done = 0;
+
+ if (proc < 0 || proc > MAXPROC_2)
+ return;
+
+ if (proc == PMAPPROC_CALLIT) {
+ if (type == CALL) {
+ iprog = getxdr_u_long();
+ ivers = getxdr_u_long();
+ iproc = getxdr_u_long();
+ stash_callit(xid, pi_frame, iprog, ivers, iproc);
+ } else {
+ x = find_callit(xid);
+ }
+ }
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line, "PORTMAP C %s",
+ procnames_short_2[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case PMAPPROC_GETPORT:
+ iprog = getxdr_u_long();
+ ivers = getxdr_u_long();
+ proto = getxdr_u_long();
+ (void) sprintf(line,
+ " prog=%d (%s) vers=%d proto=%s",
+ iprog, nameof_prog(iprog),
+ ivers,
+ getproto(proto));
+ break;
+ case PMAPPROC_CALLIT:
+ (void) sprintf(line,
+ " prog=%s vers=%d proc=%d",
+ nameof_prog(iprog),
+ ivers, iproc);
+ if (flags & F_ALLSUM) {
+ (void) getxdr_u_long(); /* length */
+ data += 16; /* prog+ver+proc+len */
+ len -= 16;
+ protoprint(flags, type, xid,
+ iprog, ivers, iproc,
+ data, len);
+ }
+ break;
+ default:
+ break;
+ }
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "PORTMAP R %s ",
+ procnames_short_2[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case PMAPPROC_GETPORT:
+ port = getxdr_u_long();
+ (void) sprintf(line, "port=%d", port);
+ break;
+ case PMAPPROC_DUMP:
+ (void) sprintf(line, "%s", sum_pmaplist());
+ break;
+ case PMAPPROC_CALLIT:
+ port = getxdr_u_long();
+ ilen = getxdr_u_long();
+ (void) sprintf(line, "port=%d len=%d",
+ port, ilen);
+ if (flags & F_ALLSUM && x != NULL) {
+ data += 8; /* port+len */
+ len -= 8;
+ protoprint(flags, type, xid,
+ x->xid_prog,
+ x->xid_vers,
+ x->xid_proc,
+ data, len);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("PMAP: ", "Portmapper", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long_2[proc]);
+ if (type == CALL) {
+ switch (proc) {
+ case PMAPPROC_NULL:
+ case PMAPPROC_SET:
+ case PMAPPROC_UNSET:
+ break;
+ case PMAPPROC_GETPORT:
+ iprog = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Program = %d (%s)",
+ iprog, nameof_prog(iprog));
+ (void) showxdr_u_long("Version = %d");
+ proto = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Protocol = %d (%s)",
+ proto, getproto(proto));
+ break;
+ case PMAPPROC_DUMP:
+ break;
+ case PMAPPROC_CALLIT:
+ (void) sprintf(get_line(0, 0),
+ "Program = %d (%s)",
+ iprog, nameof_prog(iprog));
+ (void) sprintf(get_line(0, 0),
+ "Version = %d", ivers);
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d", iproc);
+ (void) showxdr_u_long("Callit data = %d bytes");
+ show_trailer();
+ trailer_done = 1;
+ data += 16; /* prog+ver+proc+len */
+ len -= 16;
+ protoprint(flags, type, xid,
+ iprog, ivers, iproc,
+ data, len);
+ break;
+ }
+ } else {
+ switch (proc) {
+ case PMAPPROC_NULL:
+ case PMAPPROC_SET:
+ case PMAPPROC_UNSET:
+ break;
+ case PMAPPROC_GETPORT:
+ (void) showxdr_u_long("Port = %d");
+ break;
+ case PMAPPROC_DUMP:
+ show_pmaplist();
+ break;
+ case PMAPPROC_CALLIT:
+ (void) showxdr_u_long("Port = %d");
+ (void) showxdr_u_long("Length = %d bytes");
+ show_trailer();
+ trailer_done = 1;
+ if (x != NULL) {
+ protoprint(flags, type, xid,
+ x->xid_prog,
+ x->xid_vers,
+ x->xid_proc,
+ data, len);
+ }
+ break;
+ }
+ }
+ if (!trailer_done)
+ show_trailer();
+ }
+}
+
+char *
+sum_pmaplist()
+{
+ int maps = 0;
+ static char buff[16];
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, "%d+ map(s) found", maps);
+ return (buff);
+ }
+
+ while (getxdr_u_long()) {
+ (void) getxdr_u_long(); /* program */
+ (void) getxdr_u_long(); /* version */
+ (void) getxdr_u_long(); /* protocol */
+ (void) getxdr_u_long(); /* port */
+ maps++;
+ }
+
+ (void) sprintf(buff, "%d map(s) found", maps);
+ return (buff);
+}
+
+void
+show_pmaplist()
+{
+ unsigned prog, vers, proto, port;
+ int maps = 0;
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ maps. (Frame is incomplete)",
+ maps);
+ return;
+ }
+
+ (void) sprintf(get_line(0, 0), " Program Version Protocol Port");
+
+ while (getxdr_u_long()) {
+ prog = getxdr_u_long();
+ vers = getxdr_u_long();
+ proto = getxdr_u_long();
+ port = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "%8d%8d%9d%7d %s",
+ prog, vers, proto, port, nameof_prog(prog));
+ maps++;
+ }
+
+ (void) sprintf(get_line(0, 0), " %d maps", maps);
+}
+
+/*
+ * ******************************************
+ */
+char *sum_rpcblist();
+void show_rpcblist();
+char *sum_rpcb_entry_list();
+void show_rpcb_entry_list();
+
+static char *procnames_short_4[] = {
+ /*
+ * version 3 and 4 procs
+ */
+ "Null", /* 0 */
+ "SET", /* 1 */
+ "UNSET", /* 2 */
+ "GETADDR", /* 3 */
+ "DUMP", /* 4 */
+ "BCAST", /* 5 */
+ "GETTIME", /* 6 */
+ "UADDR2TADDR", /* 7 */
+ "TADDR2UADDR", /* 8 */
+ /*
+ * version 4 procs only
+ */
+ "GETVERSADDR", /* 9 */
+ "INDIRECT", /* 10 */
+ "GETADDRLIST", /* 11 */
+ "GETSTAT", /* 12 */
+};
+
+static char *procnames_long_4[] = {
+ /*
+ * version 3 and 4 procs
+ */
+ "Null procedure", /* 0 */
+ "Set address", /* 1 */
+ "Unset address", /* 2 */
+ "Get address", /* 3 */
+ "Dump the mappings", /* 4 */
+ "Broadcast call (no error)", /* 5 */
+ "Get the time", /* 6 */
+ "Universal to transport address", /* 7 */
+ "Transport to universal address", /* 8 */
+ /*
+ * version 4 procs only
+ */
+ "Get address of specific version", /* 9 */
+ "Indirect call (return error)", /* 10 */
+ "Return addresses of prog/vers", /* 11 */
+ "Get statistics", /* 12 */
+};
+
+#define MAXPROC_3 8
+#define MAXPROC_4 12
+#define RPCBPROC_NULL 0
+
+void
+interpret_pmap_4(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ unsigned prog, ver;
+ char buff1[MAXSTRINGLEN + 1];
+ int iprog, ivers, iproc, ilen;
+ extern int pi_frame;
+ struct cache_struct *x, *find_callit();
+ int trailer_done = 0;
+
+ if (proc < 0 || proc > MAXPROC_4 || (vers == 3 && proc > MAXPROC_3))
+ return;
+
+ if (proc == RPCBPROC_BCAST || proc == RPCBPROC_INDIRECT) {
+ if (type == CALL) {
+ iprog = getxdr_u_long();
+ ivers = getxdr_u_long();
+ iproc = getxdr_u_long();
+ stash_callit(xid, pi_frame,
+ iprog, ivers, iproc);
+ } else {
+ x = find_callit(xid);
+ }
+ }
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "RPCBIND C %s",
+ procnames_short_4[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case RPCBPROC_SET:
+ case RPCBPROC_UNSET:
+ case RPCBPROC_GETADDR:
+ case RPCBPROC_GETVERSADDR:
+ case RPCBPROC_GETADDRLIST:
+ prog = getxdr_u_long();
+ ver = getxdr_u_long();
+ (void) sprintf(line,
+ " prog=%d (%s) vers=%d",
+ prog, nameof_prog(prog),
+ ver);
+ break;
+ case RPCBPROC_BCAST:
+ case RPCBPROC_INDIRECT:
+ (void) sprintf(line,
+ " prog=%s vers=%d proc=%d",
+ nameof_prog(iprog),
+ ivers, iproc);
+ if (flags & F_ALLSUM) {
+ (void) getxdr_u_long(); /* length */
+ data += 16; /* prog+ver+proc+len */
+ len -= 16;
+ protoprint(flags, type, xid,
+ iprog, ivers, iproc,
+ data, len);
+ }
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, xid);
+ } else {
+ int pos;
+
+ (void) sprintf(line, "RPCBIND R %s ",
+ procnames_short_4[proc]);
+ line += strlen(line);
+ switch (proc) {
+ case RPCBPROC_GETADDR:
+ case RPCBPROC_TADDR2UADDR:
+ case RPCBPROC_GETVERSADDR:
+ (void) getxdr_string(buff1, MAXSTRINGLEN);
+ (void) sprintf(line,
+ " Uaddr=%s",
+ buff1);
+ break;
+ case RPCBPROC_BCAST:
+ case RPCBPROC_INDIRECT:
+ pos = getxdr_pos();
+ (void) getxdr_string(buff1, MAXSTRINGLEN);
+ ilen = getxdr_u_long();
+ (void) sprintf(line, "Uaddr=%s len=%d",
+ buff1, ilen);
+ if (flags & F_ALLSUM && x != NULL) {
+ pos = getxdr_pos() - pos;
+ data += pos; /* uaddr+len */
+ len -= pos;
+ protoprint(flags, type, xid,
+ x->xid_prog,
+ x->xid_vers,
+ x->xid_proc,
+ data, len);
+ }
+ break;
+ case RPCBPROC_DUMP:
+ (void) sprintf(line, "%s",
+ sum_rpcblist());
+ break;
+ case RPCBPROC_GETTIME:
+ {
+ time_t sec = getxdr_long();
+ struct tm *tmp = gmtime(&sec);
+ (void) strftime(line, MAXLINE,
+ "%d-%h-%y %T GMT", tmp);
+ }
+ break;
+ case RPCBPROC_GETADDRLIST:
+ (void) sprintf(line, "%s",
+ sum_rpcb_entry_list());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("RPCB: ", "RPC Bind", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long_4[proc]);
+ if (type == CALL) {
+ switch (proc) {
+ case RPCBPROC_NULL:
+ break;
+ case RPCBPROC_SET:
+ case RPCBPROC_UNSET:
+ case RPCBPROC_GETADDR:
+ case RPCBPROC_GETVERSADDR:
+ case RPCBPROC_GETADDRLIST:
+ (void) showxdr_u_long("Program = %d");
+ (void) showxdr_u_long("Version = %d");
+ (void) showxdr_string(64, "Netid = %s");
+ break;
+ case RPCBPROC_DUMP:
+ break;
+ case RPCBPROC_BCAST:
+ case RPCBPROC_INDIRECT:
+ (void) sprintf(get_line(0, 0),
+ "Program = %d (%s)",
+ iprog, nameof_prog(iprog));
+ (void) sprintf(get_line(0, 0),
+ "Version = %d", ivers);
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d", iproc);
+ (void) showxdr_u_long(
+ "Callit data = %d bytes");
+ show_trailer();
+ trailer_done = 1;
+ data += 16; /* prog+ver+proc+len */
+ len -= 16;
+ protoprint(flags, type, xid,
+ iprog, ivers, iproc,
+ data, len);
+ break;
+ case RPCBPROC_GETTIME:
+ break;
+ case RPCBPROC_UADDR2TADDR:
+ case RPCBPROC_TADDR2UADDR:
+ break;
+ }
+ } else {
+ switch (proc) {
+ case RPCBPROC_NULL:
+ case RPCBPROC_SET:
+ case RPCBPROC_UNSET:
+ break;
+ case RPCBPROC_GETADDR:
+ case RPCBPROC_TADDR2UADDR:
+ case RPCBPROC_GETVERSADDR:
+ (void) showxdr_string(64, "Uaddr = %s");
+ break;
+ case RPCBPROC_DUMP:
+ show_rpcblist();
+ break;
+ case RPCBPROC_BCAST:
+ case RPCBPROC_INDIRECT:
+ (void) showxdr_string(64, "Uaddr = %s");
+ (void) showxdr_u_long("Length = %d bytes");
+ show_trailer();
+ trailer_done = 1;
+ if (x != NULL) {
+ protoprint(flags, type, xid,
+ x->xid_prog,
+ x->xid_vers,
+ x->xid_proc,
+ data, len);
+ }
+ break;
+ case RPCBPROC_GETTIME:
+ {
+ int pos = getxdr_pos();
+ time_t sec = getxdr_long();
+ struct tm *tmp = gmtime(&sec);
+ (void) strftime(get_line(pos,
+ getxdr_pos()), MAXLINE,
+ "Time = %d-%h-%y %T GMT", tmp);
+ }
+ break;
+ case RPCBPROC_UADDR2TADDR:
+ break;
+ case RPCBPROC_GETADDRLIST:
+ show_rpcb_entry_list();
+ break;
+ }
+ }
+ if (!trailer_done)
+ show_trailer();
+ }
+}
+
+char *
+sum_rpcblist()
+{
+ int maps = 0;
+ static char buff[MAXSTRINGLEN + 1];
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, "%d+ map(s) found", maps);
+ return (buff);
+ }
+
+ while (getxdr_u_long()) {
+ (void) getxdr_u_long(); /* program */
+ (void) getxdr_u_long(); /* version */
+ (void) getxdr_string(buff, MAXSTRINGLEN); /* netid */
+ (void) getxdr_string(buff, MAXSTRINGLEN); /* uaddr */
+ (void) getxdr_string(buff, MAXSTRINGLEN); /* owner */
+ maps++;
+ }
+
+ (void) sprintf(buff, "%d map(s) found", maps);
+ return (buff);
+}
+
+void
+show_rpcblist()
+{
+ unsigned prog, vers;
+ char netid[MAXSTRINGLEN + 1], uaddr[MAXSTRINGLEN + 1];
+ char owner[MAXSTRINGLEN + 1];
+ int maps = 0;
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ maps. (Frame is incomplete)",
+ maps);
+ return;
+ }
+
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ " Program Vers Netid Uaddr Owner");
+
+ while (getxdr_u_long()) {
+ prog = getxdr_u_long();
+ vers = getxdr_u_long();
+ (void) getxdr_string(netid, MAXSTRINGLEN);
+ (void) getxdr_string(uaddr, MAXSTRINGLEN);
+ (void) getxdr_string(owner, MAXSTRINGLEN);
+ (void) sprintf(get_line(0, 0),
+ "%8d%5d %-12s %-18s %-10s (%s)",
+ prog, vers,
+ netid, uaddr, owner,
+ nameof_prog(prog));
+ maps++;
+ }
+
+ (void) sprintf(get_line(0, 0), " (%d maps)", maps);
+}
+
+char *
+sum_rpcb_entry_list()
+{
+ int maps = 0;
+ static char buff[MAXSTRINGLEN + 1];
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(buff, "%d+ map(s) found", maps);
+ return (buff);
+ }
+
+ while (getxdr_u_long()) {
+ (void) getxdr_string(buff, MAXSTRINGLEN); /* maddr */
+ (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_netid */
+ (void) getxdr_u_long(); /* nc_semantics */
+ (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_protofmly */
+ (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_proto */
+ maps++;
+ }
+
+ (void) sprintf(buff, "%d map(s) found", maps);
+ return (buff);
+}
+
+char *semantics_strs[] = {"", "CLTS", "COTS", "COTS-ORD", "RAW"};
+
+void
+show_rpcb_entry_list()
+{
+ char maddr[MAXSTRINGLEN + 1], netid[MAXSTRINGLEN + 1];
+ char protofmly[MAXSTRINGLEN + 1], proto[MAXSTRINGLEN + 1];
+ unsigned sem;
+ int maps = 0;
+
+ if (setjmp(xdr_err)) {
+ (void) sprintf(get_line(0, 0),
+ " %d+ maps. (Frame is incomplete)",
+ maps);
+ return;
+ }
+
+ show_space();
+ (void) sprintf(get_line(0, 0),
+ " Maddr Netid Semantics Protofmly Proto");
+
+ while (getxdr_u_long()) {
+ (void) getxdr_string(maddr, MAXSTRINGLEN);
+ (void) getxdr_string(netid, MAXSTRINGLEN);
+ sem = getxdr_u_long();
+ (void) getxdr_string(protofmly, MAXSTRINGLEN);
+ (void) getxdr_string(proto, MAXSTRINGLEN);
+ (void) sprintf(get_line(0, 0),
+ "%-12s %-12s %-8s %-8s %-8s",
+ maddr, netid,
+ semantics_strs[sem],
+ protofmly, proto);
+ maps++;
+ }
+
+ (void) sprintf(get_line(0, 0), " (%d maps)", maps);
+}
+
+#define CXID_CACHE_SIZE 16
+struct cache_struct cxid_cache[CXID_CACHE_SIZE];
+struct cache_struct *cxcpfirst = &cxid_cache[0];
+struct cache_struct *cxcp = &cxid_cache[0];
+struct cache_struct *cxcplast = &cxid_cache[CXID_CACHE_SIZE - 1];
+
+struct cache_struct *
+find_callit(xid)
+ ulong_t xid;
+{
+ struct cache_struct *x;
+
+ for (x = cxcp; x >= cxcpfirst; x--)
+ if (x->xid_num == xid)
+ return (x);
+ for (x = cxcplast; x > cxcp; x--)
+ if (x->xid_num == xid)
+ return (x);
+ return (NULL);
+}
+
+static void
+stash_callit(xid, frame, prog, vers, proc)
+ ulong_t xid;
+ int frame, prog, vers, proc;
+{
+ struct cache_struct *x;
+
+ x = find_callit(xid);
+ if (x == NULL) {
+ x = cxcp++;
+ if (cxcp > cxcplast)
+ cxcp = cxcpfirst;
+ x->xid_num = xid;
+ x->xid_frame = frame;
+ }
+ x->xid_prog = prog;
+ x->xid_vers = vers;
+ x->xid_proc = proc;
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.c
new file mode 100644
index 0000000..a056abd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.c
@@ -0,0 +1,1946 @@
+/*
+ * 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 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <net/ppp_defs.h>
+#include <net/ppp-comp.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+#include "snoop.h"
+#include "snoop_ppp.h"
+
+static int interpret_ppp_cp(int, uchar_t *, int, ppp_protoinfo_t *);
+static int interpret_cp_options(uchar_t *, int, ppp_protoinfo_t *);
+static int interpret_ppp_chap(int, uchar_t *, int, ppp_protoinfo_t *);
+static int interpret_ppp_pap(int, uchar_t *, int, ppp_protoinfo_t *);
+static int interpret_ppp_lqr(int, uchar_t *, int, ppp_protoinfo_t *);
+static ppp_protoinfo_t *ppp_getprotoinfo(uint16_t);
+static cp_optinfo_t *ppp_getoptinfo(cp_optinfo_t *, uint16_t);
+static optformat_func_t opt_format_vendor;
+static optformat_func_t opt_format_mru;
+static optformat_func_t opt_format_accm;
+static optformat_func_t opt_format_authproto;
+static optformat_func_t opt_format_qualproto;
+static optformat_func_t opt_format_magicnum;
+static optformat_func_t opt_format_fcs;
+static optformat_func_t opt_format_sdp;
+static optformat_func_t opt_format_nummode;
+static optformat_func_t opt_format_callback;
+static optformat_func_t opt_format_mrru;
+static optformat_func_t opt_format_epdisc;
+static optformat_func_t opt_format_dce;
+static optformat_func_t opt_format_linkdisc;
+static optformat_func_t opt_format_i18n;
+static optformat_func_t opt_format_ipaddresses;
+static optformat_func_t opt_format_ipcompproto;
+static optformat_func_t opt_format_ipaddress;
+static optformat_func_t opt_format_mobileipv4;
+static optformat_func_t opt_format_ifaceid;
+static optformat_func_t opt_format_ipv6compproto;
+static optformat_func_t opt_format_compoui;
+static optformat_func_t opt_format_bsdcomp;
+static optformat_func_t opt_format_staclzs;
+static optformat_func_t opt_format_mppc;
+static optformat_func_t opt_format_gandalf;
+static optformat_func_t opt_format_lzsdcp;
+static optformat_func_t opt_format_magnalink;
+static optformat_func_t opt_format_deflate;
+static optformat_func_t opt_format_encroui;
+static optformat_func_t opt_format_dese;
+static optformat_func_t opt_format_muxpid;
+
+/*
+ * Many strings below are initialized with "Unknown".
+ */
+static char unknown_string[] = "Unknown";
+
+/*
+ * Each known PPP protocol has an associated ppp_protoinfo_t in this array.
+ * Even if we can't decode the protocol (interpret_proto() == NULL),
+ * interpret_ppp() will at least print the protocol's name. There is no
+ * dependency on the ordering of the entries in this array. They have been
+ * ordered such that the most commonly used protocols are near the front.
+ * The array is delimited by a last entry of protocol of type
+ * PPP_PROTO_UNKNOWN.
+ */
+static ppp_protoinfo_t protoinfo_array[] = {
+ { PPP_IP, "IP", interpret_ip, NULL, NULL },
+ { PPP_IPV6, "IPv6", interpret_ipv6, NULL, NULL },
+ { PPP_COMP, "Compressed Data", NULL, NULL, NULL },
+ { PPP_OSI, "OSI", NULL, NULL, NULL },
+ { PPP_AT, "AppleTalk", NULL, NULL, NULL },
+ { PPP_IPX, "IPX", NULL, NULL, NULL },
+ { PPP_VJC_COMP, "VJ Compressed TCP", NULL, NULL, NULL },
+ { PPP_VJC_UNCOMP, "VJ Uncompressed TCP", NULL, NULL, NULL },
+ { PPP_BRIDGE, "Bridging", NULL, NULL, NULL },
+ { PPP_802HELLO, "802.1d Hello", NULL, NULL, NULL },
+ { PPP_MP, "MP", NULL, NULL, NULL },
+ { PPP_ENCRYPT, "Encryption", NULL, NULL, NULL },
+ { PPP_ENCRYPTFRAG, "Individual Link Encryption", NULL, NULL, NULL },
+ { PPP_MUX, "PPP Muxing", NULL, NULL, NULL },
+ { PPP_COMPFRAG, "Single Link Compressed Data", NULL, NULL, NULL },
+ { PPP_FULLHDR, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPTCP, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPNONTCP, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPUDP8, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPRTP8, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPTCPND, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPSTATE, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPUDP16, "IP Compression", NULL, NULL, NULL },
+ { PPP_COMPRTP16, "IP Compression", NULL, NULL, NULL },
+ { PPP_MPLS, "MPLS", NULL, NULL, NULL },
+ { PPP_MPLSMC, "MPLS M/C", NULL, NULL, NULL },
+ { PPP_LQR, "LQR", interpret_ppp_lqr, "PPP-LQR: ",
+ "Link Quality Report" },
+ { PPP_LCP, "LCP", interpret_ppp_cp, "PPP-LCP: ",
+ "Link Control Protocol" },
+ { PPP_IPCP, "IPCP", interpret_ppp_cp, "PPP-IPCP: ",
+ "IP Control Protocol" },
+ { PPP_IPV6CP, "IPV6CP", interpret_ppp_cp, "PPP-IPV6CP: ",
+ "IPv6 Control Protocol" },
+ { PPP_CCP, "CCP", interpret_ppp_cp, "PPP-CCP: ",
+ "Compression Control Protocol" },
+ { PPP_CCPFRAG, "CCP-Link", interpret_ppp_cp, "PPP-CCP-Link: ",
+ "Per-Link Compression Control Protocol" },
+ { PPP_ECP, "ECP", interpret_ppp_cp, "PPP-ECP: ",
+ "Encryption Control Protocol" },
+ { PPP_ECPFRAG, "ECP-Link", interpret_ppp_cp, "PPP-ECP-Link: ",
+ "Per-Link Encryption Control Protocol" },
+ { PPP_MPLSCP, "MPLSCP", NULL, NULL, NULL },
+ { PPP_OSINLCP, "OSINLCP", NULL, NULL, NULL },
+ { PPP_ATCP, "ATCP", NULL, NULL, NULL },
+ { PPP_IPXCP, "IPXCP", NULL, NULL, NULL },
+ { PPP_BACP, "BACP", NULL, NULL, NULL },
+ { PPP_BCP, "BCP", NULL, NULL, NULL },
+ { PPP_CBCP, "CBCP", NULL, NULL, NULL },
+ { PPP_BAP, "BAP", NULL, NULL, NULL },
+ { PPP_CHAP, "CHAP", interpret_ppp_chap, "CHAP: ",
+ "Challenge Handshake Authentication Protocl" },
+ { PPP_PAP, "PAP", interpret_ppp_pap, "PAP: ",
+ "Password Authentication Protocol" },
+ { PPP_EAP, "EAP", NULL, NULL, NULL },
+ { 0, unknown_string, NULL, NULL, NULL }
+};
+
+static cp_optinfo_t lcp_optinfo[] = {
+ { OPT_LCP_VENDOR, "Vendor-Specific", 6,
+ opt_format_vendor },
+ { OPT_LCP_MRU, "Maximum-Receive-Unit", 4,
+ opt_format_mru },
+ { OPT_LCP_ASYNCMAP, "Async-Control-Character-Map", 6,
+ opt_format_accm },
+ { OPT_LCP_AUTHTYPE, "Authentication-Protocol", 4,
+ opt_format_authproto },
+ { OPT_LCP_QUALITY, "Quality-Protocol", 4,
+ opt_format_qualproto },
+ { OPT_LCP_MAGICNUMBER, "Magic-Number", 6,
+ opt_format_magicnum },
+ { OPT_LCP_PCOMPRESSION, "Protocol-Field-Compression", 2, NULL },
+ { OPT_LCP_ACCOMPRESSION, "Address-and-Control-Field-Compression", 2,
+ NULL },
+ { OPT_LCP_FCSALTERN, "FCS-Alternative", 3,
+ opt_format_fcs },
+ { OPT_LCP_SELFDESCPAD, "Self-Describing-Padding", 3,
+ opt_format_sdp },
+ { OPT_LCP_NUMBERED, "Numbered-Mode", 3,
+ opt_format_nummode },
+ { OPT_LCP_MULTILINKPROC, "Multi-Link-Procedure", 2, NULL },
+ { OPT_LCP_CALLBACK, "Callback", 3,
+ opt_format_callback },
+ { OPT_LCP_CONNECTTIME, "Connect-Time", 2, NULL },
+ { OPT_LCP_COMPOUNDFRAMES, "Compound-Frames", 2, NULL },
+ { OPT_LCP_DATAENCAP, "Nominal-Data-Encapsulation", 2, NULL },
+ { OPT_LCP_MRRU, "Multilink-MRRU", 4,
+ opt_format_mrru },
+ { OPT_LCP_SSNHF, "Multilink-Short-Sequence-Number-Header-Format",
+ 2, NULL },
+ { OPT_LCP_EPDISC, "Multilink-Endpoint-Discriminator", 3,
+ opt_format_epdisc },
+ { OPT_LCP_DCEIDENT, "DCE-Identifier", 3,
+ opt_format_dce },
+ { OPT_LCP_MLPLUSPROC, "Multi-Link-Plus-Procedure", 2, NULL },
+ { OPT_LCP_LINKDISC, "Link Discriminator for BACP", 4,
+ opt_format_linkdisc },
+ { OPT_LCP_AUTH, "LCP-Authentication-Option", 2, NULL },
+ { OPT_LCP_COBS, "COBS", 2, NULL },
+ { OPT_LCP_PFXELISION, "Prefix elision", 2, NULL },
+ { OPT_LCP_MPHDRFMT, "Multilink header format", 2, NULL },
+ { OPT_LCP_I18N, "Internationalization", 6,
+ opt_format_i18n },
+ { OPT_LCP_SDL, "Simple Data Link on SONET/SDH", 2, NULL },
+ { OPT_LCP_MUXING, "Old PPP Multiplexing", 2, NULL },
+ { 0, unknown_string, 0, NULL }
+};
+
+static cp_optinfo_t ipcp_optinfo[] = {
+ { OPT_IPCP_ADDRS, "IP-Addresses", 10,
+ opt_format_ipaddresses },
+ { OPT_IPCP_COMPRESSTYPE, "IP-Compression-Protocol", 4,
+ opt_format_ipcompproto },
+ { OPT_IPCP_ADDR, "IP-Address", 6,
+ opt_format_ipaddress },
+ { OPT_IPCP_MOBILEIPV4, "Mobile-IPv4", 6,
+ opt_format_mobileipv4 },
+ { OPT_IPCP_DNS1, "Primary DNS Address", 6,
+ opt_format_ipaddress },
+ { OPT_IPCP_NBNS1, "Primary NBNS Address", 6,
+ opt_format_ipaddress },
+ { OPT_IPCP_DNS2, "Secondary DNS Address", 6,
+ opt_format_ipaddress },
+ { OPT_IPCP_NBNS2, "Secondary NBNS Address", 6,
+ opt_format_ipaddress },
+ { OPT_IPCP_SUBNET, "IP-Subnet", 6,
+ opt_format_ipaddress },
+ { 0, unknown_string, 0, NULL }
+};
+
+static cp_optinfo_t ipv6cp_optinfo[] = {
+ { OPT_IPV6CP_IFACEID, "Interface-Identifier", 10,
+ opt_format_ifaceid },
+ { OPT_IPV6CP_COMPRESSTYPE, "IPv6-Compression-Protocol", 4,
+ opt_format_ipv6compproto },
+ { 0, unknown_string, 0, NULL }
+};
+
+static cp_optinfo_t ccp_optinfo[] = {
+ { OPT_CCP_PROPRIETARY, "Proprietary Compression OUI", 6,
+ opt_format_compoui },
+ { OPT_CCP_PREDICTOR1, "Predictor type 1", 2, NULL },
+ { OPT_CCP_PREDICTOR2, "Predictor type 2", 2, NULL },
+ { OPT_CCP_PUDDLEJUMP, "Puddle Jumper", 2, NULL },
+ { OPT_CCP_HPPPC, "Hewlett-Packard PPC", 2, NULL },
+ { OPT_CCP_STACLZS, "Stac Electronics LZS", 5,
+ opt_format_staclzs },
+ { OPT_CCP_MPPC, "Microsoft PPC", 6,
+ opt_format_mppc },
+ { OPT_CCP_GANDALFFZA, "Gandalf FZA", 3,
+ opt_format_gandalf },
+ { OPT_CCP_V42BIS, "V.42bis compression", 2,
+ NULL },
+ { OPT_CCP_BSDCOMP, "BSD LZW Compress", 3,
+ opt_format_bsdcomp },
+ { OPT_CCP_LZSDCP, "LZS-DCP", 6,
+ opt_format_lzsdcp },
+ { OPT_CCP_MAGNALINK, "Magnalink", 4,
+ opt_format_magnalink },
+ { OPT_CCP_DEFLATE, "Deflate", 4,
+ opt_format_deflate },
+ { 0, unknown_string, 0, NULL }
+};
+
+static cp_optinfo_t ecp_optinfo[] = {
+ { OPT_ECP_PROPRIETARY, "Proprietary Encryption OUI", 6,
+ opt_format_encroui },
+ { OPT_ECP_DESE, "DESE", 10,
+ opt_format_dese },
+ { OPT_ECP_3DESE, "3DESE", 10,
+ opt_format_dese },
+ { OPT_ECP_DESEBIS, "DESE-bis", 10,
+ opt_format_dese },
+ { 0, unknown_string, 0, NULL }
+};
+
+static cp_optinfo_t muxcp_optinfo[] = {
+ { OPT_MUXCP_DEFAULTPID, "Default PID", 4,
+ opt_format_muxpid },
+ { 0, unknown_string, 0, NULL }
+};
+
+static char *cp_codearray[] = {
+ "(Vendor Specific)",
+ "(Configure-Request)",
+ "(Configure-Ack)",
+ "(Configure-Nak)",
+ "(Configure-Reject)",
+ "(Terminate-Request)",
+ "(Terminate-Ack)",
+ "(Code-Reject)",
+ "(Protocol-Reject)",
+ "(Echo-Request)",
+ "(Echo-Reply)",
+ "(Discard-Request)",
+ "(Identification)",
+ "(Time-Remaining)",
+ "(Reset-Request)",
+ "(Reset-Ack)"
+};
+#define MAX_CPCODE ((sizeof (cp_codearray) / sizeof (char *)) - 1)
+
+static char *pap_codearray[] = {
+ "(Unknown)",
+ "(Authenticate-Request)",
+ "(Authenticate-Ack)",
+ "(Authenticate-Nak)"
+};
+#define MAX_PAPCODE ((sizeof (pap_codearray) / sizeof (char *)) - 1)
+
+static char *chap_codearray[] = {
+ "(Unknown)",
+ "(Challenge)",
+ "(Response)",
+ "(Success)",
+ "(Failure)"
+};
+#define MAX_CHAPCODE ((sizeof (chap_codearray) / sizeof (char *)) - 1)
+
+
+int
+interpret_ppp(int flags, uchar_t *data, int len)
+{
+ uint16_t protocol;
+ ppp_protoinfo_t *protoinfo;
+ uchar_t *payload = data;
+
+ if (len < 2)
+ return (len);
+
+ GETINT16(protocol, payload);
+ len -= sizeof (uint16_t);
+
+ protoinfo = ppp_getprotoinfo(protocol);
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "PPP Protocol=0x%x (%s)", protocol, protoinfo->name);
+ } else { /* F_DTAIL */
+ show_header("PPP: ", "Point-to-Point Protocol", len);
+ show_space();
+ (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)",
+ protocol, protoinfo->name);
+ show_space();
+ }
+
+ if (protoinfo->interpret_proto != NULL) {
+ len = protoinfo->interpret_proto(flags, payload, len,
+ protoinfo);
+ }
+
+ return (len);
+}
+
+/*
+ * interpret_ppp_cp() - Interpret PPP control protocols. It is convenient
+ * to do some of the decoding of these protocols in a common function since
+ * they share packet formats. This function expects to receive data
+ * starting with the code field.
+ */
+static int
+interpret_ppp_cp(int flags, uchar_t *data, int len, ppp_protoinfo_t *protoinfo)
+{
+ uint8_t code;
+ uint8_t id;
+ char *codestr;
+ uint16_t length;
+ uchar_t *datap = data;
+
+ if (len < sizeof (ppp_pkt_t))
+ return (len);
+
+ GETINT8(code, datap);
+ GETINT8(id, datap);
+ GETINT16(length, datap);
+
+ len -= sizeof (ppp_pkt_t);
+
+ if (code <= MAX_CPCODE)
+ codestr = cp_codearray[code];
+ else
+ codestr = "";
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "%s%s", protoinfo->prefix, codestr);
+ } else { /* (flags & F_DTAIL) */
+ show_header(protoinfo->prefix, protoinfo->description, len);
+ show_space();
+
+ (void) sprintf(get_line(0, 0), "Code = %d %s", code, codestr);
+ (void) sprintf(get_line(0, 0), "Identifier = %d", id);
+ (void) sprintf(get_line(0, 0), "Length = %d", length);
+
+ show_space();
+
+ len = MIN(len, length - sizeof (ppp_pkt_t));
+ if (len == 0)
+ return (len);
+
+ switch (code) {
+ case CODE_VENDOR: {
+ uint32_t magicnum;
+ uint32_t oui;
+ char *ouistr;
+ uint8_t kind;
+
+ if (len < sizeof (magicnum) + sizeof (oui))
+ return (len);
+
+ GETINT32(magicnum, datap);
+ (void) sprintf(get_line(0, 0), "Magic-Number = 0x%08x",
+ magicnum);
+
+ GETINT32(oui, datap);
+ kind = oui & 0x000000ff;
+ oui >>= 8;
+
+ ouistr = ether_ouiname(oui);
+ if (ouistr == NULL)
+ ouistr = unknown_string;
+
+ (void) sprintf(get_line(0, 0), "OUI = 0x%06x (%s)",
+ oui, ouistr);
+ (void) sprintf(get_line(0, 0), "Kind = %d", kind);
+ show_space();
+ break;
+ }
+
+ case CODE_CONFREQ:
+ case CODE_CONFACK:
+ case CODE_CONFNAK:
+ case CODE_CONFREJ:
+ /*
+ * The above all contain protocol specific
+ * configuration options. Parse these options.
+ */
+ interpret_cp_options(datap, len, protoinfo);
+ break;
+
+ case CODE_TERMREQ:
+ case CODE_TERMACK:
+ /*
+ * The arbitrary data in these two packet types
+ * is almost always plain text. Print it as such.
+ */
+ (void) sprintf(get_line(0, 0), "Data = %.*s",
+ length - sizeof (ppp_pkt_t), datap);
+ show_space();
+ break;
+
+ case CODE_CODEREJ:
+ /*
+ * What follows is the rejected control protocol
+ * packet, starting with the code field.
+ * Conveniently, we can call interpret_ppp_cp() to
+ * decode this.
+ */
+ prot_nest_prefix = protoinfo->prefix;
+ interpret_ppp_cp(flags, datap, len, protoinfo);
+ prot_nest_prefix = "";
+ break;
+
+ case CODE_PROTREJ:
+ /*
+ * We don't print the rejected-protocol field
+ * explicitely. Instead, we cheat and pretend that
+ * the rejected-protocol field is actually the
+ * protocol field in the included PPP packet. This
+ * way, we can invoke interpret_ppp() and have it
+ * treat the included packet normally.
+ */
+ prot_nest_prefix = protoinfo->prefix;
+ interpret_ppp(flags, datap, len);
+ prot_nest_prefix = "";
+ break;
+
+ case CODE_ECHOREQ:
+ case CODE_ECHOREP:
+ case CODE_DISCREQ:
+ case CODE_IDENT:
+ case CODE_TIMEREMAIN: {
+ uint32_t magicnum;
+ char *message_label = "Identification = %.*s";
+
+ if (len < sizeof (uint32_t))
+ break;
+
+ GETINT32(magicnum, datap);
+ len -= sizeof (uint32_t);
+ (void) sprintf(get_line(0, 0), "Magic-Number = 0x%08x",
+ magicnum);
+ /*
+ * Unless this is an identification or
+ * time-remaining packet, arbitrary data follows
+ * the magic number field. The user can take a
+ * look at the hex dump for enlightenment.
+ */
+ if (code == CODE_TIMEREMAIN) {
+ uint32_t timeremaining;
+
+ if (len < sizeof (uint32_t))
+ break;
+
+ message_label = "Message = %.*s";
+
+ GETINT32(timeremaining, datap);
+ len -= sizeof (uint32_t);
+ (void) sprintf(get_line(0, 0),
+ "Seconds Remaining = %d", timeremaining);
+ }
+
+ if (code == CODE_IDENT || code == CODE_TIMEREMAIN) {
+ if (len == 0)
+ break;
+
+ (void) sprintf(get_line(0, 0), message_label,
+ len, datap);
+ }
+ show_space();
+ break;
+ }
+
+ /*
+ * Reset-Request and Reset-Ack contain arbitrary data which
+ * the user can sift through using the -x option.
+ */
+ case CODE_RESETREQ:
+ case CODE_RESETACK:
+ default:
+ break;
+ }
+ }
+ return (len);
+}
+
+
+/*
+ * interpret_cp_options() decodes control protocol configuration options.
+ * Since each control protocol has a different set of options whose type
+ * numbers overlap, the protoinfo parameter is used to get a handle on
+ * which option set to use for decoding.
+ */
+static int
+interpret_cp_options(uchar_t *optptr, int len, ppp_protoinfo_t *protoinfo)
+{
+ cp_optinfo_t *optinfo;
+ cp_optinfo_t *optinfo_ptr;
+ uint8_t optlen;
+ uint8_t opttype;
+
+ switch (protoinfo->proto) {
+ case PPP_LCP:
+ optinfo = lcp_optinfo;
+ break;
+ case PPP_IPCP:
+ optinfo = ipcp_optinfo;
+ break;
+ case PPP_IPV6CP:
+ optinfo = ipv6cp_optinfo;
+ break;
+ case PPP_CCP:
+ optinfo = ccp_optinfo;
+ break;
+ case PPP_ECP:
+ optinfo = ecp_optinfo;
+ break;
+ case PPP_MUXCP:
+ optinfo = muxcp_optinfo;
+ break;
+ default:
+ return (len);
+ break;
+ }
+
+ if (len >= 2) {
+ (void) sprintf(get_line(0, 0), "%s Configuration Options",
+ protoinfo->name);
+ show_space();
+ }
+
+ while (len >= 2) {
+ GETINT8(opttype, optptr);
+ GETINT8(optlen, optptr);
+
+ optinfo_ptr = ppp_getoptinfo(optinfo, opttype);
+
+ (void) sprintf(get_line(0, 0), "Option Type = %d (%s)", opttype,
+ optinfo_ptr->opt_name);
+ (void) sprintf(get_line(0, 0), "Option Length = %d", optlen);
+
+ /*
+ * Don't continue if there isn't enough data to
+ * contain this option, or if this type of option
+ * should contain more data than the length field
+ * claims there is.
+ */
+ if (optlen > len || optlen < optinfo_ptr->opt_minsize) {
+ (void) sprintf(get_line(0, 0),
+ "Warning: Incomplete Option");
+ show_space();
+ break;
+ }
+
+ if (optinfo_ptr->opt_formatdata != NULL) {
+ optinfo_ptr->opt_formatdata(optptr,
+ MIN(optlen - 2, len - 2));
+ }
+
+ len -= optlen;
+ optptr += optlen - 2;
+
+ show_space();
+ }
+
+ return (len);
+}
+
+static int
+interpret_ppp_chap(int flags, uchar_t *data, int len,
+ ppp_protoinfo_t *protoinfo)
+{
+ uint8_t code;
+ uint8_t id;
+ char *codestr;
+ uint16_t length;
+ int lengthleft;
+ uchar_t *datap = data;
+
+
+ if (len < sizeof (ppp_pkt_t))
+ return (len);
+
+ GETINT8(code, datap);
+ GETINT8(id, datap);
+ GETINT8(length, datap);
+
+ if (code <= MAX_CHAPCODE)
+ codestr = chap_codearray[code];
+ else
+ codestr = "";
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "%s%s", protoinfo->prefix, codestr);
+ } else { /* (flags & F_DTAIL) */
+ show_header(protoinfo->prefix, protoinfo->description, len);
+ show_space();
+
+ (void) sprintf(get_line(0, 0), "Code = %d %s", code, codestr);
+ (void) sprintf(get_line(0, 0), "Identifier = %d", id);
+ (void) sprintf(get_line(0, 0), "Length = %d", length);
+
+ show_space();
+
+ if (len < length)
+ return (len);
+
+ lengthleft = len - sizeof (ppp_pkt_t);
+
+ switch (code) {
+ case CODE_CHALLENGE:
+ case CODE_RESPONSE: {
+ uint8_t value_size;
+ uint16_t peername_size;
+
+ if (lengthleft < sizeof (value_size))
+ break;
+
+ GETINT8(value_size, datap);
+ lengthleft -= sizeof (value_size);
+ (void) sprintf(get_line(0, 0), "Value-Size = %d",
+ value_size);
+
+ if (lengthleft < sizeof (peername_size))
+ break;
+ peername_size = MIN(length - sizeof (ppp_pkt_t) -
+ value_size, lengthleft);
+ (void) sprintf(get_line(0, 0), "Name = %.*s",
+ peername_size, datap + value_size);
+
+ break;
+ }
+ case CODE_SUCCESS:
+ case CODE_FAILURE: {
+ uint16_t message_size = MIN(length - sizeof (ppp_pkt_t),
+ lengthleft);
+
+ (void) sprintf(get_line(0, 0), "Message = %.*s",
+ message_size, datap);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ show_space();
+ len -= length;
+ return (len);
+}
+
+static int
+interpret_ppp_pap(int flags, uchar_t *data, int len,
+ ppp_protoinfo_t *protoinfo)
+{
+ uint8_t code;
+ uint8_t id;
+ char *codestr;
+ uint16_t length;
+ int lengthleft;
+ uchar_t *datap = data;
+
+ if (len < sizeof (ppp_pkt_t))
+ return (len);
+
+ GETINT8(code, datap);
+ GETINT8(id, datap);
+ GETINT16(length, datap);
+
+ lengthleft = len - sizeof (ppp_pkt_t);
+
+ if (code <= MAX_PAPCODE)
+ codestr = pap_codearray[code];
+ else
+ codestr = "";
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "%s%s", protoinfo->prefix, codestr);
+ } else { /* (flags & F_DTAIL) */
+ show_header(protoinfo->prefix, protoinfo->description, len);
+ show_space();
+
+ (void) sprintf(get_line(0, 0), "Code = %d %s", code, codestr);
+ (void) sprintf(get_line(0, 0), "Identifier = %d", id);
+ (void) sprintf(get_line(0, 0), "Length = %d", length);
+
+ show_space();
+
+ if (len < length)
+ return (len);
+
+ switch (code) {
+ case CODE_AUTHREQ: {
+ uint8_t fieldlen;
+
+ if (lengthleft < sizeof (fieldlen))
+ break;
+ GETINT8(fieldlen, datap);
+ (void) sprintf(get_line(0, 0), "Peer-Id Length = %d",
+ fieldlen);
+ lengthleft -= sizeof (fieldlen);
+
+ if (lengthleft < fieldlen)
+ break;
+ (void) sprintf(get_line(0, 0), "Peer-Id = %.*s",
+ fieldlen, datap);
+ lengthleft -= fieldlen;
+
+ datap += fieldlen;
+
+ if (lengthleft < sizeof (fieldlen))
+ break;
+ GETINT8(fieldlen, datap);
+ (void) sprintf(get_line(0, 0), "Password Length = %d",
+ fieldlen);
+ lengthleft -= sizeof (fieldlen);
+
+ if (lengthleft < fieldlen)
+ break;
+ (void) sprintf(get_line(0, 0), "Password = %.*s",
+ fieldlen, datap);
+
+ break;
+ }
+ case CODE_AUTHACK:
+ case CODE_AUTHNAK: {
+ uint8_t msglen;
+
+ if (lengthleft < sizeof (msglen))
+ break;
+ GETINT8(msglen, datap);
+ (void) sprintf(get_line(0, 0), "Msg-Length = %d",
+ msglen);
+ lengthleft -= sizeof (msglen);
+
+ if (lengthleft < msglen)
+ break;
+ (void) sprintf(get_line(0, 0), "Message = %.*s",
+ msglen, datap);
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ show_space();
+ len -= length;
+ return (len);
+}
+
+
+static int
+interpret_ppp_lqr(int flags, uchar_t *data, int len,
+ ppp_protoinfo_t *protoinfo)
+{
+ lqr_pkt_t lqr_pkt;
+ if (len < sizeof (lqr_pkt_t))
+ return (len);
+
+ (void) memcpy(&lqr_pkt, data, sizeof (lqr_pkt_t));
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(), protoinfo->prefix);
+ } else { /* (flags & F_DTAIL) */
+ show_header(protoinfo->prefix, protoinfo->description, len);
+ show_space();
+
+ (void) sprintf(get_line(0, 0), "Magic-Number = 0x%08x",
+ ntohl(lqr_pkt.lqr_magic));
+ (void) sprintf(get_line(0, 0), "LastOutLQRs = %d",
+ ntohl(lqr_pkt.lqr_lastoutlqrs));
+ (void) sprintf(get_line(0, 0), "LastOutPackets = %d",
+ ntohl(lqr_pkt.lqr_lastoutpackets));
+ (void) sprintf(get_line(0, 0), "LastOutOctets = %d",
+ ntohl(lqr_pkt.lqr_lastoutoctets));
+ (void) sprintf(get_line(0, 0), "PeerInLQRs = %d",
+ ntohl(lqr_pkt.lqr_peerinlqrs));
+ (void) sprintf(get_line(0, 0), "PeerInPackets = %d",
+ ntohl(lqr_pkt.lqr_peerinpackets));
+ (void) sprintf(get_line(0, 0), "PeerInDiscards = %d",
+ ntohl(lqr_pkt.lqr_peerindiscards));
+ (void) sprintf(get_line(0, 0), "PeerInErrors = %d",
+ ntohl(lqr_pkt.lqr_peerinerrors));
+ (void) sprintf(get_line(0, 0), "PeerInOctets = %d",
+ ntohl(lqr_pkt.lqr_peerinoctets));
+ (void) sprintf(get_line(0, 0), "PeerOutLQRs = %d",
+ ntohl(lqr_pkt.lqr_peeroutlqrs));
+ (void) sprintf(get_line(0, 0), "PeerOutPackets = %d",
+ ntohl(lqr_pkt.lqr_peeroutpackets));
+ (void) sprintf(get_line(0, 0), "PeerOutOctets = %d",
+ ntohl(lqr_pkt.lqr_peeroutoctets));
+
+ show_space();
+ }
+
+ len -= sizeof (lqr_pkt_t);
+ return (len);
+}
+
+static ppp_protoinfo_t *
+ppp_getprotoinfo(uint16_t proto)
+{
+ ppp_protoinfo_t *protoinfo_ptr = &protoinfo_array[0];
+
+ while (protoinfo_ptr->proto != proto && protoinfo_ptr->proto != 0) {
+ protoinfo_ptr++;
+ }
+
+ return (protoinfo_ptr);
+}
+
+
+static cp_optinfo_t *
+ppp_getoptinfo(cp_optinfo_t optinfo_list[], uint16_t opt_type)
+{
+ cp_optinfo_t *optinfo_ptr = &optinfo_list[0];
+
+ while (optinfo_ptr->opt_type != opt_type &&
+ optinfo_ptr->opt_name != unknown_string) {
+ optinfo_ptr++;
+ }
+
+ return (optinfo_ptr);
+}
+
+
+/*
+ * Below are the functions which parse control protocol configuration
+ * options. The first argument to these functions (optdata) points to the
+ * first byte of the option after the length field. The second argument
+ * (size) is the number of bytes in the option after the length field
+ * (length - 2).
+ */
+
+/*
+ * The format of the Vendor-Specific option (rfc2153) is:
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | OUI
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ... | Kind | Value(s) ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ */
+/*ARGSUSED1*/
+static void
+opt_format_vendor(uchar_t *optdata, uint8_t size)
+{
+ uint32_t oui;
+ char *ouistr;
+ uint8_t kind;
+
+ GETINT32(oui, optdata);
+ kind = oui & 0x000000ff;
+ oui >>= 8;
+
+ ouistr = ether_ouiname(oui);
+ if (ouistr == NULL)
+ ouistr = unknown_string;
+
+ (void) sprintf(get_line(0, 0), "OUI = 0x%06x (%s)", oui, ouistr);
+ (void) sprintf(get_line(0, 0), "Kind = %d", kind);
+}
+
+/*
+ * The format of the MRU option (rfc1661) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Maximum-Receive-Unit |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_mru(uchar_t *optdata, uint8_t size)
+{
+ uint16_t mru;
+
+ GETINT16(mru, optdata);
+ (void) sprintf(get_line(0, 0), "MRU = %d", mru);
+}
+
+/*
+ * The format of the accm option (rfc1662) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | ACCM
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ACCM (cont) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_accm(uchar_t *optdata, uint8_t size)
+{
+ uint32_t accm;
+
+ GETINT32(accm, optdata);
+ (void) sprintf(get_line(0, 0), "ACCM = 0x%08x", accm);
+}
+
+/*
+ * The format of the Authentication-Protocol option (rfc1661) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Authentication-Protocol |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Data ...
+ * +-+-+-+-+
+ *
+ * For PAP (rfc1334), there is no data. For CHAP (rfc1994), there is one
+ * byte of data representing the algorithm.
+ */
+static void
+opt_format_authproto(uchar_t *optdata, uint8_t size)
+{
+ uint16_t proto;
+ ppp_protoinfo_t *auth_protoinfo;
+
+ GETINT16(proto, optdata);
+
+ auth_protoinfo = ppp_getprotoinfo(proto);
+
+ (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)", proto,
+ auth_protoinfo->name);
+
+ switch (proto) {
+ case PPP_CHAP: {
+ uint8_t algo;
+ char *algostr;
+
+ if (size < sizeof (proto) + sizeof (algo))
+ return;
+
+ GETINT8(algo, optdata);
+ switch (algo) {
+ case 5:
+ algostr = "CHAP with MD5";
+ break;
+ case 128:
+ algostr = "MS-CHAP";
+ break;
+ case 129:
+ algostr = "MS-CHAP-2";
+ break;
+ default:
+ algostr = unknown_string;
+ break;
+ }
+ (void) sprintf(get_line(0, 0), "Algorithm = %d (%s)", algo,
+ algostr);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ * The format of the Quality Protocol option (rfc1661) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Quality-Protocol |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Data ...
+ * +-+-+-+-+
+ *
+ * For LQR, the data consists of a 4 byte reporting period.
+ */
+static void
+opt_format_qualproto(uchar_t *optdata, uint8_t size)
+{
+ uint16_t proto;
+ ppp_protoinfo_t *qual_protoinfo;
+
+ GETINT16(proto, optdata);
+
+ qual_protoinfo = ppp_getprotoinfo(proto);
+
+ (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)", proto,
+ qual_protoinfo->name);
+
+ switch (proto) {
+ case PPP_LQR: {
+ uint32_t reporting_period;
+
+ if (size < sizeof (proto) + sizeof (reporting_period))
+ return;
+
+ GETINT32(reporting_period, optdata);
+ (void) sprintf(get_line(0, 0), "Reporting-Period = %d",
+ reporting_period);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ * The format of the Magic Number option (rfc1661) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Magic-Number
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Magic-Number (cont) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_magicnum(uchar_t *optdata, uint8_t size)
+{
+ uint32_t magicnum;
+
+ GETINT32(magicnum, optdata);
+ (void) sprintf(get_line(0, 0), "Magic Number = 0x%08x", magicnum);
+}
+
+/*
+ * The format of the FCS-Alternatives option (rfc1570) is:
+ *
+ * 0 1 2
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Options |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_fcs(uchar_t *optdata, uint8_t size)
+{
+ uint8_t options;
+
+ GETINT8(options, optdata);
+
+ (void) sprintf(get_line(0, 0), "Options = 0x%02x", options);
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(options, 0x01, "NULL FCS", ""));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(options, 0x02, "CCITT 16-bit FCS", ""));
+ (void) sprintf(get_line(0, 0), " %s",
+ getflag(options, 0x04, "CCITT 32-bit FCS", ""));
+}
+
+/*
+ * The format of the Self-Describing-Padding option (rfc1570) is:
+ *
+ * 0 1 2
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Maximum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_sdp(uchar_t *optdata, uint8_t size)
+{
+ uint8_t max;
+
+ GETINT8(max, optdata);
+
+ (void) sprintf(get_line(0, 0), "Maximum = %d", max);
+}
+
+/*
+ * The format of the Numbered-Mode option (rfc1663) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Window | Address...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_nummode(uchar_t *optdata, uint8_t size)
+{
+ uint8_t window;
+
+ GETINT8(window, optdata);
+ (void) sprintf(get_line(0, 0), "Window = %d", window);
+}
+
+/*
+ * The format of the Callback option (rfc1570) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Operation | Message ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static void
+opt_format_callback(uchar_t *optdata, uint8_t size)
+{
+ uint8_t operation;
+ char *opstr;
+
+ GETINT8(operation, optdata);
+ switch (operation) {
+ case 0:
+ opstr = "User Authentication";
+ break;
+ case 1:
+ opstr = "Dialing String";
+ break;
+ case 2:
+ opstr = "Location Identifier";
+ break;
+ case 3:
+ opstr = "E.164 Number";
+ break;
+ case 4:
+ opstr = "X.500 Distinguished Name";
+ break;
+ case 6:
+ opstr = "CBCP Negotiation";
+ break;
+ default:
+ opstr = unknown_string;
+ break;
+ }
+
+ (void) sprintf(get_line(0, 0), "Operation = %d (%s)", operation, opstr);
+
+ if (size > sizeof (operation)) {
+ (void) sprintf(get_line(0, 0), "Message = %.*s",
+ size - sizeof (operation), optdata);
+ }
+}
+
+/*
+ * The format of the Multilink-MRRU option (rfc1990) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type = 17 | Length = 4 | Max-Receive-Reconstructed-Unit|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_mrru(uchar_t *optdata, uint8_t size)
+{
+ uint16_t mrru;
+
+ GETINT16(mrru, optdata);
+ (void) sprintf(get_line(0, 0), "MRRU = %d", mrru);
+}
+
+/*
+ * The format of the Endpoint Discriminator option (rfc1990) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type = 19 | Length | Class | Address ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static void
+opt_format_epdisc(uchar_t *optdata, uint8_t size)
+{
+ uint8_t class;
+ char *classstr;
+ uint8_t addrlen = size - sizeof (class);
+ char *addr;
+
+ GETINT8(class, optdata);
+
+ switch (class) {
+ case 0:
+ classstr = "Null Class";
+ break;
+ case 1:
+ classstr = "Locally Assigned Address";
+ break;
+ case 2:
+ classstr = "IPv4 Address";
+ break;
+ case 3:
+ classstr = "IEE 802.1 Global MAC Address";
+ break;
+ case 4:
+ classstr = "PPP Magic-Number Block";
+ break;
+ case 5:
+ classstr = "Public Switched Network Directory Number";
+ break;
+ default:
+ classstr = unknown_string;
+ break;
+ }
+
+ (void) sprintf(get_line(0, 0), "Address Class = %d (%s)", class,
+ classstr);
+
+ if (addrlen == 0)
+ return;
+
+ addr = (char *)malloc(addrlen);
+ (void) memcpy(addr, optdata, addrlen);
+ switch (class) {
+ case 2: {
+ char addrstr[INET_ADDRSTRLEN];
+
+ if (addrlen != sizeof (in_addr_t))
+ break;
+ if (inet_ntop(AF_INET, addr, addrstr, INET_ADDRSTRLEN) !=
+ NULL) {
+ (void) sprintf(get_line(0, 0), "Address = %s", addrstr);
+ }
+ break;
+ }
+ case 3: {
+ char *addrstr;
+
+ if (addrlen != sizeof (struct ether_addr))
+ break;
+ if ((addrstr = ether_ntoa((struct ether_addr *)addr)) != NULL) {
+ (void) sprintf(get_line(0, 0), "Address = %s", addrstr);
+ }
+ break;
+ }
+ case 5: {
+ /*
+ * For this case, the address is supposed to be a plain
+ * text telephone number.
+ */
+ (void) sprintf(get_line(0, 0), "Address = %.*s", addrlen,
+ addr);
+ }
+ default:
+ break;
+ }
+
+ free(addr);
+}
+
+/*
+ * The DCE identifier option has the following format (from rfc1976):
+ *
+ * 0 1 2
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Mode |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_dce(uchar_t *optdata, uint8_t size)
+{
+ uint8_t mode;
+ char *modestr;
+
+ GETINT8(mode, optdata);
+ switch (mode) {
+ case 1:
+ modestr = "No Additional Negotiation";
+ break;
+ case 2:
+ modestr = "Full PPP Negotiation and State Machine";
+ break;
+ default:
+ modestr = unknown_string;
+ break;
+ }
+ (void) sprintf(get_line(0, 0), "Mode = %d (%s)", mode, modestr);
+}
+
+/*
+ * The format of the Link Discriminator option (rfc2125) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Link Discriminator |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_linkdisc(uchar_t *optdata, uint8_t size)
+{
+ uint16_t discrim;
+
+ GETINT16(discrim, optdata);
+
+ (void) sprintf(get_line(0, 0), "Link Discriminator = %d", discrim);
+}
+
+
+/*
+ * The format of the Internationalization option (rfc2484) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | MIBenum
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * MIBenum (cont) | Language-Tag...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static void
+opt_format_i18n(uchar_t *optdata, uint8_t size)
+{
+ uint32_t mibenum;
+ uint8_t taglen;
+
+ taglen = size - sizeof (mibenum);
+
+ GETINT32(mibenum, optdata);
+ (void) sprintf(get_line(0, 0), "MIBenum = %d", mibenum);
+
+ if (taglen > 0) {
+ (void) sprintf(get_line(0, 0), "Language Tag = %.*s", taglen,
+ optdata);
+ }
+}
+
+/*
+ * The format of the obsolete IP-Addresses option (rfc1172) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Source-IP-Address
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Source-IP-Address (cont) | Destination-IP-Address
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Destination-IP-Address (cont) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_ipaddresses(uchar_t *optdata, uint8_t size)
+{
+ in_addr_t addr;
+ char addrstr[INET_ADDRSTRLEN];
+
+ (void) memcpy(&addr, optdata, sizeof (in_addr_t));
+ if (inet_ntop(AF_INET, &addr, addrstr, INET_ADDRSTRLEN) != NULL) {
+ (void) sprintf(get_line(0, 0), "Source Address = %s",
+ addrstr);
+ }
+
+ optdata += sizeof (in_addr_t);
+
+ (void) memcpy(&addr, optdata, sizeof (in_addr_t));
+ if (inet_ntop(AF_INET, &addr, addrstr, INET_ADDRSTRLEN) != NULL) {
+ (void) sprintf(get_line(0, 0), "Destination Address = %s",
+ addrstr);
+ }
+}
+
+/*
+ * The format of the IP-Compression-Protocol option (rfc1332) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | IP-Compression-Protocol |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Data ...
+ * +-+-+-+-+
+ *
+ * For VJ Compressed TCP/IP, data consists of:
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Max-Slot-Id | Comp-Slot-Id |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * For IPHC (rfc2509), data consists of:
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TCP_SPACE | NON_TCP_SPACE |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | F_MAX_PERIOD | F_MAX_TIME |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MAX_HEADER | suboptions...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static void
+opt_format_ipcompproto(uchar_t *optdata, uint8_t size)
+{
+ uint16_t proto;
+ ppp_protoinfo_t *comp_protoinfo;
+
+ GETINT16(proto, optdata);
+
+ comp_protoinfo = ppp_getprotoinfo(proto);
+
+ (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)", proto,
+ comp_protoinfo->name);
+
+ switch (proto) {
+ case PPP_VJC_COMP: {
+ uint8_t maxslotid;
+ uint8_t compslotid;
+
+ if (size < sizeof (proto) + sizeof (maxslotid) +
+ sizeof (compslotid))
+ break;
+
+ GETINT8(maxslotid, optdata);
+ GETINT8(compslotid, optdata);
+ (void) sprintf(get_line(0, 0), "Max-Slot-Id = %d", maxslotid);
+ (void) sprintf(get_line(0, 0), "Comp-Slot Flag = 0x%x",
+ compslotid);
+ break;
+ }
+ case PPP_FULLHDR: {
+ uint16_t tcp_space;
+ uint16_t non_tcp_space;
+ uint16_t f_max_period;
+ uint16_t f_max_time;
+ uint16_t max_header;
+
+ if (size < sizeof (proto) + sizeof (tcp_space) +
+ sizeof (non_tcp_space) + sizeof (f_max_period) +
+ sizeof (f_max_time) + sizeof (max_header))
+ break;
+
+ GETINT16(tcp_space, optdata);
+ GETINT16(non_tcp_space, optdata);
+ GETINT16(f_max_period, optdata);
+ GETINT16(f_max_time, optdata);
+ GETINT16(max_header, optdata);
+
+ (void) sprintf(get_line(0, 0), "TCP_SPACE = %d", tcp_space);
+ (void) sprintf(get_line(0, 0), "NON_TCP_SPACE = %d",
+ non_tcp_space);
+ (void) sprintf(get_line(0, 0), "F_MAX_PERIOD = %d",
+ f_max_period);
+ (void) sprintf(get_line(0, 0), "F_MAX_TIME = %d", f_max_time);
+ (void) sprintf(get_line(0, 0), "MAX_HEADER = %d octets",
+ max_header);
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ * The format of the IP-Address option (rfc1332) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | IP-Address
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * IP-Address (cont) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_ipaddress(uchar_t *optdata, uint8_t size)
+{
+ in_addr_t ipaddr;
+ char addrstr[INET_ADDRSTRLEN];
+
+ (void) memcpy(&ipaddr, optdata, sizeof (in_addr_t));
+ if (inet_ntop(AF_INET, &ipaddr, addrstr, INET_ADDRSTRLEN) != NULL) {
+ (void) sprintf(get_line(0, 0), "Address = %s", addrstr);
+ }
+}
+
+/*
+ * The format of the Mobile-IPv4 option (rfc2290) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Mobile Node's ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ... Home Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_mobileipv4(uchar_t *optdata, uint8_t size)
+{
+ in_addr_t ipaddr;
+ char addrstr[INET_ADDRSTRLEN];
+
+ (void) memcpy(&ipaddr, optdata, sizeof (in_addr_t));
+ if (inet_ntop(AF_INET, &ipaddr, addrstr, INET_ADDRSTRLEN) != NULL) {
+ (void) sprintf(get_line(0, 0),
+ "Mobile Node's Home Address = %s", addrstr);
+ }
+}
+
+/*
+ * The format of the Interface-Identifier option (rfc2472) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Interface-Identifier (MS Bytes)
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Interface-Identifier (cont)
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Interface-Identifier (LS Bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_ifaceid(uchar_t *optdata, uint8_t size)
+{
+ in6_addr_t id;
+ char idstr[INET6_ADDRSTRLEN];
+
+ (void) memset(&id, 0, sizeof (in6_addr_t));
+ (void) memcpy(&id.s6_addr[8], optdata, 8);
+
+ if (inet_ntop(AF_INET6, &id, idstr, INET6_ADDRSTRLEN) != NULL) {
+ (void) sprintf(get_line(0, 0), "Interface ID = %s", idstr);
+ }
+}
+
+/*
+ * The format of the IPv6-Compression-Protocol option (rfc2472) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | IPv6-Compression-Protocol |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Data ...
+ * +-+-+-+-+
+ */
+static void
+opt_format_ipv6compproto(uchar_t *optdata, uint8_t size)
+{
+ uint16_t proto;
+ ppp_protoinfo_t *comp_protoinfo;
+
+ GETINT16(proto, optdata);
+
+ comp_protoinfo = ppp_getprotoinfo(proto);
+
+ (void) sprintf(get_line(0, 0), "Protocol = 0x%x (%s)", proto,
+ comp_protoinfo->name);
+
+ switch (proto) {
+ case PPP_FULLHDR: {
+ uint16_t tcp_space;
+ uint16_t non_tcp_space;
+ uint16_t f_max_period;
+ uint16_t f_max_time;
+ uint16_t max_header;
+
+ if (size < sizeof (proto) + sizeof (tcp_space) +
+ sizeof (non_tcp_space) + sizeof (f_max_period) +
+ sizeof (f_max_time) + sizeof (max_header))
+ return;
+
+ GETINT16(tcp_space, optdata);
+ GETINT16(non_tcp_space, optdata);
+ GETINT16(f_max_period, optdata);
+ GETINT16(f_max_time, optdata);
+ GETINT16(max_header, optdata);
+
+ (void) sprintf(get_line(0, 0), "TCP_SPACE = %d", tcp_space);
+ (void) sprintf(get_line(0, 0), "NON_TCP_SPACE = %d",
+ non_tcp_space);
+ (void) sprintf(get_line(0, 0), "F_MAX_PERIOD = %d",
+ f_max_period);
+ (void) sprintf(get_line(0, 0), "F_MAX_TIME = %d", f_max_time);
+ (void) sprintf(get_line(0, 0), "MAX_HEADER = %d octets",
+ max_header);
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ * The format of the Proprietary Compression OUI option (rfc1962) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | OUI ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * OUI | Subtype | Values...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ */
+/*ARGSUSED1*/
+static void
+opt_format_compoui(uchar_t *optdata, uint8_t size)
+{
+ uint32_t oui;
+ uint8_t subtype;
+ char *ouistr;
+
+ GETINT32(oui, optdata);
+ subtype = oui & 0x000000ff;
+ oui >>= 8;
+
+ ouistr = ether_ouiname(oui);
+ if (ouistr == NULL)
+ ouistr = unknown_string;
+ (void) sprintf(get_line(0, 0), "OUI = 0x%06x (%s)", oui, ouistr);
+ (void) sprintf(get_line(0, 0), "Subtype = 0x%x", subtype);
+}
+
+/*
+ * The format of the Stac LZS configuration option (rfc1974) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | History Count |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Check Mode |
+ * +-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_staclzs(uchar_t *optdata, uint8_t size)
+{
+ uint16_t hcount;
+ uint8_t cmode;
+
+ GETINT16(hcount, optdata);
+ GETINT8(cmode, optdata);
+
+ cmode &= 0x07;
+
+ (void) sprintf(get_line(0, 0), "History Count = %d", hcount);
+ (void) sprintf(get_line(0, 0), "Check Mode = %d", cmode);
+}
+
+/*
+ * The format of MPPC configuration option (rfc2118) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Supported Bits |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Supported Bits |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_mppc(uchar_t *optdata, uint8_t size)
+{
+ uint32_t sb;
+
+ GETINT32(sb, optdata);
+
+ (void) sprintf(get_line(0, 0), "Supported Bits = 0x%x", sb);
+}
+
+/*
+ * The format of the Gandalf FZA configuration option (rfc1993) is:
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | History | Version ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_gandalf(uchar_t *optdata, uint8_t size)
+{
+ uint8_t history;
+
+ GETINT8(history, optdata);
+ (void) sprintf(get_line(0, 0), "Maximum History Size = %d bits",
+ history);
+}
+
+/*
+ * The format of the BSD Compress configuration option (rfc1977) is:
+ *
+ * 0 1 2
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Vers| Dict |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_bsdcomp(uchar_t *optdata, uint8_t size)
+{
+ uint8_t version;
+ uint8_t codesize;
+
+ GETINT8(codesize, optdata);
+
+ version = codesize >> 5;
+ codesize &= 0x1f;
+
+ (void) sprintf(get_line(0, 0), "Version = 0x%x", version);
+ (void) sprintf(get_line(0, 0), "Maximum Code Size = %d bits", codesize);
+}
+
+/*
+ * The format of the LZS-DCP configuration option (rfc1967) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | History Count |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Check Mode | Process Mode |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_lzsdcp(uchar_t *optdata, uint8_t size)
+{
+ uint16_t history;
+ uint8_t mode;
+ char *modestr;
+
+ GETINT16(history, optdata);
+ (void) sprintf(get_line(0, 0), "History Count = %d", history);
+
+ /* check mode */
+ GETINT8(mode, optdata);
+ switch (mode) {
+ case 0:
+ modestr = "None";
+ break;
+ case 1:
+ modestr = "LCB";
+ break;
+ case 2:
+ modestr = "Sequence Number";
+ break;
+ case 3:
+ modestr = "Sequence Number + LCB (default)";
+ break;
+ default:
+ modestr = unknown_string;
+ break;
+ }
+ (void) sprintf(get_line(0, 0), "Check Mode = %d (%s)", mode, modestr);
+
+ /* process mode */
+ GETINT8(mode, optdata);
+ switch (mode) {
+ case 0:
+ modestr = "None (default)";
+ break;
+ case 1:
+ modestr = "Process-Uncompressed";
+ break;
+ default:
+ modestr = unknown_string;
+ break;
+ }
+ (void) sprintf(get_line(0, 0), "Process Mode = %d (%s)", mode, modestr);
+
+}
+
+/*
+ * The format of the Magnalink configuration option (rfc1975) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length |FE |P| History | # Contexts |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_magnalink(uchar_t *optdata, uint8_t size)
+{
+ uint8_t features;
+ uint8_t pflag;
+ uint8_t history;
+ uint8_t contexts;
+
+ GETINT8(history, optdata);
+ GETINT8(contexts, optdata);
+
+ features = history >> 6;
+ pflag = (history >> 5) & 0x01;
+ history &= 0x1f;
+
+ (void) sprintf(get_line(0, 0), "Features = 0x%d", features);
+ (void) sprintf(get_line(0, 0), "Packet Flag = %d", pflag);
+ (void) sprintf(get_line(0, 0), "History Size = %d", history);
+ (void) sprintf(get_line(0, 0), "Contexts = %d", contexts);
+}
+
+/*
+ * The format of the Deflate configuration option (rfc1979) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length |Window | Method| MBZ |Chk|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_deflate(uchar_t *optdata, uint8_t size)
+{
+ uint8_t window;
+ uint8_t method;
+ uint8_t chk;
+
+ GETINT8(method, optdata);
+ window = method >> 4;
+ method &= 0x0f;
+
+ GETINT8(chk, optdata);
+ chk &= 0x03;
+
+ (void) sprintf(get_line(0, 0), "Maximum Window Size = %d", window);
+ (void) sprintf(get_line(0, 0), "Compression Method = 0x%x", method);
+ (void) sprintf(get_line(0, 0), "Check Method = 0x%x", chk);
+}
+
+/*
+ * The format of the Proprietary Encryption OUI option (rfc1968) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | OUI ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * OUI | Subtype | Values...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ */
+/*ARGSUSED1*/
+static void
+opt_format_encroui(uchar_t *optdata, uint8_t size)
+{
+ uint32_t oui;
+ uint8_t subtype;
+ char *ouistr;
+
+ GETINT32(oui, optdata);
+ subtype = oui & 0x000000ff;
+ oui >>= 8;
+
+ ouistr = ether_ouiname(oui);
+ if (ouistr == NULL)
+ ouistr = unknown_string;
+ (void) sprintf(get_line(0, 0), "OUI = 0x%06x (%s)", oui, ouistr);
+ (void) sprintf(get_line(0, 0), "Subtype = 0x%x", subtype);
+}
+
+/*
+ * The format of the DESE, DESE-bis, and 3DESE configuration options
+ * (rfc1969, rfc2419, and rfc2420) are:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type = 3 | Length | Initial Nonce ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_dese(uchar_t *optdata, uint8_t size)
+{
+ (void) sprintf(get_line(0, 0),
+ "Initial Nonce = 0x%02x%02x%02x%02x%02x%02x%02x%02x",
+ optdata[0], optdata[1], optdata[2], optdata[3], optdata[4],
+ optdata[5], optdata[6], optdata[7]);
+}
+
+/*
+ * The format of the PPPMux Default Protocol Id option
+ * (draft-ietf-pppext-pppmux-02.txt) is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type = 1 | Length = 4 | Default PID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/*ARGSUSED1*/
+static void
+opt_format_muxpid(uchar_t *optdata, uint8_t size)
+{
+ uint16_t defpid;
+
+ GETINT16(defpid, optdata);
+ (void) sprintf(get_line(0, 0), "Default PID = %d", defpid);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.h
new file mode 100644
index 0000000..0f158c5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ppp.h
@@ -0,0 +1,192 @@
+/*
+ * 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 (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SNOOP_PPP_H
+#define _SNOOP_PPP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Control Protocol (LCP, IPCP, etc.) message code numbers.
+ */
+#define CODE_VENDOR 0 /* Vendor Specif Code */
+#define CODE_CONFREQ 1 /* Configuration Request */
+#define CODE_CONFACK 2 /* Configuration Ack */
+#define CODE_CONFNAK 3 /* Configuration Nak */
+#define CODE_CONFREJ 4 /* Configuration Reject */
+#define CODE_TERMREQ 5 /* Termination Request */
+#define CODE_TERMACK 6 /* Termination Ack */
+#define CODE_CODEREJ 7 /* Code Reject */
+/*
+ * LCP specific codes.
+ */
+#define CODE_PROTREJ 8 /* Protocol Reject */
+#define CODE_ECHOREQ 9 /* Echo Request */
+#define CODE_ECHOREP 10 /* Echo Reply */
+#define CODE_DISCREQ 11 /* Discard Request */
+#define CODE_IDENT 12 /* Identification */
+#define CODE_TIMEREMAIN 13 /* Time Remaining */
+/*
+ * CCP and ECP specific codes.
+ */
+#define CODE_RESETREQ 14
+#define CODE_RESETACK 15
+
+/*
+ * CHAP codes.
+ */
+#define CODE_CHALLENGE 1
+#define CODE_RESPONSE 2
+#define CODE_SUCCESS 3
+#define CODE_FAILURE 4
+
+/*
+ * PAP codes.
+ */
+#define CODE_AUTHREQ 1
+#define CODE_AUTHACK 2
+#define CODE_AUTHNAK 3
+
+/*
+ * Option types for various control protocols.
+ */
+#define OPT_LCP_VENDOR 0
+#define OPT_LCP_MRU 1
+#define OPT_LCP_ASYNCMAP 2
+#define OPT_LCP_AUTHTYPE 3
+#define OPT_LCP_QUALITY 4
+#define OPT_LCP_MAGICNUMBER 5
+#define OPT_LCP_PCOMPRESSION 7
+#define OPT_LCP_ACCOMPRESSION 8
+#define OPT_LCP_FCSALTERN 9
+#define OPT_LCP_SELFDESCPAD 10
+#define OPT_LCP_NUMBERED 11
+#define OPT_LCP_MULTILINKPROC 12
+#define OPT_LCP_CALLBACK 13
+#define OPT_LCP_CONNECTTIME 14
+#define OPT_LCP_COMPOUNDFRAMES 15
+#define OPT_LCP_DATAENCAP 16
+#define OPT_LCP_MRRU 17
+#define OPT_LCP_SSNHF 18
+#define OPT_LCP_EPDISC 19
+#define OPT_LCP_DCEIDENT 21
+#define OPT_LCP_MLPLUSPROC 22
+#define OPT_LCP_LINKDISC 23
+#define OPT_LCP_AUTH 24
+#define OPT_LCP_COBS 25
+#define OPT_LCP_PFXELISION 26
+#define OPT_LCP_MPHDRFMT 27
+#define OPT_LCP_I18N 28
+#define OPT_LCP_SDL 29
+#define OPT_LCP_MUXING 30
+
+#define OPT_IPCP_ADDRS 1
+#define OPT_IPCP_COMPRESSTYPE 2
+#define OPT_IPCP_ADDR 3
+#define OPT_IPCP_MOBILEIPV4 4
+#define OPT_IPCP_DNS1 129
+#define OPT_IPCP_NBNS1 130
+#define OPT_IPCP_DNS2 131
+#define OPT_IPCP_NBNS2 132
+#define OPT_IPCP_SUBNET 144
+
+#define OPT_IPV6CP_IFACEID 1
+#define OPT_IPV6CP_COMPRESSTYPE 2
+
+#define OPT_CCP_PROPRIETARY 0
+#define OPT_CCP_PREDICTOR1 1
+#define OPT_CCP_PREDICTOR2 2
+#define OPT_CCP_PUDDLEJUMP 3
+#define OPT_CCP_HPPPC 16
+#define OPT_CCP_STACLZS 17
+#define OPT_CCP_MPPC 18
+#define OPT_CCP_GANDALFFZA 19
+#define OPT_CCP_V42BIS 20
+#define OPT_CCP_BSDCOMP 21
+#define OPT_CCP_LZSDCP 23
+#define OPT_CCP_MAGNALINK 24
+#define OPT_CCP_DEFLATE 26
+
+#define OPT_ECP_PROPRIETARY 0
+#define OPT_ECP_DESE 1
+#define OPT_ECP_3DESE 2
+#define OPT_ECP_DESEBIS 3
+
+#define OPT_MUXCP_DEFAULTPID 1
+
+/*
+ * ppp_protoinfo_t's contain properties of PPP protocols which
+ * interpret_ppp() needs in order to properly decode the protocol data.
+ */
+typedef struct ppp_protoinfo {
+ uint16_t proto; /* protocol number */
+ char *name; /* protocol name */
+ int (*interpret_proto)(); /* interpret function */
+ char *prefix; /* string printed on detail lines */
+ char *description; /* string printed in detail header */
+} ppp_protoinfo_t;
+
+
+/*
+ * cp_optinfo contains information on control protocol options.
+ */
+typedef void optformat_func_t(uchar_t *, uint8_t);
+typedef struct cp_optinfo {
+ uint8_t opt_type;
+ char *opt_name;
+ uint8_t opt_minsize; /* min size of option, including type and len */
+ optformat_func_t *opt_formatdata;
+} cp_optinfo_t;
+
+
+/*
+ * Packet format for PPP control and authentication protocols.
+ */
+typedef struct ppp_pkt {
+ uint8_t code;
+ uint8_t id;
+ uint16_t length;
+} ppp_pkt_t;
+
+/*
+ * Structure of an LQR packet.
+ */
+typedef struct lqr_pkt {
+ uint32_t lqr_magic;
+ uint32_t lqr_lastoutlqrs;
+ uint32_t lqr_lastoutpackets;
+ uint32_t lqr_lastoutoctets;
+ uint32_t lqr_peerinlqrs;
+ uint32_t lqr_peerinpackets;
+ uint32_t lqr_peerindiscards;
+ uint32_t lqr_peerinerrors;
+ uint32_t lqr_peerinoctets;
+ uint32_t lqr_peeroutlqrs;
+ uint32_t lqr_peeroutpackets;
+ uint32_t lqr_peeroutoctets;
+} lqr_pkt_t;
+
+#endif /* _SNOOP_PPP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pppoe.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pppoe.c
new file mode 100644
index 0000000..0939271
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pppoe.c
@@ -0,0 +1,399 @@
+/*
+ * 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 (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/pppoe.h>
+#include "snoop.h"
+
+/*
+ * These two macros extract the version and type fields respectively from
+ * the first byte of the PPPoE header.
+ */
+#define POE_VERS(x) (((x) >> 4) & 0x0f)
+#define POE_TYPE(x) ((x) & 0x0f)
+
+typedef void interpret_func_t(uint8_t *, uint16_t);
+
+typedef struct taginfo {
+ char *tag_name;
+ uint16_t tag_type;
+ interpret_func_t *interpret_tagvalue;
+} taginfo_t;
+
+
+static char *pppoe_codetoname(int, boolean_t);
+static taginfo_t *pppoe_gettaginfo(uint16_t);
+static void print_hexdata(char *, uint8_t *, uint16_t);
+static void print_utf8string(char *, char *, uint16_t);
+static char *print_linetag(char *);
+static interpret_func_t interpret_tags;
+static interpret_func_t interpret_hexdata;
+static interpret_func_t interpret_service;
+static interpret_func_t interpret_access;
+static interpret_func_t interpret_cookie;
+static interpret_func_t interpret_vendor;
+static interpret_func_t interpret_relay;
+static interpret_func_t interpret_error;
+static interpret_func_t interpret_hurl;
+static interpret_func_t interpret_motm;
+static interpret_func_t interpret_rteadd;
+
+
+static taginfo_t taginfo_array[] = {
+ { "End-Of-List", POETT_END, interpret_hexdata },
+ { "Service-Name", POETT_SERVICE, interpret_service },
+ { "AC-Name", POETT_ACCESS, interpret_access },
+ { "Host-Uniq", POETT_UNIQ, interpret_hexdata },
+ { "AC-Cookie", POETT_COOKIE, interpret_cookie },
+ { "Vendor-Specific", POETT_VENDOR, interpret_vendor },
+ { "Relay-Session-Id", POETT_RELAY, interpret_relay },
+ { "Service-Name-Error", POETT_NAMERR, interpret_error },
+ { "AC-System-Error", POETT_SYSERR, interpret_error },
+ { "Generic-Error", POETT_GENERR, interpret_error },
+ { "Multicast-Capable", POETT_MULTI, interpret_hexdata },
+ { "Host-URL", POETT_HURL, interpret_hurl },
+ { "Message-Of-The-Minute", POETT_MOTM, interpret_motm },
+ { "IP-Route-Add", POETT_RTEADD, interpret_rteadd },
+ { "Unknown TAG", 0, NULL }
+};
+
+
+int
+interpret_pppoe(int flags, poep_t *poep, int len)
+{
+ uint8_t code = poep->poep_code;
+ uint8_t *payload;
+
+ if (len < sizeof (poep_t))
+ return (len);
+
+ payload = (uint8_t *)poep + sizeof (poep_t);
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(), "PPPoE %s",
+ pppoe_codetoname(code, B_FALSE));
+ } else { /* flags & F_DTAIL */
+ show_header("PPPoE: ", "PPP Over Ethernet", len);
+ show_space();
+
+ (void) sprintf(get_line(0, 0),
+ "Version = %d", POE_VERS(poep->poep_version_type));
+
+ (void) sprintf(get_line(0, 0),
+ "Type = %d", POE_TYPE(poep->poep_version_type));
+
+ (void) sprintf(get_line(0, 0),
+ "Code = %d (%s)", code, pppoe_codetoname(code, B_TRUE));
+
+ (void) sprintf(get_line(0, 0),
+ "Session Id = %d", ntohs(poep->poep_session_id));
+
+ (void) sprintf(get_line(0, 0),
+ "Length = %d bytes", ntohs(poep->poep_length));
+
+ show_space();
+
+ len -= sizeof (poep_t);
+ len = MIN(len, ntohs(poep->poep_length));
+
+ if (poep->poep_code != 0 && poep->poep_length > 0) {
+ interpret_tags(payload, len);
+ }
+ }
+
+ if (poep->poep_code == 0) {
+ return (interpret_ppp(flags, payload, len));
+ }
+ return (len);
+}
+
+
+/*
+ * interpret_tags() prints PPPoE Discovery Stage TAGs in detail.
+ */
+static void
+interpret_tags(uint8_t *payload, uint16_t length)
+{
+ uint8_t *tagptr = payload;
+ uint16_t tag_length;
+ uint16_t tag_type;
+ uint8_t *tag_value;
+ taginfo_t *tinfo;
+
+ while (length >= POET_HDRLEN) {
+ tag_type = POET_GET_TYPE(tagptr);
+ tag_length = POET_GET_LENG(tagptr);
+
+ tinfo = pppoe_gettaginfo(tag_type);
+
+ show_header("PPPoE: ", tinfo->tag_name,
+ tag_length + POET_HDRLEN);
+
+ (void) sprintf(get_line(0, 0),
+ "Tag Type = %d", tag_type);
+
+ (void) sprintf(get_line(0, 0),
+ "Tag Length = %d bytes", tag_length);
+
+ length -= POET_HDRLEN;
+ if (tag_length > length) {
+ (void) sprintf(get_line(0, 0),
+ "Warning: Truncated Packet");
+ show_space();
+ break;
+ }
+
+ /*
+ * unknown tags or tags which should always have 0 length
+ * are not interpreted any further.
+ */
+ tag_value = POET_DATA(tagptr);
+ if (tag_length != 0 && tinfo->interpret_tagvalue != NULL)
+ tinfo->interpret_tagvalue(tag_value, tag_length);
+
+ show_space();
+ length -= tag_length;
+ tagptr = POET_NEXT(tagptr);
+ }
+}
+
+static char *
+pppoe_codetoname(int code, boolean_t verbose)
+{
+ char *name;
+
+ switch (code) {
+ case POECODE_DATA:
+ name = "Session";
+ break;
+ case POECODE_PADO:
+ if (verbose)
+ name = "Active Discovery Offer";
+ else
+ name = "PADO";
+ break;
+ case POECODE_PADI:
+ if (verbose)
+ name = "Active Discovery Initiation";
+ else
+ name = "PADI";
+ break;
+ case POECODE_PADR:
+ if (verbose)
+ name = "Active Discovery Request";
+ else
+ name = "PADR";
+ break;
+ case POECODE_PADS:
+ if (verbose)
+ name = "Active Discovery Session-Confirmation";
+ else
+ name = "PADS";
+ break;
+ case POECODE_PADT:
+ if (verbose)
+ name = "Active Discovery Terminate";
+ else
+ name = "PADT";
+ break;
+ case POECODE_PADM:
+ if (verbose)
+ name = "Active Discovery Message";
+ else
+ name = "PADM";
+ break;
+ case POECODE_PADN:
+ if (verbose)
+ name = "Active Discovery Network";
+ else
+ name = "PADN";
+ break;
+ default:
+ name = "Unknown Code";
+ }
+
+ return (name);
+}
+
+static taginfo_t *
+pppoe_gettaginfo(uint16_t type)
+{
+ taginfo_t *taginfo_ptr = &taginfo_array[0];
+ int i = 0;
+
+ while (taginfo_ptr->tag_type != type &&
+ taginfo_ptr->interpret_tagvalue != NULL) {
+ taginfo_ptr = &taginfo_array[++i];
+ }
+
+ return (taginfo_ptr);
+}
+
+static void
+interpret_hexdata(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("Data = ");
+ print_hexdata(endofline, tag_value, tag_length);
+}
+
+static void
+interpret_service(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("Service Name = ");
+ print_utf8string(endofline, (char *)tag_value, tag_length);
+}
+
+static void
+interpret_access(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("AC Name = ");
+ print_utf8string(endofline, (char *)tag_value, tag_length);
+}
+
+static void
+interpret_cookie(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("Cookie = ");
+ print_hexdata(endofline, tag_value, tag_length);
+}
+
+static void
+interpret_vendor(uint8_t *tag_value, uint16_t tag_length)
+{
+ uint8_t *vendor_data;
+ uint32_t vendorid;
+ char *endofline;
+
+ vendorid = ntohl(*(uint32_t *)tag_value);
+ (void) sprintf(get_line(0, 0),
+ "Vendor ID = %d", vendorid);
+
+ if (tag_length > 4) {
+ vendor_data = tag_value + 4;
+ endofline = print_linetag("Vendor Data = ");
+ print_hexdata(endofline, vendor_data, tag_length - 4);
+ }
+}
+
+static void
+interpret_relay(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("ID = ");
+ print_hexdata(endofline, tag_value, tag_length);
+}
+
+static void
+interpret_error(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("Error = ");
+ print_utf8string(endofline, (char *)tag_value, tag_length);
+}
+
+static void
+interpret_hurl(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("URL = ");
+ print_utf8string(endofline, (char *)tag_value, tag_length);
+}
+
+static void
+interpret_motm(uint8_t *tag_value, uint16_t tag_length)
+{
+ char *endofline;
+
+ endofline = print_linetag("Message = ");
+ print_utf8string(endofline, (char *)tag_value, tag_length);
+}
+
+static void
+interpret_rteadd(uint8_t *tag_value, uint16_t tag_length)
+{
+ char dest[INET_ADDRSTRLEN];
+ char mask[INET_ADDRSTRLEN];
+ char gateway[INET_ADDRSTRLEN];
+ uint32_t metric;
+
+ if (tag_length == 16) {
+ (void) inet_ntop(AF_INET, tag_value, dest,
+ INET_ADDRSTRLEN);
+ (void) inet_ntop(AF_INET, &tag_value[4], mask,
+ INET_ADDRSTRLEN);
+ (void) inet_ntop(AF_INET, &tag_value[8], gateway,
+ INET_ADDRSTRLEN);
+ metric = ntohl(*(uint32_t *)&tag_value[12]);
+ sprintf(get_line(0, 0),
+ "Destination\tNetmask\tGateway\tMetric");
+ sprintf(get_line(0, 0),
+ "%s\t%s\t%s\t%d", dest, mask, gateway, metric);
+ }
+}
+
+static void
+print_hexdata(char *line, uint8_t *data, uint16_t length)
+{
+ uint16_t index = 0;
+
+ line += sprintf(line, "0x");
+
+ while (index < length) {
+ line += sprintf(line, "%02x", data[index++]);
+ }
+}
+
+static void
+print_utf8string(char *firstline, char *string, uint16_t length)
+{
+ (void) sprintf(firstline, "%.*s", length, string);
+}
+
+static char *
+print_linetag(char *string)
+{
+ char *line = get_line(0, 0);
+ return (line + sprintf(line, string));
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip.c
new file mode 100644
index 0000000..c8a897c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip.c
@@ -0,0 +1,440 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#define RIPVERSION RIPv2
+#include <protocols/routed.h>
+#include "snoop.h"
+
+static const char *show_cmd(int);
+static int get_numtokens(unsigned int);
+static const struct rip_sec_entry *rip_next_sec_entry(
+ const struct rip_sec_entry *, int);
+
+int
+interpret_rip(int flags, struct rip *rip, int fraglen)
+{
+ const struct netinfo *nip;
+ const struct entryinfo *ep;
+ const struct netauth *nap;
+ const struct rip_sec_entry *rsep, *rsn;
+ const struct rip_emetric *rep;
+ const uint32_t *tokp;
+ int len, count;
+ const char *cmdstr, *auth;
+ struct in_addr dst;
+ uint32_t mval;
+ const struct sockaddr_in *sin;
+ /* Room for IP destination + "/" + IP mask */
+ char addrstr[15+1+15+1];
+ /* Room for "RIPv" + uint8_t as %d */
+ char ripvers[4+3+1];
+
+ /* RIP header is 4 octets long */
+ if ((len = fraglen - 4) < 0)
+ return (0);
+
+ if (flags & F_SUM) {
+ switch (rip->rip_cmd) {
+ case RIPCMD_REQUEST: cmdstr = "C"; break;
+ case RIPCMD_RESPONSE: cmdstr = "R"; break;
+ case RIPCMD_TRACEON: cmdstr = "Traceon"; break;
+ case RIPCMD_TRACEOFF: cmdstr = "Traceoff"; break;
+ case RIPCMD_POLL: cmdstr = "Poll"; break;
+ case RIPCMD_POLLENTRY: cmdstr = "Poll entry"; break;
+ case RIPCMD_SEC_RESPONSE: cmdstr = "R - SEC"; break;
+ case RIPCMD_SEC_T_RESPONSE: cmdstr = "R - SEC_T"; break;
+ default: cmdstr = "?"; break;
+ }
+
+ if (rip->rip_vers == RIPv1)
+ (void) strlcpy(ripvers, "RIP", sizeof (ripvers));
+ else
+ (void) snprintf(ripvers, sizeof (ripvers), "RIPv%d",
+ rip->rip_vers);
+
+ switch (rip->rip_cmd) {
+ case RIPCMD_REQUEST:
+ case RIPCMD_RESPONSE:
+ case RIPCMD_POLL:
+ nip = rip->rip_nets;
+ auth = "";
+ if (len >= sizeof (*nip) &&
+ nip->n_family == RIP_AF_AUTH) {
+ nap = (struct netauth *)nip;
+ len -= sizeof (*nip);
+ if (nap->a_type == RIP_AUTH_MD5 &&
+ len >= ntohs(nap->au.a_md5.md5_auth_len))
+ len -= ntohs(nap->au.a_md5.
+ md5_auth_len);
+ auth = " +Auth";
+ }
+ count = len / sizeof (*nip);
+ len %= sizeof (*nip);
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "%s %s (%d destinations%s%s)", ripvers, cmdstr,
+ count, (len != 0 ? "?" : ""), auth);
+ break;
+
+ case RIPCMD_TRACEON:
+ case RIPCMD_TRACEOFF:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "%s %s File=\"%.*s\"", ripvers, cmdstr, len,
+ rip->rip_tracefile);
+ len = 0;
+ break;
+
+ case RIPCMD_SEC_RESPONSE:
+ case RIPCMD_SEC_T_RESPONSE:
+ if (len < sizeof (rip->rip_tsol.rip_generation))
+ break;
+ len -= sizeof (rip->rip_tsol.rip_generation);
+ count = 0;
+ rsep = rip->rip_tsol.rip_sec_entry;
+ while (len > 0) {
+ rsn = rip_next_sec_entry(rsep, len);
+ if (rsn == NULL)
+ break;
+ len -= (const char *)rsn - (const char *)rsep;
+ rsep = rsn;
+ count++;
+ }
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "%s %s (%d destinations%s)", ripvers, cmdstr,
+ count, (len != 0 ? "?" : ""));
+ break;
+
+ default:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "%s %d (%s)", ripvers, rip->rip_cmd, cmdstr);
+ len = 0;
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+
+ len = fraglen - 4;
+ show_header("RIP: ", "Routing Information Protocol", fraglen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Opcode = %d (%s)", rip->rip_cmd,
+ show_cmd(rip->rip_cmd));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Version = %d", rip->rip_vers);
+
+ switch (rip->rip_cmd) {
+ case RIPCMD_REQUEST:
+ case RIPCMD_RESPONSE:
+ case RIPCMD_POLL:
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Destination Next Hop "
+ "Tag Metric");
+ for (nip = rip->rip_nets; len >= sizeof (*nip); nip++,
+ len -= sizeof (*nip)) {
+ if (nip->n_family == RIP_AF_AUTH) {
+ nap = (const struct netauth *)nip;
+ if (nap->a_type == RIP_AUTH_NONE) {
+ (void) snprintf(get_line
+ ((char *)nip - dlc_header,
+ sizeof (*nip)),
+ get_line_remain(),
+ " *** Auth None");
+ } else if (nap->a_type == RIP_AUTH_PW) {
+ (void) snprintf(get_line
+ ((char *)nip - dlc_header,
+ sizeof (*nip)),
+ get_line_remain(),
+ " *** Auth PW \"%.*s\"",
+ RIP_AUTH_PW_LEN,
+ nap->au.au_pw);
+ } else if (nap->a_type ==
+ RIP_AUTH_MD5) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ " *** Auth MD5 pkt len %d, "
+ "keyid %d, sequence %08lX, "
+ "authlen %d",
+ ntohs(nap->au.a_md5.
+ md5_pkt_len),
+ nap->au.a_md5.md5_keyid,
+ (long)ntohl(nap->au.a_md5.
+ md5_seqno),
+ ntohs(nap->au.a_md5.
+ md5_auth_len));
+ if (len - sizeof (*nip) >=
+ ntohs(nap->au.a_md5.
+ md5_auth_len))
+ len -= ntohs(nap->au.
+ a_md5.md5_auth_len);
+ else
+ len = sizeof (*nip);
+ } else {
+ (void) snprintf(get_line
+ ((char *)nip - dlc_header,
+ sizeof (*nip)),
+ get_line_remain(),
+ " *** Auth Type %d?",
+ ntohs(nap->a_type));
+ }
+ continue;
+ }
+ if (nip->n_family == RIP_AF_UNSPEC &&
+ rip->rip_cmd == RIPCMD_REQUEST) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ " *** All routes");
+ continue;
+ }
+ if (nip->n_family != RIP_AF_INET) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ " *** Address Family %d?",
+ ntohs(nip->n_family));
+ continue;
+ }
+ if (nip->n_dst == htonl(RIP_DEFAULT)) {
+ (void) strcpy(addrstr, "default");
+ } else {
+ dst.s_addr = nip->n_dst;
+ (void) strlcpy(addrstr, inet_ntoa(dst),
+ sizeof (addrstr));
+ }
+ if (nip->n_dst != htonl(RIP_DEFAULT) &&
+ rip->rip_vers >= RIPv2) {
+ count = strlen(addrstr);
+ mval = ntohl(nip->n_mask);
+ /* LINTED */
+ if (mval == INADDR_ANY) {
+ /* No mask */;
+ } else if ((mval + (mval & -mval)) ==
+ 0) {
+ (void) snprintf(addrstr + count,
+ sizeof (addrstr) - count,
+ "/%d", 33 - ffs(mval));
+ } else {
+ dst.s_addr = nip->n_mask;
+ (void) snprintf(addrstr + count,
+ sizeof (addrstr) - count,
+ "/%s", inet_ntoa(dst));
+ }
+ }
+ dst.s_addr = nip->n_nhop;
+ mval = ntohl(nip->n_metric);
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "%-31s %-15s %-6d %d%s",
+ addrstr,
+ dst.s_addr == htonl(INADDR_ANY) ?
+ "--" : addrtoname(AF_INET, &dst),
+ ntohs(nip->n_tag),
+ mval,
+ (mval == HOPCNT_INFINITY ?
+ " (not reachable)" : ""));
+ }
+ break;
+
+ case RIPCMD_POLLENTRY:
+ if (len < sizeof (*ep))
+ break;
+ len -= sizeof (*ep);
+ ep = (const struct entryinfo *)rip->rip_nets;
+ /* LINTED */
+ sin = (const struct sockaddr_in *)&ep->rtu_dst;
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Destination = %s %s",
+ inet_ntoa(sin->sin_addr),
+ addrtoname(AF_INET, (void *)&sin->sin_addr));
+ /* LINTED */
+ sin = (const struct sockaddr_in *)&ep->rtu_router;
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Router = %s %s",
+ inet_ntoa(sin->sin_addr),
+ addrtoname(AF_INET, (void *)&sin->sin_addr));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Flags = %4x", (unsigned)ep->rtu_flags);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "State = %d", ep->rtu_state);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Timer = %d", ep->rtu_timer);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Metric = %d", ep->rtu_metric);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Int flags = %8x", ep->int_flags);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Int name = \"%.*s\"", sizeof (ep->int_name),
+ ep->int_name);
+ break;
+
+ case RIPCMD_SEC_RESPONSE:
+ case RIPCMD_SEC_T_RESPONSE:
+ if (len < sizeof (rip->rip_tsol.rip_generation))
+ break;
+ len -= sizeof (rip->rip_tsol.rip_generation);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Generation = %u",
+ (unsigned)ntohl(rip->rip_tsol.rip_generation));
+ rsep = rip->rip_tsol.rip_sec_entry;
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Address E-METRIC");
+ rsep = rip->rip_tsol.rip_sec_entry;
+ while (len > 0) {
+ char *cp;
+ int blen, num;
+
+ rsn = rip_next_sec_entry(rsep, len);
+ if (rsn == NULL)
+ break;
+ dst.s_addr = rsep->rip_dst;
+ cp = get_line(0, 0);
+ blen = get_line_remain();
+ (void) snprintf(cp, blen, "%-16s ",
+ inet_ntoa(dst));
+ cp += 17;
+ blen -= 17;
+ rep = rsep->rip_emetric;
+ for (count = ntohl(rsep->rip_count); count > 0;
+ count--) {
+ (void) snprintf(cp, blen, "metric=%d",
+ ntohs(rep->rip_metric));
+ blen -= strlen(cp);
+ cp += strlen(cp);
+ tokp = rep->rip_token;
+ num = get_numtokens(
+ ntohs(rep->rip_mask));
+ /* advance to the next emetric */
+ rep = (const struct rip_emetric *)
+ &rep->rip_token[num];
+ if (num > 0) {
+ (void) snprintf(cp, blen,
+ ",tokens=%lx",
+ (long)ntohl(*tokp));
+ tokp++;
+ num--;
+ } else {
+ (void) strlcpy(cp, ",no tokens",
+ blen);
+ }
+ while (num > 0) {
+ blen -= strlen(cp);
+ cp += strlen(cp);
+ (void) snprintf(cp, blen,
+ ",%lx",
+ (long)ntohl(*tokp));
+ tokp++;
+ num--;
+ }
+ blen -= strlen(cp);
+ cp += strlen(cp);
+ }
+ if (rsep->rip_count == 0) {
+ (void) strlcpy(cp,
+ "NULL (not reachable)", blen);
+ }
+ len -= (const char *)rsn - (const char *)rsep;
+ rsep = rsn;
+ }
+ break;
+
+ case RIPCMD_TRACEON:
+ case RIPCMD_TRACEOFF:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Trace file = %.*s", len, rip->rip_tracefile);
+ len = 0;
+ break;
+ }
+ }
+
+ return (fraglen - len);
+}
+
+static const char *
+show_cmd(int c)
+{
+ switch (c) {
+ case RIPCMD_REQUEST:
+ return ("route request");
+ case RIPCMD_RESPONSE:
+ return ("route response");
+ case RIPCMD_TRACEON:
+ return ("route trace on");
+ case RIPCMD_TRACEOFF:
+ return ("route trace off");
+ case RIPCMD_POLL:
+ return ("route poll");
+ case RIPCMD_POLLENTRY:
+ return ("route poll entry");
+ case RIPCMD_SEC_RESPONSE:
+ return ("route sec response");
+ case RIPCMD_SEC_T_RESPONSE:
+ return ("route sec_t response");
+ }
+ return ("?");
+}
+
+static int
+get_numtokens(unsigned int mask)
+{
+ int num = 0;
+
+ while (mask != 0) {
+ num++;
+ mask &= mask - 1;
+ }
+ return (num);
+}
+
+static const struct rip_sec_entry *
+rip_next_sec_entry(const struct rip_sec_entry *rsep, int len)
+{
+ const struct rip_emetric *rep;
+ const char *limit = (const char *)rsep + len;
+ long count;
+
+ if ((const char *)(rep = rsep->rip_emetric) > limit)
+ return (NULL);
+ count = ntohl(rsep->rip_count);
+ while (count > 0) {
+ if ((const char *)rep->rip_token > limit)
+ return (NULL);
+ rep = (struct rip_emetric *)
+ &rep->rip_token[get_numtokens(ntohs(rep->rip_mask))];
+ if ((const char *)rep > limit)
+ return (NULL);
+ count--;
+ }
+ return ((const struct rip_sec_entry *)rep);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip6.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip6.c
new file mode 100644
index 0000000..9f7b10e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rip6.c
@@ -0,0 +1,140 @@
+/*
+ * 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" /* SunOS */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <inet/led.h>
+#include <inet/ip6.h>
+#include <arpa/inet.h>
+#include <protocols/routed.h>
+#include <protocols/ripngd.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+static char *show_cmd6();
+
+static struct in6_addr all_zeroes_addr = { { 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0 } };
+
+int
+interpret_rip6(int flags, struct rip6 *rip6, int fraglen)
+{
+ char *p;
+ struct netinfo6 *n;
+ int len, count;
+ struct in6_addr *dst;
+ int notdefault = 0;
+ char dststr[INET6_ADDRSTRLEN];
+
+ if (flags & F_SUM) {
+ switch (rip6->rip6_cmd) {
+ case RIPCMD6_REQUEST: p = "C"; break;
+ case RIPCMD6_RESPONSE: p = "R"; break;
+ default: p = "?"; break;
+ }
+
+ switch (rip6->rip6_cmd) {
+ case RIPCMD6_REQUEST:
+ case RIPCMD6_RESPONSE:
+ len = fraglen - 4;
+ count = 0;
+ for (n = rip6->rip6_nets;
+ len >= sizeof (struct netinfo6); n++) {
+ count++;
+ len -= sizeof (struct netinfo6);
+ }
+ (void) sprintf(get_sum_line(),
+ "RIPng %s (%d destinations)", p, count);
+ break;
+ default:
+ (void) sprintf(get_sum_line(), "RIPng %s", p);
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+
+ show_header("RIPng: ", "Routing Information Protocol for IPv6",
+ fraglen);
+ show_space();
+ (void) sprintf(get_line((char *)(uintptr_t)rip6->rip6_cmd -
+ dlc_header, 1), "Opcode = %d (%s)", rip6->rip6_cmd,
+ show_cmd6(rip6->rip6_cmd));
+ (void) sprintf(get_line((char *)(uintptr_t)rip6->rip6_vers -
+ dlc_header, 1), "Version = %d", rip6->rip6_vers);
+
+ switch (rip6->rip6_cmd) {
+ case RIPCMD6_REQUEST:
+ case RIPCMD6_RESPONSE:
+ show_space();
+ (void) sprintf(get_line(0, 0), "Address"
+ " Prefix Metric");
+ len = fraglen - 4;
+ for (n = rip6->rip6_nets;
+ len >= sizeof (struct netinfo6); n++) {
+ if (rip6->rip6_vers > 0) {
+ n->rip6_metric = n->rip6_metric;
+ }
+ dst = &n->rip6_prefix;
+ notdefault = bcmp((caddr_t *)dst,
+ (caddr_t *)&all_zeroes_addr, sizeof (*dst));
+ (void) inet_ntop(AF_INET6, (char *)dst, dststr,
+ INET6_ADDRSTRLEN);
+ (void) sprintf(get_line((char *)n - dlc_header,
+ sizeof (struct netinfo6)),
+ "%-30s %-10d %-5d %s",
+ notdefault ? dststr :
+ " (default)", n->rip6_prefix_length,
+ n->rip6_metric, n->rip6_metric == 16 ?
+ " (not reachable)":"");
+ len -= sizeof (struct netinfo);
+ }
+ break;
+ }
+ }
+ return (fraglen);
+}
+
+static char *
+show_cmd6(c)
+ int c;
+{
+ switch (c) {
+ case RIPCMD6_REQUEST: return ("route request");
+ case RIPCMD6_RESPONSE: return ("route response");
+ }
+ return ("?");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpc.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpc.c
new file mode 100644
index 0000000..be39961
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpc.c
@@ -0,0 +1,771 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <sys/tiuser.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/auth_unix.h>
+#include <rpc/auth_des.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/svc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpc/pmap_prot.h>
+#include "snoop.h"
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+int pos;
+struct cache_struct *find_xid();
+extern jmp_buf xdr_err;
+void protoprint();
+void print_rpcsec_gss_cred(int xid, int authlen);
+char *nameof_prog();
+char *nameof_astat();
+char *nameof_why();
+static void rpc_detail_call(int, int, int, int, int, int, char *, int);
+static void rpc_detail_reply(int, int, struct cache_struct *, char *, int len);
+static void print_creds(int);
+static void print_verif(int);
+static void stash_xid(ulong_t, int, int, int, int);
+
+#define LAST_FRAG ((ulong_t)1 << 31)
+
+int
+interpret_rpc(int flags, char *rpc, int fraglen, int type)
+{
+ ulong_t xid;
+ int direction;
+ struct cache_struct *x;
+ int rpcvers, prog, vers, proc;
+ int status, astat, rstat, why;
+ char *lp;
+ unsigned recmark;
+ int markpos;
+ extern int pi_frame;
+ int lo, hi;
+
+ xdr_init(rpc, fraglen);
+
+ if (setjmp(xdr_err)) {
+ if (flags & F_DTAIL)
+ (void) sprintf(get_line(0, 0),
+ "---- short frame ---");
+ return (fraglen);
+ }
+
+ if (type == IPPROTO_TCP) { /* record mark */
+ markpos = getxdr_pos();
+ recmark = getxdr_long();
+ }
+
+ xid = getxdr_u_long();
+ direction = getxdr_long();
+
+ if (direction == CALL) {
+ rpcvers = getxdr_long();
+ pos = getxdr_pos();
+ prog = getxdr_long();
+ vers = getxdr_long();
+ proc = getxdr_long();
+ stash_xid(xid, pi_frame, prog, vers, proc);
+ if (!(flags & (F_SUM | F_DTAIL))) {
+ protoprint(flags, CALL, xid, prog, vers, proc,
+ rpc, fraglen);
+ }
+ } else {
+ x = find_xid(xid);
+ }
+
+ if (flags & F_SUM) {
+ switch (direction) {
+ case CALL:
+ (void) sprintf(get_sum_line(),
+ "RPC C XID=%lu PROG=%d (%s) VERS=%d PROC=%d",
+ xid,
+ prog, nameof_prog(prog),
+ vers, proc);
+ if (getxdr_long() == RPCSEC_GSS) { /* Cred auth type */
+ extract_rpcsec_gss_cred_info(xid);
+ /* RPCSEC_GSS cred auth data */
+ } else {
+ xdr_skip(getxdr_long());
+ /* non RPCSEC_GSS cred auth data */
+ }
+ xdr_skip(4); /* Verf auth type */
+ xdr_skip(RNDUP(getxdr_long())); /* Verf auth data */
+
+ protoprint(flags, CALL, xid, prog, vers, proc,
+ rpc, fraglen);
+ break;
+
+ case REPLY:
+ lp = get_sum_line();
+ if (x == NULL)
+ (void) sprintf(lp, "RPC R XID=%lu", xid);
+ else
+ (void) sprintf(lp, "RPC R (#%d) XID=%lu",
+ x->xid_frame, xid);
+
+ lp += strlen(lp);
+ status = getxdr_long();
+ switch (status) {
+ case MSG_ACCEPTED:
+ /* eat flavor and verifier */
+ (void) getxdr_long();
+ xdr_skip(RNDUP(getxdr_long()));
+ astat = getxdr_long();
+ (void) sprintf(lp, " %s",
+ nameof_astat(astat));
+ lp += strlen(lp);
+
+ switch (astat) {
+ case SUCCESS:
+ if (x) {
+ protoprint(flags, REPLY,
+ xid,
+ x->xid_prog,
+ x->xid_vers,
+ x->xid_proc,
+ rpc, fraglen);
+ }
+ break;
+
+ case PROG_UNAVAIL :
+ case PROG_MISMATCH:
+ case PROC_UNAVAIL :
+ lo = getxdr_long();
+ hi = getxdr_long();
+ (void) sprintf(lp,
+ " (low=%d, high=%d)",
+ lo, hi);
+ break;
+
+ case GARBAGE_ARGS:
+ case SYSTEM_ERR:
+ default:
+ ;
+ }
+ break;
+
+ case MSG_DENIED:
+ rstat = getxdr_long();
+
+ switch (rstat) {
+ case RPC_MISMATCH:
+ lo = getxdr_long();
+ hi = getxdr_long();
+ (void) sprintf(lp,
+ " Vers mismatch (low=%d, high=%d)",
+ lo, hi);
+ break;
+
+ case AUTH_ERROR:
+ why = getxdr_u_long();
+ (void) sprintf(lp,
+ " Can't authenticate (%s)",
+ nameof_why(why));
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("RPC: ", "SUN RPC Header", fraglen);
+ show_space();
+ if (type == IPPROTO_TCP) { /* record mark */
+ (void) sprintf(get_line(markpos, markpos+4),
+ "Record Mark: %s fragment, length = %d",
+ recmark & LAST_FRAG ? "last" : "",
+ recmark & ~LAST_FRAG);
+ }
+
+ (void) sprintf(get_line(0, 0),
+ "Transaction id = %lu",
+ xid);
+ (void) sprintf(get_line(0, 0),
+ "Type = %d (%s)",
+ direction,
+ direction == CALL ? "Call":"Reply");
+
+ switch (direction) {
+ case CALL:
+ rpc_detail_call(flags, xid, rpcvers,
+ prog, vers, proc, rpc, fraglen);
+ break;
+ case REPLY:
+ rpc_detail_reply(flags, xid, x, rpc, fraglen);
+ break;
+ }
+ }
+
+ return (fraglen);
+}
+
+static void
+rpc_detail_call(int flags, int xid, int rpcvers, int prog, int vers, int proc,
+ char *data, int len)
+{
+ char *nameof_flavor();
+ char *nameof_prog();
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "RPC version = %d",
+ rpcvers);
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "Program = %d (%s), version = %d, procedure = %d",
+ prog, nameof_prog(prog), vers, proc);
+ print_creds(xid);
+ print_verif(CALL);
+ show_trailer();
+ protoprint(flags, CALL, xid, prog, vers, proc, data, len);
+}
+
+char *
+nameof_flavor(flavor)
+ int flavor;
+{
+ switch (flavor) {
+ case AUTH_NONE : return ("None");
+ case AUTH_UNIX : return ("Unix");
+ case AUTH_SHORT: return ("Unix short");
+ case AUTH_DES : return ("DES");
+ case RPCSEC_GSS: return ("RPCSEC_GSS");
+ default: return ("unknown");
+ }
+}
+
+char *
+tohex(char *p, int len)
+{
+ int i, j;
+ static char hbuff[1024];
+ static char *hexstr = "0123456789ABCDEF";
+ char toobig = 0;
+
+ if (len * 2 > sizeof (hbuff)) {
+ toobig++;
+ len = sizeof (hbuff) / 2;
+ }
+
+ j = 0;
+ for (i = 0; i < len; i++) {
+ hbuff[j++] = hexstr[p[i] >> 4 & 0x0f];
+ hbuff[j++] = hexstr[p[i] & 0x0f];
+ }
+
+ if (toobig) {
+ hbuff[len * 2 - strlen("<Too Long>")] = '\0';
+ strcat(hbuff, "<Too Long>");
+ } else
+ hbuff[j] = '\0';
+
+ return (hbuff);
+}
+
+static void
+print_creds(int xid)
+{
+ int pos, flavor, authlen;
+ int uid, gid, len;
+ int tlen, idlen;
+ int i, namekind;
+ char *p, *line;
+
+ pos = getxdr_pos();
+ flavor = getxdr_long();
+ authlen = getxdr_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "Credentials: Flavor = %d (%s), len = %d bytes",
+ flavor, nameof_flavor(flavor), authlen);
+ if (authlen <= 0)
+ return;
+
+ switch (flavor) {
+ case AUTH_UNIX:
+ (void) showxdr_time(" Time = %s");
+ (void) showxdr_string(MAX_MACHINE_NAME, " Hostname = %s");
+ pos = getxdr_pos();
+ uid = getxdr_u_long();
+ gid = getxdr_u_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " Uid = %d, Gid = %d",
+ uid, gid);
+ len = getxdr_u_long();
+ line = get_line(pos, len * 4);
+ if (len == 0)
+ (void) sprintf(line, " Groups = (none)");
+ else {
+ (void) sprintf(line, " Groups = ");
+ line += strlen(line);
+ while (len--) {
+ gid = getxdr_u_long();
+ (void) sprintf(line, "%d ", gid);
+ line += strlen(line);
+ }
+ }
+ break;
+
+ case AUTH_DES:
+ namekind = getxdr_u_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " Name kind = %d (%s)",
+ namekind,
+ namekind == ADN_FULLNAME ?
+ "fullname" : "nickname");
+ switch (namekind) {
+ case ADN_FULLNAME:
+ (void) showxdr_string(64,
+ " Network name = %s");
+ (void) showxdr_hex(8,
+ " Conversation key = 0x%s (DES encrypted)");
+ (void) showxdr_hex(4,
+ " Window = 0x%s (DES encrypted)");
+ break;
+
+ case ADN_NICKNAME:
+ (void) showxdr_hex(4, " Nickname = 0x%s");
+ break;
+ };
+ break;
+
+ case RPCSEC_GSS:
+ print_rpcsec_gss_cred(xid, authlen);
+ break;
+
+ default:
+ (void) showxdr_hex(authlen, "[%s]");
+ break;
+ }
+}
+
+static void
+print_verif(int direction)
+{
+ int pos, flavor, verlen;
+
+ pos = getxdr_pos();
+ flavor = getxdr_long();
+ verlen = getxdr_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "Verifier : Flavor = %d (%s), len = %d bytes",
+ flavor, nameof_flavor(flavor), verlen);
+ if (verlen == 0)
+ return;
+
+ switch (flavor) {
+ case AUTH_DES:
+ (void) showxdr_hex(8, " Timestamp = 0x%s (DES encrypted)");
+ if (direction == CALL)
+ (void) showxdr_hex(4,
+ " Window = 0x%s (DES encrypted)");
+ else
+ (void) showxdr_hex(4, " Nickname = 0x%s");
+ break;
+
+ /* For other flavors like AUTH_NONE, AUTH_UNIX, RPCSEC_GSS etc. */
+ default:
+ (void) showxdr_hex(verlen, "[%s]");
+ break;
+ }
+}
+
+struct rpcnames {
+ int rp_prog;
+ char *rp_name;
+} rpcnames[] = {
+100000, "PMAP", /* Portmapper */
+100001, "RSTAT", /* Remote stats */
+100002, "RUSERS", /* Remote users */
+100003, "NFS", /* Nfs */
+100004, "NIS", /* Network Information Service */
+100005, "MOUNT", /* Mount demon */
+100006, "DBX", /* Remote dbx */
+100007, "NISBIND", /* NIS binder */
+100008, "WALL", /* Shutdown msg */
+100009, "NISPASSWD", /* Yppasswd server */
+100010, "ETHERSTAT", /* Ether stats */
+100011, "RQUOTA", /* Disk quotas */
+100012, "SPRAY", /* Spray packets */
+100013, "IBM3270", /* 3270 mapper */
+100014, "IBMRJE", /* RJE mapper */
+100015, "SELNSVC", /* Selection service */
+100016, "RDATABASE", /* Remote database access */
+100017, "REX", /* Remote execution */
+100018, "ALICE", /* Alice Office Automation */
+100019, "SCHED", /* Scheduling service */
+100020, "LLM", /* Local lock manager */
+100021, "NLM", /* Network lock manager */
+100022, "X25INR", /* X.25 inr protocol */
+100023, "STATMON1", /* Status monitor 1 */
+100024, "STATMON2", /* Status monitor 2 */
+100025, "SELNLIB", /* Selection library */
+100026, "BOOTPARAM", /* Boot parameters service */
+100027, "MAZEPROG", /* Mazewars game */
+100028, "NISUPDATE", /* NIS update */
+100029, "KEYSERVE", /* Key server */
+100030, "SECURECMD", /* Secure login */
+100031, "NETFWDI", /* NFS net forwarder init */
+100032, "NETFWDT", /* NFS net forwarder trans */
+100033, "SUNLINKMAP", /* Sunlink MAP */
+100034, "NETMON", /* Network monitor */
+100035, "DBASE", /* Lightweight database */
+100036, "PWDAUTH", /* Password authorization */
+100037, "TFS", /* Translucent file svc */
+100038, "NSE", /* NSE server */
+100039, "NSE_ACTIVATE", /* NSE activate daemon */
+100040, "SUNVIEW_HELP", /* Sunview help */
+100041, "PNP", /* PNP install */
+100042, "IPADDR_ALLOC", /* IP addr allocator */
+100043, "FILEHANDLE", /* Show filehandle */
+100044, "MVSNFS", /* MVS NFS mount */
+100045, "REM_FILEOP_USER", /* Remote user file operations */
+100046, "BATCH_NISUPDATE", /* Batched ypupdate */
+100047, "NEM", /* Network execution mgr */
+100048, "RAYTRACE_RD", /* Raytrace/mandelbrot remote daemon */
+100049, "RAYTRACE_LD", /* Raytrace/mandelbrot local daemon */
+100050, "REM_FILEOP_GROUP", /* Remote group file operations */
+100051, "REM_FILEOP_SYSTEM", /* Remote system file operations */
+100052, "REM_SYSTEM_ROLE", /* Remote system role operations */
+100055, "IOADMD", /* Ioadmd */
+100056, "FILEMERGE", /* Filemerge */
+100057, "NAMEBIND", /* Name Binding Program */
+100058, "NJE", /* Sunlink NJE */
+100059, "MVSATTR", /* MVSNFS get attribute service */
+100060, "RMGR", /* SunAccess/SunLink resource manager */
+100061, "UIDALLOC", /* UID allocation service */
+100062, "LBSERVER", /* License broker */
+100063, "LBBINDER", /* NETlicense client binder */
+100064, "GIDALLOC", /* GID allocation service */
+100065, "SUNISAM", /* SunIsam */
+100066, "RDBSRV", /* Remote Debug Server */
+100067, "NETDIR", /* Network directory daemon */
+100068, "CMSD", /* Network calendar program */
+100069, "NISXFR", /* NIS transfer */
+100070, "TIMED", /* RPC.timed */
+100071, "BUGTRAQ", /* Bugtraqd */
+100072, "NeFS", /* Internal use only */
+100073, "BILLBOARD", /* Connectathon Billboard - NFS */
+100074, "BILLBOARD", /* Connectathon Billboard - X */
+100075, "SCHEDROOM", /* Sun meeting room scheduler */
+100076, "AUTHNEGOTIATE", /* Authentication negotiation */
+100077, "ATTRPROG", /* Database manipulation */
+100080, "AUTODUMP", /* Sun consulting special */
+100081, "EVENT_SVC", /* Event protocol */
+100085, "ARM_PSD", /* ARM policy */
+100086, "ARMTOD", /* ARM TOD */
+100087, "NA.ADMIN", /* Sun (SNAG) administration agent */
+100099, "PLD", /* Genesil 8.1 hot plot */
+100101, "NA.EVENT", /* SNM (SunNet Manager) event dispatcher */
+100102, "NA.LOGGER", /* SNM report logger */
+100103, "NA.DISCOVER", /* SNM network discovery agent */
+100104, "NA.SYNC", /* SNM sync interface agent */
+100105, "NA.DISKINFO", /* SNM disk info agent */
+100106, "NA.IOSTAT", /* SNM iostat agent */
+100107, "NA.HOSTPERF", /* SNM rstat proxy agent */
+100108, "NA.CONFIG", /* SNM host configuration agent */
+100109, "NA.ACTIVITY", /* SNM activity daemon */
+100111, "NA.LPSTAT", /* SNM printer agent */
+100112, "NA.HOSTMEM", /* SNM host network memory agent */
+100113, "NA.SAMPLE", /* SNM sample agent */
+100114, "NA.X25", /* SNM X.25 agent */
+100115, "NA.PING", /* SNM ping proxy agent */
+100116, "NA.RPCNFS", /* SNM rpc and nfs agent */
+100117, "NA.HOSTIF", /* SNM host interface agent */
+100118, "NA.ETHERIF", /* SNM ethernet interface agent */
+100119, "NA.IPPATH", /* SNM traceroute proxy agent */
+100120, "NA.IPROUTES", /* SNM routing table agent */
+100121, "NA.LAYERS", /* SNM protocol layers gent */
+100122, "NA.SNMP", /* SNM SNMP proxy agent */
+100123, "NA.TRAFFIC", /* SNM network traffic agent */
+100124, "NA.DNI", /* DNI (DECnet) proxy agent */
+100125, "NA.CHAT", /* IBM Channel attach proxy agent */
+100126, "NA.FDDI", /* FDDI agent */
+100127, "NA.FDDISMT", /* FDDI SMT proxy agent */
+100128, "NA.MHS", /* MHS agent */
+100130, "SNM_GRAPHER", /* SNM 3D grapher */
+100132, "NA.TR", /* Token Ring agent */
+100134, "NA.TOKENRING", /* Token Ring agent */
+100136, "NA.FRAMERELAY", /* Frame Relay agent */
+100175, "NA.SNMPTRAP", /* SNM SNMP trap daemon */
+100180, "NA.MIPROUTES", /* SNM multicast routing table agent */
+100201, "MVSNFSSTAT", /* MVS/NFS Memory usage statistic server */
+100227, "NFS_ACL", /* NFS ACL support */
+101002, "NSELINKTOOL", /* NSE link daemon */
+101003, "NSELINKAPP", /* NSE link application */
+110001, "GOLABEL", /* SunOS MLS */
+110002, "PUC", /* SunOS MLS */
+150001, "PCNFSD", /* PC passwd authorization */
+150002, "TOPS", /* TOPS name mapping */
+150003, "TOPS", /* TOPS external attribute storage */
+150004, "TOPS", /* TOPS hierarchical file system */
+150005, "TOPS", /* TOPS NFS transparency extensions */
+150006, "SOLARNET_FW", /* SolarNet Framework protocol */
+160001, "CM", /* Nihon Sun - Japanese Input system */
+300004, "FRAME 1", /* Frame program 1 */
+300009, "FRAME 2", /* Frame program 2 */
+390101, "RAP", /* Legato RAP protocol */
+390102, "RAPRD", /* Legato RAP resource dir protocol */
+500021, "ZNS", /* Zeus Network Service */
+};
+
+int
+compare(a, b)
+ register struct rpcnames *a, *b;
+{
+ return (a->rp_prog - b->rp_prog);
+}
+
+char *
+nameof_prog(prog)
+ int prog;
+{
+ struct rpcnames *r;
+ struct rpcnames *bsearch();
+ int elems = sizeof (rpcnames) / sizeof (*r);
+
+ r = bsearch(&prog, rpcnames, elems, sizeof (*r), compare);
+ if (r)
+ return (r->rp_name);
+
+ if (prog >= 0x40000000 && prog <= 0x5fffffff)
+ return ("transient");
+
+ return ("?");
+}
+
+char *
+nameof_astat(status)
+ int status;
+{
+ switch (status) {
+ case SUCCESS : return ("Success");
+ case PROG_UNAVAIL : return ("Program unavailable");
+ case PROG_MISMATCH: return ("Program number mismatch");
+ case PROC_UNAVAIL : return ("Procedure unavailable");
+ case GARBAGE_ARGS : return ("Garbage arguments");
+ case SYSTEM_ERR : return ("System error");
+ default: return ("unknown");
+ }
+}
+
+char *
+nameof_why(why)
+ int why;
+{
+ switch (why) {
+ case AUTH_BADCRED: return ("bogus credentials (seal broken)");
+ case AUTH_REJECTEDCRED: return ("client should begin new session");
+ case AUTH_BADVERF: return ("bogus verifier (seal broken)");
+ case AUTH_REJECTEDVERF: return ("verifier expired or was replayed");
+ case AUTH_TOOWEAK: return ("too weak");
+ case AUTH_INVALIDRESP: return ("bogus response verifier");
+ case AUTH_TIMEEXPIRE: return ("time of credential expired");
+ case AUTH_TKT_FILE: return ("something wrong with ticket file");
+ case AUTH_DECODE: return ("can't decode authenticator");
+ case AUTH_NET_ADDR: return ("net address in ticket wrong");
+ case RPCSEC_GSS_NOCRED: return ("no credentials for user");
+ case RPCSEC_GSS_FAILED: return ("GSS failure, credentials deleted");
+ case AUTH_FAILED:
+ default:
+ return ("unknown reason");
+ }
+}
+
+static void
+rpc_detail_reply(int flags, int xid, struct cache_struct *x, char *data,
+ int len)
+{
+ int status;
+ int astat, rstat, why;
+ int pos;
+
+ if (x) {
+ (void) sprintf(get_line(0, 0),
+ "This is a reply to frame %d",
+ x->xid_frame);
+ }
+ pos = getxdr_pos();
+ status = getxdr_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "Status = %d (%s)",
+ status, status ? "Denied" : "Accepted");
+
+ switch (status) {
+ case MSG_ACCEPTED:
+ print_verif(REPLY);
+ pos = getxdr_pos();
+ astat = getxdr_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "Accept status = %d (%s)",
+ astat, nameof_astat(astat));
+
+ switch (astat) {
+ case SUCCESS:
+ if (x) {
+ show_trailer();
+ protoprint(flags, REPLY, xid,
+ x->xid_prog, x->xid_vers, x->xid_proc,
+ data, len);
+ }
+ break;
+ case PROG_UNAVAIL :
+ break;
+ case PROG_MISMATCH:
+ case PROC_UNAVAIL :
+ showxdr_long(" Low = %d");
+ showxdr_long(" High = %d");
+ break;
+ case GARBAGE_ARGS:
+ case SYSTEM_ERR:
+ default:
+ ;
+ }
+
+ break;
+
+ case MSG_DENIED:
+ pos = getxdr_pos();
+ rstat = getxdr_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "Reject status = %d (%s)",
+ rstat,
+ rstat ? "can't authenticate"
+ : "version mismatch");
+
+ switch (rstat) {
+ case RPC_MISMATCH:
+ showxdr_long(" Low = %d");
+ showxdr_long(" High = %d");
+ break;
+ case AUTH_ERROR:
+ why = getxdr_u_long();
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " Why = %d (%s)",
+ why, nameof_why(why));
+ break;
+ }
+ break;
+ }
+}
+
+/*
+ * Return true if this is a valid RPC packet
+ */
+int
+valid_rpc(char *rpc, int rpclen)
+{
+ XDR xdrm;
+ struct rpc_msg msg;
+
+ if (rpclen < 12)
+ return (0);
+
+ xdrmem_create(&xdrm, rpc, rpclen, XDR_DECODE);
+ if (xdr_u_int(&xdrm, &msg.rm_xid) &&
+ xdr_u_int(&xdrm, (uint_t *)&msg.rm_direction)) {
+ switch (msg.rm_direction) {
+ case CALL:
+ if (xdr_rpcvers(&xdrm, &msg.rm_call.cb_rpcvers) &&
+ msg.rm_call.cb_rpcvers == 2)
+ return (1);
+ break;
+ case REPLY:
+ if (xdr_u_int(&xdrm,
+ (uint_t *)&msg.rm_reply.rp_stat) &&
+ (msg.rm_reply.rp_stat == MSG_ACCEPTED ||
+ msg.rm_reply.rp_stat == MSG_DENIED))
+ return (1);
+ break;
+ }
+ }
+
+ return (0);
+}
+
+struct cache_struct *xcpfirst = &xid_cache[0];
+struct cache_struct *xcp = &xid_cache[0];
+struct cache_struct *xcplast = &xid_cache[XID_CACHE_SIZE - 1];
+
+struct cache_struct *
+find_xid(xid)
+ ulong_t xid;
+{
+ struct cache_struct *x;
+
+ for (x = xcp; x >= xcpfirst; x--)
+ if (x->xid_num == xid)
+ return (x);
+ for (x = xcplast; x > xcp; x--)
+ if (x->xid_num == xid)
+ return (x);
+ return (NULL);
+}
+
+static void
+stash_xid(ulong_t xid, int frame, int prog, int vers, int proc)
+{
+ struct cache_struct *x;
+
+ x = find_xid(xid);
+ if (x == NULL) {
+ x = xcp++;
+ if (xcp > xcplast)
+ xcp = xcpfirst;
+ x->xid_num = xid;
+ x->xid_frame = frame;
+ }
+ x->xid_prog = prog;
+ x->xid_vers = vers;
+ x->xid_proc = proc;
+ x->xid_gss_proc = RPCSEC_GSS_DATA;
+ x->xid_gss_service = rpc_gss_svc_default;
+}
+
+void
+check_retransmit(line, xid)
+ char *line;
+ ulong_t xid;
+{
+ struct cache_struct *x;
+ extern int pi_frame;
+
+ x = find_xid(xid);
+ if (x && x->xid_frame != pi_frame)
+ (void) strcat(line, " (retransmit)");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcprint.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcprint.c
new file mode 100644
index 0000000..8d61536
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcprint.c
@@ -0,0 +1,106 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/tiuser.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include "snoop.h"
+
+#define RPC_TRANSIENT_START 0x40000000
+#define RPC_TRANSIENT_END 0x5fffffff
+
+int rpcsec_gss_control_proc(int type, int flags, int xid);
+
+int rpcsec_gss_pre_proto(int type, int flags, int xid,
+ int prog, int vers, int proc);
+
+void rpcsec_gss_post_proto(int flags, int xid);
+
+void
+protoprint(flags, type, xid, prog, vers, proc, data, len)
+ ulong_t xid;
+ int flags, type, prog, vers, proc;
+ char *data;
+ int len;
+{
+ char *name;
+ void (*interpreter)(int, int, int, int, int, char *, int);
+
+ switch (prog) {
+ case 100000: interpreter = interpret_pmap; break;
+ case 100001: interpreter = interpret_rstat; break;
+ case 100003: interpreter = interpret_nfs; break;
+ case 100004: interpreter = interpret_nis; break;
+ case 100005: interpreter = interpret_mount; break;
+ case 100007: interpreter = interpret_nisbind; break;
+ case 100011: interpreter = interpret_rquota; break;
+ case 100021: interpreter = interpret_nlm; break;
+ case 100026: interpreter = interpret_bparam; break;
+ case 100227: interpreter = interpret_nfs_acl; break;
+ case 150006: interpreter = interpret_solarnet_fw; break;
+ default: interpreter = NULL;
+ }
+
+ /*
+ * if rpc in transient range and proc is 0 or 1, then
+ * guess that it is the nfsv4 callback protocol
+ */
+ if (prog >= RPC_TRANSIENT_START && prog <= RPC_TRANSIENT_END &&
+ (proc == 0 || proc == 1))
+ interpreter = interpret_nfs4_cb;
+
+ /*
+ * If the RPC header indicates it's using the RPCSEC_GSS_*
+ * control procedure, print it.
+ */
+ if (rpcsec_gss_control_proc(type, flags, xid)) {
+ return;
+ }
+
+ if (interpreter == NULL) {
+ if (!(flags & F_SUM))
+ return;
+ name = nameof_prog(prog);
+ if (*name == '?' || strcmp(name, "transient") == 0)
+ return;
+ (void) sprintf(get_sum_line(), "%s %c",
+ name,
+ type == CALL ? 'C' : 'R');
+ } else {
+ /* Pre-processing based on different RPCSEC_GSS services. */
+ if (rpcsec_gss_pre_proto(type, flags, xid, prog, vers, proc))
+ return;
+
+ (*interpreter) (flags, type, xid, vers, proc, data, len);
+
+ /* Post-processing based on different RPCSEC_GSS services. */
+ rpcsec_gss_post_proto(flags, xid);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcsec.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcsec.c
new file mode 100644
index 0000000..e3579dc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpcsec.c
@@ -0,0 +1,403 @@
+/*
+ * 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.
+ * Copyright 2012 Milan Jurik. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/tiuser.h>
+#include <setjmp.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <rpc/rpcsec_gss.h>
+#include <string.h>
+#include "snoop.h"
+
+extern jmp_buf xdr_err;
+
+struct cache_struct *find_xid();
+char *nameof_prog(int prog);
+static void print_rpc_gss_init_arg(int, struct cache_struct *);
+static void print_rpc_gss_init_res(int);
+
+char *
+rpcsec_gss_proc_to_string(unsigned int proc)
+{
+ switch (proc) {
+ case RPCSEC_GSS_DATA: return "RPCSEC_GSS_DATA"; break;
+ case RPCSEC_GSS_INIT: return "RPCSEC_GSS_INIT"; break;
+ case RPCSEC_GSS_CONTINUE_INIT:
+ return ("RPCSEC_GSS_CONTINUE_INIT");
+ case RPCSEC_GSS_DESTROY:
+ return ("RPCSEC_GSS_DESTROY");
+ default: return ("unknown");
+
+ }
+}
+
+
+char *
+rpcsec_gss_service_to_string(rpc_gss_service_t service)
+{
+ switch (service) {
+ case rpc_gss_svc_none: return "none"; break;
+ case rpc_gss_svc_integrity: return "integrity"; break;
+ case rpc_gss_svc_privacy: return "privacy"; break;
+ default: return "unknown"; break;
+
+ }
+}
+
+/*
+ * Print detailed RPCSEC_GSS cred data.
+ */
+void
+print_rpcsec_gss_cred(int xid, int authlen)
+{
+ unsigned int seq_num;
+ unsigned int handle_len;
+ unsigned int rpcsec_gss_ver;
+ rpc_gss_service_t rpcsec_gss_service;
+ unsigned int rpcsec_gss_proc;
+ char *handle, *line;
+ struct cache_struct *x;
+ int pos;
+
+ pos = getxdr_pos();
+ rpcsec_gss_ver = getxdr_u_long();
+
+ /* see if we know this version or not */
+
+ if (rpcsec_gss_ver != 1) {
+ (void) showxdr_hex(authlen, "[%s]");
+ return;
+ }
+
+ rpcsec_gss_proc = getxdr_u_long();
+ seq_num = getxdr_u_long();
+ rpcsec_gss_service = getxdr_enum();
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " version = %u", rpcsec_gss_ver);
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " gss control procedure = %u (%s)",
+ rpcsec_gss_proc,
+ rpcsec_gss_proc_to_string(rpcsec_gss_proc));
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " sequence num = %u", seq_num);
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " service = %d (%s)", rpcsec_gss_service,
+ rpcsec_gss_service_to_string(rpcsec_gss_service));
+ pos = getxdr_pos();
+ handle_len = getxdr_u_long();
+ handle = getxdr_hex(handle_len);
+ line = get_line(pos, getxdr_pos());
+ sprintf(line, " handle: length = %d, data = [%s]",
+ handle_len, handle);
+ x = find_xid(xid);
+ if (x) {
+ x->xid_gss_proc = rpcsec_gss_proc;
+ x->xid_gss_service = rpcsec_gss_service;
+ }
+}
+
+/*
+ * Based on different RPCSEC_GSS services supported, maybe a
+ * special handling is needed before printing the arguments.
+ *
+ * For integrity service : print the sequence number.
+ * For privacy service : do not print the arguments.
+ */
+int
+rpcsec_gss_pre_proto(int type, int flags, int xid,
+ int prog, int vers, int proc)
+{
+ int seq;
+ struct cache_struct *x;
+
+ if (! (x = find_xid(xid)))
+ return (0);
+
+ switch (x->xid_gss_service) {
+ case rpc_gss_svc_default:
+ case rpc_gss_svc_none:
+ break; /* standard call args */
+ case rpc_gss_svc_integrity:
+ /* length of rpc_gss_data_t encoded in the databody_integ */
+ getxdr_u_long();
+ /* read the seq number */
+ seq = getxdr_u_long();
+ if (flags & F_ALLSUM) {
+ (void) sprintf(get_sum_line(), "%s %c seq_num = %u",
+ "RPC RPCSEC_GSS", type == CALL ? 'C' : 'R',
+ seq);
+ } else if (flags & F_DTAIL) {
+ sprintf(get_line(0, 0),
+ "RPCSEC_GSS data seq_num = %u", seq);
+ show_space();
+ }
+ /* call args follow */
+ break;
+ case rpc_gss_svc_privacy: {
+ char *progname = nameof_prog(prog);
+ char prognum[32];
+
+ if (*progname == '?') {
+ sprintf(prognum, "%d", prog);
+ progname = prognum;
+ }
+
+ if (flags & F_SUM || flags & F_ALLSUM) {
+ (void) sprintf(get_sum_line(),
+ "%s %c %s ver(%d) proc(%d) (data encrypted) ",
+ "RPC RPCSEC_GSS", type == CALL ? 'C' : 'R',
+ progname, vers, proc);
+ } else if (flags & F_DTAIL) {
+ unsigned int args_len;
+
+ args_len = getxdr_u_long();
+ sprintf(get_line(0, 0),
+ "RPCSEC_GSS %s ver(%d) proc(%d)",
+ progname, vers, proc);
+ sprintf(get_line(0, 0),
+ "(%s args encrypted, len = %d bytes)",
+ type == CALL ? "CALL" : "REPLY", args_len);
+ show_space();
+ }
+ }
+ return (1);
+
+ default:
+ break;
+ }
+ return (0);
+}
+
+/*
+ * Based on different RPCSEC_GSS services supported, maybe a
+ * special handling is needed after printing the arguments.
+ *
+ * For integrity service : print the checksum.
+ */
+void
+rpcsec_gss_post_proto(int flags, int xid)
+{
+ char *line;
+
+ struct cache_struct *x;
+
+ if (! (x = find_xid(xid)))
+ return;
+
+ switch (x->xid_gss_service) {
+ case rpc_gss_svc_default:
+ case rpc_gss_svc_none:
+ case rpc_gss_svc_privacy:
+ /* nothing left */
+ break;
+ case rpc_gss_svc_integrity:
+ if (flags & F_ALLSUM) {
+ line = get_sum_line();
+ sprintf(line, "RPC RPCSEC_GSS C (checksum)");
+ } else if (flags & F_DTAIL) {
+ unsigned int checksum_len;
+ char *checksum;
+
+ show_header("RPC: ", "RPCSEC_GSS", 0);
+ show_space();
+ checksum_len = getxdr_u_long();
+ checksum = getxdr_hex(checksum_len);
+ sprintf(get_line(0, 0),
+ "checksum: len = %d", checksum_len);
+ sprintf(get_line(0, 0), "[%s]", checksum);
+ show_trailer();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Print RPCSEC_GSS control procedures protocol data,
+ * No-op for RPCSEC_GSS_DATA.
+ */
+int
+rpcsec_gss_control_proc(int type, int flags, int xid)
+{
+ int seq;
+
+ struct cache_struct *x;
+
+ if (! (x = find_xid(xid)))
+ return (0);
+
+ if (x->xid_gss_proc != RPCSEC_GSS_DATA) {
+ if (flags & F_SUM) {
+ if (type == CALL) {
+ (void) sprintf(get_sum_line(), "%s %c %u (%s)",
+ "RPC RPCSEC_GSS",
+ type == CALL ? 'C' : 'R',
+ x->xid_gss_proc,
+ rpcsec_gss_proc_to_string(x->xid_gss_proc));
+ }
+ } else if (flags & F_DTAIL) {
+ if (x->xid_gss_proc == RPCSEC_GSS_INIT ||
+ x->xid_gss_proc == RPCSEC_GSS_CONTINUE_INIT) {
+ if (type == CALL) {
+ print_rpc_gss_init_arg(flags, x);
+ } else {
+ print_rpc_gss_init_res(flags);
+ }
+ }
+ }
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Skip the header RPCSEC_GSS cred data and
+ * put service and control type in the xid cache.
+ */
+void
+extract_rpcsec_gss_cred_info(int xid)
+{
+ unsigned int seq_num;
+ unsigned int handle_len;
+ unsigned int flavor_len;
+ unsigned int rpcsec_gss_ver;
+ rpc_gss_service_t rpcsec_gss_service;
+ unsigned int rpcsec_gss_proc;
+ struct cache_struct *x;
+
+ flavor_len = getxdr_u_long();
+ rpcsec_gss_ver = getxdr_u_long();
+ /* see if we know this version or not */
+ if (rpcsec_gss_ver != 1) {
+ longjmp(xdr_err, 1);
+ }
+ rpcsec_gss_proc = getxdr_u_long();
+ seq_num = getxdr_u_long();
+ rpcsec_gss_service = getxdr_enum();
+ /* skip the handle */
+ xdr_skip(RNDUP(getxdr_u_long()));
+
+ if (x = find_xid(xid)) {
+ x->xid_gss_service = rpcsec_gss_service;
+ x->xid_gss_proc = rpcsec_gss_proc;
+ }
+
+}
+
+/*
+ * Print the argument data for the RPCSEC_GSS_INIT control procedure.
+ */
+static void
+print_rpc_gss_init_arg(int flags, struct cache_struct *x)
+{
+
+ char *token, *line;
+ unsigned int token_len;
+ int pos = 0;
+
+ /*
+ * see if we need to print out the rpc_gss_init_arg structure
+ * or not.
+ */
+
+ if (x->xid_gss_proc != RPCSEC_GSS_INIT &&
+ x->xid_gss_proc != RPCSEC_GSS_CONTINUE_INIT) {
+ return;
+ }
+
+ /* print it */
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ "RPCSEC_GSS_INIT args:");
+
+ pos = getxdr_pos();
+ token_len = getxdr_u_long();
+ token = getxdr_hex(token_len);
+ line = get_line(pos, getxdr_pos());
+ sprintf(line, " gss token: length = %d, data = [%d bytes]",
+ token_len, token_len);
+
+ show_trailer();
+}
+
+/*
+ * Print the results data for the RPCSEC_GSS_INIT control procedure.
+ */
+void
+print_rpc_gss_init_res(int flags)
+{
+
+ char *handle, *token, *line;
+ unsigned int token_len, handle_len;
+ unsigned int major, minor, seq_window;
+
+ int pos = 0;
+ struct cache_struct *x;
+
+ /* print it */
+
+ (void) sprintf(get_line(pos, getxdr_pos()), "RPCSEC_GSS_INIT result:");
+
+ pos = getxdr_pos();
+ handle_len = getxdr_u_long();
+ handle = getxdr_hex(handle_len);
+ line = get_line(pos, getxdr_pos());
+ sprintf(line, " handle: length = %d, data = [%s]",
+ handle_len, handle);
+ pos = getxdr_pos();
+ major = getxdr_u_long();
+ minor = getxdr_u_long();
+ seq_window = getxdr_u_long();
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " gss_major status = %u", major);
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " gss_minor status = %u", minor);
+
+ (void) sprintf(get_line(pos, getxdr_pos()),
+ " sequence window = %u", seq_window);
+ pos = getxdr_pos();
+ token_len = getxdr_u_long();
+ token = getxdr_hex(token_len);
+ line = get_line(pos, getxdr_pos());
+ sprintf(line, " gss token: length = %d, data = [%d bytes]",
+ token_len, token_len);
+ show_trailer();
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c
new file mode 100644
index 0000000..b84ee3c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c
@@ -0,0 +1,452 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include "snoop.h"
+
+struct porttable {
+ int pt_num;
+ char *pt_short;
+};
+
+static const struct porttable pt_udp[] = {
+ { IPPORT_ECHO, "ECHO" },
+ { IPPORT_DISCARD, "DISCARD" },
+ { IPPORT_DAYTIME, "DAYTIME" },
+ { IPPORT_CHARGEN, "CHARGEN" },
+ { IPPORT_TIMESERVER, "TIME" },
+ { IPPORT_NAMESERVER, "NAME" },
+ { IPPORT_DOMAIN, "DNS" },
+ { IPPORT_MDNS, "MDNS" },
+ { IPPORT_BOOTPS, "BOOTPS" },
+ { IPPORT_BOOTPC, "BOOTPC" },
+ { IPPORT_TFTP, "TFTP" },
+ { IPPORT_FINGER, "FINGER" },
+/* { 111, "PORTMAP" }, Just Sun RPC */
+ { IPPORT_NTP, "NTP" },
+ { IPPORT_NETBIOS_NS, "NBNS" },
+ { IPPORT_NETBIOS_DGM, "NBDG" },
+ { IPPORT_LDAP, "LDAP" },
+ { IPPORT_SLP, "SLP" },
+/* Mobile IP defines a set of new control messages sent over UDP port 434 */
+ { IPPORT_MIP, "Mobile IP" },
+ { IPPORT_BIFFUDP, "BIFF" },
+ { IPPORT_WHOSERVER, "WHO" },
+ { IPPORT_SYSLOG, "SYSLOG" },
+ { IPPORT_TALK, "TALK" },
+ { IPPORT_ROUTESERVER, "RIP" },
+ { IPPORT_RIPNG, "RIPng" },
+ { IPPORT_DHCPV6C, "DHCPv6C" },
+ { IPPORT_DHCPV6S, "DHCPv6S" },
+ { 550, "NEW-RWHO" },
+ { 560, "RMONITOR" },
+ { 561, "MONITOR" },
+ { IPPORT_SOCKS, "SOCKS" },
+ { 0, NULL }
+};
+
+static struct porttable pt_tcp[] = {
+ { 1, "TCPMUX" },
+ { IPPORT_ECHO, "ECHO" },
+ { IPPORT_DISCARD, "DISCARD" },
+ { IPPORT_SYSTAT, "SYSTAT" },
+ { IPPORT_DAYTIME, "DAYTIME" },
+ { IPPORT_NETSTAT, "NETSTAT" },
+ { IPPORT_CHARGEN, "CHARGEN" },
+ { 20, "FTP-DATA" },
+ { IPPORT_FTP, "FTP" },
+ { IPPORT_TELNET, "TELNET" },
+ { IPPORT_SMTP, "SMTP" },
+ { IPPORT_TIMESERVER, "TIME" },
+ { 39, "RLP" },
+ { IPPORT_NAMESERVER, "NAMESERVER" },
+ { IPPORT_WHOIS, "NICNAME" },
+ { IPPORT_DOMAIN, "DNS" },
+ { 70, "GOPHER" },
+ { IPPORT_RJE, "RJE" },
+ { IPPORT_FINGER, "FINGER" },
+ { IPPORT_HTTP, "HTTP" },
+ { IPPORT_TTYLINK, "LINK" },
+ { IPPORT_SUPDUP, "SUPDUP" },
+ { 101, "HOSTNAME" },
+ { 102, "ISO-TSAP" },
+ { 103, "X400" },
+ { 104, "X400-SND" },
+ { 105, "CSNET-NS" },
+ { 109, "POP-2" },
+/* { 111, "PORTMAP" }, Just Sun RPC */
+ { 113, "AUTH" },
+ { 117, "UUCP-PATH" },
+ { 119, "NNTP" },
+ { IPPORT_NTP, "NTP" },
+ { IPPORT_NETBIOS_SSN, "NBT" },
+ { 143, "IMAP" },
+ { 144, "NeWS" },
+ { IPPORT_LDAP, "LDAP" },
+ { IPPORT_SLP, "SLP" },
+ { 443, "HTTPS" },
+ { 445, "SMB" },
+ { IPPORT_EXECSERVER, "EXEC" },
+ { IPPORT_LOGINSERVER, "RLOGIN" },
+ { IPPORT_CMDSERVER, "RSHELL" },
+ { IPPORT_PRINTER, "PRINTER" },
+ { 530, "COURIER" },
+ { 540, "UUCP" },
+ { 600, "PCSERVER" },
+ { IPPORT_SOCKS, "SOCKS" },
+ { 1524, "INGRESLOCK" },
+ { 2904, "M2UA" },
+ { 2905, "M3UA" },
+ { 6000, "XWIN" },
+ { IPPORT_HTTP_ALT, "HTTP (proxy)" },
+ { 9900, "IUA" },
+ { 0, NULL },
+};
+
+char *
+getportname(int proto, in_port_t port)
+{
+ const struct porttable *p, *pt;
+
+ switch (proto) {
+ case IPPROTO_SCTP: /* fallthru */
+ case IPPROTO_TCP: pt = pt_tcp; break;
+ case IPPROTO_UDP: pt = pt_udp; break;
+ default: return (NULL);
+ }
+
+ for (p = pt; p->pt_num; p++) {
+ if (port == p->pt_num)
+ return (p->pt_short);
+ }
+ return (NULL);
+}
+
+int
+reservedport(int proto, int port)
+{
+ const struct porttable *p, *pt;
+
+ switch (proto) {
+ case IPPROTO_TCP: pt = pt_tcp; break;
+ case IPPROTO_UDP: pt = pt_udp; break;
+ default: return (NULL);
+ }
+ for (p = pt; p->pt_num; p++) {
+ if (port == p->pt_num)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Need to be able to register an
+ * interpreter for transient ports.
+ * See TFTP interpreter.
+ */
+#define MAXTRANS 64
+static struct ttable {
+ int t_port;
+ int (*t_proc)(int, char *, int);
+} transients [MAXTRANS];
+
+int
+add_transient(int port, int (*proc)(int, char *, int))
+{
+ static struct ttable *next = transients;
+
+ next->t_port = port;
+ next->t_proc = proc;
+
+ if (++next >= &transients[MAXTRANS])
+ next = transients;
+
+ return (1);
+}
+
+static struct ttable *
+is_transient(int port)
+{
+ struct ttable *p;
+
+ for (p = transients; p->t_port && p < &transients[MAXTRANS]; p++) {
+ if (port == p->t_port)
+ return (p);
+ }
+
+ return (NULL);
+}
+
+void
+del_transient(int port)
+{
+ struct ttable *p;
+
+ for (p = transients; p->t_port && p < &transients[MAXTRANS]; p++) {
+ if (port == p->t_port)
+ p->t_port = -1;
+ }
+}
+
+static void
+interpret_syslog(int flags, char dir, int port, const char *syslogstr,
+ int dlen)
+{
+ static const char *pris[] = {
+ "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug"
+ };
+ static const char *facs[] = {
+ "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", "news",
+ "uucp", NULL, NULL, NULL, NULL, "audit", NULL, "cron", "local0",
+ "local1", "local2", "local3", "local4", "local5", "local6", "local7"
+ };
+
+ int composit;
+ int pri = -1;
+ int facil = -1;
+ boolean_t bogus = B_TRUE;
+ int priostrlen = 0;
+ int datalen = dlen;
+ char unknown[4]; /* for unrecognized ones */
+ const char *facilstr = "BAD";
+ const char *pristr = "FMT";
+ const char *data = syslogstr;
+
+ /*
+ * Is there enough data to interpret (left bracket + at least 3 chars
+ * which could be digits, right bracket, or space)?
+ */
+ if (datalen >= 4 && data != NULL) {
+ if (*data == '<') {
+ const int FACS_LEN = sizeof (facs) / sizeof (facs[0]);
+ char buffer[4];
+ char *end;
+
+ data++;
+ datalen--;
+
+ (void) strlcpy(buffer, data, sizeof (buffer));
+ composit = strtoul(buffer, &end, 0);
+ data += end - buffer;
+ if (*data == '>') {
+ data++;
+ datalen -= end - buffer + 1;
+
+ pri = composit & 0x7;
+ facil = (composit & 0xF8) >> 3;
+
+ if ((facil >= FACS_LEN) ||
+ (facs[facil] == NULL)) {
+ snprintf(unknown, sizeof (unknown),
+ "%d", facil);
+ facilstr = unknown;
+ } else {
+ facilstr = facs[facil];
+ }
+ pristr = pris[pri];
+ priostrlen = dlen - datalen;
+ bogus = B_FALSE;
+ } else {
+ data = syslogstr;
+ datalen = dlen;
+ }
+ }
+ }
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "SYSLOG %c port=%d %s.%s: %s",
+ dir, port, facilstr, pristr,
+ show_string(syslogstr, dlen, 20));
+
+ }
+
+ if (flags & F_DTAIL) {
+ static char syslog[] = "SYSLOG: ";
+ show_header(syslog, syslog, dlen);
+ show_space();
+ (void) snprintf(get_detail_line(0, 0), MAXLINE,
+ "%s%sPriority: %.*s%s(%s.%s)", prot_nest_prefix, syslog,
+ priostrlen, syslogstr, bogus ? "" : " ",
+ facilstr, pristr);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "\"%s\"",
+ show_string(syslogstr, dlen, 60));
+ show_trailer();
+ }
+}
+
+int src_port, dst_port, curr_proto;
+
+int
+interpret_reserved(int flags, int proto, in_port_t src, in_port_t dst,
+ char *data, int dlen)
+{
+ const char *pn;
+ int dir, port, which;
+ char pbuff[16], hbuff[32];
+ struct ttable *ttabp;
+
+ src_port = src;
+ dst_port = dst;
+ curr_proto = proto;
+
+ pn = getportname(proto, src);
+ if (pn != NULL) {
+ dir = 'R';
+ port = dst;
+ which = src;
+ } else {
+ pn = getportname(proto, dst);
+ if (pn == NULL) {
+ ttabp = is_transient(src);
+ if (ttabp) {
+ (ttabp->t_proc)(flags, data, dlen);
+ return (1);
+ }
+ ttabp = is_transient(dst);
+ if (ttabp) {
+ (ttabp->t_proc)(flags, data, dlen);
+ return (1);
+ }
+ return (0);
+ }
+
+ dir = 'C';
+ port = src;
+ which = dst;
+ }
+
+ if ((dst == IPPORT_DOMAIN || src == IPPORT_DOMAIN ||
+ dst == IPPORT_MDNS || src == IPPORT_MDNS) &&
+ proto != IPPROTO_TCP) {
+ interpret_dns(flags, proto, (uchar_t *)data, dlen, which);
+ return (1);
+ }
+
+ if (dst == IPPORT_SYSLOG && proto != IPPROTO_TCP) {
+ /*
+ * TCP port 514 is rshell. UDP port 514 is syslog.
+ */
+ interpret_syslog(flags, dir, port, (const char *)data, dlen);
+ return (1);
+ }
+
+ if (dlen > 0) {
+ switch (which) {
+ case IPPORT_BOOTPS:
+ case IPPORT_BOOTPC:
+ (void) interpret_dhcp(flags, (struct dhcp *)data,
+ dlen);
+ return (1);
+ case IPPORT_DHCPV6S:
+ case IPPORT_DHCPV6C:
+ (void) interpret_dhcpv6(flags, (uint8_t *)data, dlen);
+ return (1);
+ case IPPORT_TFTP:
+ (void) interpret_tftp(flags, (struct tftphdr *)data,
+ dlen);
+ return (1);
+ case IPPORT_HTTP:
+ case IPPORT_HTTP_ALT:
+ (void) interpret_http(flags, data, dlen);
+ return (1);
+ case IPPORT_NTP:
+ (void) interpret_ntp(flags, (struct ntpdata *)data,
+ dlen);
+ return (1);
+ case IPPORT_NETBIOS_NS:
+ interpret_netbios_ns(flags, (uchar_t *)data, dlen);
+ return (1);
+ case IPPORT_NETBIOS_DGM:
+ interpret_netbios_datagram(flags, (uchar_t *)data,
+ dlen);
+ return (1);
+ case IPPORT_NETBIOS_SSN:
+ case 445:
+ /*
+ * SMB on port 445 is a subset of NetBIOS SMB
+ * on port 139. The same interpreter can be used
+ * for both.
+ */
+ interpret_netbios_ses(flags, (uchar_t *)data, dlen);
+ return (1);
+ case IPPORT_LDAP:
+ interpret_ldap(flags, data, dlen, src, dst);
+ return (1);
+ case IPPORT_SLP:
+ interpret_slp(flags, data, dlen);
+ return (1);
+ case IPPORT_MIP:
+ interpret_mip_cntrlmsg(flags, (uchar_t *)data, dlen);
+ return (1);
+ case IPPORT_ROUTESERVER:
+ (void) interpret_rip(flags, (struct rip *)data, dlen);
+ return (1);
+ case IPPORT_RIPNG:
+ (void) interpret_rip6(flags, (struct rip6 *)data,
+ dlen);
+ return (1);
+ case IPPORT_SOCKS:
+ if (dir == 'C')
+ (void) interpret_socks_call(flags, data, dlen);
+ else
+ (void) interpret_socks_reply(flags, data,
+ dlen);
+ return (1);
+ }
+ }
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "%s %c port=%d %s",
+ pn, dir, port,
+ show_string(data, dlen, 20));
+ }
+
+ if (flags & F_DTAIL) {
+ (void) snprintf(pbuff, sizeof (pbuff), "%s: ", pn);
+ (void) snprintf(hbuff, sizeof (hbuff), "%s: ", pn);
+ show_header(pbuff, hbuff, dlen);
+ show_space();
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "\"%s\"",
+ show_string(data, dlen, 60));
+ show_trailer();
+ }
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rquota.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rquota.c
new file mode 100644
index 0000000..13f5dc6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rquota.c
@@ -0,0 +1,164 @@
+/*
+ * 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 (c) 1991, 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include <netinet/in.h>
+#include <rpc/types.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <rpcsvc/rquota.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+static char *procnames_short[] = {
+ "Null", /* 0 */
+ "GETQUOTA", /* 1 */
+ "GETACTIVE", /* 2 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Get quotas", /* 1 */
+ "Get active quotas", /* 2 */
+};
+
+#define MAXPROC 2
+
+static void show_quota(void);
+
+void
+interpret_rquota(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+ char buff[RQ_PATHLEN + 1];
+ int status;
+ int uid;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) getxdr_string(buff, RQ_PATHLEN);
+ uid = getxdr_long();
+ (void) sprintf(line,
+ "RQUOTA C %s Uid=%d Path=%s",
+ procnames_short[proc],
+ uid, buff);
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "RQUOTA R %s ",
+ procnames_short[proc]);
+ line += strlen(line);
+ status = getxdr_u_long();
+ if (status == Q_OK)
+ (void) sprintf(line, "OK");
+ else if (status == Q_NOQUOTA)
+ (void) sprintf(line, "No quota");
+ else if (status == Q_EPERM)
+ (void) sprintf(line, "No permission");
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("RQUOTA: ", "Remote Quota Check", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+
+ if (type == CALL) {
+ switch (proc) {
+ case RQUOTAPROC_GETQUOTA:
+ case RQUOTAPROC_GETACTIVEQUOTA:
+ (void) showxdr_string(RQ_PATHLEN,
+ "Path = %s");
+ (void) showxdr_long("User id = %d");
+ break;
+ }
+ } else {
+ status = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ "Status = %lu (%s)",
+ status,
+ status == Q_OK ? "OK" :
+ status == Q_NOQUOTA ? "No quota" :
+ status == Q_EPERM ? "No permission":"");
+
+ if (status == Q_OK)
+ show_quota();
+ }
+
+ show_trailer();
+ }
+}
+
+static void
+show_quota()
+{
+ int active;
+
+ (void) showxdr_u_long("Block size = %lu");
+ active = getxdr_u_long();
+ (void) sprintf(get_line(0, 0),
+ " Quota checking = %lu (%s)",
+ active,
+ active ? "on" : "off");
+ (void) showxdr_u_long(" Blocks hard limit = %lu");
+ (void) showxdr_u_long(" Blocks soft limit = %lu");
+ (void) showxdr_u_long(" Current block count = %lu");
+ (void) show_space();
+ (void) showxdr_u_long(" File hard limit = %lu");
+ (void) showxdr_u_long(" File soft limit = %lu");
+ (void) showxdr_u_long(" Current file count = %lu");
+ (void) show_space();
+ (void) showxdr_u_long("Excessive blocks limit = %lu sec");
+ (void) showxdr_u_long("Excessive files limit = %lu sec");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rstat.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rstat.c
new file mode 100644
index 0000000..e45205a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rstat.c
@@ -0,0 +1,272 @@
+/*
+ * 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 (c) 1991, 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ident "%Z%%M% %I% %E% SMI" /* SunOS */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <sys/tiuser.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+void detail_stats(); /* Version 1 */
+void detail_statsswtch(); /* Version 2 */
+void detail_statstime(); /* Version 3 */
+void detail_statsvar(); /* Version 4 */
+
+static char *procnames_short[] = {
+ "Null", /* 0 */
+ "Get Statistics", /* 1 */
+ "Have Disk", /* 2 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Get Statistics", /* 1 */
+ "Have Disk", /* 2 */
+};
+
+#define MAXPROC 2
+
+void
+interpret_rstat(flags, type, xid, vers, proc, data, len)
+ int flags, type, xid, vers, proc;
+ char *data;
+ int len;
+{
+ char *line;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "RSTAT C %s",
+ procnames_short[proc]);
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "RSTAT R %s ",
+ procnames_short[proc]);
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("RSTAT: ", "RSTAT Get Statistics", len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Proc = %d (%s)",
+ proc, procnames_long[proc]);
+
+ if (type == REPLY) {
+ switch (proc) {
+ case 1:
+ switch (vers) {
+ case 1:
+ detail_stats();
+ break;
+ case 2:
+ detail_statsswtch();
+ break;
+ case 3:
+ detail_statstime();
+ break;
+ case 4:
+ detail_statsvar();
+ break;
+ }
+ break;
+ case 2:
+ (void) showxdr_u_long(
+ "Result = %lu");
+ break;
+ }
+ }
+ show_trailer();
+ }
+}
+
+void
+detail_stats()
+{
+ show_space();
+ (void) sprintf(get_line(0, 0), "CPU Times:");
+ (void) showxdr_long(" Time (1) = %d");
+ (void) showxdr_long(" Time (2) = %d");
+ (void) showxdr_long(" Time (3) = %d");
+ (void) showxdr_long(" Time (4) = %d");
+ show_space();
+ (void) sprintf(get_line(0, 0), "Disk Transfers:");
+ (void) showxdr_long(" Transfers(1) = %d");
+ (void) showxdr_long(" Transfers(2) = %d");
+ (void) showxdr_long(" Transfers(3) = %d");
+ (void) showxdr_long(" Transfers(4) = %d");
+ show_space();
+ (void) showxdr_u_long("Pages in = %lu");
+ (void) showxdr_u_long("Pages out = %lu");
+ (void) showxdr_u_long("Swaps in = %lu");
+ (void) showxdr_u_long("Swaps out = %lu");
+ (void) showxdr_u_long("Interrupts = %lu");
+ show_space();
+ (void) showxdr_long("Receive packets = %d");
+ (void) showxdr_long("Receive errors = %d");
+ (void) showxdr_long("Transmit packets = %d");
+ (void) showxdr_long("Transmit errors = %d");
+ (void) showxdr_long("Collisions = %d");
+}
+
+void
+detail_statsswtch()
+{
+ show_space();
+ (void) sprintf(get_line(0, 0), "CPU Times:");
+ (void) showxdr_long(" Time (1) = %d");
+ (void) showxdr_long(" Time (2) = %d");
+ (void) showxdr_long(" Time (3) = %d");
+ (void) showxdr_long(" Time (4) = %d");
+ show_space();
+ (void) sprintf(get_line(0, 0), "Disk Transfers:");
+ (void) showxdr_long(" Transfers(1) = %d");
+ (void) showxdr_long(" Transfers(2) = %d");
+ (void) showxdr_long(" Transfers(3) = %d");
+ (void) showxdr_long(" Transfers(4) = %d");
+ show_space();
+ (void) showxdr_u_long("Pages in = %lu");
+ (void) showxdr_u_long("Pages out = %lu");
+ (void) showxdr_u_long("Swaps in = %lu");
+ (void) showxdr_u_long("Swaps out = %lu");
+ (void) showxdr_u_long("Interrupts = %lu");
+ show_space();
+ (void) showxdr_long("Receive packets = %d");
+ (void) showxdr_long("Receive errors = %d");
+ (void) showxdr_long("Transmit packets = %d");
+ (void) showxdr_long("Transmit errors = %d");
+ (void) showxdr_long("Collisions = %d");
+ show_space();
+ (void) showxdr_u_long("V switch = %lu");
+ (void) showxdr_long("Average run 0 = %d");
+ (void) showxdr_long("Average run 1 = %d");
+ (void) showxdr_long("Average run 2 = %d");
+ show_space();
+ (void) showxdr_date("Boot time: = %s");
+}
+
+void
+detail_statstime()
+{
+ show_space();
+ (void) sprintf(get_line(0, 0), "CPU Times:");
+ (void) showxdr_long(" Time (1) = %d");
+ (void) showxdr_long(" Time (2) = %d");
+ (void) showxdr_long(" Time (3) = %d");
+ (void) showxdr_long(" Time (4) = %d");
+ show_space();
+ (void) sprintf(get_line(0, 0), "Disk Transfers:");
+ (void) showxdr_long(" Transfers(1) = %d");
+ (void) showxdr_long(" Transfers(2) = %d");
+ (void) showxdr_long(" Transfers(3) = %d");
+ (void) showxdr_long(" Transfers(4) = %d");
+ show_space();
+ (void) showxdr_u_long("Pages in = %lu");
+ (void) showxdr_u_long("Pages out = %lu");
+ (void) showxdr_u_long("Swaps in = %lu");
+ (void) showxdr_u_long("Swaps out = %lu");
+ (void) showxdr_u_long("Interrupts = %lu");
+ show_space();
+ (void) showxdr_long("Receive packets = %d");
+ (void) showxdr_long("Receive errors = %d");
+ (void) showxdr_long("Transmit packets = %d");
+ (void) showxdr_long("Transmit errors = %d");
+ (void) showxdr_long("Collisions = %d");
+ show_space();
+ (void) showxdr_u_long("V switch = %lu");
+ (void) showxdr_long("Average run 0 = %d");
+ (void) showxdr_long("Average run 1 = %d");
+ (void) showxdr_long("Average run 2 = %d");
+ show_space();
+ (void) showxdr_date("Boot time: = %s");
+ (void) showxdr_date("Current time = %s");
+}
+
+void
+detail_statsvar()
+{
+ int i, n;
+
+ show_space();
+ (void) sprintf(get_line(0, 0), "CPU Times:");
+ n = getxdr_u_long();
+ for (i = 1; i <= n; i++) {
+ (void) sprintf(get_line(0, 0),
+ " Time (%2d) = %d", i, getxdr_long());
+ }
+ show_space();
+ (void) sprintf(get_line(0, 0), "Disk Transfers:");
+ n = getxdr_u_long();
+ for (i = 1; i <= n; i++) {
+ (void) sprintf(get_line(0, 0),
+ " Transfers (%2d) = %d", i, getxdr_long());
+ }
+ show_space();
+ (void) showxdr_u_long("Pages in = %lu");
+ (void) showxdr_u_long("Pages out = %lu");
+ (void) showxdr_u_long("Swaps in = %lu");
+ (void) showxdr_u_long("Swaps out = %lu");
+ (void) showxdr_u_long("Interrupts = %lu");
+ show_space();
+ (void) showxdr_long("Receive packets = %d");
+ (void) showxdr_long("Receive errors = %d");
+ (void) showxdr_long("Transmit packets = %d");
+ (void) showxdr_long("Transmit errors = %d");
+ (void) showxdr_long("Collisions = %d");
+ show_space();
+ (void) showxdr_u_long("V switch = %lu");
+ (void) showxdr_long("Average run 0 = %d");
+ (void) showxdr_long("Average run 1 = %d");
+ (void) showxdr_long("Average run 2 = %d");
+ show_space();
+ (void) showxdr_date("Boot time: = %s");
+ (void) showxdr_date("Current time = %s");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rtmp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rtmp.c
new file mode 100644
index 0000000..db3054a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rtmp.c
@@ -0,0 +1,181 @@
+/*
+ * 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 (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <at.h>
+#include <snoop.h>
+
+static void show_rtmp_tuples(uint8_t *, int);
+
+static char *
+rtmp_func_long(uint8_t fun)
+{
+ switch (fun) {
+ case RTMP_REQ:
+ return ("Request");
+ case RTMP_RDR_SH:
+ return ("Route Data Request, split horizon");
+ case RTMP_RDR_NSH:
+ return ("Route Data Request, no split horizon");
+ default:
+ return ("unknown");
+ }
+}
+
+static char *
+rtmp_func_short(uint8_t fun)
+{
+ switch (fun) {
+ case RTMP_REQ:
+ return ("Req");
+ case RTMP_RDR_SH:
+ return ("RDR, sh");
+ case RTMP_RDR_NSH:
+ return ("RDR, no sh");
+ default:
+ return ("unknown");
+ }
+}
+
+void
+interpret_rtmp(int flags, struct ddp_hdr *ddp, int len)
+{
+ uint8_t *data;
+ uint16_t snet;
+ uint8_t node;
+ int tuples;
+ int runt;
+ char extended;
+
+ len -= DDPHDR_SIZE;
+ if (len < 0)
+ goto out;
+
+ data = (uint8_t *)ddp + DDPHDR_SIZE;
+
+ switch (ddp->ddp_type) {
+ case DDP_TYPE_RTMPRQ: /* simple rtmp */
+ if (len < 1)
+ goto out;
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "RTMP F=%s",
+ rtmp_func_short(data[0]));
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("RTMP: ", "RTMP Header", len);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Func = %d (%s)",
+ data[0], rtmp_func_long(data[0]));
+ }
+ break;
+ case DDP_TYPE_RTMPRESP: /* RTMP data */
+ if (len < 3)
+ goto out;
+
+ snet = get_short(data);
+ if (data[2] != 8) /* ID length is always 8 */
+ return;
+ node = data[3]; /* assume id_len == 8 */
+ extended = (data[6] != RTMP_FILLER) &&
+ (get_short(&data[4]) != 0);
+
+ tuples = (len - 4) / 3;
+ runt = (len - 4) % 3; /* integral length? */
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "RTMP Data Snet=%d, Snode=%d%s",
+ snet, node, runt != 0 ? " (short)" : "");
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("RTMP: ", "RTMP Header", len);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "RTMP Data, Length = %d%s",
+ len, runt != 0 ? " (short packet)" : "");
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Senders Net = %d, Sender Node %d",
+ snet, node);
+ if (extended)
+ show_rtmp_tuples(&data[4], tuples);
+ else
+ show_rtmp_tuples(&data[7], tuples-1);
+ }
+
+ break;
+ }
+ return;
+out:
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "RTMP (short packet)");
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("RTMP: ", "RTMP Header", len);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "(short packet)");
+ }
+}
+
+static void
+show_rtmp_tuples(uint8_t *p, int tuples)
+{
+ while (tuples > 0) {
+
+ if (p[2] & RTMP_EXTEND) { /* extended tuple? */
+
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Network = %d-%d, Distance = %d",
+ get_short(p), get_short(&p[3]),
+ p[2] & RTMP_DIST_MASK);
+ p += 6;
+ tuples -= 2;
+ } else {
+
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Network = %d, Distance = %d",
+ get_short(p), p[2]);
+ p += 3;
+ tuples--;
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_sctp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_sctp.c
new file mode 100644
index 0000000..8aec695
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_sctp.c
@@ -0,0 +1,1208 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <inet/common.h>
+#include <netinet/in.h>
+#include <netinet/sctp.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include "snoop.h"
+
+/*
+ * Snoop interpreter for SCTP (rfc2960).
+ *
+ * To add support for an upper-layer protocol, modify either
+ * the port-dispatcher in snoop_rport.c, or the protocol ID
+ * dispatcher at the bottom of this file (or both).
+ */
+
+static void interpret_protoid(int, uint32_t, char *, int);
+extern char *prot_prefix;
+
+/*
+ * This defines the length of internal, unbounded buffers. We set
+ * this to be MAXLINE (the maximum verbose display line length) -
+ * 64, which should be enough for all necessary descriptions. 64
+ * bytes seems like a reasonably conservative estimate of the
+ * maximum prefix length snoop may add to any text buffer it hands out.
+ */
+#define BUFLEN MAXLINE - 64
+
+/*
+ * Common structure to hold descriptions and parsers for all
+ * chunks, parameters, and errors. Each parser should implement
+ * this interface:
+ *
+ * void parse(int flags, uint8_t cflags, void *data, int datalen);
+ *
+ * Where flags is the snoop flags, cflags are the chunk flags, data
+ * is the chunk or parameter data (not including the chunk or
+ * parameter header), and datalen is the length of the chunk or
+ * parameter data (again not including any headers).
+ */
+typedef void parse_func_t(int, uint8_t, const void *, int);
+
+typedef struct {
+ uint16_t id;
+ const char *sdesc; /* short description */
+ const char *vdesc; /* verbose description */
+ parse_func_t *parse; /* parser function */
+} dispatch_t;
+
+static void interpret_params(const void *, int, char *, const dispatch_t *,
+ int, int);
+
+/*
+ * Chunk parsers
+ */
+static parse_func_t parse_abort_chunk, parse_data_chunk, parse_error_chunk,
+ parse_init_chunk, parse_opaque_chunk, parse_sack_chunk,
+ parse_shutdone_chunk, parse_shutdown_chunk, parse_asconf_chunk,
+ parse_ftsn_chunk;
+
+
+/*
+ * Chunk parser dispatch table. There are few enough chunks defined
+ * in the core protocol, and they are sequential, so the chunk code
+ * can be used as the index into this array for the common case.
+ * It is still necessary to check that the code and index match,
+ * since optional extensions will not follow sequentially the
+ * core chunks.
+ */
+static const dispatch_t chunk_dispatch_table[] = {
+/* code F_SUM desc F_DTAIL desc parser function */
+ { CHUNK_DATA, "Data", "Data Chunk",
+ parse_data_chunk },
+ { CHUNK_INIT, "Init", "Init Chunk",
+ parse_init_chunk },
+ { CHUNK_INIT_ACK, "Init ACK", "Init ACK Chunk",
+ parse_init_chunk },
+ { CHUNK_SACK, "SACK", "SACK Chunk",
+ parse_sack_chunk },
+ { CHUNK_HEARTBEAT, "Heartbeat", "Heartbeat Chunk",
+ parse_opaque_chunk },
+ { CHUNK_HEARTBEAT_ACK, "Heartbeat ACK", "Heartbeat ACK Chunk",
+ parse_opaque_chunk },
+ { CHUNK_ABORT, "Abort", "Abort Chunk",
+ parse_abort_chunk },
+ { CHUNK_SHUTDOWN, "Shutdown", "Shutdown Chunk",
+ parse_shutdown_chunk },
+ { CHUNK_SHUTDOWN_ACK, "Shutdown ACK", "Shutdown ACK Chunk",
+ NULL },
+ { CHUNK_ERROR, "Err", "Error Chunk",
+ parse_error_chunk },
+ { CHUNK_COOKIE, "Cookie", "Cookie Chunk",
+ parse_opaque_chunk },
+ { CHUNK_COOKIE_ACK, "Cookie ACK", "Cookie ACK Chunk",
+ parse_opaque_chunk },
+ { CHUNK_ECNE, "ECN Echo", "ECN Echo Chunk",
+ parse_opaque_chunk },
+ { CHUNK_CWR, "CWR", "CWR Chunk",
+ parse_opaque_chunk },
+ { CHUNK_SHUTDOWN_COMPLETE, "Shutdown Done", "Shutdown Done",
+ parse_shutdone_chunk },
+ { CHUNK_FORWARD_TSN, "FORWARD TSN", "Forward TSN Chunk",
+ parse_ftsn_chunk },
+ { CHUNK_ASCONF_ACK, "ASCONF ACK", "ASCONF ACK Chunk",
+ parse_asconf_chunk },
+ { CHUNK_ASCONF, "ASCONF", "ASCONF Chunk",
+ parse_asconf_chunk }
+};
+
+/*
+ * Parameter Parsers
+ */
+static parse_func_t parse_encap_param, parse_int32_param, parse_ip4_param,
+ parse_ip6_param, parse_opaque_param, parse_suppaddr_param,
+ parse_unrec_chunk, parse_addip_param, parse_asconferr_param,
+ parse_asconfok_param, parse_addiperr_param;
+
+/*
+ * Parameter parser dispatch table. The summary description is not
+ * used here. Strictly speaking, parameter types are defined within
+ * the context of a chunk type. However, thus far the IETF WG has
+ * agreed to follow the convention that parameter types are globally
+ * unique (and why not, with a 16-bit namespace). However, if this
+ * ever changes, there will need to be different parameter dispatch
+ * tables for each chunk type.
+ */
+static const dispatch_t parm_dispatch_table[] = {
+/* code F_SUM desc F_DTAIL desc parser function */
+ { PARM_UNKNOWN, "", "Unknown Parameter",
+ parse_opaque_param },
+ { PARM_HBINFO, "", "Heartbeat Info",
+ parse_opaque_param },
+ { PARM_ADDR4, "", "IPv4 Address",
+ parse_ip4_param },
+ { PARM_ADDR6, "", "IPv6 Address",
+ parse_ip6_param },
+ { PARM_COOKIE, "", "Cookie",
+ parse_opaque_param },
+ { PARM_UNRECOGNIZED, "", "Unrecognized Param",
+ parse_encap_param },
+ { PARM_COOKIE_PRESERVE, "", "Cookie Preservative",
+ parse_opaque_param },
+ { 10, "", "Reserved for ECN",
+ parse_opaque_param },
+ { PARM_ADDR_HOST_NAME, "", "Host Name Parameter",
+ parse_opaque_param },
+ { PARM_SUPP_ADDRS, "", "Supported Addresses",
+ parse_suppaddr_param },
+ { PARM_ECN_CAPABLE, "", "ECN Capable",
+ parse_opaque_param },
+ { PARM_ADD_IP, "", "Add IP",
+ parse_addip_param },
+ { PARM_DEL_IP, "", "Del IP",
+ parse_addip_param },
+ { PARM_ASCONF_ERROR, "", "ASCONF Error Ind",
+ parse_asconferr_param },
+ { PARM_PRIMARY_ADDR, "", "Set Primary Address",
+ parse_addip_param },
+ { PARM_FORWARD_TSN, "", "Forward TSN",
+ NULL },
+ { PARM_ASCONF_SUCCESS, "", "ASCONF Success Ind",
+ parse_asconfok_param }
+};
+
+/*
+ * Errors have the same wire format at parameters.
+ */
+static const dispatch_t err_dispatch_table[] = {
+/* code F_SUM desc F_DTAIL desc parser function */
+ { SCTP_ERR_UNKNOWN, "", "Unknown Error",
+ parse_opaque_param },
+ { SCTP_ERR_BAD_SID, "", "Invalid Stream ID",
+ parse_opaque_param },
+ { SCTP_ERR_MISSING_PARM, "", "Missing Parameter",
+ parse_opaque_param },
+ { SCTP_ERR_STALE_COOKIE, "", "Stale Cookie",
+ parse_int32_param },
+ { SCTP_ERR_NO_RESOURCES, "", "Out Of Resources",
+ parse_opaque_param },
+ { SCTP_ERR_BAD_ADDR, "", "Unresolvable Address",
+ parse_opaque_param },
+ { SCTP_ERR_UNREC_CHUNK, "", "Unrecognized Chunk",
+ parse_unrec_chunk },
+ { SCTP_ERR_BAD_MANDPARM, "", "Bad Mandatory Parameter",
+ parse_opaque_param },
+ { SCTP_ERR_UNREC_PARM, "", "Unrecognized Parameter",
+ parse_opaque_param },
+ { SCTP_ERR_NO_USR_DATA, "", "No User Data",
+ parse_int32_param },
+ { SCTP_ERR_COOKIE_SHUT, "", "Cookie During Shutdown",
+ parse_opaque_param },
+ { SCTP_ERR_DELETE_LASTADDR, "", "Delete Last Remaining Address",
+ parse_addiperr_param },
+ { SCTP_ERR_RESOURCE_SHORTAGE, "", "Resource Shortage",
+ parse_addiperr_param },
+ { SCTP_ERR_DELETE_SRCADDR, "", "Delete Source IP Address",
+ parse_addiperr_param },
+ { SCTP_ERR_AUTH_ERR, "", "Not authorized",
+ parse_addiperr_param }
+};
+
+/*
+ * These are global because the data chunk parser needs them to dispatch
+ * to ULPs. The alternative is to add source and dest port arguments
+ * to every parser, which seems even messier (since *only* the data
+ * chunk parser needs it)...
+ */
+static in_port_t sport, dport;
+
+/* Summary line miscellany */
+static int sumlen;
+static char scratch[MAXLINE];
+static char *sumline;
+
+#define SUMAPPEND(fmt) \
+ sumlen -= snprintf fmt; \
+ (void) strlcat(sumline, scratch, sumlen)
+
+#define DUMPHEX_MAX 16
+
+static const dispatch_t *
+lookup_dispatch(int id, const dispatch_t *tbl, int tblsz)
+{
+ int i;
+
+ /*
+ * Try fast lookup first. The common chunks defined in RFC2960
+ * will have indices aligned with their IDs, so this works for
+ * the common case.
+ */
+ if (id < (tblsz - 1)) {
+ if (id == tbl[id].id) {
+ return (tbl + id);
+ }
+ }
+
+ /*
+ * Nope - probably an extension. Search the whole table,
+ * starting from the end, since extensions are at the end.
+ */
+ for (i = tblsz - 1; i >= 0; i--) {
+ if (id == tbl[i].id) {
+ return (tbl + i);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * Dumps no more than the first DUMPHEX_MAX bytes in hex. If
+ * the user wants more, they can use the -x option to snoop.
+ */
+static void
+dumphex(const uchar_t *payload, int payload_len, char *msg)
+{
+ int index;
+ int end;
+ char buf[BUFLEN];
+
+ if (payload_len == 0) {
+ return;
+ }
+
+ end = payload_len > DUMPHEX_MAX ? DUMPHEX_MAX : payload_len;
+
+ for (index = 0; index < end; index++) {
+ (void) snprintf(&buf[index * 3], 4, " %.2x", payload[index]);
+ }
+
+ if (payload_len > DUMPHEX_MAX) {
+ (void) strlcat(buf, " ...", BUFLEN);
+ }
+
+ (void) snprintf(get_line(0, 0), BUFLEN, msg, buf);
+}
+
+/*
+ * Present perscribed action for unknowns according to rfc2960. Works
+ * for chunks and parameters as well if the parameter type is
+ * shifted 8 bits right.
+ */
+static const char *
+get_action_desc(uint8_t id)
+{
+ if ((id & 0xc0) == 0xc0) {
+ return (": skip on unknown, return error");
+ } else if ((id & 0x80) == 0x80) {
+ return (": skip on unknown, no error");
+ } else if ((id & 0x40) == 0x40) {
+ return (": stop on unknown, return error");
+ }
+
+ /* Top two bits are clear */
+ return (": stop on unknown, no error");
+}
+
+/* ARGSUSED */
+static void
+parse_asconfok_param(int flags, uint8_t notused, const void *data, int dlen)
+{
+ uint32_t *cid;
+
+ if (dlen < sizeof (*cid)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Incomplete ASCONF Success Ind parameter");
+ return;
+ }
+ cid = (uint32_t *)data;
+ (void) snprintf(get_line(0, 0), get_line_remain(), " ASCONF CID = %u",
+ ntohl(*cid));
+}
+
+/* ARGSUSED */
+static void
+parse_asconferr_param(int flags, uint8_t notused, const void *data, int dlen)
+{
+ uint32_t *cid;
+
+ if (dlen < sizeof (*cid)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Incomplete ASCONF Error Ind parameter");
+ return;
+ }
+ cid = (uint32_t *)data;
+ (void) snprintf(get_line(0, 0), get_line_remain(), " ASCONF CID = %u",
+ ntohl(*cid));
+
+ interpret_params(cid + 1, dlen - sizeof (*cid), "Error",
+ err_dispatch_table, A_CNT(err_dispatch_table), flags);
+}
+
+/* ARGSUSED */
+static void
+parse_addiperr_param(int flags, uint8_t notused, const void *data, int dlen)
+{
+
+ interpret_params(data, dlen, "Parameter",
+ parm_dispatch_table, A_CNT(parm_dispatch_table), flags);
+}
+
+/* ARGSUSED */
+static void
+parse_addip_param(int flags, uint8_t notused, const void *data, int dlen)
+{
+
+ uint32_t *cid;
+
+ if (dlen < sizeof (*cid)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Incomplete ASCONF Error Ind parameter");
+ return;
+ }
+ cid = (uint32_t *)data;
+ (void) snprintf(get_line(0, 0), get_line_remain(), " ASCONF CID = %u",
+ ntohl(*cid));
+
+ interpret_params(cid + 1, dlen - sizeof (*cid), "Parameter",
+ parm_dispatch_table, A_CNT(parm_dispatch_table), flags);
+}
+
+/* ARGSUSED */
+static void
+parse_ip4_param(int flags, uint8_t notused, const void *data, int datalen)
+{
+ char abuf[INET_ADDRSTRLEN];
+ char *ap;
+
+ if (datalen < sizeof (in_addr_t)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Incomplete IPv4 Addr parameter");
+ return;
+ }
+
+ ap = (char *)inet_ntop(AF_INET, data, abuf, INET_ADDRSTRLEN);
+ if (ap == NULL) {
+ ap = "<Bad Address>";
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(), " Addr = %s", ap);
+}
+
+/* ARGSUSED */
+static void
+parse_ip6_param(int flags, uint8_t notused, const void *data, int datalen)
+{
+ char abuf[INET6_ADDRSTRLEN];
+ char *ap;
+
+ if (datalen < sizeof (in6_addr_t)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Incomplete IPv6 Addr parameter");
+ return;
+ }
+
+ ap = (char *)inet_ntop(AF_INET6, data, abuf, INET6_ADDRSTRLEN);
+ if (ap == NULL) {
+ ap = "<Bad Address>";
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(), " Addr = %s", ap);
+}
+
+/* ARGSUSED */
+static void
+parse_int32_param(int flags, uint8_t notused, const void *data, int datalen)
+{
+ if (datalen < 4) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Incomplete INT32 parameter");
+ return;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(), " INT32 = %u",
+ ntohl(*(uint32_t *)data));
+}
+
+/* ARGSUSED */
+static void
+parse_suppaddr_param(int flags, uint8_t notused, const void *data, int dlen)
+{
+ const uint16_t *type;
+
+ if (dlen < 2) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete Supported Addr parameter");
+ return;
+ }
+
+ type = data;
+ while (dlen > 0) {
+ switch (ntohs(*type)) {
+ case PARM_ADDR4:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " IPv4");
+ break;
+ case PARM_ADDR6:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " IPv6");
+ break;
+ case PARM_ADDR_HOST_NAME:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Host Name");
+ break;
+ default:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Unknown Type (%hu)", ntohs(*type));
+ break;
+ }
+ dlen -= sizeof (*type);
+ type++;
+ }
+}
+
+/*ARGSUSED*/
+static void
+parse_encap_param(int flags, uint8_t notused, const void *data, int dlen)
+{
+ if (dlen < sizeof (sctp_parm_hdr_t)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete Parameter");
+ return;
+ }
+
+ interpret_params(data, dlen, "Parameter",
+ parm_dispatch_table, A_CNT(parm_dispatch_table), flags);
+}
+
+/* ARGSUSED */
+static void
+parse_unrec_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ const sctp_chunk_hdr_t *cp = data;
+ const dispatch_t *dp;
+ const char *actstr;
+
+ if (datalen < sizeof (*cp)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete Unrecognized Chunk Error");
+ return;
+ }
+
+ /* Maybe snoop knows about this chunk? */
+ dp = lookup_dispatch(cp->sch_id, chunk_dispatch_table,
+ A_CNT(chunk_dispatch_table));
+ if (dp != NULL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Chunk Type = %u (%s)", cp->sch_id, dp->vdesc);
+ } else {
+ actstr = get_action_desc(cp->sch_id);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Chunk Type = %u%s", cp->sch_id, actstr);
+ }
+}
+
+/*
+ * Same as parse_opaque_chunk except for the indentation.
+ */
+/* ARGSUSED */
+static void
+parse_opaque_param(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ dumphex(data, datalen, " Data = %s");
+}
+
+/*
+ * Loops through all parameters (or errors) until it has read
+ * datalen bytes of information, finding a parser for each.
+ * The tbl argument allows the caller to specify which dispatch
+ * table to use, making this function useful for both parameters
+ * and errors. The type argument is used to denote whether this
+ * is an error or parameter in detailed mode.
+ */
+static void
+interpret_params(const void *data, int datalen, char *type,
+ const dispatch_t *tbl, int tbl_size, int flags)
+{
+ const sctp_parm_hdr_t *hdr = data;
+ uint16_t plen;
+ uint16_t ptype;
+ const char *desc;
+ parse_func_t *parse;
+ int pad;
+ const dispatch_t *dp;
+ const char *actstr;
+
+ for (;;) {
+ /*
+ * Adjust for padding: if the address isn't aligned, there
+ * should be some padding. So skip over the padding and
+ * adjust hdr accordingly. RFC2960 mandates that all
+ * parameters must be 32-bit aligned WRT the enclosing chunk,
+ * which ensures that this parameter header will
+ * be 32-bit aligned in memory. We must, of course, bounds
+ * check fraglen before actually trying to use hdr, in
+ * case the packet has been mangled or is the product
+ * of a buggy implementation.
+ */
+ if ((pad = (uintptr_t)hdr % SCTP_ALIGN) != 0) {
+ pad = SCTP_ALIGN - pad;
+ datalen -= pad;
+ /* LINTED pointer cast may result in improper alignment */
+ hdr = (sctp_parm_hdr_t *)((char *)hdr + pad);
+ }
+
+ /* Need to compare against 0 1st, since sizeof is unsigned */
+ if (datalen < 0 || datalen < sizeof (*hdr)) {
+ if (datalen > 0) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "==> Extra data after last parameter");
+ }
+ return;
+ }
+ plen = ntohs(hdr->sph_len);
+ if (datalen < plen || plen < sizeof (*hdr)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Incomplete %s", type);
+ return;
+ }
+
+ /* Get description and parser */
+ ptype = ntohs(hdr->sph_type);
+ desc = "Unknown Parameter Type";
+ parse = parse_opaque_param;
+ dp = lookup_dispatch(ptype, tbl, tbl_size);
+ if (dp != NULL) {
+ desc = dp->vdesc;
+ parse = dp->parse;
+ }
+
+ show_space();
+ if (dp != NULL) {
+ actstr = "";
+ } else {
+ actstr = get_action_desc((uint8_t)(ptype >> 8));
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ------- SCTP %s Type = %s (%u%s)", type, desc, ptype,
+ actstr);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Data length = %hu", plen - sizeof (*hdr));
+
+ if (parse != NULL) {
+ parse(flags, 0, (char *)(hdr + 1),
+ plen - sizeof (*hdr));
+ }
+ datalen -= plen;
+ /* LINTED pointer cast may result in improper alignment */
+ hdr = (sctp_parm_hdr_t *)((char *)hdr + plen);
+ }
+}
+
+/* ARGSUSED */
+static void
+parse_ftsn_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ uint32_t *ftsn;
+ ftsn_entry_t *ftsn_entry;
+
+ if (datalen < (sizeof (*ftsn) + sizeof (*ftsn_entry))) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete FORWARD-TSN chunk");
+ }
+ return;
+ }
+
+ ftsn = (uint32_t *)data;
+ if (flags & F_SUM) {
+ SUMAPPEND((scratch, MAXLINE, "CTSN %x ", ntohl(*ftsn)));
+ return;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(), "Cum TSN= %x",
+ ntohl(*ftsn));
+
+ datalen -= sizeof (*ftsn);
+ ftsn_entry = (ftsn_entry_t *)(ftsn + 1);
+ while (datalen >= sizeof (*ftsn_entry)) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "SID = %u : SSN = %u", ntohs(ftsn_entry->ftsn_sid),
+ ntohs(ftsn_entry->ftsn_ssn));
+ datalen -= sizeof (*ftsn_entry);
+ ftsn_entry++;
+ }
+}
+
+/* ARGSUSED */
+static void
+parse_asconf_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ uint32_t *sn;
+
+ if (datalen < sizeof (*sn)) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete ASCONF chunk");
+ }
+ return;
+ }
+
+ sn = (uint32_t *)data;
+ if (flags & F_SUM) {
+ SUMAPPEND((scratch, MAXLINE, "sn %x ", ntohl(*sn)));
+ return;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(), "Serial Number= %x",
+ ntohl(*sn));
+ interpret_params(sn + 1, datalen - sizeof (*sn), "Parameter",
+ parm_dispatch_table, A_CNT(parm_dispatch_table), flags);
+}
+
+static void
+parse_init_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ const sctp_init_chunk_t *icp = data;
+
+ if (datalen < sizeof (*icp)) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete INIT chunk");
+ }
+ return;
+ }
+
+ if (flags & F_SUM) {
+ SUMAPPEND((scratch, MAXLINE, "tsn %x str %hu/%hu win %u ",
+ ntohl(icp->sic_inittsn), ntohs(icp->sic_outstr),
+ ntohs(icp->sic_instr), ntohl(icp->sic_a_rwnd)));
+ return;
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(), "Flags = 0x%.2x",
+ cflags);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Initiate tag = 0x%.8x", ntohl(icp->sic_inittag));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Advertised receiver window credit = %u", ntohl(icp->sic_a_rwnd));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Outbound streams = %hu", ntohs(icp->sic_outstr));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Inbound streams = %hu", ntohs(icp->sic_instr));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Initial TSN = 0x%.8x", ntohl(icp->sic_inittsn));
+
+ if (datalen > sizeof (*icp)) {
+ interpret_params(icp + 1, datalen - sizeof (*icp),
+ "Parameter", parm_dispatch_table,
+ A_CNT(parm_dispatch_table), flags);
+ }
+}
+
+static void
+parse_data_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ const sctp_data_chunk_t *dcp = data;
+ char *payload;
+ uint32_t ppid;
+
+ if (datalen < sizeof (*dcp)) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete DATA chunk %d (%d)", datalen,
+ sizeof (*dcp));
+ }
+ return;
+ }
+
+ ppid = ntohl(dcp->sdc_payload_id);
+ /* This is the actual data len, excluding the data chunk header. */
+ datalen -= sizeof (*dcp);
+
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "flags = 0x%.2x", cflags);
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(cflags, SCTP_DATA_UBIT, "unordered", "ordered"));
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(cflags, SCTP_DATA_BBIT,
+ "beginning", "(beginning unset)"));
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(cflags, SCTP_DATA_EBIT, "end", "(end unset)"));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "TSN = 0x%.8x", ntohl(dcp->sdc_tsn));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Stream ID = %hu", ntohs(dcp->sdc_sid));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Stream Sequence Number = %hu", ntohs(dcp->sdc_ssn));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Payload Protocol ID = 0x%.8x", ppid);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Data Length = %d", datalen);
+ show_space();
+ }
+ if (flags & F_SUM) {
+ SUMAPPEND((scratch, MAXLINE, "len %d tsn %x str %hu/%hu "
+ "ppid %x ", datalen, ntohl(dcp->sdc_tsn),
+ ntohs(dcp->sdc_sid), ntohs(dcp->sdc_ssn), ppid));
+ }
+
+ /*
+ * Go to the next protocol layer, but not if we are in
+ * summary mode only. In summary mode, each ULP parse would
+ * create a new line, and if there were several data chunks
+ * bundled together in the packet, this would confuse snoop's
+ * packet numbering and timestamping.
+ *
+ * SCTP carries two ways to determine an ULP: ports and the
+ * payload protocol identifier (ppid). Since ports are the
+ * better entrenched convention, we first try interpret_reserved().
+ * If that fails to find a parser, we try by the PPID.
+ */
+ if (!(flags & F_ALLSUM) && !(flags & F_DTAIL)) {
+ return;
+ }
+
+ payload = (char *)(dcp + 1);
+ if (!interpret_reserved(flags, IPPROTO_SCTP, sport, dport, payload,
+ datalen) && ppid != 0) {
+
+ interpret_protoid(flags, ppid, payload, datalen);
+ }
+
+ /*
+ * Reset the protocol prefix, since it may have been changed
+ * by a ULP interpreter.
+ */
+ prot_prefix = "SCTP: ";
+}
+
+/* ARGSUSED */
+static void
+parse_sack_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ const sctp_sack_chunk_t *scp = data;
+ uint16_t numfrags, numdups;
+ sctp_sack_frag_t *frag;
+ int i;
+ uint32_t *tsn;
+
+ if (datalen < sizeof (*scp)) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete SACK chunk");
+ }
+ return;
+ }
+
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Cumulative TSN ACK = 0x%.8x", ntohl(scp->ssc_cumtsn));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Advertised Receiver Window Credit = %u",
+ ntohl(scp->ssc_a_rwnd));
+ numfrags = ntohs(scp->ssc_numfrags);
+ numdups = ntohs(scp->ssc_numdups);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Number of Fragments = %hu", numfrags);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Number of Duplicates = %hu", numdups);
+
+ /* Display any gap reports */
+ datalen -= sizeof (*scp);
+ if (datalen < (numfrags * sizeof (*frag))) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Malformed gap report listing");
+ return;
+ }
+ frag = (sctp_sack_frag_t *)(scp + 1);
+ for (i = 0; i < numfrags; i++) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Fragment #%d: Start = %hu, end = %hu", i,
+ ntohs(frag->ssf_start), ntohs(frag->ssf_end));
+ frag += 1;
+ }
+
+ /* Display any duplicate reports */
+ datalen -= numfrags * sizeof (*frag);
+ if (datalen < (numdups * sizeof (*tsn))) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " ==> Malformed duplicate report listing");
+ return;
+ }
+ /* LINTED pointer cast may result in improper alignment */
+ tsn = (uint32_t *)frag;
+ for (i = 0; i < numdups; i++) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ " Duplicate #%d: TSN = %x", i, *tsn);
+ tsn++;
+ }
+ }
+ if (flags & F_SUM) {
+ SUMAPPEND((scratch, MAXLINE,
+ "tsn %x win %u gaps/dups %hu/%hu ", ntohl(scp->ssc_cumtsn),
+ ntohl(scp->ssc_a_rwnd), ntohs(scp->ssc_numfrags),
+ ntohs(scp->ssc_numdups)));
+ }
+}
+
+/* ARGSUSED */
+static void
+parse_shutdown_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ const uint32_t *cumtsn = data;
+
+ if (datalen < sizeof (*cumtsn)) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete Shutdown chunk");
+ }
+ return;
+ }
+
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Cumulative TSN = 0x%.8x", ntohl(*cumtsn));
+ }
+ if (flags & F_SUM) {
+ SUMAPPEND((scratch, MAXLINE, "tsn %x", ntohl(*cumtsn)));
+ }
+}
+
+/* ARGSUSED */
+static void
+parse_error_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ if (!(flags & F_DTAIL)) {
+ return;
+ }
+
+ interpret_params(data, datalen, "Error", err_dispatch_table,
+ A_CNT(err_dispatch_table), flags);
+}
+
+static void
+parse_abort_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ if (!(flags & F_DTAIL)) {
+ return;
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(), "flags = 0x%.2x",
+ cflags);
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(cflags, SCTP_TBIT, "TCB not destroyed", "TCB destroyed"));
+
+ interpret_params(data, datalen, "Error", err_dispatch_table,
+ A_CNT(err_dispatch_table), flags);
+}
+
+/* ARGSUSED2 */
+static void
+parse_shutdone_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ if (!(flags & F_DTAIL)) {
+ return;
+ }
+
+ (void) snprintf(get_line(0, 0), get_line_remain(), "flags = 0x%.2x",
+ cflags);
+ (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
+ getflag(cflags, SCTP_TBIT, "TCB not destroyed", "TCB destroyed"));
+}
+
+/* ARGSUSED */
+static void
+parse_opaque_chunk(int flags, uint8_t cflags, const void *data, int datalen)
+{
+ if (!(flags & F_DTAIL)) {
+ return;
+ }
+ if (datalen == 0) {
+ return;
+ }
+
+ dumphex(data, datalen, "Data = %s");
+}
+
+/*
+ * Loops through all chunks until it has read fraglen bytes of
+ * information, finding a parser for each. If any parameters are
+ * present, interpret_params() is then called. Returns the remaining
+ * fraglen.
+ */
+static int
+interpret_chunks(int flags, sctp_chunk_hdr_t *cp, int fraglen)
+{
+ uint16_t clen;
+ int signed_len;
+ int pad;
+ const char *desc;
+ parse_func_t *parse;
+ const dispatch_t *dp;
+ const char *actstr;
+
+ for (;;) {
+ /*
+ * Adjust for padding: if the address isn't aligned, there
+ * should be some padding. So skip over the padding and
+ * adjust hdr accordingly. RFC2960 mandates that all
+ * chunks must be 32-bit aligned WRT the SCTP common hdr,
+ * which ensures that this chunk header will
+ * be 32-bit aligned in memory. We must, of course, bounds
+ * check fraglen before actually trying to use hdr, in
+ * case the packet has been mangled or is the product
+ * of a buggy implementation.
+ */
+ if ((pad = (uintptr_t)cp % SCTP_ALIGN) != 0) {
+ pad = SCTP_ALIGN - pad;
+ fraglen -= pad;
+ /* LINTED pointer cast may result in improper alignment */
+ cp = (sctp_chunk_hdr_t *)((char *)cp + pad);
+ }
+
+ /* Need to compare against 0 1st, since sizeof is unsigned */
+ if (fraglen < 0 || fraglen < sizeof (*cp)) {
+ if (fraglen > 0 && flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "==> Extra data after last chunk");
+ }
+ return (fraglen);
+ }
+
+ clen = ntohs(cp->sch_len);
+ if (fraglen < clen) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "==> Corrupted chunk");
+ }
+ return (fraglen);
+ }
+
+ signed_len = clen - sizeof (*cp);
+ if (signed_len < 0) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "==> Incomplete or corrupted chunk");
+ }
+ return (0);
+ }
+
+ /* Get description and parser */
+ dp = lookup_dispatch(cp->sch_id, chunk_dispatch_table,
+ A_CNT(chunk_dispatch_table));
+ if (dp != NULL) {
+ if (flags & F_SUM) {
+ desc = dp->sdesc;
+ } else if (flags & F_DTAIL) {
+ desc = dp->vdesc;
+ }
+ parse = dp->parse;
+ } else {
+ if (flags & F_SUM) {
+ desc = "UNK";
+ } else if (flags & F_DTAIL) {
+ desc = "Unknown Chunk Type";
+ }
+ parse = parse_opaque_chunk;
+ }
+
+ if (flags & F_SUM) {
+ SUMAPPEND((scratch, MAXLINE, "%s ", desc));
+ }
+ if (flags & F_DTAIL) {
+ show_space();
+
+ if (dp != NULL) {
+ actstr = "";
+ } else {
+ actstr = get_action_desc(cp->sch_id);
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "------- SCTP Chunk Type = %s (%u%s)", desc,
+ cp->sch_id, actstr);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Chunk length = %hu", clen);
+ }
+
+ if (parse != NULL) {
+ parse(flags, cp->sch_flags, (char *)(cp + 1),
+ signed_len);
+ }
+
+ fraglen -= clen;
+
+ /* LINTED pointer cast may result in improper alignment */
+ cp = (sctp_chunk_hdr_t *)((char *)cp + clen);
+ }
+}
+
+void
+interpret_sctp(int flags, sctp_hdr_t *sctp, int iplen, int fraglen)
+{
+ int len_from_iphdr;
+ sctp_chunk_hdr_t *cp;
+ char *pn;
+ char buff[32];
+
+ /*
+ * Alignment check. If the header is 32-bit aligned, all other
+ * protocol units will also be aligned, as mandated by rfc2960.
+ * Buggy packets will be caught and flagged by chunk and
+ * parameter bounds checking.
+ * If the header is not aligned, however, we drop the packet.
+ */
+ if (!IS_P2ALIGNED(sctp, SCTP_ALIGN)) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> SCTP header not aligned, dropping");
+ }
+ return;
+ }
+
+ fraglen -= sizeof (*sctp);
+ if (fraglen < 0) {
+ if (flags & F_DTAIL) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "==> Incomplete sctp header");
+ }
+ return;
+ }
+ /* If fraglen is somehow longer than the IP payload, adjust it */
+ len_from_iphdr = iplen - sizeof (*sctp);
+ if (fraglen > len_from_iphdr) {
+ fraglen = len_from_iphdr;
+ }
+
+ /* Keep track of the ports */
+ sport = ntohs(sctp->sh_sport);
+ dport = ntohs(sctp->sh_dport);
+
+ /* Set pointer to first chunk */
+ cp = (sctp_chunk_hdr_t *)(sctp + 1);
+
+ if (flags & F_SUM) {
+ sumline = get_sum_line();
+ *sumline = '\0';
+ sumlen = MAXLINE;
+
+ SUMAPPEND((scratch, MAXLINE, "SCTP D=%d S=%d ", dport, sport));
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("SCTP: ", "SCTP Header", fraglen);
+ show_space();
+
+ pn = getportname(IPPROTO_SCTP, (ushort_t)sport);
+ if (pn == NULL) {
+ pn = "";
+ } else {
+ (void) snprintf(buff, sizeof (buff), "(%s)", pn);
+ pn = buff;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Source port = %hu %s", sport, pn);
+
+ pn = getportname(IPPROTO_SCTP, (ushort_t)dport);
+ if (pn == NULL) {
+ pn = "";
+ } else {
+ (void) snprintf(buff, sizeof (buff), "(%s)", pn);
+ pn = buff;
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Destination port = %hu %s", dport, pn);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Verification tag = 0x%.8x", ntohl(sctp->sh_verf));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "CRC-32c = 0x%.8x", ntohl(sctp->sh_chksum));
+ }
+
+ (void) interpret_chunks(flags, cp, fraglen);
+
+ if (flags & F_DTAIL) {
+ show_space();
+ }
+}
+
+/*
+ * Payload protocol ID table. Add new ULP information and parsers
+ * here.
+ */
+
+struct protoid_table {
+ int pid_num;
+ char *pid_short;
+ char *pid_long;
+};
+
+static struct protoid_table pid_sctp[] = {
+ 1, "IUA", "ISDN Q.921 User Adaption Layer",
+ 2, "M2UA", "SS7 MTP2 User Adaption Layer",
+ 3, "M3UA", "SS7 MTP3 User Adaption Layer",
+ 4, "SUA", "SS7 SCCP User Adaption Layer",
+ 5, "M2PA", "SS7 MTP2-User Peer-to-Peer Adaption Layer",
+ 6, "V5UA", "V5UA",
+ 0, NULL, "",
+};
+
+static void
+interpret_protoid(int flags, uint32_t ppid, char *data, int dlen)
+{
+ struct protoid_table *p;
+ char pbuf[16];
+
+ /*
+ * Branch to a ULP interpreter here, or continue on to
+ * the default parser, which just tries to display
+ * printable characters from the payload.
+ */
+
+ for (p = pid_sctp; p->pid_num; p++) {
+ if (ppid == p->pid_num) {
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "D=%d S=%d %s %s", dport, sport,
+ p->pid_short, show_string(data, dlen, 20));
+ }
+
+ if (flags & F_DTAIL) {
+ (void) snprintf(pbuf, MAXLINE, "%s: ",
+ p->pid_short);
+ show_header(pbuf, p->pid_long, dlen);
+ show_space();
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "\"%s\"",
+ show_string(data, dlen, 60));
+ show_trailer();
+ }
+
+ return;
+ }
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_slp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_slp.c
new file mode 100644
index 0000000..9feb3e9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_slp.c
@@ -0,0 +1,1963 @@
+/*
+ * 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 (c) 1998,2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <iconv.h>
+#include "snoop.h"
+#include "slp.h"
+
+#define MAXSUMLEN 30
+
+/* define VERIFYSLP to enable full message checking in summary mode */
+#define VERIFYSLP
+
+/* Globals -- ugly, yes, but fast and easy in macros */
+static int msglength;
+static int retlength;
+static char *msgend; /* the end of the summary message buffer */
+static char *p; /* current position in the packet */
+static char *msgbuf; /* message buffer for summary mode */
+static boolean_t url_auth = B_FALSE;
+static boolean_t attr_auth = B_FALSE;
+static boolean_t fresh = B_FALSE;
+static boolean_t overflow = B_FALSE;
+static int v1_charset = 0; /* character set; only in V1 */
+
+/* Entry points for parsing the protocol */
+static int interpret_slp_v1(int, struct slpv1_hdr *, int);
+static int interpret_slp_v2(int, struct slpv2_hdr *, int);
+
+/* header parsing */
+static int v1_header(int, struct slpv1_hdr *, int);
+static int v2_header(int, struct slpv2_hdr *, int *, int);
+static int v2_finish(struct slpv2_hdr *, int);
+
+/* V2 auth blocks */
+static int slpv2_authblock(int);
+
+/* From snoop_rport: */
+extern int add_transient(int, int (*)());
+
+/*
+ * Functions for parsing each protocol message
+ * Each function takes the interpreter's flags argument as its input
+ * parameter, and returns 1 on success, or 0 on message corruption.
+ * retlength is set as a side-effect in summary mode.
+ */
+static int v2_srv_rqst(int);
+static int v2_srv_rply(int);
+static int v2_srv_reg(int);
+static int v2_srv_dereg(int);
+static int v2_srv_ack(int);
+static int v2_attr_rqst(int);
+static int v2_attr_rply(int);
+static int v2_daadvert(int);
+static int v2_srv_type_rqst(int);
+static int v2_srv_type_rply(int);
+static int v2_saadvert(int);
+
+static int v1_srv_rqst(int);
+static int v1_srv_rply(int);
+static int v1_srv_reg(int);
+static int v1_srv_dereg(int);
+static int v1_srv_ack(int);
+static int v1_attr_rqst(int);
+static int v1_attr_rply(int);
+static int v1_daadvert(int);
+static int v1_srv_type_rqst(int);
+static int v1_srv_type_rply(int);
+
+/*
+ * The dispatch tables for handling individual messages, keyed by
+ * function number.
+ */
+typedef int function_handler();
+
+#define V2_MAX_FUNCTION 11
+
+static function_handler *v2_functions[V2_MAX_FUNCTION + 1] = {
+ (function_handler *) NULL,
+ (function_handler *) v2_srv_rqst,
+ (function_handler *) v2_srv_rply,
+ (function_handler *) v2_srv_reg,
+ (function_handler *) v2_srv_dereg,
+ (function_handler *) v2_srv_ack,
+ (function_handler *) v2_attr_rqst,
+ (function_handler *) v2_attr_rply,
+ (function_handler *) v2_daadvert,
+ (function_handler *) v2_srv_type_rqst,
+ (function_handler *) v2_srv_type_rply,
+ (function_handler *) v2_saadvert };
+
+#define V1_MAX_FUNCTION 10
+
+static function_handler *v1_functions[V1_MAX_FUNCTION + 1] = {
+ (function_handler *) NULL,
+ (function_handler *) v1_srv_rqst,
+ (function_handler *) v1_srv_rply,
+ (function_handler *) v1_srv_reg,
+ (function_handler *) v1_srv_dereg,
+ (function_handler *) v1_srv_ack,
+ (function_handler *) v1_attr_rqst,
+ (function_handler *) v1_attr_rply,
+ (function_handler *) v1_daadvert,
+ (function_handler *) v1_srv_type_rqst,
+ (function_handler *) v1_srv_type_rply };
+
+/* TCP continuation handling */
+static boolean_t tcp_continuation = B_FALSE;
+
+#define MAX_TCPCONT 16
+
+static struct tcp_cont {
+ int dst_port;
+ char *msg;
+ int totallen;
+ int curr_offset;
+} *tcp_cont[MAX_TCPCONT];
+
+static int current_tcp_cont;
+
+static void reg_tcp_cont(char *, int, int, int);
+static int add_tcp_cont(struct tcp_cont *, char *, int);
+static struct tcp_cont *find_tcp_cont(int);
+static void remove_tcp_cont(int);
+
+/* Conversions from numbers to strings */
+static char *slpv2_func(int, boolean_t);
+static char *slpv2_error(unsigned short);
+static char *slpv1_func(int, boolean_t);
+static char *slpv1_error(unsigned short);
+static char *slpv1_charset(unsigned short);
+
+/*
+ * The only external entry point to the SLP interpreter. This function
+ * simply dispatches the packet based on the version.
+ */
+void interpret_slp(int flags, char *slp, int fraglen) {
+ extern int dst_port, curr_proto;
+ struct tcp_cont *tce = NULL;
+
+ msglength = fraglen;
+ retlength = 0;
+ p = slp;
+
+ /* check if this is a TCP continuation */
+ if (flags & F_DTAIL && curr_proto == IPPROTO_TCP) {
+ tce = find_tcp_cont(dst_port);
+ if (tce) {
+ if (add_tcp_cont(tce, slp, fraglen)) {
+ slp = tce->msg;
+ fraglen = tce->curr_offset;
+ tcp_continuation = B_TRUE;
+ }
+ }
+ }
+ if (*slp == 2 || tce)
+ interpret_slp_v2(flags, (void *)slp, fraglen);
+ else
+ interpret_slp_v1(flags, (void *)slp, fraglen);
+
+ tcp_continuation = B_FALSE;
+}
+
+/*
+ * Primitives. These are implemented as much as possible as macros for
+ * speed.
+ */
+
+#define FIELD_DEFAULT 0
+#define FIELD_PREVRESP 1
+#define FIELD_TYPENA 2
+
+static long long netval = 0; /* need signed 64 bit quantity */
+
+/* gets two bytes from p and leaves the result in netval */
+#define nbtohs() \
+ netval = ((int)(p[0] & 0xff)) << 8; \
+ netval += ((int)(p[1] & 0xff))
+
+/* gets four bytes from p and leaves the result in netval */
+#define nbtohl() \
+ netval = ((int)(p[0] & 0xff)) << 24; \
+ netval += ((int)(p[1] & 0xff)) << 16; \
+ netval += ((int)(p[2] & 0xff)) << 8; \
+ netval += ((int)(p[3] & 0xff))
+
+#define get_byte() \
+ if (msglength >= 1) { \
+ netval = *p; \
+ p++; \
+ msglength--; \
+ } else \
+ netval = -1
+
+#define GETBYTE(x) \
+ get_byte(); \
+ if ((retlength = netval) < 0) \
+ return (0); \
+ x = netval
+
+#define SKIPBYTE \
+ get_byte(); \
+ if ((retlength = netval) < 0) \
+ return (0); \
+
+/*
+ * gets two bytes from p, leaves the result in netval, and updates
+ * msglength and p.
+ */
+#define get_short() \
+ if (msglength >= sizeof (unsigned short)) { \
+ nbtohs(); \
+ p += sizeof (unsigned short); \
+ msglength -= sizeof (unsigned short); \
+ } else \
+ netval = -1
+
+#define GETSHORT(x) \
+ get_short(); \
+ if ((retlength = netval) < 0) \
+ return (0); \
+ x = netval
+
+#define SKIPSHORT \
+ get_short(); \
+ if ((retlength = netval) < 0) \
+ return (0)
+
+#define get_int24(pp) \
+ netval = ((int)((pp)[0] & 0xff)) << 16; \
+ netval += ((int)((pp)[1] & 0xff)) << 8; \
+ netval += ((int)((pp)[2] & 0xff))
+
+static void slp_prevresp(char *p) {
+ char *p2;
+
+ /* cycle through all entries */
+ for (; p != NULL; p = p2) {
+ p2 = strchr(p, ',');
+ if (p2 != NULL)
+ *p2++ = '\0';
+
+ /* print entry at p */
+ sprintf(get_line(0, 0), " \"%s\"", p);
+ }
+}
+
+static int skip_field(int type) {
+ unsigned short stringlen;
+
+ get_short();
+ if (netval < 0) {
+ return (-1);
+ }
+ stringlen = netval;
+
+ /* special case for NA field in SrvTypeRqst */
+ if (type == FIELD_TYPENA && stringlen == 0xffff) {
+ stringlen = 0;
+ }
+
+ if (stringlen > msglength) {
+ return (-1);
+ }
+
+ msglength -= stringlen;
+ p += stringlen;
+
+ return (stringlen);
+}
+
+#define SKIPFIELD(type) \
+ if ((retlength = skip_field(type)) < 0) \
+ return (0)
+
+#define GETFIELD \
+ get_short(); \
+ if ((retlength = netval) < 0) \
+ return (0); \
+ strncat(msgbuf, p, (retlength > MAXSUMLEN ? MAXSUMLEN : retlength)); \
+ p += retlength; \
+ msglength -= retlength
+
+/*
+ * Determines from the first five bytes of a potential SLP header
+ * if the following message is really an SLP message. Returns 1 if
+ * it is a real SLP message, 0 if not.
+ */
+int valid_slp(unsigned char *slphdr, int len) {
+ struct slpv1_hdr slp1;
+ struct slpv2_hdr slp2;
+
+ len -= (8 /* udp */ + 20 /* IP */ + 14 /* ether */);
+ /* a valid version will be 1 or 2 */
+ switch (*slphdr) {
+ case 1:
+ memcpy(&slp1, slphdr, 5);
+ /* valid function? */
+ if (slp1.function > V1_MAX_FUNCTION) {
+ return (0);
+ }
+ /* valid length heuristic */
+ if (slp1.length > len) {
+ return (0);
+ }
+ return (1);
+ case 2:
+ memcpy(&slp2, slphdr, 5);
+ /* valid function? */
+ if (slp2.function > V2_MAX_FUNCTION) {
+ return (0);
+ }
+ /* valid length heuristic */
+ get_int24(&(slp2.l1));
+ if (netval > len) {
+ return (0);
+ }
+ return (1);
+ default:
+ return (0);
+ }
+}
+
+/*
+ * Converts a V1 char encoding to UTF8. If this fails, returns 0,
+ * otherwise, 1. This function is the union of iconv UTF-8
+ * modules and character sets registered with IANA.
+ */
+static int make_utf8(char *outbuf, size_t outlen,
+ const char *inbuf, size_t inlen) {
+ iconv_t cd;
+ size_t converted;
+
+ switch (v1_charset) {
+ case 4:
+ case 1004:
+ cd = iconv_open("UTF-8", "8859-1");
+ break;
+ case 5:
+ cd = iconv_open("UTF-8", "8859-2");
+ break;
+ case 6:
+ cd = iconv_open("UTF-8", "8859-3");
+ break;
+ case 7:
+ cd = iconv_open("UTF-8", "8859-4");
+ break;
+ case 8:
+ cd = iconv_open("UTF-8", "8859-5");
+ break;
+ case 9:
+ cd = iconv_open("UTF-8", "8859-6");
+ break;
+ case 10:
+ cd = iconv_open("UTF-8", "8859-7");
+ break;
+ case 11:
+ cd = iconv_open("UTF-8", "8859-8");
+ break;
+ case 12:
+ cd = iconv_open("UTF-8", "8859-9");
+ break;
+ case 13:
+ cd = iconv_open("UTF-8", "8859-10");
+ break;
+ case 37:
+ cd = iconv_open("UTF-8", "ko_KR-iso2022-7");
+ break;
+ case 104:
+ cd = iconv_open("UTF-8", "iso2022");
+ break;
+ case 1000:
+ cd = iconv_open("UTF-8", "UCS-2");
+ break;
+ case 1001:
+ cd = iconv_open("UTF-8", "UCS-4");
+ break;
+ default:
+ /*
+ * charset not set, or reserved, or not supported, so
+ * just copy it and hope for the best.
+ */
+ converted = outlen < inlen ? outlen : inlen;
+ memcpy(outbuf, inbuf, converted);
+ outbuf[converted] = 0;
+ return (1);
+ }
+
+ if (cd == (iconv_t)-1) {
+ return (0);
+ }
+
+ if ((converted = iconv(cd, &inbuf, &inlen, &outbuf, &outlen))
+ == (size_t)-1) {
+ return (0);
+ }
+
+ outbuf[converted] = 0;
+ iconv_close(cd);
+
+ return (1);
+}
+
+static int slp_field(char *tag, int type) {
+ int length;
+
+ get_short();
+ if (netval < 0) {
+ return (-1);
+ }
+ length = netval;
+
+ /* special case for NA field in SrvTypeRqst */
+ if (type == FIELD_TYPENA && length == 0xffff) {
+ sprintf(get_line(0, 0), "%s: length = -1: Use all NAs", tag);
+ return (0);
+ }
+
+ sprintf(get_line(0, 0), "%s: length = %d", tag, length);
+ if (length > msglength) {
+ /* framing error: message is not long enough to contain data */
+ sprintf(get_line(0, 0),
+ " [Framing error: remaining pkt length = %u]",
+ msglength);
+ return (-1);
+ }
+
+ if (length > 0) {
+ char *buf = malloc(length + 1);
+ if (buf != NULL) {
+ if (v1_charset) {
+ if (!make_utf8(buf, length, p, length)) {
+ strcpy(buf, "[Invalid Character Encoding]");
+ }
+ } else {
+ memcpy(buf, p, length);
+ buf[length] = '\0'; /* ensure null-terminated */
+ }
+
+ switch (type) {
+ case FIELD_PREVRESP:
+ slp_prevresp(buf);
+ break;
+
+ default:
+ sprintf(get_line(0, 0), " \"%s\"", buf);
+ break;
+ }
+ free(buf);
+ }
+
+ p += length;
+ msglength -= length;
+ }
+
+ /* return ok */
+ return (0);
+}
+
+static int slpv2_url(int cnt) {
+ time_t exp;
+ int lifetime, length, n;
+
+ /* reserved */
+ get_byte();
+ if (netval < 0)
+ return (-1);
+
+ /* lifetime */
+ get_short();
+ if ((lifetime = netval) < 0)
+ return (-1);
+
+ /* length */
+ get_short();
+ if ((length = netval) < 0)
+ return (-1);
+
+ /* time */
+ exp = time(0) + lifetime;
+ if (cnt == -1)
+ sprintf(get_line(0, 0),
+ "URL: length = %u, lifetime = %d (%24.24s)",
+ length, lifetime, ctime(&exp));
+ else
+ /* number the URLs to make it easier to parse them */
+ sprintf(get_line(0, 0),
+ "URL %d: length = %u, lifetime = %d (%24.24s)",
+ cnt, length, lifetime, ctime(&exp));
+
+ if (length > msglength) {
+ if (!tcp_continuation)
+ /* framing error: message is not long enough to contain data */
+ sprintf(get_line(0, 0),
+ " [Framing error: remaining pkt length = %u]",
+ msglength);
+ return (-1);
+ }
+
+ if (length > 0) {
+ char *buf = malloc(length + 1);
+ if (buf != NULL) {
+ memcpy(buf, p, length);
+ buf[length] = '\0'; /* ensure null-terminated */
+ sprintf(get_line(0, 0), " \"%s\"", buf);
+ free(buf);
+ }
+ }
+ msglength -= length;
+ p += length;
+
+ get_byte();
+ if ((n = netval) < 0)
+ return (-1);
+
+ if (n > 0) {
+ int i;
+ sprintf(get_line(0, 0), "%d Authentication Blocks", n);
+ for (i = 0; i < n; i++)
+ if ((length = slpv2_authblock(i)) < 0)
+ return (-1);
+ }
+ return (0);
+}
+
+#define DOFIELD(tag, type) \
+ if (slp_field(tag, type) < 0) \
+ return (0)
+
+#define V2_DOURL(x) \
+ if (slpv2_url(x) < 0) \
+ return (0)
+
+#define V2_DOERRCODE \
+ if (msglength < sizeof (unsigned short)) \
+ return (0); \
+ nbtohs(); \
+ errcode = netval; \
+ sprintf(get_line(0, 0), "Error code = %d, %s", \
+ errcode, slpv2_error(errcode)); \
+ p += sizeof (unsigned short); \
+ msglength -= sizeof (unsigned short); \
+ if (errcode != OK) \
+ msglength = 0; /* skip rest of message */ \
+ if (errcode != OK) \
+ return (0)
+
+#define V2_DOAUTH(cnt) \
+ if (slpv2_authblock(cnt) < 0) \
+ return (0)
+
+#define V2_DOTIMESTAMP \
+ if (msglength < 4) \
+ return (0); \
+ nbtohl(); \
+ timestamp = netval; \
+ sprintf(get_line(0, 0), "Timestamp = %u, %s", \
+ timestamp, (timestamp ? convert_ts(timestamp) : "0")); \
+ p += 4; \
+ msglength -= 4
+
+/* some V1 macros */
+#define SKIPAUTH(auth) \
+ if (auth && ((retlength = skip_v1authblock()) < 0)) \
+ return (0)
+
+#define DOERRCODE \
+ if (msglength < sizeof (unsigned short)) \
+ return (0); \
+ nbtohs(); \
+ errcode = netval; \
+ sprintf(get_line(0, 0), "Error code = %d, %s", errcode, \
+ slpv1_error(errcode)); \
+ p += sizeof (unsigned short); \
+ msglength -= sizeof (unsigned short); \
+ if (errcode != OK) \
+ return (0)
+
+#define DOURL \
+ if (slpv1_url(url_auth) < 0) \
+ return (0)
+
+#define DOAUTH(auth) \
+ if (auth && slpv1_authblock() < 0) \
+ return (0)
+
+/*
+ * TCP Continuation handling
+ * We keep track of continuations in a fixed size cache, so as to prevent
+ * memory leaks if some continuations are never finished. The continuations
+ * are indexed by their destination ports.
+ */
+static void reg_tcp_cont(char *msg, int totallen,
+ int fraglen, int dst_port) {
+ struct tcp_cont *tce = malloc(sizeof (*tce));
+
+ /* always overwrite the entry at current_tcp_cont */
+ if (tcp_cont[current_tcp_cont]) {
+ free(tcp_cont[current_tcp_cont]->msg);
+ free(tcp_cont[current_tcp_cont]);
+ }
+
+ tce->dst_port = dst_port;
+ tce->msg = malloc(totallen);
+ memcpy(tce->msg, msg, fraglen);
+ tce->totallen = totallen;
+ tce->curr_offset = fraglen;
+
+ tcp_cont[current_tcp_cont++] = tce;
+ if (current_tcp_cont == MAX_TCPCONT)
+ current_tcp_cont = 0;
+}
+
+/* returns 0 if there is a mismatch error, 1 on success */
+static int add_tcp_cont(struct tcp_cont *tce, char *msg, int fraglen) {
+ if ((fraglen + tce->curr_offset) > tce->totallen)
+ return (0);
+
+ memcpy(tce->msg + tce->curr_offset, msg, fraglen);
+ tce->curr_offset += fraglen;
+ return (1);
+}
+
+static struct tcp_cont *find_tcp_cont(int dst_port) {
+ int i;
+ for (i = current_tcp_cont; i >= 0; i--)
+ if (tcp_cont[i] && tcp_cont[i]->dst_port == dst_port)
+ return (tcp_cont[i]);
+
+ for (i = MAX_TCPCONT -1; i > current_tcp_cont; i--)
+ if (tcp_cont[i] && tcp_cont[i]->dst_port == dst_port)
+ return (tcp_cont[i]);
+
+ return (NULL);
+}
+
+static void remove_tcp_cont(int dst_port) {
+ int i;
+ for (i = current_tcp_cont; i >= 0; i--)
+ if (tcp_cont[i] && tcp_cont[i]->dst_port == dst_port) {
+ free(tcp_cont[i]->msg);
+ free(tcp_cont[i]);
+ tcp_cont[i] = NULL;
+ return;
+ }
+
+ for (i = MAX_TCPCONT -1; i > current_tcp_cont; i--)
+ if (tcp_cont[i] && tcp_cont[i]->dst_port == dst_port) {
+ free(tcp_cont[i]->msg);
+ free(tcp_cont[i]);
+ tcp_cont[i] = NULL;
+ return;
+ }
+}
+
+/*
+ * V2 interpreter
+ */
+
+static int interpret_slp_v2(int flags, struct slpv2_hdr *slp, int fraglen) {
+ extern int src_port, dst_port, curr_proto;
+ char msgbuf_real[256];
+ int totallen = 0;
+
+ msgbuf = msgbuf_real;
+
+ /*
+ * Somewhat of a hack to decode traffic from a server that does
+ * not send udp replies from its SLP src port.
+ */
+
+ if (curr_proto == IPPROTO_UDP &&
+ dst_port == 427 &&
+ src_port != 427) {
+ add_transient(src_port, (int (*)())interpret_slp);
+ }
+
+ /* parse the header */
+ if (v2_header(flags, slp, &totallen, fraglen)) {
+
+ if (slp->function <= V2_MAX_FUNCTION && slp->function > 0) {
+
+ /* Parse the message body */
+ if ((v2_functions[slp->function])(flags)) {
+
+ /* finish any remaining tasks */
+ v2_finish(slp, flags);
+
+ }
+
+ }
+
+ }
+
+ /* summary error check */
+ if (flags & F_SUM) {
+ if (retlength < 0) {
+ if (curr_proto == IPPROTO_TCP)
+ sprintf(get_sum_line(),
+ "%s [partial TCP message]", msgbuf);
+ else if (overflow)
+ sprintf(get_sum_line(), "%s [OVERFLOW]", msgbuf);
+ else
+ sprintf(get_sum_line(), "%s [CORRUPTED MESSAGE]", msgbuf);
+ }
+#ifdef VERIFYSLP
+ else if (msglength > 0)
+ sprintf(get_sum_line(), "%s +%d", msgbuf, msglength);
+#endif
+ else
+ sprintf(get_sum_line(), "%s", msgbuf);
+ } else if (flags & F_DTAIL) {
+ /* detailed error check */
+ if (msglength > 0) {
+ if (tcp_continuation) {
+ sprintf(get_line(0, 0),
+ "[TCP Continuation, %d bytes remaining]",
+ totallen - fraglen);
+ } else
+ sprintf(get_line(0, 0),
+ "[%d extra bytes at end of SLP message]", msglength);
+ }
+
+ show_trailer();
+
+ if (tcp_continuation && msglength == 0)
+ remove_tcp_cont(dst_port);
+ }
+
+ return (0);
+}
+
+static int v2_header(int flags,
+ struct slpv2_hdr *slp,
+ int *totallen,
+ int fraglen) {
+ extern int curr_proto, dst_port;
+ char *prototag = (curr_proto == IPPROTO_TCP ? "/tcp" : "");
+
+ if ((slp->flags & V2_OVERFLOW) == V2_OVERFLOW)
+ overflow = B_TRUE;
+
+ /* summary mode header parsing */
+ if (flags & F_SUM) {
+
+ /* make sure we have at least a header */
+ if (msglength < sizeof (*slp)) {
+ sprintf(get_sum_line(), "SLP V2 [Incomplete Header]");
+ return (0);
+ }
+
+ sprintf(msgbuf, "SLP V2 %s [%d%s] ",
+ slpv2_func(slp->function, B_TRUE),
+ ntohs(slp->xid), prototag);
+
+ /* skip to end of header */
+ msgend = msgbuf + strlen(msgbuf);
+ msglength -= sizeof (*slp);
+ p += sizeof (*slp);
+
+ /* skip language tag */
+ SKIPFIELD(FIELD_DEFAULT);
+ } else if (flags & F_DTAIL) {
+ char *lang;
+ int len;
+
+ /* detailed mode header parsing */
+ show_header("SLP: ", "Service Location Protocol (v2)", fraglen);
+ show_space();
+
+ if (msglength < sizeof (*slp)) {
+ sprintf(get_line(0, 0), "==> Incomplete SLP header");
+ return (0);
+ }
+
+ sprintf(get_line(0, 0), "Version = %d", slp->vers);
+ sprintf(get_line(0, 0), "Function = %d, %s",
+ slp->function, slpv2_func(slp->function, B_FALSE));
+ get_int24(&(slp->l1));
+ *totallen = netval;
+ sprintf(get_line(0, 0), "Message length = %u", *totallen);
+ /* check for TCP continuation */
+ if (curr_proto == IPPROTO_TCP &&
+ *totallen > msglength &&
+ !tcp_continuation) {
+ tcp_continuation = B_TRUE;
+ reg_tcp_cont((char *)slp, *totallen, msglength, dst_port);
+ }
+
+ if (!tcp_continuation && *totallen != msglength) {
+ sprintf(get_line(0, 0),
+ " (Stated and on-the-wire lengths differ)");
+ }
+ /* flags */
+ sprintf(get_line(0, 0), "Flags = 0x%02x", slp->flags);
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V2_OVERFLOW,
+ "overflow", "no overflow"));
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V2_FRESH,
+ "fresh registration", "no fresh registration"));
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V2_MCAST,
+ "request multicast / broadcast", "unicast"));
+ /* check reserved flags that must be zero */
+ if ((slp->flags & 7) != 0) {
+ sprintf(get_line(0, 0),
+ " .... .xxx = %d (reserved flags nonzero)",
+ slp->flags & 7);
+ }
+ /* end of flags */
+
+ /* language tag */
+ p = (char *)slp + sizeof (*slp);
+ msglength -= sizeof (*slp);
+ GETSHORT(len);
+ if (len > msglength) {
+ sprintf(get_line(0, 0),
+ "Language Tag Length = %u [CORRUPT MESSAGE]",
+ len);
+ return (0);
+ }
+
+ lang = get_line(0, 0);
+ strcpy(lang, "Language Tag = ");
+ strncat(lang, p, len);
+ sprintf(get_line(0, 0), "XID = %u", ntohs(slp->xid));
+
+ /* set msglength to remaining length of SLP message */
+ p += len;
+ msglength -= len;
+ }
+
+ return (1);
+}
+
+static int v2_finish(struct slpv2_hdr *slp, int flags) {
+ unsigned int firstop;
+
+ if (!(flags & F_DTAIL))
+ return (1);
+
+ /* check for options */
+ get_int24(&(slp->o1));
+ firstop = netval;
+
+ if (firstop) {
+ unsigned short op_id;
+ unsigned short nextop;
+ char *op_class;
+
+ for (;;) {
+ unsigned short real_oplen;
+
+ if (msglength < 4) {
+ sprintf(get_line(0, 0),
+ "Option expected but not present");
+ return (0);
+ }
+
+ nbtohs();
+ op_id = netval;
+ p += sizeof (unsigned short);
+ msglength -= sizeof (unsigned short);
+ nbtohs();
+ nextop = netval;
+ p += sizeof (unsigned short);
+ msglength -= sizeof (unsigned short);
+
+ real_oplen = nextop ? nextop : msglength;
+
+ /* known options */
+ switch (op_id) {
+ case 1:
+ sprintf(get_line(0, 0),
+ "Option: Required Attribute Missing");
+ DOFIELD("Template IDVer", FIELD_DEFAULT);
+ DOFIELD("Required Attrs", FIELD_DEFAULT);
+ break;
+ default:
+ sprintf(get_line(0, 0), "Option: Unknown");
+ p += (real_oplen - 4);
+ msglength -= (real_oplen - 4);
+ break;
+ }
+
+ if (op_id < 0x3fff)
+ op_class = "Standardized, optional";
+ else if (op_id < 0x7fff)
+ op_class = "Standardized, mandatory";
+ else if (op_id < 0x8fff)
+ op_class = "Not standardized, private";
+ else if (op_id < 0xffff)
+ op_class = "Reserved";
+ sprintf(get_line(0, 0), "Option ID = 0x%04x, %s",
+ op_id, op_class);
+ if (nextop &&
+ ((nextop - 4) > msglength) &&
+ !tcp_continuation) {
+ sprintf(get_line(0, 0),
+ "[Framing error: remaining pkt length = %u]",
+ msglength);
+ return (0);
+ }
+
+ sprintf(get_line(0, 0), "Option Length = %u", real_oplen);
+
+ if (!nextop)
+ break;
+ }
+ }
+
+ return (1);
+}
+
+#ifdef VERIFYSLP
+static int skip_v2authblock() {
+ unsigned short length, slen;
+
+ /* auth header */
+ if (msglength < 10)
+ return (-1);
+
+ /* block descriptor: 2 bytes */
+ p += sizeof (unsigned short);
+ /* length */
+ nbtohs();
+ length = netval;
+ p += sizeof (unsigned short);
+ /* timestamp */
+ p += 4;
+ /* SPI String length */
+ nbtohs();
+ slen = netval;
+ p += sizeof (unsigned short);
+
+ msglength -= 10;
+ if (slen > msglength || length > (msglength + 10))
+ return (-1);
+
+ p += slen;
+ msglength -= slen;
+
+ /* structured auth block */
+ p += (length - 10 - slen);
+ msglength -= (length - 10 - slen);
+ return (0);
+}
+#endif
+
+static char *display_bsd(unsigned short bsd) {
+ switch (bsd) {
+ case 1: return ("MD5 with RSA");
+ case 2: return ("DSA with SHA-1");
+ case 3: return ("Keyed HMAC with MD5");
+ default: return ("Unknown BSD");
+ }
+}
+
+static char *slpv2_func(int t, boolean_t s) {
+ static char buf[128];
+
+ switch (t) {
+ case V2_SRVRQST: return s? "SrvRqst" : "Service Request";
+ case V2_SRVRPLY: return s? "SrvRply" : "Service Reply";
+ case V2_SRVREG: return s? "SrvReg" : "Service Registration";
+ case V2_SRVDEREG:
+ return (s ? "SrvDereg" : "Service Deregistration");
+ case V2_SRVACK: return s? "SrvAck" : "Service Acknowledge";
+ case V2_ATTRRQST: return s? "AttrRqst" : "Attribute Request";
+ case V2_ATTRRPLY: return s? "AttrRply" : "Attribute Reply";
+ case V2_DAADVERT: return s? "DAAdvert" : "DA advertisement";
+ case V2_SRVTYPERQST:
+ return (s ? "SrvTypeRqst" : "Service Type Request");
+ case V2_SRVTYPERPLY:
+ return (s ? "SrvTypeRply" : "Service Type Reply");
+ case V2_SAADVERT: return s? "SAAdvert" : "SA advertisement";
+ }
+ sprintf(buf, "(func %d)", t);
+ return (s ? buf : "unknown function");
+}
+
+static char *slpv2_error(unsigned short code) {
+ static char buf[128];
+
+ switch (code) {
+ case OK: return "ok";
+ case LANG_NOT_SUPPORTED: return "language not supported";
+ case PROTOCOL_PARSE_ERR: return "protocol parse error";
+ case INVALID_REGISTRATION: return "invalid registration";
+ case SCOPE_NOT_SUPPORTED: return "scope not supported";
+ case AUTHENTICATION_UNKNOWN: return "authentication unknown";
+ case V2_AUTHENTICATION_ABSENT: return "authentication absent";
+ case V2_AUTHENTICATION_FAILED: return "authentication failed";
+ case V2_VER_NOT_SUPPORTED: return "version not supported";
+ case V2_INTERNAL_ERROR: return "internal error";
+ case V2_DA_BUSY_NOW: return "DA busy";
+ case V2_OPTION_NOT_UNDERSTOOD: return "option not understood";
+ case V2_INVALID_UPDATE: return "invalid update";
+ case V2_RQST_NOT_SUPPORTED: return "request not supported";
+ case INVALID_LIFETIME: return "invalid lifetime";
+ }
+ sprintf(buf, "error %d", code);
+ return (buf);
+}
+
+static char *convert_ts(unsigned int timestamp) {
+ /* timestamp is in UNIX time */
+ static char buff[128];
+
+ strcpy(buff, ctime((time_t *)&timestamp));
+ buff[strlen(buff) - 1] = '\0';
+ return (buff);
+}
+
+static int slpv2_authblock(int cnt) {
+ unsigned short bsd, length, slen;
+ char *pp, *scopes;
+ unsigned int timestamp;
+
+ if (msglength < 10) {
+ sprintf(get_line(0, 0),
+ " [no room for auth block header: remaining msg length = %u]",
+ msglength);
+ return (-1);
+ }
+
+ /* bsd */
+ nbtohs();
+ bsd = netval;
+ p += sizeof (unsigned short);
+
+ /* length */
+ nbtohs();
+ length = netval;
+ p += sizeof (unsigned short);
+
+ /* timestamp */
+ nbtohl();
+ timestamp = netval;
+ p += 4;
+
+ /* SPI String length */
+ nbtohs();
+ slen = netval;
+ p += sizeof (unsigned short);
+
+ msglength -= 10;
+ if (slen > msglength) {
+ sprintf(get_line(0, 0),
+ " [no room for auth block scopes: remaining msg length = %u]",
+ msglength);
+ return (-1);
+ }
+
+ if (length > (msglength + 10)) {
+ if (!tcp_continuation)
+ /* framing error: message is not long enough to contain data */
+ sprintf(get_line(0, 0),
+ " [Framing error: remaining pkt length = %u]",
+ msglength);
+ return (-1);
+ }
+
+ scopes = p;
+ p += slen;
+ msglength -= slen;
+
+ sprintf(get_line(0, 0),
+ "Auth block %d: timestamp = %s", cnt,
+ (timestamp) ? convert_ts(timestamp) : "0");
+
+ pp = get_line(0, 0);
+ strcpy(pp, " SPI = ");
+ strncat(pp, scopes, slen);
+
+ sprintf(get_line(0, 0),
+ " block desc = 0x%04x: %s", bsd, display_bsd(bsd));
+
+ sprintf(get_line(0, 0), " length = %u", length);
+
+ p += (length - 10 - slen);
+ msglength -= (length - 10 - slen);
+ return (0);
+}
+
+static int v2_srv_rqst(int flags) {
+ if (flags & F_SUM) {
+ SKIPFIELD(FIELD_DEFAULT); /* PR list */
+ GETFIELD; /* service type */
+ SKIPFIELD(FIELD_DEFAULT); /* scopes */
+ strcat(msgend, " [");
+ GETFIELD; /* predicate */
+ strcat(msgend, "]");
+ SKIPFIELD(FIELD_DEFAULT); /* SPI */
+ } else if (flags & F_DTAIL) {
+ DOFIELD("Previous responders", FIELD_DEFAULT);
+ DOFIELD("Service type", FIELD_DEFAULT);
+ DOFIELD("Scopes", FIELD_DEFAULT);
+ DOFIELD("Predicate string", FIELD_DEFAULT);
+ DOFIELD("Requested SPI", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v2_srv_rply(int flags) {
+ unsigned short itemcnt, errcode;
+ int n;
+
+ if (flags & F_SUM) {
+ int i, auth_cnt;
+
+ GETSHORT(errcode);
+ if (errcode != OK) {
+ strcat(msgbuf, slpv2_error(errcode));
+ msglength = 0; /* skip rest of message */
+ return (0);
+ } else {
+ GETSHORT(itemcnt);
+ sprintf(msgend, "%d URL entries", itemcnt);
+#ifdef VERIFYSLP
+ for (n = 0; n < itemcnt; n++) {
+ SKIPBYTE; /* reserved */
+ SKIPSHORT; /* lifetime */
+ SKIPFIELD(FIELD_DEFAULT); /* URL */
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; auth_cnt++)
+ if (skip_v2authblock() < 0)
+ return (0);
+ }
+#endif
+ }
+ } else if (flags & F_DTAIL) {
+ V2_DOERRCODE;
+ GETSHORT(itemcnt);
+ sprintf(get_line(0, 0), "URL entry count = %d", itemcnt);
+ for (n = 0; n < itemcnt; n++) {
+ V2_DOURL(n);
+ }
+ }
+
+ return (1);
+}
+
+static int v2_srv_reg(int flags) {
+ int i, auth_cnt;
+
+ if (flags & F_SUM) {
+ SKIPBYTE; /* reserved */
+ SKIPSHORT; /* lifetime */
+ GETFIELD; /* URL */
+#ifdef VERIFYSLP
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ if (skip_v2authblock() < 0)
+ return (0);
+ SKIPFIELD(FIELD_DEFAULT); /* type */
+ SKIPFIELD(FIELD_DEFAULT); /* scopes */
+ SKIPFIELD(FIELD_DEFAULT); /* attrs */
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ if (skip_v2authblock() < 0)
+ return (0);
+#endif
+ } if (flags & F_DTAIL) {
+ V2_DOURL(-1);
+ DOFIELD("Service type", FIELD_DEFAULT);
+ DOFIELD("Scopes", FIELD_DEFAULT);
+ DOFIELD("Attribute list", FIELD_DEFAULT);
+ /* auth */
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ V2_DOAUTH(i);
+ }
+
+ return (1);
+}
+
+static int v2_srv_dereg(int flags) {
+ if (flags & F_SUM) {
+ int i, auth_cnt;
+
+ SKIPFIELD(FIELD_DEFAULT); /* scopes */
+ SKIPBYTE; /* reserved */
+ SKIPSHORT; /* lifetime */
+ GETFIELD; /* URL */
+
+#ifdef VERIFYSLP
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ if (skip_v2authblock() < 0)
+ return (0);
+ SKIPFIELD(FIELD_DEFAULT); /* attrs */
+#endif
+ } else if (flags & F_DTAIL) {
+ DOFIELD("Scopes", FIELD_DEFAULT);
+ V2_DOURL(-1);
+ DOFIELD("Tag list", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v2_srv_ack(int flags) {
+ unsigned short errcode;
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ strcat(msgbuf, slpv2_error(errcode));
+ } else if (flags & F_DTAIL) {
+ V2_DOERRCODE;
+ }
+
+ return (1);
+}
+
+static int v2_attr_rqst(int flags) {
+ if (flags & F_SUM) {
+ SKIPFIELD(FIELD_DEFAULT); /* PR list */
+ GETFIELD; /* URL */
+ SKIPFIELD(FIELD_DEFAULT); /* scopes */
+ strcat(msgend, " [");
+ GETFIELD; /* attrs */
+ strcat(msgend, "]");
+
+#ifdef VERIFYSLP
+ SKIPFIELD(FIELD_DEFAULT); /* SPI */
+#endif
+ } else if (flags & F_DTAIL) {
+ DOFIELD("Previous responders", FIELD_DEFAULT);
+ DOFIELD("URL", FIELD_DEFAULT);
+ DOFIELD("Scopes", FIELD_DEFAULT);
+ DOFIELD("Tag list", FIELD_DEFAULT);
+ DOFIELD("Requested SPI", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v2_attr_rply(int flags) {
+ int auth_cnt, i;
+ unsigned short errcode;
+
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ if (errcode != OK) {
+ strcat(msgbuf, slpv2_error(errcode));
+ msglength = 0; /* skip rest of message */
+ return (0);
+ } else {
+ GETFIELD; /* attr list */
+
+#ifdef VERIFYSLP
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ if (skip_v2authblock() < 0)
+ return (0);
+#endif
+ }
+ } else if (flags & F_DTAIL) {
+ V2_DOERRCODE;
+ DOFIELD("Attribute list", FIELD_DEFAULT);
+ /* auth */
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ V2_DOAUTH(i);
+ }
+
+ return (1);
+}
+
+static int v2_daadvert(int flags) {
+ int auth_cnt, i;
+ unsigned short errcode;
+ unsigned int timestamp;
+
+ if (flags & F_SUM) {
+ SKIPSHORT; /* error code */
+ SKIPSHORT; SKIPSHORT; /* timestamp */
+ GETFIELD; /* URL */
+
+#ifdef VERIFYSLP
+ SKIPFIELD(FIELD_DEFAULT); /* scopes */
+ SKIPFIELD(FIELD_DEFAULT); /* attrs */
+ SKIPFIELD(FIELD_DEFAULT); /* SPIs */
+
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ if (skip_v2authblock() < 0)
+ return (0);
+#endif
+ } else if (flags & F_DTAIL) {
+ V2_DOERRCODE;
+ V2_DOTIMESTAMP;
+ DOFIELD("URL", FIELD_DEFAULT);
+ DOFIELD("Scope list", FIELD_DEFAULT);
+ DOFIELD("Attribute list", FIELD_DEFAULT);
+ DOFIELD("Configured SPIs", FIELD_DEFAULT);
+ /* auth */
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ V2_DOAUTH(i);
+ }
+
+ return (1);
+}
+
+static int v2_srv_type_rqst(int flags) {
+ if (flags & F_SUM) {
+ SKIPFIELD(FIELD_DEFAULT); /* prev responders */
+ SKIPFIELD(FIELD_TYPENA); /* naming authority */
+ GETFIELD; /* scope */
+ } else if (flags & F_DTAIL) {
+ DOFIELD("Previous responders", FIELD_DEFAULT);
+ DOFIELD("Naming authority", FIELD_TYPENA);
+ DOFIELD("Scopes", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v2_srv_type_rply(int flags) {
+ unsigned short errcode;
+
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ if (errcode != OK)
+ strcat(msgbuf, slpv2_error(errcode));
+ else
+ GETFIELD;
+ } else if (flags & F_DTAIL) {
+ V2_DOERRCODE;
+ DOFIELD("Service types", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v2_saadvert(int flags) {
+ int auth_cnt, i;
+
+ if (flags & F_SUM) {
+ GETFIELD; /* URL */
+
+#ifdef VERIFYSLP
+ SKIPFIELD(FIELD_DEFAULT); /* scopes */
+ SKIPFIELD(FIELD_DEFAULT); /* attrs */
+
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ if (skip_v2authblock() < 0)
+ return (0);
+#endif
+ } else if (flags & F_DTAIL) {
+ DOFIELD("URL", FIELD_DEFAULT);
+ DOFIELD("Scopes", FIELD_DEFAULT);
+ DOFIELD("Attribute list", FIELD_DEFAULT);
+ /* auth */
+ GETBYTE(auth_cnt);
+ for (i = 0; i < auth_cnt; i++)
+ V2_DOAUTH(i);
+ }
+
+ return (1);
+}
+
+/*
+ * V1 Interpreter
+ */
+
+static int interpret_slp_v1(int flags, struct slpv1_hdr *slp, int fraglen) {
+ char msgbuf_real[256];
+ extern int src_port, dst_port, curr_proto;
+ boolean_t overflow = B_FALSE;
+
+ msgbuf = msgbuf_real;
+
+ if (msglength >= sizeof (*slp)) {
+ if ((slp->flags & V1_URL_AUTH) == V1_URL_AUTH)
+ url_auth = B_TRUE;
+ if ((slp->flags & V1_ATTR_AUTH) == V1_ATTR_AUTH)
+ attr_auth = B_TRUE;
+ if ((slp->flags & V1_FRESH_REG) == V1_FRESH_REG)
+ fresh = B_TRUE;
+ if ((slp->flags & V1_OVERFLOW) == V1_OVERFLOW)
+ overflow = B_TRUE;
+ }
+
+ /*
+ * Somewhat of a hack to decode traffic from a server that does
+ * not send udp replies from its SLP src port.
+ */
+ if (curr_proto == IPPROTO_UDP &&
+ dst_port == 427 &&
+ src_port != 427)
+ add_transient(src_port, (int (*)())interpret_slp);
+
+ /* parse the header */
+ if (v1_header(flags, slp, fraglen)) {
+
+ if (slp->function <= V1_MAX_FUNCTION && slp->function > 0) {
+
+ /* Parse the message body */
+ (v1_functions[slp->function])(flags);
+
+ }
+
+ }
+
+ /* summary error check */
+ if (flags & F_SUM) {
+ if (retlength < 0) {
+ if (curr_proto == IPPROTO_TCP)
+ sprintf(get_sum_line(),
+ "%s [partial TCP message]",
+ msgbuf);
+ else if (overflow)
+ sprintf(get_sum_line(), "%s [OVERFLOW]", msgbuf);
+ else
+ sprintf(get_sum_line(), "%s [CORRUPTED MESSAGE]", msgbuf);
+ }
+#ifdef VERIFYSLP
+ else if (msglength > 0)
+ sprintf(get_sum_line(), "%s +%d", msgbuf, msglength);
+#endif
+ else
+ sprintf(get_sum_line(), "%s", msgbuf);
+ } else if (flags & F_DTAIL) {
+ /* detail error check */
+ if (msglength > 0) {
+ sprintf(get_line(0, 0),
+ "[%d extra bytes at end of SLP message]", msglength);
+ }
+
+ show_trailer();
+
+ }
+
+ v1_charset = 0;
+
+ return (0);
+}
+
+static int v1_header(int flags,
+ struct slpv1_hdr *slp,
+ int fraglen) {
+ extern int src_port, dst_port, curr_proto;
+ char *prototag = (curr_proto == IPPROTO_TCP? "/tcp" : "");
+
+ if (flags & F_SUM) {
+ char portflag = ' ';
+
+ if (msglength < sizeof (*slp)) {
+ sprintf(msgbuf, "SLP V1 [incomplete header]");
+ return (0);
+ }
+
+ if (slp->vers != 1) {
+ if (curr_proto == IPPROTO_TCP)
+ sprintf(msgbuf, "SLP [TCP Continuation]");
+ else
+ sprintf(msgbuf, "SLP [unknown version %d]", slp->vers);
+ return (0);
+ }
+
+ if (src_port != 427 && dst_port != 427)
+ portflag = '-';
+
+ sprintf(msgbuf, "SLP V1%c%s [%d%s] ", portflag,
+ slpv1_func(slp->function, B_TRUE),
+ ntohs(slp->xid), prototag);
+ msgend = msgbuf + strlen(msgbuf);
+ msglength -= sizeof (*slp);
+ p += sizeof (*slp);
+ } else if (flags & F_DTAIL) {
+ show_header("SLP: ", "Service Location Protocol (v1)", fraglen);
+ show_space();
+
+ if (msglength < sizeof (*slp)) {
+ sprintf(get_line(0, 0), "==> Incomplete SLP header");
+ return (0);
+ }
+
+ sprintf(get_line(0, 0), "Version = %d", slp->vers);
+ if (slp->vers != 1) {
+ if (curr_proto == IPPROTO_TCP)
+ sprintf(get_line(0, 0), "==> TCP continuation");
+ else
+ sprintf(get_line(0, 0), "==> Unexpected version number");
+ return (0);
+ }
+ sprintf(get_line(0, 0), "Function = %d, %s",
+ slp->function, slpv1_func(slp->function, B_FALSE));
+ sprintf(get_line(0, 0), "Message length = %u", ntohs(slp->length));
+
+ /* flags */
+ sprintf(get_line(0, 0), "Flags = 0x%02x", slp->flags);
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V1_OVERFLOW,
+ "overflow", "no overflow"));
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V1_MONOLINGUAL,
+ "monolingual", "not monolingual"));
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V1_URL_AUTH,
+ "url authentication", "no url authentication"));
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V1_ATTR_AUTH,
+ "attribute authentication", "no attribute authentication"));
+ sprintf(get_line(0, 0), " %s",
+ getflag(slp->flags, V1_FRESH_REG,
+ "fresh registration", "no fresh registration"));
+ /* check reserved flags that must be zero */
+ if ((slp->flags & 7) != 0) {
+ sprintf(get_line(0, 0),
+ " .... .xxx = %d (reserved flags nonzero)",
+ slp->flags & 7);
+ }
+ /* end of flags */
+
+ sprintf(get_line(0, 0), "Dialect = %u", slp->dialect);
+ sprintf(get_line(0, 0), "Language = 0x%02x%02x, %c%c",
+ slp->language[0], slp->language[1],
+ slp->language[0], slp->language[1]);
+ v1_charset = ntohs(slp->charset);
+ sprintf(get_line(0, 0), "Character encoding = %u, %s",
+ v1_charset,
+ slpv1_charset(v1_charset));
+ sprintf(get_line(0, 0), "XID = %u", ntohs(slp->xid));
+
+ /* set msglength to remaining length of SLP message */
+ msglength -= sizeof (*slp);
+ p += sizeof (*slp);
+ }
+
+ return (1);
+}
+
+static char *slpv1_func(int t, boolean_t s) {
+ static char buf[128];
+ switch (t) {
+ case V1_SRVREQ: return s? "SrvRqst" : "Service Request";
+ case V1_SRVRPLY: return s? "SrvRply" : "Service Reply";
+ case V1_SRVREG: return s? "SrvReg" : "Service Registration";
+ case V1_SRVDEREG: return s?
+ "SrvDereg" : "Service Deregistration";
+ case V1_SRVACK: return s? "SrvAck" : "Service Acknowledge";
+ case V1_ATTRRQST: return s? "AttrRqst" : "Attribute Request";
+ case V1_ATTRRPLY: return s? "AttrRply" : "Attribute Reply";
+ case V1_DAADVERT: return s? "DAAdvert" : "DA advertisement";
+ case V1_SRVTYPERQST:return s? "SrvTypeRqst" : "Service Type Request";
+ case V1_SRVTYPERPLY:return s? "SrvTypeRply" : "Service Type Reply";
+ }
+ sprintf(buf, "(func %d)", t);
+ return (s ? buf : "unknown function");
+}
+
+static char *slpv1_error(unsigned short code) {
+ static char buf[128];
+
+ switch (code) {
+ case OK: return "ok";
+ case LANG_NOT_SUPPORTED: return "language not supported";
+ case PROTOCOL_PARSE_ERR: return "protocol parse error";
+ case INVALID_REGISTRATION: return "invalid registration";
+ case SCOPE_NOT_SUPPORTED: return "scope not supported";
+ case CHARSET_NOT_UNDERSTOOD:return "character set not understood";
+ case AUTHENTICATION_INVALID:return "invalid authentication";
+ case NOT_SUPPORTED_YET: return "not yet supported";
+ case REQUEST_TIMED_OUT: return "request timed out";
+ case COULD_NOT_INIT_NET_RESOURCES:
+ return ("could not initialize net resources");
+ case COULD_NOT_ALLOCATE_MEMORY:
+ return ("could not allocate memory");
+ case PARAMETER_BAD: return "bad parameter";
+ case INTERNAL_NET_ERROR: return "internal network error";
+ case INTERNAL_SYSTEM_ERROR: return "internal system error";
+ }
+ sprintf(buf, "error %d", code);
+ return (buf);
+}
+
+/*
+ * Character set info from
+ * www.isi.edu/in-notes/iana/assignments/character-sets
+ *
+ * Assigned MIB enum Numbers
+ * -------------------------
+ * 0 Reserved
+ * 1 Reserved
+ * 3-106 Set By Standards Organizations
+ * 1000-1010 Unicode / 10646
+ * 2000-2087 Vendor
+ * 2250-2258 Vendor
+ *
+ * MIBenum: 3
+ * Alias: US-ASCII (preferred MIME name)
+ * Source: ECMA registry [RFC1345]
+ *
+ * MIBenum: 106
+ * Name: UTF-8
+ * Source: RFC 2044
+ */
+
+static char *slpv1_charset(unsigned short code) {
+ if (code <= 1)
+ return ("Reserved");
+ if (code == 3)
+ return ("US-ASCII");
+ if (code == 4)
+ return ("latin1");
+ if (code == 106)
+ return ("UTF-8");
+ if (code >= 3 && code <= 106)
+ return ("set by standards organization");
+ if (code >= 1000 && code <= 1010)
+ return ("Unicode variant");
+ if ((code >= 2000 && code <= 2087) ||
+ (code >= 2250 && code <= 2258))
+ return ("Vendor assigned");
+
+ return ("unknown");
+}
+
+#ifdef VERIFYSLP
+static int skip_v1authblock() {
+ unsigned short length;
+
+ /* auth header: 12 bytes total */
+ if (msglength < 12)
+ return (-1);
+
+ /* timestamp: 8 bytes */
+ p += 8; /* timestamp: 8 bytes */
+ p += sizeof (short); /* block descriptor: 2 bytes */
+ nbtohs();
+ length = netval;
+ p += sizeof (short);
+ msglength -= 12;
+
+ if (length > msglength) {
+ /* framing error: message is not long enough to contain data */
+ return (-1);
+ }
+
+ p += length;
+ msglength -= length;
+ return (0);
+}
+#endif
+
+static int slpv1_authblock() {
+ unsigned short bsd, length;
+ char msgbuf[128];
+ int n;
+
+ if (msglength < 12) {
+ sprintf(get_line(0, 0),
+ " [no room for auth block: remaining msg length = %u]",
+ msglength);
+ return (-1);
+ }
+
+ /* timestamp: 8 bytes */
+ *msgbuf = '\0';
+ for (n = 0; n < 8; n++, p += 1) {
+ char tmp[16];
+ sprintf(tmp, "%02x", (unsigned char)(*p));
+ strcat(msgbuf, tmp);
+ }
+
+ nbtohs();
+ bsd = netval;
+ p += sizeof (short);
+ nbtohs();
+ length = netval;
+ p += sizeof (short);
+ msglength -= 12;
+
+ sprintf(get_line(0, 0),
+ " Auth block: timestamp = %s",
+ msgbuf);
+ sprintf(get_line(0, 0),
+ " block desc = 0x%04x, length = %u",
+ bsd, length);
+ if (length > msglength) {
+ /* framing error: message is not long enough to contain data */
+ sprintf(get_line(0, 0),
+ " [Framing error: remaining pkt length = %u]", msglength);
+ return (-1);
+ }
+
+ p += length;
+ msglength -= length;
+ return (0);
+}
+
+static int slpv1_url(boolean_t auth_present) {
+ time_t exp;
+ int lifetime, length;
+
+ get_short();
+ if ((lifetime = netval) < 0)
+ return (-1);
+ get_short();
+ if ((length = netval) < 0)
+ return (-1);
+
+ exp = time(0) + lifetime;
+ sprintf(get_line(0, 0), "URL: length = %u, lifetime = %d (%24.24s)",
+ length, lifetime, ctime(&exp));
+ if (length > msglength) {
+ /* framing error: message is not long enough to contain data */
+ sprintf(get_line(0, 0),
+ " [Framing error: remaining pkt length = %u]", msglength);
+ return (-1);
+ }
+
+ if (length > 0) {
+ char *buf = malloc(length + 1);
+ if (buf != NULL) {
+ if (!make_utf8(buf, length, p, length)) {
+ strcpy(buf, "[Invalid Character Encoding]");
+ }
+ sprintf(get_line(0, 0), " \"%s\"", buf);
+ free(buf);
+ }
+ }
+ msglength -= length;
+ p += length;
+
+ if (auth_present)
+ return (slpv1_authblock());
+
+ return (0);
+}
+
+static int v1_srv_rqst(int flags) {
+ if (flags & F_SUM) {
+ SKIPFIELD(FIELD_PREVRESP); /* prev responders */
+ GETFIELD; /* predicate */
+ } else if (flags & F_DTAIL) {
+ DOFIELD("Previous responders", FIELD_PREVRESP);
+ DOFIELD("predicate string", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v1_srv_rply(int flags) {
+ unsigned short errcode, itemcnt;
+ int n;
+
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ if (errcode != OK) {
+ strcat(msgbuf, slpv1_error(errcode));
+ } else {
+ GETSHORT(itemcnt);
+ sprintf(msgend, "%d URL entries", itemcnt);
+#ifdef VERIFYSLP
+ for (n = 0; n < itemcnt; n++) {
+ SKIPSHORT; /* lifetime */
+ SKIPFIELD(FIELD_DEFAULT); /* URL */
+ SKIPAUTH(url_auth); /* URL auth */
+ }
+#endif
+ }
+ } else if (flags & F_DTAIL) {
+ DOERRCODE;
+ GETSHORT(itemcnt);
+ sprintf(get_line(0, 0), "URL entry count = %d", itemcnt);
+ for (n = 0; n < itemcnt; n++) {
+ DOURL;
+ }
+ }
+
+ return (1);
+}
+
+static int v1_srv_reg(int flags) {
+ if (flags & F_SUM) {
+ SKIPSHORT; /* lifetime */
+ GETFIELD; /* URL */
+#ifdef VERIFYSLP
+ SKIPAUTH(url_auth); /* URL auth */
+ SKIPFIELD(FIELD_DEFAULT); /* attribute list */
+ SKIPAUTH(attr_auth); /* attr auth */
+#endif
+ } else if (flags & F_DTAIL) {
+ DOURL;
+ DOFIELD("Attribute list", FIELD_DEFAULT);
+ DOAUTH(attr_auth);
+ }
+
+ return (1);
+}
+
+static int v1_srv_ack(int flags) {
+ unsigned short errcode;
+
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ strcat(msgbuf, slpv1_error(errcode));
+ if (errcode == OK && fresh) {
+ strcat(msgbuf, " [Fresh]");
+ }
+ } else if (flags & F_DTAIL) {
+ DOERRCODE;
+ }
+
+ return (1);
+}
+
+static int v1_srv_dereg(int flags) {
+ if (flags & F_SUM) {
+ GETFIELD; /* URL */
+#ifdef VERIFYSLP
+ SKIPAUTH(url_auth);
+ SKIPFIELD(FIELD_DEFAULT); /* tag spec */
+#endif
+ } else if (flags & F_DTAIL) {
+ DOFIELD("URL", FIELD_DEFAULT);
+ DOAUTH(url_auth);
+ DOFIELD("Tag spec", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v1_attr_rqst(int flags) {
+ if (flags & F_SUM) {
+ SKIPFIELD(FIELD_PREVRESP); /* prev responders */
+ GETFIELD; /* URL */
+#ifdef VERIFYSLP
+ SKIPFIELD(FIELD_DEFAULT); /* scope */
+ SKIPFIELD(FIELD_DEFAULT); /* select list */
+#endif
+ } else if (flags & F_DTAIL) {
+ DOFIELD("Previous responders", FIELD_PREVRESP);
+ DOFIELD("URL", FIELD_DEFAULT);
+ DOFIELD("Scope", FIELD_DEFAULT);
+ DOFIELD("Select list", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v1_attr_rply(int flags) {
+ unsigned short errcode;
+
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ if (errcode != OK) {
+ strcat(msgbuf, slpv1_error(errcode));
+ } else {
+ GETFIELD; /* attr list */
+#ifdef VERIFYSLP
+ SKIPAUTH(attr_auth);
+#endif
+ }
+ } else if (flags & F_DTAIL) {
+ DOERRCODE;
+ DOFIELD("Attribute list", FIELD_DEFAULT);
+ DOAUTH(attr_auth);
+ }
+
+ return (1);
+}
+
+static int v1_daadvert(int flags) {
+ unsigned short errcode;
+
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ if (errcode != OK) {
+ strcat(msgbuf, slpv1_error(errcode));
+ } else {
+ GETFIELD; /* URL */
+#ifdef VERIFYSLP
+ SKIPFIELD(FIELD_DEFAULT); /* scope list */
+#endif
+ }
+ } else if (flags & F_DTAIL) {
+ DOERRCODE;
+ DOFIELD("URL", FIELD_DEFAULT);
+ DOFIELD("Scope list", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v1_srv_type_rqst(int flags) {
+ if (flags & F_SUM) {
+ SKIPFIELD(FIELD_PREVRESP); /* prev responders */
+ SKIPFIELD(FIELD_TYPENA); /* naming authority */
+ GETFIELD; /* scope */
+ } else if (flags & F_DTAIL) {
+ DOFIELD("Previous responders", FIELD_PREVRESP);
+ DOFIELD("Naming authority", FIELD_TYPENA);
+ DOFIELD("Scope string", FIELD_DEFAULT);
+ }
+
+ return (1);
+}
+
+static int v1_srv_type_rply(int flags) {
+ unsigned short errcode, itemcnt;
+ int n;
+
+ if (flags & F_SUM) {
+ GETSHORT(errcode);
+ if (errcode != OK) {
+ strcat(msgbuf, slpv1_error(errcode));
+ } else {
+ GETSHORT(itemcnt);
+ sprintf(msgend, "%d type entries", itemcnt);
+#ifdef VERIFYSLP
+ for (n = 0; n < itemcnt; n++) {
+ SKIPFIELD(FIELD_DEFAULT); /* Service type item */
+ }
+#endif
+ }
+ } else if (flags & F_DTAIL) {
+ DOERRCODE;
+ GETSHORT(itemcnt);
+ sprintf(get_line(0, 0), "Service type count = %d", itemcnt);
+ for (n = 0; n < itemcnt; n++) {
+ DOFIELD(" Service type item", FIELD_DEFAULT);
+ }
+ }
+
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_smb.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_smb.c
new file mode 100644
index 0000000..cfad4b3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_smb.c
@@ -0,0 +1,2136 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * References used throughout this code:
+ *
+ * [CIFS/1.0] : A Common Internet File System (CIFS/1.0) Protocol
+ * Internet Engineering Task Force (IETF) draft
+ * Paul J. Leach, Microsoft, Dec. 1997
+ *
+ * [X/Open-SMB] : X/Open CAE Specification;
+ * Protocols for X/Open PC Interworking: SMB, Version 2
+ * X/Open Document Number: C209
+ */
+
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "snoop.h"
+
+/*
+ * SMB Format (header)
+ * [X/Open-SMB, Sec. 5.1]
+ */
+struct smb {
+ uchar_t idf[4]; /* identifier, contains 0xff, 'SMB' */
+ uchar_t com; /* command code */
+ uchar_t err[4]; /* NT Status, or error class+code */
+ uchar_t flags;
+ uchar_t flags2[2];
+ uchar_t re[12];
+ uchar_t tid[2];
+ uchar_t pid[2];
+ uchar_t uid[2];
+ uchar_t mid[2];
+ /*
+ * immediately after the above 32 byte header:
+ * unsigned char WordCount;
+ * unsigned short ParameterWords[ WordCount ];
+ * unsigned short ByteCount;
+ * unsigned char ParameterBytes[ ByteCount ];
+ */
+};
+
+/* smb flags */
+#define SERVER_RESPONSE 0x80
+
+/* smb flags2 */
+#define FLAGS2_EXT_SEC 0x0800 /* Extended security */
+#define FLAGS2_NT_STATUS 0x4000 /* NT status codes */
+#define FLAGS2_UNICODE 0x8000 /* String are Unicode */
+
+static void interpret_sesssetupX(int, uchar_t *, int, char *, int);
+static void interpret_tconX(int, uchar_t *, int, char *, int);
+static void interpret_trans(int, uchar_t *, int, char *, int);
+static void interpret_trans2(int, uchar_t *, int, char *, int);
+static void interpret_negprot(int, uchar_t *, int, char *, int);
+static void interpret_default(int, uchar_t *, int, char *, int);
+
+/*
+ * Trans2 subcommand codes
+ * [X/Open-SMB, Sec. 16.1.7]
+ */
+#define TRANS2_OPEN 0x00
+#define TRANS2_FIND_FIRST 0x01
+#define TRANS2_FIND_NEXT2 0x02
+#define TRANS2_QUERY_FS_INFORMATION 0x03
+#define TRANS2_QUERY_PATH_INFORMATION 0x05
+#define TRANS2_SET_PATH_INFORMATION 0x06
+#define TRANS2_QUERY_FILE_INFORMATION 0x07
+#define TRANS2_SET_FILE_INFORMATION 0x08
+#define TRANS2_CREATE_DIRECTORY 0x0D
+
+
+struct decode {
+ char *name;
+ void (*func)(int, uchar_t *, int, char *, int);
+ char *callfmt;
+ char *replyfmt;
+};
+
+/*
+ * SMB command codes (function names)
+ * [X/Open-SMB, Sec. 5.2]
+ */
+static struct decode SMBtable[256] = {
+ /* 0x00 */
+ { "mkdir", 0, 0, 0 },
+ { "rmdir", 0, 0, 0 },
+ { "open", 0, 0, 0 },
+ { "create", 0, 0, 0 },
+
+ {
+ "close", 0,
+ /* [X/Open-SMB, Sec. 7.10] */
+ "WFileID\0"
+ "lLastModTime\0"
+ "dByteCount\0\0",
+ "dByteCount\0\0"
+ },
+
+ { "flush", 0, 0, 0 },
+ { "unlink", 0, 0, 0 },
+
+ {
+ "move", 0,
+ /* [X/Open-SMB, Sec. 7.11] */
+ "wFileAttributes\0"
+ "dByteCount\0r\0"
+ "UFileName\0r\0"
+ "UNewPath\0\0",
+ "dByteCount\0\0"
+ },
+
+ {
+ "getatr", 0,
+ /* [X/Open-SMB, Sec. 8.4] */
+ "dBytecount\0r\0"
+ "UFileName\0\0",
+ "wFileAttributes\0"
+ "lTime\0"
+ "lSize\0"
+ "R\0R\0R\0R\0R\0"
+ "dByteCount\0\0"
+ },
+
+ { "setatr", 0, 0, 0 },
+
+ {
+ "read", 0,
+ /* [X/Open-SMB, Sec. 7.4] */
+ "WFileID\0"
+ "wI/0 Bytes\0"
+ "LFileOffset\0"
+ "WBytesLeft\0"
+ "dByteCount\0\0",
+ "WDataLength\0"
+ "R\0R\0R\0R\0"
+ "dByteCount\0\0"
+ },
+
+ {
+ "write", 0,
+ /* [X/Open-SMB, Sec. 7.5] */
+ "WFileID\0"
+ "wI/0 Bytes\0"
+ "LFileOffset\0"
+ "WBytesLeft\0"
+ "dByteCount\0\0",
+ "WDataLength\0"
+ "dByteCount\0\0"
+ },
+
+ { "lock", 0, 0, 0 },
+ { "unlock", 0, 0, 0 },
+ { "ctemp", 0, 0, 0 },
+ { "mknew", 0, 0, 0 },
+
+ /* 0x10 */
+ {
+ "chkpth", 0,
+ /* [X/Open-SMB, Sec. 8.7] */
+ "dByteCount\0r\0"
+ "UFile\0\0",
+ "dByteCount\0\0"
+ },
+
+ { "exit", 0, 0, 0 },
+ { "lseek", 0, 0, 0 },
+ { "lockread", 0, 0, 0 },
+ { "writeunlock", 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ {
+ "readbraw", 0,
+ /* [X/Open-SMB, Sec. 10.1] */
+ "WFileID\0"
+ "LFileOffset\0"
+ "wMaxCount\0"
+ "wMinCount\0"
+ "lTimeout\0R\0"
+ "dByteCount\0\0", 0
+ },
+
+ { "readbmpx", 0, 0, 0 },
+ { "readbs", 0, 0, 0 },
+ { "writebraw", 0, 0, 0 },
+ { "writebmpx", 0, 0, 0 },
+ { "writebs", 0, 0, 0 },
+
+ /* 0x20 */
+ { "writec", 0, 0, 0 },
+ { "qrysrv", 0, 0, 0 },
+ { "setattrE", 0, 0, 0 },
+ { "getattrE", 0, 0, 0 },
+
+ {
+ "lockingX", 0,
+ /* [X/Open-SMB, Sec. 12.2] */
+ "wChainedCommand\0"
+ "wNextOffset\0"
+ "WFileID\0"
+ "wLockType\0"
+ "lOpenTimeout\0"
+ "W#Unlocks\0"
+ "W#Locks\0"
+ "dByteCount\0\0", 0
+ },
+
+ { "trans", interpret_trans, 0, 0 },
+ { "transs", 0, 0, 0 },
+ { "ioctl", 0, 0, 0 },
+ { "ioctls", 0, 0, 0 },
+ { "copy", 0, 0, 0 },
+ { "move", 0, 0, 0 },
+ { "echo", 0, 0, 0 },
+ { "writeclose", 0, 0, 0 },
+
+ {
+ /* [X/Open-SMB, Sec. 12.1] */
+ "openX", 0,
+ /* call */
+ "wChainedCommand\0"
+ "wNextOffset\0"
+ "wFlags\0"
+ "wMode\0"
+ "wSearchAttributes\0"
+ "wFileAttributes\0"
+ "lTime\0"
+ "wOpenFunction\0"
+ "lFileSize\0"
+ "lOpenTimeout\0R\0R\0"
+ "dByteCount\0r\0"
+ "UFileName\0\0",
+ /* reply */
+ "wChainedCommand\0"
+ "wNextOffset\0"
+ "WFileID\0"
+ "wAttributes\0"
+ "lTime\0"
+ "LSize\0"
+ "wOpenMode\0"
+ "wFileType\0"
+ "wDeviceState\0"
+ "wActionTaken\0"
+ "lUniqueFileID\0R\0"
+ "wBytecount\0\0"
+ },
+
+ {
+ /* [CIFS 4.2.4] */
+ "readX", 0,
+ /* call */
+ "wChainedCommand\0"
+ "wNextOffset\0"
+ "WFileID\0"
+ "LOffset\0"
+ "DMaxCount\0"
+ "dMinCount\0"
+ "dMaxCountHigh\0"
+ "R\0"
+ "wRemaining\0"
+ "lOffsetHigh\0"
+ "dByteCount\0\0",
+ /* reply */
+ "wChainedCommand\0"
+ "wNextOffset\0"
+ "dRemaining\0R\0R\0"
+ "DCount\0"
+ "dDataOffset\0"
+ "dCountHigh\0"
+ "R\0R\0R\0R\0"
+ "dByteCount\0\0"
+ },
+
+ {
+ /* [CIFS 4.2.5] */
+ "writeX", 0,
+ /* call */
+ "wChainedCommand\0"
+ "wNextOffset\0"
+ "WFileID\0"
+ "LOffset\0R\0R\0"
+ "wWriteMode\0"
+ "wRemaining\0"
+ "dDataLenHigh\0"
+ "DDataLen\0"
+ "dDataOffset\0"
+ "lOffsetHigh\0\0",
+ /* reply */
+ "wChainedCommand\0"
+ "wNextOffset\0"
+ "DCount\0"
+ "wRemaining\0"
+ "wCountHigh\0\0"
+ },
+
+ /* 0x30 */
+ { 0, 0, 0, 0 },
+ { "closeTD", 0, 0, 0 },
+ { "trans2", interpret_trans2, 0, 0 },
+ { "trans2s", 0, 0, 0 },
+ {
+ "findclose", 0,
+ /* [X/Open-SMB, Sec. 15.4 ] */
+ "WFileID\0"
+ "dByteCount\0\0",
+ "dByteCount\0\0"
+ },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ /* 0x40 */
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ /* 0x50 */
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ /* 0x60 */
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ /* 0x70 */
+ { "tcon", 0, 0, 0 },
+ {
+ "tdis", 0,
+ /* [X/Open-SMB, Sec. 6.3] */
+ "dByteCount\0\0",
+ "dByteCount\0\0"
+ },
+ { "negprot", interpret_negprot, 0, 0 },
+ { "sesssetupX", interpret_sesssetupX, 0, 0 },
+ {
+ "uloggoffX", 0,
+ /* [X/Open-SMB, Sec. 15.5] */
+ "wChainedCommand\0"
+ "wNextOffset\0\0",
+ "wChainedCommnad\0"
+ "wNextOffset\0\0" },
+ { "tconX", interpret_tconX, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ /* 0x80 */
+ { "dskattr", 0, 0, 0 },
+ { "search", 0, 0, 0 },
+ { "ffirst", 0, 0, 0 },
+ { "funique", 0, 0, 0 },
+ { "fclose", 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ /* 0x90 */
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ /* 0xa0 */
+ /*
+ * Command codes 0xa0 to 0xa7 are from
+ * [CIFS/1.0, Sec. 5.1]
+ */
+ { "_NT_Trans", 0, 0, 0 },
+ { "_NT_Trans2", 0, 0, 0 },
+ {
+ /* [CIFS/1.0, Sec. 4.2.1] */
+ "_NT_CreateX", 0,
+ /* Call */
+ "wChainedCommand\0"
+ "wNextOffset\0r\0"
+ "dNameLength\0"
+ "lCreateFlags\0"
+ "lRootDirFID\0"
+ "lDesiredAccess\0"
+ "lAllocSizeLow\0"
+ "lAllocSizeHigh\0"
+ "lNTFileAttributes\0"
+ "lShareAccess\0"
+ "lOpenDisposition\0"
+ "lCreateOption\0"
+ "lImpersonationLevel\0"
+ "bSecurityFlags\0"
+ "dByteCount\0r\0"
+ "UFileName\0\0",
+ /* Reply */
+ "wChainedCommand\0"
+ "wNextOffset\0"
+ "bOplockLevel\0"
+ "WFileID\0"
+ "lCreateAction\0\0"
+ },
+ { 0, 0, 0, 0 },
+ {
+ "_NT_Cancel", 0,
+ /* [CIFS/1.0, Sec. 4.1.8] */
+ "dByteCount\0", 0
+ },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ /* 0xb0 */
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ /* 0xc0 */
+ { "splopen", 0, 0, 0 },
+ { "splwr", 0, 0, 0 },
+ { "splclose", 0, 0, 0 },
+ { "splretq", 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ /* 0xd0 */
+ { "sends", 0, 0, 0 },
+ { "sendb", 0, 0, 0 },
+ { "fwdname", 0, 0, 0 },
+ { "cancelf", 0, 0, 0 },
+ { "getmac", 0, 0, 0 },
+ { "sendstrt", 0, 0, 0 },
+ { "sendend", 0, 0, 0 },
+ { "sendtxt", 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ /* 0xe0 */
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ /* 0xf0 */
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 }
+};
+
+/* Helpers to get values in Intel order (often mis-aligned). */
+static uint16_t
+get2(uchar_t *p) {
+ return (p[0] + (p[1]<<8));
+}
+static uint32_t
+get4(uchar_t *p) {
+ return (p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24));
+}
+static uint64_t
+get8(uchar_t *p) {
+ return (get4(p) | ((uint64_t)get4(p+4) << 32));
+}
+
+/*
+ * Support displaying NT times.
+ * Number of seconds between 1970 and 1601 year
+ * (134774 days)
+ */
+static const uint64_t DIFF1970TO1601 = 11644473600ULL;
+static const uint32_t TEN_MIL = 10000000UL;
+static char *
+format_nttime(uint64_t nt_time)
+{
+ uint64_t nt_sec; /* seconds */
+ uint64_t nt_tus; /* tenths of uSec. */
+ uint32_t ux_nsec;
+ int64_t ux_sec;
+
+ /* Optimize time zero. */
+ if (nt_time == 0) {
+ ux_sec = 0;
+ ux_nsec = 0;
+ goto out;
+ }
+
+ nt_sec = nt_time / TEN_MIL;
+ nt_tus = nt_time % TEN_MIL;
+
+ if (nt_sec <= DIFF1970TO1601) {
+ ux_sec = 0;
+ ux_nsec = 0;
+ goto out;
+ }
+ ux_sec = nt_sec - DIFF1970TO1601;
+ ux_nsec = nt_tus * 100;
+
+out:
+ return (format_time(ux_sec, ux_nsec));
+}
+
+/*
+ * This is called by snoop_netbios.c.
+ * This is the external entry point.
+ */
+void
+interpret_smb(int flags, uchar_t *data, int len)
+{
+ struct smb *smb;
+ struct decode *decoder;
+ char xtra[MAXLINE];
+ ushort_t smb_flags2;
+ void (*func)(int, uchar_t *, int, char *, int);
+
+ if (len < sizeof (struct smb))
+ return;
+
+ smb = (struct smb *)data;
+ decoder = &SMBtable[smb->com & 255];
+ smb_flags2 = get2(smb->flags2);
+ xtra[0] = '\0';
+
+ /*
+ * SMB Header description
+ * [X/Open-SMB, Sec. 5.1]
+ */
+ if (flags & F_DTAIL) {
+ show_header("SMB: ", "SMB Header", len);
+ show_space();
+
+ if (smb->flags & SERVER_RESPONSE)
+ show_line("SERVER RESPONSE");
+ else
+ show_line("CLIENT REQUEST");
+
+ if (decoder->name)
+ show_printf("Command code = 0x%x (SMB%s)",
+ smb->com, decoder->name);
+ else
+ show_printf("Command code = 0x%x", smb->com);
+
+ /*
+ * NT status or error class/code
+ * [X/Open-SMB, Sec. 5.6]
+ */
+ if (smb_flags2 & FLAGS2_NT_STATUS) {
+ show_printf("NT Status = %x", get4(smb->err));
+ } else {
+ /* Error classes [X/Open-SMB, Sec. 5.6] */
+ show_printf("Error class/code = %d/%d",
+ smb->err[0], get2(&smb->err[2]));
+ }
+
+ show_printf("Flags summary = 0x%.2x", smb->flags);
+ show_printf("Flags2 summary = 0x%.4x", smb_flags2);
+ show_printf("Tree ID (TID) = 0x%.4x", get2(smb->tid));
+ show_printf("Proc. ID (PID) = 0x%.4x", get2(smb->pid));
+ show_printf("User ID (UID) = 0x%.4x", get2(smb->uid));
+ show_printf("Mux. ID (MID) = 0x%.4x", get2(smb->mid));
+ show_space();
+ }
+
+ if ((func = decoder->func) == NULL)
+ func = interpret_default;
+ (*func)(flags, (uchar_t *)data, len, xtra, sizeof (xtra));
+
+ if (flags & F_SUM) {
+ char *p;
+ int sz, tl;
+
+ /* Will advance p and decr. sz */
+ p = get_sum_line();
+ sz = MAXLINE;
+
+ /* Call or Reply */
+ if (smb->flags & SERVER_RESPONSE)
+ tl = snprintf(p, sz, "SMB R");
+ else
+ tl = snprintf(p, sz, "SMB C");
+ p += tl;
+ sz -= tl;
+
+ /* The name, if known, else the cmd code */
+ if (decoder->name) {
+ tl = snprintf(p, sz, " Cmd=SMB%s", decoder->name);
+ } else {
+ tl = snprintf(p, sz, " Cmd=0x%02X", smb->com);
+ }
+ p += tl;
+ sz -= tl;
+
+ /*
+ * The "extra" (cmd-specific summary).
+ * If non-null, has leading blank.
+ */
+ if (xtra[0] != '\0') {
+ tl = snprintf(p, sz, "%s", xtra);
+ p += tl;
+ sz -= tl;
+ }
+
+ /*
+ * NT status or error class/code
+ * [X/Open-SMB, Sec. 5.6]
+ *
+ * Only show for response, not call.
+ */
+ if (smb->flags & SERVER_RESPONSE) {
+ if (smb_flags2 & FLAGS2_NT_STATUS) {
+ uint_t status = get4(smb->err);
+ snprintf(p, sz, " Status=0x%x", status);
+ } else {
+ uchar_t errcl = smb->err[0];
+ ushort_t code = get2(&smb->err[2]);
+ snprintf(p, sz, " Error=%d/%d", errcl, code);
+ }
+ }
+ }
+
+ if (flags & F_DTAIL)
+ show_trailer();
+}
+
+static void
+output_bytes(uchar_t *data, int bytecount)
+{
+ int i;
+ char buff[80];
+ char word[10];
+
+ (void) strlcpy(buff, " ", sizeof (buff));
+ for (i = 0; i < bytecount; i++) {
+ snprintf(word, sizeof (word), "%.2x ", data[i]);
+ (void) strlcat(buff, word, sizeof (buff));
+ if ((i+1)%16 == 0 || i == (bytecount-1)) {
+ show_line(buff);
+ (void) strlcpy(buff, " ", sizeof (buff));
+ }
+ }
+}
+
+/*
+ * Based on the Unicode Standard, http://www.unicode.org/
+ * "The Unicode Standard: A Technical Introduction", June 1998
+ */
+static int
+unicode2ascii(char *outstr, int outlen, uchar_t *instr, int inlen)
+{
+ int i = 0, j = 0;
+ char c;
+
+ while (i < inlen && j < (outlen-1)) {
+ /* Show unicode chars >= 256 as '?' */
+ if (instr[i+1])
+ c = '?';
+ else
+ c = instr[i];
+ if (c == '\0')
+ break;
+ outstr[j] = c;
+ i += 2;
+ j++;
+ }
+ outstr[j] = '\0';
+ return (j);
+}
+
+/*
+ * Convenience macro to copy a string from the data,
+ * either in UCS-2 or ASCII as indicated by UCS.
+ * OBUF must be an array type (see sizeof) and
+ * DP must be an L-value (this increments it).
+ */
+#define GET_STRING(OBUF, DP, UCS) \
+{ \
+ int _len, _sz = sizeof (OBUF); \
+ if (UCS) { \
+ if (((uintptr_t)DP) & 1) \
+ DP++; \
+ _len = unicode2ascii(OBUF, _sz, DP, 2 * _sz); \
+ DP += 2 * (_len + 1); \
+ } else { \
+ _len = strlcpy(OBUF, (char *)DP, _sz); \
+ DP += (_len + 1); \
+ } \
+}
+
+/*
+ * TRANS2 information levels
+ * [X/Open-SMB, Sec. 16.1.6]
+ */
+static void
+get_info_level(char *outstr, int outsz, int value)
+{
+
+ switch (value) {
+ case 1:
+ snprintf(outstr, outsz, "Standard");
+ break;
+ case 2:
+ snprintf(outstr, outsz, "Query EA Size");
+ break;
+ case 3:
+ snprintf(outstr, outsz, "Query EAS from List");
+ break;
+ case 0x101:
+ snprintf(outstr, outsz, "Directory Info");
+ break;
+ case 0x102:
+ snprintf(outstr, outsz, "Full Directory Info");
+ break;
+ case 0x103:
+ snprintf(outstr, outsz, "Names Info");
+ break;
+ case 0x104:
+ snprintf(outstr, outsz, "Both Directory Info");
+ break;
+ default:
+ snprintf(outstr, outsz, "Unknown");
+ break;
+ }
+}
+
+/*
+ * Interpret TRANS2_QUERY_PATH subcommand
+ * [X/Open-SMB, Sec. 16.7]
+ */
+/* ARGSUSED */
+static void
+output_trans2_querypath(int flags, uchar_t *data, char *xtra, int xsz)
+{
+ int length;
+ char filename[256];
+
+ if (flags & F_SUM) {
+ length = snprintf(xtra, xsz, " QueryPathInfo");
+ xtra += length;
+ xsz -= length;
+ data += 6;
+ (void) unicode2ascii(filename, 256, data, 512);
+ snprintf(xtra, xsz, " File=%s", filename);
+ }
+
+ if (flags & F_DTAIL) {
+ show_line("FunctionName = QueryPathInfo");
+ show_printf("InfoLevel = 0x%.4x", get2(data));
+ data += 6;
+ (void) unicode2ascii(filename, 256, data, 512);
+ show_printf("FileName = %s", filename);
+ }
+}
+
+/*
+ * Interpret TRANS2_QUERY_FILE subcommand
+ * [X/Open-SMB, Sec. 16.9]
+ */
+/* ARGSUSED */
+static void
+output_trans2_queryfile(int flags, uchar_t *data, char *xtra, int xsz)
+{
+ int length;
+
+ if (flags & F_SUM) {
+ length = snprintf(xtra, xsz, " QueryFileInfo");
+ xtra += length;
+ xsz -= length;
+ snprintf(xtra, xsz, " FileID=0x%x", get2(data));
+ }
+
+ if (flags & F_DTAIL) {
+ show_line("FunctionName = QueryFileInfo");
+ show_printf("FileID = 0x%.4x", get2(data));
+ data += 2;
+ show_printf("InfoLevel = 0x%.4x", get2(data));
+ }
+}
+
+/*
+ * Interpret TRANS2_SET_FILE subcommand
+ * [X/Open-SMB, Sec. 16.10]
+ */
+/* ARGSUSED */
+static void
+output_trans2_setfile(int flags, uchar_t *data, char *xtra, int xsz)
+{
+ int length;
+
+ if (flags & F_SUM) {
+ length = snprintf(xtra, xsz, " SetFileInfo");
+ xtra += length;
+ xsz -= length;
+ snprintf(xtra, xsz, " FileID=0x%x", get2(data));
+ }
+
+ if (flags & F_DTAIL) {
+ show_line("FunctionName = SetFileInfo");
+ show_printf("FileID = 0x%.4x", get2(data));
+ data += 2;
+ show_printf("InfoLevel = 0x%.4x", get2(data));
+ }
+}
+
+/*
+ * Interpret TRANS2_FIND_FIRST subcommand
+ * [X/Open-SMB, Sec. 16.3]
+ */
+/* ARGSUSED */
+static void
+output_trans2_findfirst(int flags, uchar_t *data, char *xtra, int xsz)
+{
+ int length;
+ char filename[256];
+ char infolevel[100];
+
+ if (flags & F_SUM) {
+ length = snprintf(xtra, xsz, " Findfirst");
+ xtra += length;
+ xsz -= length;
+ data += 12;
+ (void) unicode2ascii(filename, 256, data, 512);
+ snprintf(xtra, xsz, " File=%s", filename);
+ }
+
+ if (flags & F_DTAIL) {
+ show_line("FunctionName = Findfirst");
+ show_printf("SearchAttributes = 0x%.4x", get2(data));
+ data += 2;
+ show_printf("FindCount = 0x%.4x", get2(data));
+ data += 2;
+ show_printf("FindFlags = 0x%.4x", get2(data));
+ data += 2;
+ get_info_level(infolevel, sizeof (infolevel), get2(data));
+ show_printf("InfoLevel = %s", infolevel);
+ data += 6;
+ (void) unicode2ascii(filename, 256, data, 512);
+ show_printf("FileName = %s", filename);
+ }
+}
+
+
+/*
+ * Interpret TRANS2_FIND_NEXT subcommand
+ * [X/Open-SMB, Sec. 16.4]
+ */
+/* ARGSUSED */
+static void
+output_trans2_findnext(int flags, uchar_t *data, char *xtra, int xsz)
+{
+ int length;
+ char filename[256];
+ char infolevel[100];
+
+ if (flags & F_SUM) {
+ length = snprintf(xtra, xsz, " Findnext");
+ xtra += length;
+ xsz -= length;
+ data += 12;
+ (void) unicode2ascii(filename, 256, data, 512);
+ snprintf(xtra, xsz, " File=%s", filename);
+ }
+
+ if (flags & F_DTAIL) {
+ show_line("FunctionName = Findnext");
+ show_printf("FileID = 0x%.4x", get2(data));
+ data += 2;
+ show_printf("FindCount = 0x%.4x", get2(data));
+ data += 2;
+ get_info_level(infolevel, sizeof (infolevel), get2(data));
+ show_printf("InfoLevel = %s", infolevel);
+ data += 2;
+ show_printf("FindKey = 0x%.8x", get4(data));
+ data += 4;
+ show_printf("FindFlags = 0x%.4x", get2(data));
+ data += 2;
+ (void) unicode2ascii(filename, 256, data, 512);
+ show_printf("FileName = %s", filename);
+ }
+}
+
+/*
+ * Interpret a "Negprot" SMB
+ * [X/Open-SMB, Sec. 6.1]
+ */
+/* ARGSUSED */
+static void
+interpret_negprot(int flags, uchar_t *data, int len, char *xtra, int xsz)
+{
+ int i, last, length;
+ int bytecount;
+ int key_len;
+ int wordcount;
+ char tbuf[256];
+ struct smb *smbdata;
+ uchar_t *protodata;
+ uchar_t *byte0;
+ uint64_t nttime;
+ uint32_t caps;
+ ushort_t smb_flags2;
+
+ smbdata = (struct smb *)data;
+ smb_flags2 = get2(smbdata->flags2);
+ protodata = (uchar_t *)data + sizeof (struct smb);
+ wordcount = *protodata++;
+
+ if ((smbdata->flags & SERVER_RESPONSE) == 0) {
+ /*
+ * request packet:
+ * short bytecount;
+ * struct { char fmt; char name[]; } dialects
+ */
+ bytecount = get2(protodata);
+ protodata += 2;
+ byte0 = protodata;
+
+ if (flags & F_DTAIL)
+ show_printf("ByteCount = %d", bytecount);
+ if (bytecount > len)
+ bytecount = len;
+
+ /* Walk the list of dialects. */
+ i = last = 0;
+ tbuf[0] = '\0';
+ while (protodata < (byte0 + bytecount - 2)) {
+ if (*protodata++ != 2) /* format code */
+ break;
+ length = strlcpy(tbuf, (char *)protodata,
+ sizeof (tbuf));
+ protodata += (length + 1);
+ if (flags & F_DTAIL) {
+ show_printf("Dialect[%d] = %s",
+ i, tbuf);
+ }
+ last = i++;
+ }
+ if (flags & F_SUM) {
+ /*
+ * Just print the last dialect, which is
+ * normally the interesting one.
+ */
+ snprintf(xtra, xsz, " Dialect[%d]=%s", last, tbuf);
+ }
+ } else {
+ /* Parse reply */
+ if (flags & F_SUM) {
+ snprintf(xtra, xsz, " Dialect#=%d", protodata[0]);
+ }
+ if ((flags & F_DTAIL) == 0)
+ return;
+ if (wordcount < 13)
+ return;
+ show_printf("WordCount = %d", wordcount);
+ show_printf("Dialect Index = %d", protodata[0]);
+ protodata += 2;
+ show_printf("Security Mode = 0x%x", protodata[0]);
+ protodata++;
+ show_printf("MaxMPXRequests = %d", get2(protodata));
+ protodata += 2;
+ show_printf("MaxVCs = %d", get2(protodata));
+ protodata += 2;
+ show_printf("MaxBufferSize = %d", get4(protodata));
+ protodata += 4;
+ show_printf("MaxRawBuffer = %d", get4(protodata));
+ protodata += 4;
+ show_printf("SessionKey = 0x%.8x", get4(protodata));
+ protodata += 4;
+
+ caps = get4(protodata);
+ protodata += 4;
+ show_printf("Capabilities = 0x%.8x", caps);
+
+ /* Server Time */
+ nttime = get8(protodata);
+ protodata += 8;
+ show_printf("Server Time = %s", format_nttime(nttime));
+
+ show_printf("Server TZ = %d", get2(protodata));
+ protodata += 2;
+
+ key_len = *protodata++;
+ show_printf("KeyLength = %d", key_len);
+ bytecount = get2(protodata);
+ protodata += 2;
+ show_printf("ByteCount = %d", bytecount);
+
+ if (smb_flags2 & FLAGS2_EXT_SEC) {
+ show_printf("Server GUID (16)");
+ output_bytes(protodata, 16);
+ protodata += 16;
+ show_printf("Security Blob (SPNEGO)");
+ output_bytes(protodata, bytecount - 16);
+ } else {
+ show_printf("NTLM Challenge: (%d)", key_len);
+ output_bytes(protodata, key_len);
+ protodata += key_len;
+ /*
+ * Get Unicode from capabilities here,
+ * as flags2 typically doesn't have it.
+ * Also, this one is NOT aligned!
+ */
+ tbuf[0] = '\0';
+ if (caps & 4) {
+ (void) unicode2ascii(tbuf, sizeof (tbuf),
+ protodata, 2 * sizeof (tbuf));
+ } else {
+ (void) strlcpy(tbuf, (char *)protodata,
+ sizeof (tbuf));
+ }
+ show_printf("Server Domain = %s", tbuf);
+ }
+ }
+}
+
+/*
+ * LAN Manager remote admin function names.
+ * [X/Open-SMB, Appendix B.8]
+ */
+static const char *apiname_table[] = {
+ "RNetShareEnum",
+ "RNetShareGetInfo",
+ "NetShareSetInfo",
+ "NetShareAdd",
+ "NetShareDel",
+ "NetShareCheck",
+ "NetSessionEnum",
+ "NetSessionGetInfo",
+ "NetSessionDel",
+ "NetConnectionEnum",
+ "NetFileEnum",
+ "NetFileGetInfo",
+ "NetFileClose",
+ "RNetServerGetInfo",
+ "NetServerSetInfo",
+ "NetServerDiskEnum",
+ "NetServerAdminCommand",
+ "NetAuditOpen",
+ "NetAuditClear",
+ "NetErrorLogOpen",
+ "NetErrorLogClear",
+ "NetCharDevEnum",
+ "NetCharDevGetInfo",
+ "NetCharDevControl",
+ "NetCharDevQEnum",
+ "NetCharDevQGetInfo",
+ "NetCharDevQSetInfo",
+ "NetCharDevQPurge",
+ "RNetCharDevQPurgeSelf",
+ "NetMessageNameEnum",
+ "NetMessageNameGetInfo",
+ "NetMessageNameAdd",
+ "NetMessageNameDel",
+ "NetMessageNameFwd",
+ "NetMessageNameUnFwd",
+ "NetMessageBufferSend",
+ "NetMessageFileSend",
+ "NetMessageLogFileSet",
+ "NetMessageLogFileGet",
+ "NetServiceEnum",
+ "RNetServiceInstall",
+ "RNetServiceControl",
+ "RNetAccessEnum",
+ "RNetAccessGetInfo",
+ "RNetAccessSetInfo",
+ "RNetAccessAdd",
+ "RNetAccessDel",
+ "NetGroupEnum",
+ "NetGroupAdd",
+ "NetGroupDel",
+ "NetGroupAddUser",
+ "NetGroupDelUser",
+ "NetGroupGetUsers",
+ "NetUserEnum",
+ "RNetUserAdd",
+ "NetUserDel",
+ "NetUserGetInfo",
+ "RNetUserSetInfo",
+ "RNetUserPasswordSet",
+ "NetUserGetGroups",
+ "NetWkstaLogon",
+ "NetWkstaLogoff",
+ "NetWkstaSetUID",
+ "NetWkstaGetInfo",
+ "NetWkstaSetInfo",
+ "NetUseEnum",
+ "NetUseAdd",
+ "NetUseDel",
+ "NetUseGetInfo",
+ "DosPrintQEnum",
+ "DosPrintQGetInfo",
+ "DosPrintQSetInfo",
+ "DosPrintQAdd",
+ "DosPrintQDel",
+ "DosPrintQPause",
+ "DosPrintQContinue",
+ "DosPrintJobEnum",
+ "DosPrintJobGetInfo",
+ "RDosPrintJobSetInfo",
+ "DosPrintJobAdd",
+ "DosPrintJobSchedule",
+ "RDosPrintJobDel",
+ "RDosPrintJobPause",
+ "RDosPrintJobContinue",
+ "DosPrintDestEnum",
+ "DosPrintDestGetInfo",
+ "DosPrintDestControl",
+ "NetProfileSave",
+ "NetProfileLoad",
+ "NetStatisticsGet",
+ "NetStatisticsClear",
+ "NetRemoteTOD",
+ "NetBiosEnum",
+ "NetBiosGetInfo",
+ "NetServerEnum",
+ "I_NetServerEnum",
+ "NetServiceGetInfo",
+ "NetSplQmAbort",
+ "NetSplQmClose",
+ "NetSplQmEndDoc",
+ "NetSplQmOpen",
+ "NetSplQmStartDoc",
+ "NetSplQmWrite",
+ "DosPrintQPurge",
+ "NetServerEnum2"
+};
+static const int apinum_max = (
+ sizeof (apiname_table) /
+ sizeof (apiname_table[0]));
+
+static const char *
+pipeapi_name(int code)
+{
+ char *name;
+
+ switch (code) {
+ case 0x01:
+ name = "SetNmPipeState";
+ break;
+ case 0x11:
+ name = "RawReadNmPipe";
+ break;
+ case 0x21:
+ name = "QueryNmPipeState";
+ break;
+ case 0x22:
+ name = "QueryNmPipeInfo";
+ break;
+ case 0x23:
+ name = "PeekNmPipe";
+ break;
+ case 0x26:
+ name = "XactNmPipe";
+ break;
+ case 0x31:
+ name = "RawWriteNmPipe";
+ break;
+ case 0x36:
+ name = "ReadNmPipe";
+ break;
+ case 0x37:
+ name = "WriteNmPipe";
+ break;
+ case 0x53:
+ name = "WaitNmPipe";
+ break;
+ case 0x54:
+ name = "CallNmPipe";
+ break;
+ default:
+ name = "?";
+ break;
+ }
+ return (name);
+}
+
+/*
+ * Interpret a "trans" SMB
+ * [X/Open-SMB, Appendix B]
+ *
+ * This is very much like "trans2" below.
+ */
+/* ARGSUSED */
+static void
+interpret_trans(int flags, uchar_t *data, int len, char *xtra, int xsz)
+{
+ struct smb *smb;
+ uchar_t *vwv; /* word parameters */
+ int wordcount;
+ uchar_t *byteparms;
+ int bytecount;
+ int parambytes;
+ int paramoffset;
+ int setupcount;
+ int subcode;
+ uchar_t *setupdata;
+ uchar_t *params;
+ int apinum;
+ int isunicode;
+ char filename[256];
+ const char *apiname;
+ const char *subcname;
+ ushort_t smb_flags2;
+
+ smb = (struct smb *)data;
+ smb_flags2 = get2(smb->flags2);
+ vwv = (uchar_t *)data + sizeof (struct smb);
+ wordcount = *vwv++;
+
+ /* Is the pathname in unicode? */
+ isunicode = smb_flags2 & FLAGS2_UNICODE;
+
+ byteparms = vwv + (2 * wordcount);
+ bytecount = get2(byteparms);
+ byteparms += 2;
+
+ /*
+ * Print the lengths before we (potentially) bail out
+ * due to lack of data (so the user knows why we did).
+ */
+ if (flags & F_DTAIL)
+ show_printf("WordCount = %d", wordcount);
+
+ /* Get length and location of params and setup data. */
+ if (!(smb->flags & SERVER_RESPONSE)) {
+ /* CALL */
+ if (wordcount < 14)
+ return;
+ parambytes = get2(vwv + (2 * 9));
+ paramoffset = get2(vwv + (2 * 10));
+ setupcount = *(vwv + (2 * 13));
+ setupdata = vwv + (2 * 14);
+ } else {
+ /* REPLY */
+ if (wordcount < 10)
+ return;
+ parambytes = get2(vwv + (2 * 3));
+ paramoffset = get2(vwv + (2 * 4));
+ setupcount = *(vwv + (2 * 9));
+ setupdata = vwv + (2 * 10);
+ }
+
+ /* The parameters are offset from the SMB header. */
+ params = data + paramoffset;
+
+ if ((smb->flags & SERVER_RESPONSE) == 0) {
+ /* This is a CALL. */
+
+ if (setupcount > 0)
+ subcode = get2(setupdata);
+ else
+ subcode = -1; /* invalid */
+ subcname = pipeapi_name(subcode);
+
+ if (parambytes > 0)
+ apinum = params[0];
+ else
+ apinum = -1; /* invalid */
+ if (0 <= apinum && apinum < apinum_max)
+ apiname = apiname_table[apinum];
+ else
+ apiname = "?";
+
+ if (flags & F_SUM) {
+ int tl;
+ /* Only get one or the other */
+ if (*subcname != '?') {
+ tl = snprintf(xtra, xsz,
+ " Func=%s", subcname);
+ xtra += tl;
+ xsz -= tl;
+ }
+ if (*apiname != '?')
+ snprintf(xtra, xsz,
+ " Func=%s", apiname);
+ return;
+ }
+ if ((flags & F_DTAIL) == 0)
+ return;
+
+ /* print the word parameters */
+ show_printf("TotalParamBytes = %d", get2(vwv));
+ show_printf("TotalDataBytes = %d", get2(vwv+2));
+ show_printf("MaxParamBytes = %d", get2(vwv+4));
+ show_printf("MaxDataBytes = %d", get2(vwv+6));
+ show_printf("MaxSetupWords = %d", vwv[8]);
+ show_printf("TransFlags = 0x%.4x", get2(vwv+10));
+ show_printf("Timeout = 0x%.8x", get4(vwv+12));
+ /* skip Reserved2 */
+ show_printf("ParamBytes = %d", parambytes);
+ show_printf("ParamOffset = %d", paramoffset);
+ show_printf("DataBytes = %d", get2(vwv+22));
+ show_printf("DataOffset = %d", get2(vwv+24));
+ show_printf("SetupWords = %d", setupcount);
+ show_printf("ByteCount = %d", bytecount);
+
+ /* That finishes the VWV, now the misc. stuff. */
+ if (setupcount > 0)
+ show_printf("NmPipeFunc = 0x%x (%s)",
+ subcode, subcname);
+ if (parambytes > 0)
+ show_printf("RAP_Func = %d (%s)",
+ apinum, apiname);
+
+ /* Finally, print the byte parameters. */
+ GET_STRING(filename, byteparms, isunicode);
+ show_printf("FileName = %s", filename);
+ } else {
+ /* This is a REPLY. */
+ if (flags & F_SUM)
+ return;
+ if ((flags & F_DTAIL) == 0)
+ return;
+ /* print the word parameters */
+ show_printf("TotalParamBytes = %d", get2(vwv));
+ show_printf("TotalDataBytes = %d", get2(vwv+2));
+ /* skip Reserved */
+ show_printf("ParamBytes = 0x%.4x", parambytes);
+ show_printf("ParamOffset = 0x%.4x", paramoffset);
+ show_printf("ParamDispl. = 0x%.4x", get2(vwv+10));
+ show_printf("DataBytes = 0x%.4x", get2(vwv+12));
+ show_printf("DataOffset = 0x%.4x", get2(vwv+14));
+ show_printf("DataDispl. = 0x%.4x", get2(vwv+16));
+ show_printf("SetupWords = %d", setupcount);
+ show_printf("ByteCount = %d", bytecount);
+
+ show_printf("ParamVec (%d)", parambytes);
+ output_bytes(params, parambytes);
+ }
+}
+
+/*
+ * Interpret a "TconX" SMB
+ * [X/Open-SMB, Sec. 11.4]
+ */
+/* ARGSUSED */
+static void
+interpret_tconX(int flags, uchar_t *data, int len, char *xtra, int xsz)
+{
+ int length;
+ int isunicode;
+ int bytecount;
+ int wordcount;
+ int andxcmd;
+ int andxoffset;
+ int tconflags;
+ int pw_len;
+ char path[256];
+ char tbuf[256];
+ char svc[8];
+ struct smb *smbdata;
+ uchar_t *tcondata;
+ ushort_t smb_flags2;
+
+ smbdata = (struct smb *)data;
+ smb_flags2 = get2(smbdata->flags2);
+ tcondata = (uchar_t *)data + sizeof (struct smb);
+ wordcount = *tcondata++;
+
+ isunicode = smb_flags2 & FLAGS2_UNICODE;
+
+ if ((smbdata->flags & SERVER_RESPONSE) == 0) {
+ /* Request */
+ if (wordcount < 4)
+ return;
+ andxcmd = get2(tcondata);
+ tcondata += 2;
+ andxoffset = get2(tcondata);
+ tcondata += 2;
+ tconflags = get2(tcondata);
+ tcondata += 2;
+ pw_len = get2(tcondata);
+ tcondata += 2;
+ bytecount = get2(tcondata);
+ tcondata += 2;
+
+ /* skip password */
+ if (pw_len > len)
+ pw_len = len;
+ tcondata += pw_len;
+
+ GET_STRING(path, tcondata, isunicode);
+ (void) strlcpy(svc, (char *)tcondata, sizeof (svc));
+
+ if (flags & F_SUM) {
+ snprintf(xtra, xsz, " Share=%s", path);
+ return;
+ }
+
+ if ((flags & F_DTAIL) == 0)
+ return;
+
+ show_printf("WordCount = %d", wordcount);
+ show_printf("ChainedCommand = 0x%.2x", andxcmd);
+ show_printf("NextOffset = 0x%.4x", andxoffset);
+ show_printf("TconFlags = 0x%.4x", tconflags);
+ show_printf("PasswordLength = 0x%.4x", pw_len);
+ show_printf("ByteCount = %d", bytecount);
+ show_printf("SharePath = %s", path);
+ show_printf("ServiceType = %s", svc);
+ } else {
+ /* response */
+ if (wordcount < 3)
+ return;
+ andxcmd = get2(tcondata);
+ tcondata += 2;
+ andxoffset = get2(tcondata);
+ tcondata += 2;
+ tconflags = get2(tcondata);
+ tcondata += 2;
+ bytecount = get2(tcondata);
+ tcondata += 2;
+
+ length = strlcpy(svc, (char *)tcondata, sizeof (svc));
+ tcondata += (length + 1);
+
+ if (flags & F_SUM) {
+ snprintf(xtra, xsz, " Type=%s", svc);
+ return;
+ }
+ if ((flags & F_DTAIL) == 0)
+ return;
+
+ show_printf("WordCount = %d", wordcount);
+ show_printf("ChainedCommand = 0x%.2x", andxcmd);
+ show_printf("NextOffset = 0x%.4x", andxoffset);
+ show_printf("OptionalSupport = 0x%.4x", tconflags);
+ show_printf("ByteCount = %d", bytecount);
+ show_printf("ServiceType = %s", svc);
+ GET_STRING(tbuf, tcondata, isunicode);
+ show_printf("NativeFS = %s", tbuf);
+ }
+}
+
+/*
+ * Interpret a "SesssetupX" SMB
+ * [X/Open-SMB, Sec. 11.3]
+ */
+/* ARGSUSED */
+static void
+interpret_sesssetupX(int flags, uchar_t *data, int len, char *xtra, int xsz)
+{
+ int bytecount;
+ int lm_pw_len;
+ int ext_security;
+ int sec_blob_len;
+ int isunicode;
+ int nt_pw_len;
+ int wordcount;
+ int cap;
+ char tbuf[256];
+ struct smb *smbdata;
+ uchar_t *setupdata;
+ ushort_t smb_flags2;
+
+ smbdata = (struct smb *)data;
+ smb_flags2 = get2(smbdata->flags2);
+ setupdata = (uchar_t *)data + sizeof (struct smb);
+ wordcount = *setupdata++;
+
+ isunicode = smb_flags2 & FLAGS2_UNICODE;
+ ext_security = smb_flags2 & FLAGS2_EXT_SEC;
+
+ if ((smbdata->flags & SERVER_RESPONSE) == 0) {
+ /* request summary */
+ if (flags & F_SUM) {
+ if (ext_security) {
+ /* No decoder for SPNEGO */
+ snprintf(xtra, xsz, " (SPNEGO)");
+ return;
+ }
+ if (wordcount != 13)
+ return;
+ setupdata += 14;
+ lm_pw_len = get2(setupdata);
+ setupdata += 2;
+ nt_pw_len = get2(setupdata);
+ setupdata += 6;
+ cap = get4(setupdata);
+ setupdata += 6 + lm_pw_len + nt_pw_len;
+
+ GET_STRING(tbuf, setupdata, isunicode);
+ snprintf(xtra, xsz, " Username=%s", tbuf);
+ }
+
+ if ((flags & F_DTAIL) == 0)
+ return;
+
+ /* request detail */
+ show_printf("WordCount = %d", wordcount);
+ if (wordcount < 7)
+ return;
+ /* words 0 - 6 */
+ show_printf("ChainedCommand = 0x%.2x", setupdata[0]);
+ setupdata += 2;
+ show_printf("NextOffset = 0x%.4x", get2(setupdata));
+ setupdata += 2;
+ show_printf("MaxBufferSize = %d", get2(setupdata));
+ setupdata += 2;
+ show_printf("MaxMPXRequests = %d", get2(setupdata));
+ setupdata += 2;
+ show_printf("VCNumber = %d", get2(setupdata));
+ setupdata += 2;
+ show_printf("SessionKey = 0x%.8x", get4(setupdata));
+ setupdata += 4;
+
+ if (ext_security) {
+ if (wordcount != 12)
+ return;
+ /* word 7 */
+ sec_blob_len = get2(setupdata);
+ setupdata += 2;
+ show_printf("Sec. blob len = %d", sec_blob_len);
+ /* words 8, 9 (reserved) */
+ setupdata += 4;
+ } else {
+ if (wordcount != 13)
+ return;
+ /* word 7 */
+ lm_pw_len = get2(setupdata);
+ setupdata += 2;
+ show_printf("LM_Hash_Len = %d", lm_pw_len);
+ /* word 8 */
+ nt_pw_len = get2(setupdata);
+ setupdata += 2;
+ show_printf("NT_Hash_Len = %d", nt_pw_len);
+ /* words 9, 10 (reserved) */
+ setupdata += 4;
+ }
+
+ cap = get4(setupdata);
+ show_printf("Capabilities = 0x%.8x", cap);
+ setupdata += 4;
+
+ bytecount = get2(setupdata);
+ setupdata += 2;
+ show_printf("ByteCount = %d", bytecount);
+
+ if (ext_security) {
+ /* No decoder for SPNEGO. Just dump hex. */
+ show_printf("Security blob: (SPNEGO)");
+ output_bytes(setupdata, sec_blob_len);
+ setupdata += sec_blob_len;
+ } else {
+ /* Dump password hashes */
+ if (lm_pw_len > 0) {
+ show_printf("LM Hash (%d bytes)", lm_pw_len);
+ output_bytes(setupdata, lm_pw_len);
+ setupdata += lm_pw_len;
+ }
+ if (nt_pw_len > 0) {
+ show_printf("NT Hash (%d bytes)", nt_pw_len);
+ output_bytes(setupdata, nt_pw_len);
+ setupdata += nt_pw_len;
+ }
+
+ /* User */
+ GET_STRING(tbuf, setupdata, isunicode);
+ show_printf("AccountName = %s", tbuf);
+
+ /* Domain */
+ GET_STRING(tbuf, setupdata, isunicode);
+ show_printf("DomainName = %s", tbuf);
+ }
+
+ /*
+ * Remainder is the same for etc. sec. or not
+ * Native OS, Native LanMan
+ */
+ GET_STRING(tbuf, setupdata, isunicode);
+ show_printf("NativeOS = %s", tbuf);
+
+ GET_STRING(tbuf, setupdata, isunicode);
+ show_printf("NativeLanman = %s", tbuf);
+ } else {
+ /* response summary */
+ if (flags & F_SUM) {
+ if (ext_security) {
+ /* No decoder for SPNEGO */
+ snprintf(xtra, xsz, " (SPNEGO)");
+ }
+ return;
+ }
+
+ if ((flags & F_DTAIL) == 0)
+ return;
+
+ /* response detail */
+ show_printf("WordCount = %d", wordcount);
+ if (wordcount < 3)
+ return;
+
+ show_printf("ChainedCommand = 0x%.2x", setupdata[0]);
+ setupdata += 2;
+ show_printf("NextOffset = 0x%.4x", get2(setupdata));
+ setupdata += 2;
+ show_printf("SetupAction = 0x%.4x", get2(setupdata));
+ setupdata += 2;
+
+ if (ext_security) {
+ if (wordcount != 4)
+ return;
+ sec_blob_len = get2(setupdata);
+ setupdata += 2;
+ show_printf("Sec. blob len = %d", sec_blob_len);
+ } else {
+ if (wordcount != 3)
+ return;
+ }
+
+ bytecount = get2(setupdata);
+ setupdata += 2;
+ show_printf("ByteCount = %d", bytecount);
+
+ if (ext_security) {
+ /* No decoder for SPNEGO. Just dump hex. */
+ show_line("Security blob: (SPNEGO)");
+ output_bytes(setupdata, sec_blob_len);
+ setupdata += sec_blob_len;
+ }
+
+ /*
+ * Native OS, Native LanMan
+ */
+ GET_STRING(tbuf, setupdata, isunicode);
+ show_printf("NativeOS = %s", tbuf);
+
+ GET_STRING(tbuf, setupdata, isunicode);
+ show_printf("NativeLanman = %s", tbuf);
+
+ if (ext_security == 0) {
+ GET_STRING(tbuf, setupdata, isunicode);
+ show_printf("DomainName = %s", tbuf);
+ }
+ }
+}
+
+/*
+ * Interpret "Trans2" SMB
+ * [X/Open-SMB, Sec. 16]
+ *
+ * This is very much like "trans" above.
+ */
+/* ARGSUSED */
+static void
+interpret_trans2(int flags, uchar_t *data, int len, char *xtra, int xsz)
+{
+ struct smb *smb;
+ uchar_t *vwv; /* word parameters */
+ int wordcount;
+ uchar_t *byteparms;
+ int bytecount;
+ int parambytes;
+ int paramoffset;
+ int setupcount;
+ int subcode;
+ uchar_t *setupdata;
+ uchar_t *params;
+ char *name;
+
+ smb = (struct smb *)data;
+ vwv = (uchar_t *)data + sizeof (struct smb);
+ wordcount = *vwv++;
+
+ byteparms = vwv + (2 * wordcount);
+ bytecount = get2(byteparms);
+ byteparms += 2;
+
+ /*
+ * Print the lengths before we (potentially) bail out
+ * due to lack of data (so the user knows why we did).
+ */
+ if (flags & F_DTAIL) {
+ show_printf("WordCount = %d", wordcount);
+ show_printf("ByteCount = %d", bytecount);
+ }
+
+ /* Get length and location of params and setup data. */
+ if (!(smb->flags & SERVER_RESPONSE)) {
+ /* CALL */
+ if (wordcount < 14)
+ return;
+ parambytes = get2(vwv + (2 * 9));
+ paramoffset = get2(vwv + (2 * 10));
+ setupcount = *(vwv + (2 * 13));
+ setupdata = vwv + (2 * 14);
+ } else {
+ /* REPLY */
+ if (wordcount < 10)
+ return;
+ parambytes = get2(vwv + (2 * 3));
+ paramoffset = get2(vwv + (2 * 4));
+ setupcount = *(vwv + (2 * 9));
+ setupdata = vwv + (2 * 10);
+ }
+ if (setupcount > 0)
+ subcode = get2(setupdata);
+ else
+ subcode = -1; /* invalid */
+
+ /* The parameters are offset from the SMB header. */
+ params = data + paramoffset;
+
+ if (flags & F_DTAIL && !(smb->flags & SERVER_RESPONSE)) {
+ /* This is a CALL. */
+ /* print the word parameters */
+ show_printf("TotalParamBytes = %d", get2(vwv));
+ show_printf("TotalDataBytes = %d", get2(vwv+2));
+ show_printf("MaxParamBytes = %d", get2(vwv+4));
+ show_printf("MaxDataBytes = %d", get2(vwv+6));
+ show_printf("MaxSetupWords = %d", vwv[8]);
+ show_printf("TransFlags = 0x%.4x", get2(vwv+10));
+ show_printf("Timeout = 0x%.8x", get4(vwv+12));
+ /* skip Reserved2 */
+ show_printf("ParamBytes = 0x%.4x", parambytes);
+ show_printf("ParamOffset = 0x%.4x", paramoffset);
+ show_printf("DataBytes = 0x%.4x", get2(vwv+22));
+ show_printf("DataOffset = 0x%.4x", get2(vwv+24));
+ show_printf("SetupWords = %d", setupcount);
+
+ /* That finishes the VWV, now the misc. stuff. */
+ show_printf("FunctionCode = %d", subcode);
+ }
+
+ if (!(smb->flags & SERVER_RESPONSE)) {
+ /* This is a CALL. Do sub-function. */
+ switch (subcode) {
+ case TRANS2_OPEN:
+ name = "Open";
+ goto name_only;
+ case TRANS2_FIND_FIRST:
+ output_trans2_findfirst(flags, params, xtra, xsz);
+ break;
+ case TRANS2_FIND_NEXT2:
+ output_trans2_findnext(flags, params, xtra, xsz);
+ break;
+ case TRANS2_QUERY_FS_INFORMATION:
+ name = "QueryFSInfo";
+ goto name_only;
+ case TRANS2_QUERY_PATH_INFORMATION:
+ output_trans2_querypath(flags, params, xtra, xsz);
+ break;
+ case TRANS2_SET_PATH_INFORMATION:
+ name = "SetPathInfo";
+ goto name_only;
+ case TRANS2_QUERY_FILE_INFORMATION:
+ output_trans2_queryfile(flags, params, xtra, xsz);
+ break;
+ case TRANS2_SET_FILE_INFORMATION:
+ output_trans2_setfile(flags, params, xtra, xsz);
+ break;
+ case TRANS2_CREATE_DIRECTORY:
+ name = "CreateDir";
+ goto name_only;
+
+ default:
+ name = "Unknown";
+ /* fall through */
+ name_only:
+ if (flags & F_SUM)
+ snprintf(xtra, xsz, " %s", name);
+ if (flags & F_DTAIL)
+ show_printf("FunctionName = %s", name);
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL && smb->flags & SERVER_RESPONSE) {
+ /* This is a REPLY. */
+ /* print the word parameters */
+ show_printf("TotalParamBytes = %d", get2(vwv));
+ show_printf("TotalDataBytes = %d", get2(vwv+2));
+ /* skip Reserved */
+ show_printf("ParamBytes = 0x%.4x", parambytes);
+ show_printf("ParamOffset = 0x%.4x", paramoffset);
+ show_printf("ParamDispl. = 0x%.4x", get2(vwv+10));
+ show_printf("DataBytes = 0x%.4x", get2(vwv+12));
+ show_printf("DataOffset = 0x%.4x", get2(vwv+14));
+ show_printf("DataDispl. = 0x%.4x", get2(vwv+16));
+ show_printf("SetupWords = %d", setupcount);
+
+ output_bytes(byteparms, bytecount);
+ }
+}
+
+
+static void
+interpret_default(int flags, uchar_t *data, int len, char *xtra, int xsz)
+{
+ int slength;
+ int i, tl;
+ int isunicode;
+ int printit;
+ int wordcount;
+ int outsz;
+ char *outstr;
+ char *format;
+ char valuetype;
+ char word[10];
+ char *label;
+ char tempstr[256];
+ uchar_t *comdata, *limit;
+ char buff[80];
+ struct smb *smbdata;
+ struct decode *decoder;
+ uchar_t bval;
+ ushort_t wval;
+ ushort_t smb_flags2;
+ uint_t lval;
+
+ smbdata = (struct smb *)data;
+ smb_flags2 = get2(smbdata->flags2);
+ comdata = (uchar_t *)data + sizeof (struct smb);
+ wordcount = *comdata++;
+ limit = data + len;
+
+ isunicode = smb_flags2 & FLAGS2_UNICODE;
+ decoder = &SMBtable[smbdata->com & 255];
+
+ if (smbdata->flags & SERVER_RESPONSE)
+ format = decoder->replyfmt;
+ else
+ format = decoder->callfmt;
+
+ if (!format || strlen(format) == 0) {
+ if (flags & F_SUM)
+ return;
+ show_printf("WordCount = %d", wordcount);
+ if (wordcount == 0)
+ return;
+ show_line("Word values (in hex):");
+ buff[0] = '\0';
+ for (i = 0; i < wordcount; i++) {
+ snprintf(word, sizeof (word), "%.4x ", get2(comdata));
+ comdata += 2;
+ if (comdata >= limit)
+ wordcount = i+1; /* terminate */
+ (void) strlcat(buff, word, sizeof (buff));
+ if (((i+1) & 7) == 0 || i == (wordcount-1)) {
+ show_line(buff);
+ strcpy(buff, "");
+ }
+ }
+ return;
+ }
+
+ if (flags & F_DTAIL)
+ show_printf("WordCount = %d", wordcount);
+
+ outstr = xtra;
+ outsz = xsz;
+
+ valuetype = format[0];
+ while (valuetype != '\0') {
+ if (comdata >= limit)
+ break;
+ label = format+1;
+ printit = (flags & F_DTAIL) || (valuetype <= 'Z');
+
+ switch (valuetype) {
+ case 'W':
+ case 'w':
+ wval = get2(comdata);
+ comdata += 2;
+ if (!printit)
+ break;
+ if (flags & F_DTAIL)
+ show_printf(
+ "%s = 0x%.4x", label, wval);
+ else {
+ tl = snprintf(outstr, outsz,
+ " %s=0x%x", label, wval);
+ outstr += tl;
+ outsz -= tl;
+ }
+ break;
+
+ case 'D':
+ case 'd':
+ wval = get2(comdata);
+ comdata += 2;
+ if (!printit)
+ break;
+ if (flags & F_DTAIL)
+ show_printf(
+ "%s = %d", label, wval);
+ else {
+ tl = snprintf(outstr, outsz,
+ " %s=%d", label, wval);
+ outstr += tl;
+ outsz -= tl;
+ }
+ break;
+
+ case 'L':
+ case 'l':
+ lval = get4(comdata);
+ comdata += 4;
+ if (!printit)
+ break;
+ if (flags & F_DTAIL)
+ show_printf(
+ "%s = 0x%.8x", label, lval);
+ else {
+ tl = snprintf(outstr, outsz,
+ " %s=0x%x", label, lval);
+ outstr += tl;
+ outsz -= tl;
+ }
+ break;
+
+ case 'B':
+ case 'b':
+ bval = comdata[0];
+ comdata += 1;
+ if (!printit)
+ break;
+ if (flags & F_DTAIL)
+ show_printf(
+ "%s = 0x%.2x", label, bval);
+ else {
+ tl = snprintf(outstr, outsz,
+ " %s=0x%x", label, bval);
+ outstr += tl;
+ outsz -= tl;
+ }
+ break;
+
+ case 'r':
+ comdata++;
+ break;
+
+ case 'R':
+ comdata += 2;
+ break;
+
+ case 'U':
+ case 'u':
+ /* Unicode or ASCII string. */
+ GET_STRING(tempstr, comdata, isunicode);
+ if (!printit)
+ break;
+ if (flags & F_DTAIL)
+ show_printf(
+ "%s = %s", label, tempstr);
+ else {
+ tl = snprintf(outstr, outsz,
+ " %s=%s", label, tempstr);
+ outstr += tl;
+ outsz -= tl;
+ }
+ break;
+
+ case 'S':
+ case 's':
+ slength = strlcpy(tempstr, (char *)comdata,
+ sizeof (tempstr));
+ comdata += (slength+1);
+ if (!printit)
+ break;
+ if (flags & F_DTAIL)
+ show_printf(
+ "%s = %s", label, tempstr);
+ else {
+ tl = snprintf(outstr, outsz,
+ " %s=%s", label, tempstr);
+ outstr += tl;
+ outsz -= tl;
+ }
+ break;
+ }
+ format += (strlen(format) + 1);
+ valuetype = format[0];
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_socks.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_socks.c
new file mode 100644
index 0000000..59cd38e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_socks.c
@@ -0,0 +1,472 @@
+/*
+ * 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 (c) 1998-1999,2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include "snoop.h"
+
+static void put_method(char *cp, int method);
+static void put_socks5_addr(char *cp, const unsigned char *buf, int fraglen);
+static void put_socks4_res(char *cp, int code);
+static void put_socks5_res(char *cp, int code);
+
+int
+interpret_socks_call(flags, line, fraglen)
+ int flags;
+ char *line;
+ int fraglen;
+{
+ unsigned char *buf = (unsigned char *)line;
+ char *cp;
+ struct in_addr ipaddr;
+ int i, n;
+
+ if (flags & F_SUM) {
+ cp = get_sum_line();
+ if (fraglen >= 2) {
+ switch (buf[0]) {
+ case 4: /* SOCKS4 */
+ n = buf[1];
+ switch (n) {
+ case 1:
+ case 2:
+ if (fraglen >= 8) {
+ (void) memcpy(&ipaddr, &buf[4],
+ sizeof (ipaddr));
+ (void) sprintf(cp,
+ "SOCKS4 %s %s:%u",
+ addrtoname(AF_INET, &ipaddr),
+ (n == 1)? "CONNECT": "BIND",
+ (buf[2] << 8) | buf[3]);
+ cp += strlen(cp);
+ if (fraglen > 8) {
+ (void) sprintf(cp, " User=");
+ cp += strlen(cp);
+ for (i = 8;
+ i < 40 && i < fraglen;
+ ++i) {
+ if (buf[i] == '\0')
+ break;
+ *cp++ = buf[i];
+ }
+ if (i == 40) {
+ *cp++ = '.';
+ *cp++ = '.';
+ *cp++ = '.';
+ }
+ *cp = '\0';
+ }
+ }
+ break;
+ default:
+ (void) sprintf(cp, "SOCKS4 OP=%u", n);
+ }
+ break;
+ case 5: /* SOCKS5 */
+ n = buf[1];
+ if (2 + n == fraglen) {
+ (void) sprintf(cp,
+ "SOCKS5 CONTACT NMETHODS=%d:", n);
+ cp += strlen(cp);
+ for (i = 0; i < n && 2 + i < fraglen; ++i) {
+ put_method(cp, buf[2 + i]);
+ cp += strlen(cp);
+ }
+ } else if (fraglen >= 6 && buf[2] == 0) {
+ const char *cmd;
+
+ if (n < 1 || n > 3) {
+ (void) sprintf(cp,
+ "SOCKS (send data): %s",
+ show_string(line, fraglen, 20));
+ } else {
+ switch (n) {
+ case 1:
+ cmd = "CONNECT";
+ break;
+ case 2:
+ cmd = "BIND";
+ break;
+ case 3:
+ cmd = "ASSOCIATE_UDP";
+ break;
+ }
+ (void) sprintf(cp, "SOCKS5 %s ", cmd);
+ cp += strlen(cp);
+ put_socks5_addr(cp, &buf[3],
+ fraglen - 3);
+ }
+ } else {
+ (void) sprintf(cp, "SOCKS (send data): %s",
+ show_string(line, fraglen, 20));
+ }
+ break;
+ default:
+ (void) sprintf(cp, "SOCKS (send data): %s",
+ show_string(line, fraglen, 20));
+ }
+ } else {
+ (void) sprintf(cp, "SOCKS (send data): %s",
+ show_string(line, fraglen, 20));
+ }
+
+ } /* if (flags & F_SUM) */
+
+ if (flags & F_DTAIL) {
+ show_header("SOCKS: ", "SOCKS Header", fraglen);
+ show_space();
+ cp = get_line(0, 0);
+ if (fraglen >= 2) {
+ switch (buf[0]) {
+ case 4:
+ (void) sprintf(cp, "Version = 4");
+ n = buf[1];
+ switch (n) {
+ case 1:
+ case 2:
+ (void) sprintf(get_line(0, 0),
+ "Operation = %s",
+ (n == 1)? "CONNECT": "BIND");
+ if (fraglen >= 8) {
+ (void) memcpy(&ipaddr, &buf[4],
+ sizeof (ipaddr));
+ (void) sprintf(get_line(0, 0),
+ "Destination = %s:%u",
+ addrtoname(AF_INET,
+ &ipaddr),
+ (buf[2] << 8) | buf[3]);
+ if (fraglen > 8) {
+ cp = get_line(0, 0);
+ (void) sprintf(cp,
+ "User = ");
+ cp += strlen(cp);
+ for (i = 8;
+ i < 40; ++i) {
+ if
+ (buf[i] == '\0')
+ break;
+ *cp++ = buf[i];
+ }
+ if (i == 40) {
+ *cp++ = '.';
+ *cp++ = '.';
+ *cp++ = '.';
+ }
+ *cp = '\0';
+ }
+ }
+ break;
+ default:
+ (void) sprintf(get_line(0, 0),
+ "Operation = %u (unknown)", n);
+ }
+ break;
+ case 5: /* SOCKS5 */
+ (void) sprintf(cp, "Version = 5");
+ n = buf[1];
+ if (2 + n == fraglen) {
+ (void) sprintf(get_line(0, 0),
+ "Number of methods = %u", n);
+ for (i = 0;
+ i < n && 2 + i < fraglen; ++i) {
+ cp = get_line(0, 0);
+ (void) sprintf(cp,
+ "Method %3u =", i);
+ cp += strlen(cp);
+ put_method(cp, buf[2 + i]);
+ }
+ } else if (fraglen >= 6 && buf[2] == 0) {
+ const char *cmd;
+ if (n < 1 || n > 3) {
+ (void) sprintf(cp,
+ "SOCKS (send data): %s",
+ show_string(line,
+ fraglen, 20));
+ } else {
+ switch (n) {
+ case 1:
+ cmd = "CONNECT";
+ break;
+ case 2:
+ cmd = "BIND";
+ break;
+ case 3:
+ cmd = "ASSOCIATE_UDP";
+ break;
+ }
+ (void) sprintf(get_line(0, 0),
+ "Operation = %s ", cmd);
+ put_socks5_addr(get_line(0, 0),
+ &buf[3], fraglen - 3);
+ break;
+ }
+ } else
+ (void) sprintf(cp,
+ " SOCKS (send data): %s",
+ show_string(line, fraglen,
+ 20));
+ break;
+ default:
+ (void) sprintf(cp,
+ "SOCKS (send data): %s",
+ show_string(line, fraglen, 20));
+ }
+ show_space();
+ } else
+ (void) sprintf(cp,
+ "SOCKS (send data): %s",
+ show_string(line, fraglen, 20));
+ }
+
+out:
+ return (fraglen);
+}
+
+int
+interpret_socks_reply(flags, line, fraglen)
+ int flags;
+ char *line;
+ int fraglen;
+{
+ unsigned char *buf = (unsigned char *)line;
+ char *cp;
+ struct in_addr ipaddr;
+
+ if (flags & F_SUM) {
+ cp = get_sum_line();
+ if (fraglen >= 2) {
+ switch (buf[0]) {
+ case 0:
+ (void) sprintf(cp, "SOCKS4 ");
+ cp += strlen(cp);
+ if (fraglen >= 8) {
+ (void) memcpy(&ipaddr, &buf[4],
+ sizeof (ipaddr));
+ (void) sprintf(cp, "%s:%u ",
+ addrtoname(AF_INET, &ipaddr),
+ (buf[2] << 8) | buf[3]);
+ cp += strlen(cp);
+ }
+ /* reply version, no SOCKS version in v4 */
+ put_socks4_res(cp, buf[1]);
+ break;
+ case 5:
+ (void) sprintf(cp, "SOCKS5 method accepted:");
+ cp += strlen(cp);
+ put_method(cp, buf[1]);
+ break;
+ default:
+ (void) sprintf(cp, "SOCKS (recv data)");
+ }
+ } else
+ (void) sprintf(cp, "SOCKS (recv data)");
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("SOCKS: ", "SOCKS Header", fraglen);
+ show_space();
+ cp = get_line(0, 0);
+ if (fraglen >= 2) {
+ switch (buf[0]) {
+ case 0:
+ /* reply version, no SOCKS version in v4 */
+ (void) sprintf(cp,
+ "Reply version = 0 (SOCKS version 4)");
+ if (fraglen >= 8) {
+ (void) memcpy(&ipaddr, &buf[4],
+ sizeof (ipaddr));
+ (void) sprintf(get_line(0, 0),
+ "Destination %s:%u ",
+ addrtoname(AF_INET, &ipaddr),
+ (buf[2] << 8) | buf[3]);
+ }
+ cp = get_line(0, 0);
+ (void) sprintf(cp, "Result code = %u ", buf[1]);
+ cp += strlen(cp);
+ put_socks4_res(cp, buf[1]);
+ break;
+ case 5:
+ (void) sprintf(cp, "Reply version = 5");
+ if (fraglen == 2) {
+ cp = get_line(0, 0);
+ (void) sprintf(cp, "Method accepted =");
+ cp += strlen(cp);
+ put_method(cp, buf[1]);
+ } else if (fraglen >= 6 && buf[2] == 0x00) {
+ cp = get_line(0, 0);
+ (void) sprintf(cp, "Status = ");
+ cp += strlen(cp);
+ put_socks5_res(cp, buf[1]);
+ put_socks5_addr(get_line(0, 0),
+ &buf[3], fraglen - 3);
+ }
+ break;
+ default:
+ (void) sprintf(cp, "(recv data)");
+ }
+ } else
+ (void) sprintf(cp, "(recv data)");
+ show_space();
+ }
+
+out:
+ return (fraglen);
+}
+
+static void
+put_method(char *cp, int method)
+{
+ switch (method) {
+ case 0:
+ (void) sprintf(cp, " NOAUTH");
+ break;
+ case 1:
+ (void) sprintf(cp, " GSSAPI");
+ break;
+ case 2:
+ (void) sprintf(cp, " USERNAME/PASSWD");
+ break;
+ case 255:
+ (void) sprintf(cp, " NONE");
+ break;
+ default:
+ (void) sprintf(cp, " 0x%02x (unknown)", method);
+ }
+}
+
+static void
+put_socks5_addr(char *cp, const unsigned char *buf, int fraglen)
+{
+ struct in_addr ipaddr;
+ int i;
+
+ switch (buf[0]) {
+ case 1:
+ /* IPv4 */
+ (void) sprintf(cp, "Address = ");
+ cp += strlen(cp);
+ if (1 + 4 + 2 <= fraglen) {
+ (void) memcpy(&ipaddr, &buf[1], sizeof (ipaddr));
+ (void) sprintf(cp, "%s:%u",
+ addrtoname(AF_INET, &ipaddr),
+ (buf[5] << 8) | buf[5 + 1]);
+ } else
+ (void) strcat(cp, "(IPv4)");
+ break;
+ case 3:
+ /* domain name */
+ (void) sprintf(cp, "Domain name = ");
+ cp += strlen(cp);
+ for (i = 0; i <= buf[1] && 1 + i < fraglen; ++i)
+ *cp++ = buf[1 + i];
+ if (1 + i + 2 <= fraglen)
+ (void) sprintf(cp, ":%u",
+ (buf[1 + i] << 8) | buf[1 + i + 1]);
+ else
+ *cp = '\0';
+ break;
+ case 4:
+ /* IPv6 */
+ (void) sprintf(cp, "Address = ");
+ if (1 + 16 <= fraglen) {
+ for (i = 0; i < 16; ++i) {
+ if (i > 0)
+ *cp++ = '.';
+ (void) sprintf(cp, "%u", buf[1 + i]);
+ cp += strlen(cp);
+ }
+ if (1 + 16 + 2 <= fraglen) {
+ (void) sprintf(cp, ":%u",
+ (buf[1 + 16] << 8) | buf[1 + 16 + 1]);
+ }
+ } else
+ (void) strcat(cp, "(IPv6)");
+ break;
+ default:
+ (void) sprintf(cp, "Address type = 0x%02x (unknown)", buf[0]);
+ }
+}
+
+static void
+put_socks4_res(char *cp, int code)
+{
+ switch (code) {
+ case 90:
+ (void) sprintf(cp, "request granted");
+ break;
+ case 91:
+ (void) sprintf(cp, "request rejected or failed");
+ break;
+ case 92:
+ (void) sprintf(cp, "socksd can't connect to client's identd");
+ break;
+ case 93:
+ (void) sprintf(cp, "identity mismatch");
+ break;
+ default:
+ (void) sprintf(cp, "0x%02x (unknown)", code);
+ }
+}
+
+static void
+put_socks5_res(char *cp, int code)
+{
+ switch (code) {
+ case 0:
+ (void) strcpy(cp, "succeeded");
+ break;
+ case 1:
+ (void) strcpy(cp, "general SOCKS server failure");
+ break;
+ case 2:
+ (void) strcpy(cp, "connection not allowed by ruleset");
+ break;
+ case 3:
+ (void) strcpy(cp, "network unreachable");
+ break;
+ case 4:
+ (void) strcpy(cp, "host unreachable");
+ break;
+ case 5:
+ (void) strcpy(cp, "connection refused");
+ break;
+ case 6:
+ (void) strcpy(cp, "TTL expired");
+ break;
+ case 7:
+ (void) strcpy(cp, "command not supported");
+ break;
+ case 8:
+ (void) strcpy(cp, "address type not supported");
+ break;
+ default:
+ (void) sprintf(cp, "code 0x%02x", code);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_solarnet.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_solarnet.c
new file mode 100644
index 0000000..9e2e74b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_solarnet.c
@@ -0,0 +1,299 @@
+/*
+ * 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 (c) 1993, 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%W% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <setjmp.h>
+#include <limits.h>
+#include <netinet/in.h>
+#include <string.h>
+
+#include <rpc/types.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/clnt.h>
+#include <rpc/rpc_msg.h>
+#include <fw.h>
+#include <fw_rpc.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+extern jmp_buf xdr_err;
+
+static char *procnames_short[] = {
+ "Null", /* 0 */
+ "INVOKE", /* 1 */
+ "MORE", /* 2 */
+ "KILL", /* 3 */
+};
+
+static char *procnames_long[] = {
+ "Null procedure", /* 0 */
+ "Invoke operation", /* 1 */
+ "More data", /* 2 */
+ "Kill operation", /* 3 */
+};
+
+#define MAXPROC 3
+
+enum Rec_type {
+ REC_TYPE_NORM = 0,
+ REC_TYPE_EOR = 1,
+ REC_TYPE_EOF = 2
+};
+typedef enum Rec_type Rec_type;
+
+void
+interpret_solarnet_fw(
+ int flags,
+ int type,
+ int xid,
+ int vers,
+ int proc,
+ char *data,
+ int len)
+{
+ char *line;
+ char buff[CTXTLEN + 1];
+ ulong_t thresh;
+ char op[CTXTLEN + 1];
+ bool_t b;
+ Fw_err e;
+ Rec_type rt;
+ int new_row = 1, row = 0;
+
+ if (proc < 0 || proc > MAXPROC)
+ return;
+
+ if (flags & F_SUM) {
+ if (setjmp(xdr_err)) {
+ return;
+ }
+
+ line = get_sum_line();
+
+ if (type == CALL) {
+ (void) sprintf(line,
+ "SOLARNET C %s",
+ procnames_short[proc]);
+ line += strlen(line);
+
+ switch (proc) {
+ case FW_INVOKE:
+ (void) sprintf(line, " %s",
+ getxdr_string(buff, CTXTLEN));
+ line += strlen(line);
+ (void) sprintf(line, "/%s",
+ getxdr_string(buff, CTXTLEN));
+ line += strlen(line);
+ getxdr_string(buff, CTXTLEN);
+ if (strlen(buff) != 0) {
+ (void) sprintf(line, ".%s", buff);
+ line += strlen(line);
+ }
+ (void) getxdr_string(buff, CTXTLEN);
+ thresh = getxdr_u_long();
+ if (thresh == ULONG_MAX)
+ (void) sprintf(line, " (all)");
+ else
+ (void) sprintf(line, " %lu", thresh);
+ line += strlen(line);
+ (void) getxdr_context(buff, CTXTLEN);
+ break;
+ case FW_MORE:
+ (void) getxdr_context(buff, CTXTLEN);
+ sscanf(buff, "%*s %*s %s.%*s", op);
+ op[strlen(op)-1] = '\0';
+ (void) sprintf(line, " %s", op);
+ line += strlen(line);
+ thresh = getxdr_u_long();
+ if (thresh == ULONG_MAX)
+ (void) sprintf(line, " (all)");
+ else
+ (void) sprintf(line, " %lu", thresh);
+ line += strlen(line);
+ break;
+ case FW_KILL:
+ (void) getxdr_context(buff, CTXTLEN);
+ sscanf(buff, "%*s %*s %s.%*s", op);
+ op[strlen(op)-1] = '\0';
+ (void) sprintf(line, " %s", op);
+ line += strlen(line);
+ break;
+ default:
+ break;
+ }
+
+ check_retransmit(line, xid);
+ } else {
+ (void) sprintf(line, "SOLARNET R %s",
+ procnames_short[proc]);
+ line += strlen(line);
+ b = getxdr_bool();
+ if (b) {
+ e = getxdr_enum();
+ if (e == FW_ERR_FW)
+ sprintf(line, " FW");
+ else if (e == FW_ERR_OP)
+ sprintf(line, " OP");
+ else
+ sprintf(line, " NOERR");
+ line += strlen(line);
+ if (e != FW_ERR_NONE) {
+ sprintf(line, " %lu", getxdr_u_long());
+ line += strlen(line);
+ (void) getxdr_bool();
+ sprintf(line, " %s",
+ getxdr_string(buff, CTXTLEN));
+ line += strlen(line);
+ }
+ } else {
+ sprintf(line, " Success");
+ line += strlen(line);
+ }
+ b = getxdr_bool();
+ if (b) {
+ sprintf(line, " %lu rows", getxdr_u_long());
+ line += strlen(line);
+ } else {
+ sprintf(line, " (No output)");
+ line += strlen(line);
+ }
+ }
+ }
+
+ if ((flags & F_DTAIL)) {
+ show_header("SOLARNET: ", "Solarnet Administration Service",
+ len);
+ show_space();
+ if (setjmp(xdr_err)) {
+ return;
+ }
+ (void) sprintf(get_line(0, 0), "Proc = %d (%s)", proc,
+ procnames_long[proc]);
+ if (type == CALL) {
+ switch (proc) {
+ case FW_INVOKE:
+ (void) showxdr_string(CTXTLEN, "Category: %s");
+ (void) showxdr_string(CTXTLEN, "Operation: %s");
+ (void) showxdr_string(CTXTLEN, "Version: %s");
+ (void) showxdr_string(CTXTLEN, "Locale: %s");
+ (void) showxdr_u_long("Threshold: %lu rows");
+ (void) showxdr_context("Context: %s");
+ b = getxdr_bool();
+ if (!b) {
+ sprintf(get_line(0, 0),
+ "No input arguments");
+ break;
+ }
+ thresh = showxdr_u_long("Input rows = %lu");
+ (void) getxdr_bool();
+ do {
+ rt = getxdr_enum();
+ if (rt == REC_TYPE_NORM) {
+ if (new_row) {
+ sprintf(get_line(0, 0),
+ "Row %d", ++row);
+ new_row = 0;
+ }
+ (void) getxdr_string(buff,
+ CTXTLEN);
+ (void) getxdr_string(op,
+ CTXTLEN);
+ sprintf(get_line(0, 0),
+ "\t%s = %s", buff, op);
+ } else if (rt == REC_TYPE_EOR) {
+ new_row = 1;
+ }
+ } while (rt != REC_TYPE_EOF);
+ break;
+ case FW_MORE:
+ (void) showxdr_context("Context: %s");
+ (void) showxdr_u_long("Threshold: %lu rows");
+ break;
+ case FW_KILL:
+ (void) showxdr_context("Context: %s");
+ break;
+ default:
+ break;
+ }
+ } else {
+ b = getxdr_bool();
+ if (b) {
+ e = getxdr_enum();
+ if (e == FW_ERR_FW) {
+ showxdr_u_long(
+ "Framework error code %lu");
+ } else if (e == FW_ERR_OP) {
+ showxdr_u_long(
+ "Operation error code %lu");
+ } else {
+ showxdr_u_long("No error %*lu");
+ }
+ (void) getxdr_bool();
+ (void) getxdr_string(buff, CTXTLEN);
+ if (e != FW_ERR_NONE) {
+ sprintf(get_line(0, 0),
+ "Error message: %s", buff);
+ } else {
+ }
+ } else {
+ sprintf(get_line(0, 0),
+ "Operation was successful");
+ }
+ b = getxdr_bool();
+ if (b) {
+ showxdr_u_long("Output rows: %lu");
+ (void) getxdr_bool();
+ do {
+ rt = getxdr_enum();
+ if (rt == REC_TYPE_NORM) {
+ if (new_row) {
+ sprintf(get_line(0, 0),
+ "Row %d", ++row);
+ new_row = 0;
+ }
+ (void) getxdr_string(buff,
+ CTXTLEN);
+ (void) getxdr_string(op,
+ CTXTLEN);
+ sprintf(get_line(0, 0),
+ "\t%s = %s", buff, op);
+ } else if (rt == REC_TYPE_EOR) {
+ new_row = 1;
+ }
+ } while (rt != REC_TYPE_EOF);
+ } else {
+ sprintf(get_line(0, 0), "No output");
+ }
+ }
+ show_trailer();
+ }
+
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c
new file mode 100644
index 0000000..b0e918f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tcp.c
@@ -0,0 +1,435 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <netinet/tcp.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+
+#define TCPOPT_HEADER_LEN 2
+#define TCPOPT_TSTAMP_LEN 10
+#define TCPOPT_SACK_LEN 8
+
+/*
+ * Convert a network byte order 32 bit integer to a host order integer.
+ * ntohl() cannot be used because option values may not be aligned properly.
+ */
+#define GET_UINT32(opt) (((uint_t)*((uchar_t *)(opt) + 0) << 24) | \
+ ((uint_t)*((uchar_t *)(opt) + 1) << 16) | \
+ ((uint_t)*((uchar_t *)(opt) + 2) << 8) | \
+ ((uint_t)*((uchar_t *)(opt) + 3)))
+
+static void print_tcpoptions_summary(uchar_t *, int, char *);
+static void print_tcpoptions(uchar_t *, int);
+
+static const struct {
+ unsigned int tf_flag;
+ const char *tf_name;
+} tcp_flags[] = {
+ { TH_SYN, "Syn" },
+ { TH_FIN, "Fin" },
+ { TH_RST, "Rst" },
+ { TH_PUSH, "Push" },
+ { TH_ECE, "ECE" },
+ { TH_CWR, "CWR" },
+ { 0, NULL }
+};
+
+int
+interpret_tcp(int flags, struct tcphdr *tcp, int iplen, int fraglen)
+{
+ char *data;
+ int hdrlen, tcplen;
+ int sunrpc = 0;
+ char *pname;
+ char buff[32];
+ char *line, *endline;
+ unsigned int i;
+
+ hdrlen = tcp->th_off * 4;
+ data = (char *)tcp + hdrlen;
+ tcplen = iplen - hdrlen;
+ fraglen -= hdrlen;
+ if (fraglen < 0)
+ return (fraglen + hdrlen); /* incomplete header */
+ if (fraglen > tcplen)
+ fraglen = tcplen;
+
+ if (flags & F_SUM) {
+ line = get_sum_line();
+ endline = line + MAXLINE;
+ (void) snprintf(line, endline - line, "TCP D=%d S=%d",
+ ntohs(tcp->th_dport), ntohs(tcp->th_sport));
+ line += strlen(line);
+
+ for (i = 0; tcp_flags[i].tf_name != NULL; i++) {
+ if (tcp->th_flags & tcp_flags[i].tf_flag) {
+ (void) snprintf(line, endline - line, " %s",
+ tcp_flags[i].tf_name);
+ line += strlen(line);
+ }
+ }
+
+ if (tcp->th_flags & TH_URG) {
+ (void) snprintf(line, endline - line, " Urg=%u",
+ ntohs(tcp->th_urp));
+ line += strlen(line);
+ }
+ if (tcp->th_flags & TH_ACK) {
+ (void) snprintf(line, endline - line, " Ack=%u",
+ ntohl(tcp->th_ack));
+ line += strlen(line);
+ }
+ if (ntohl(tcp->th_seq)) {
+ (void) snprintf(line, endline - line, " Seq=%u Len=%d",
+ ntohl(tcp->th_seq), tcplen);
+ line += strlen(line);
+ }
+ (void) snprintf(line, endline - line, " Win=%d",
+ ntohs(tcp->th_win));
+ print_tcpoptions_summary((uchar_t *)(tcp + 1),
+ (int)(tcp->th_off * 4 - sizeof (struct tcphdr)), line);
+ }
+
+ sunrpc = !reservedport(IPPROTO_TCP, ntohs(tcp->th_dport)) &&
+ !reservedport(IPPROTO_TCP, ntohs(tcp->th_sport)) &&
+ valid_rpc(data + 4, fraglen - 4);
+
+ if (flags & F_DTAIL) {
+
+ show_header("TCP: ", "TCP Header", tcplen);
+ show_space();
+ (void) sprintf(get_line((char *)(uintptr_t)tcp->th_sport -
+ dlc_header, 2), "Source port = %d", ntohs(tcp->th_sport));
+
+ if (sunrpc) {
+ pname = "(Sun RPC)";
+ } else {
+ pname = getportname(IPPROTO_TCP, ntohs(tcp->th_dport));
+ if (pname == NULL) {
+ pname = "";
+ } else {
+ (void) sprintf(buff, "(%s)", pname);
+ pname = buff;
+ }
+ }
+ (void) sprintf(get_line((char *)(uintptr_t)tcp->th_dport -
+ dlc_header, 2), "Destination port = %d %s",
+ ntohs(tcp->th_dport), pname);
+ (void) sprintf(get_line((char *)(uintptr_t)tcp->th_seq -
+ dlc_header, 4), "Sequence number = %u",
+ ntohl(tcp->th_seq));
+ (void) sprintf(get_line((char *)(uintptr_t)tcp->th_ack - dlc_header, 4),
+ "Acknowledgement number = %u",
+ ntohl(tcp->th_ack));
+ (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_ack - dlc_header) +
+ 4, 1), "Data offset = %d bytes", tcp->th_off * 4);
+ (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
+ dlc_header) + 4, 1), "Flags = 0x%02x", tcp->th_flags);
+ (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
+ dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_CWR,
+ "ECN congestion window reduced",
+ "No ECN congestion window reduced"));
+ (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
+ dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_ECE,
+ "ECN echo", "No ECN echo"));
+ (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
+ dlc_header) + 4, 1), " %s",
+ getflag(tcp->th_flags, TH_URG,
+ "Urgent pointer", "No urgent pointer"));
+ (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
+ dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_ACK,
+ "Acknowledgement", "No acknowledgement"));
+ (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
+ dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_PUSH,
+ "Push", "No push"));
+ (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
+ dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_RST,
+ "Reset", "No reset"));
+ (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
+ dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_SYN,
+ "Syn", "No Syn"));
+ (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
+ dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_FIN,
+ "Fin", "No Fin"));
+ (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_win - dlc_header) +
+ 4, 1), "Window = %d", ntohs(tcp->th_win));
+ /* XXX need to compute checksum and print whether correct */
+ (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_sum - dlc_header) +
+ 4, 1), "Checksum = 0x%04x", ntohs(tcp->th_sum));
+ (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_urp - dlc_header) +
+ 4, 1), "Urgent pointer = %d", ntohs(tcp->th_urp));
+
+ /* Print TCP options - if any */
+
+ print_tcpoptions((uchar_t *)(tcp + 1),
+ tcp->th_off * 4 - sizeof (struct tcphdr));
+
+ show_space();
+ }
+
+ /* go to the next protocol layer */
+
+ if (!interpret_reserved(flags, IPPROTO_TCP,
+ ntohs(tcp->th_sport),
+ ntohs(tcp->th_dport),
+ data, fraglen)) {
+ if (sunrpc && fraglen > 0)
+ interpret_rpc(flags, data, fraglen, IPPROTO_TCP);
+ }
+
+ return (tcplen);
+}
+
+static void
+print_tcpoptions(opt, optlen)
+ uchar_t *opt;
+ int optlen;
+{
+ int len;
+ char *line;
+ uchar_t *sack_opt;
+ uchar_t *end_opt;
+ int sack_len;
+
+ if (optlen <= 0) {
+ (void) sprintf(get_line((char *)&opt - dlc_header, 1),
+ "No options");
+ return;
+ }
+
+ (void) sprintf(get_line((char *)&opt - dlc_header, 1),
+ "Options: (%d bytes)", optlen);
+
+ while (optlen > 0) {
+ line = get_line((char *)&opt - dlc_header, 1);
+ len = opt[1];
+ switch (opt[0]) {
+ case TCPOPT_EOL:
+ (void) strcpy(line, " - End of option list");
+ return;
+ case TCPOPT_NOP:
+ (void) strcpy(line, " - No operation");
+ len = 1;
+ break;
+ case TCPOPT_MAXSEG:
+ (void) sprintf(line,
+ " - Maximum segment size = %d bytes",
+ (opt[2] << 8) + opt[3]);
+ break;
+ case TCPOPT_WSCALE:
+ (void) sprintf(line, " - Window scale = %d", opt[2]);
+ break;
+ case TCPOPT_TSTAMP:
+ /* Sanity check. */
+ if (optlen < TCPOPT_TSTAMP_LEN) {
+ (void) sprintf(line,
+ " - Incomplete TS option");
+ } else {
+ (void) sprintf(line,
+ " - TS Val = %u, TS Echo = %u",
+ GET_UINT32(opt + 2),
+ GET_UINT32(opt + 6));
+ }
+ break;
+ case TCPOPT_SACK_PERMITTED:
+ (void) sprintf(line, " - SACK permitted option");
+ break;
+ case TCPOPT_SACK:
+ /*
+ * Sanity check. Total length should be greater
+ * than just the option header length.
+ */
+ if (len <= TCPOPT_HEADER_LEN ||
+ opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) {
+ (void) sprintf(line,
+ " - Incomplete SACK option");
+ break;
+ }
+ sack_len = opt[1] - TCPOPT_HEADER_LEN;
+ sack_opt = opt + TCPOPT_HEADER_LEN;
+ end_opt = opt + optlen;
+
+ (void) sprintf(line, " - SACK blocks:");
+ line = get_line((char *)&opt - dlc_header, 1);
+ (void) sprintf(line, " ");
+ while (sack_len > 0) {
+ char sack_blk[MAXLINE + 1];
+
+ /*
+ * sack_len may not tell us the truth about
+ * the real length... Need to be careful
+ * not to step beyond the option buffer.
+ */
+ if (sack_opt + TCPOPT_SACK_LEN > end_opt) {
+ (void) strcat(line,
+ "...incomplete SACK block");
+ break;
+ }
+ (void) sprintf(sack_blk, "(%u-%u) ",
+ GET_UINT32(sack_opt),
+ GET_UINT32(sack_opt + 4));
+ (void) strcat(line, sack_blk);
+ sack_opt += TCPOPT_SACK_LEN;
+ sack_len -= TCPOPT_SACK_LEN;
+ }
+ break;
+ default:
+ (void) sprintf(line,
+ " - Option %d (unknown - %d bytes) %s",
+ opt[0],
+ len - 2,
+ tohex((char *)&opt[2], len - 2));
+ break;
+ }
+ if (len <= 0) {
+ (void) sprintf(line, " - Incomplete option len %d",
+ len);
+ break;
+ }
+ opt += len;
+ optlen -= len;
+ }
+}
+
+/*
+ * This function is basically the same as print_tcpoptions() except that
+ * all options are printed on the same line.
+ */
+static void
+print_tcpoptions_summary(uchar_t *opt, int optlen, char *line)
+{
+ int len;
+ uchar_t *sack_opt;
+ uchar_t *end_opt;
+ int sack_len;
+ char options[MAXLINE + 1];
+
+ if (optlen <= 0) {
+ return;
+ }
+
+ (void) strcat(line, " Options=<");
+ while (optlen > 0) {
+ len = opt[1];
+ switch (opt[0]) {
+ case TCPOPT_EOL:
+ (void) strcat(line, "eol>");
+ return;
+ case TCPOPT_NOP:
+ (void) strcat(line, "nop");
+ len = 1;
+ break;
+ case TCPOPT_MAXSEG:
+ (void) sprintf(options, "mss %d",
+ (opt[2] << 8) + opt[3]);
+ (void) strcat(line, options);
+ break;
+ case TCPOPT_WSCALE:
+ (void) sprintf(options, "wscale %d", opt[2]);
+ (void) strcat(line, options);
+ break;
+ case TCPOPT_TSTAMP:
+ /* Sanity check. */
+ if (optlen < TCPOPT_TSTAMP_LEN) {
+ (void) strcat(line, "tstamp|");
+ } else {
+ (void) sprintf(options,
+ "tstamp %u %u", GET_UINT32(opt + 2),
+ GET_UINT32(opt + 6));
+ (void) strcat(line, options);
+ }
+ break;
+ case TCPOPT_SACK_PERMITTED:
+ (void) strcat(line, "sackOK");
+ break;
+ case TCPOPT_SACK:
+ /*
+ * Sanity check. Total length should be greater
+ * than just the option header length.
+ */
+ if (len <= TCPOPT_HEADER_LEN ||
+ opt[1] <= TCPOPT_HEADER_LEN || len < opt[1]) {
+ (void) strcat(line, "sack|");
+ break;
+ }
+ sack_len = opt[1] - TCPOPT_HEADER_LEN;
+ sack_opt = opt + TCPOPT_HEADER_LEN;
+ end_opt = opt + optlen;
+
+ (void) strcat(line, "sack");
+ while (sack_len > 0) {
+ /*
+ * sack_len may not tell us the truth about
+ * the real length... Need to be careful
+ * not to step beyond the option buffer.
+ */
+ if (sack_opt + TCPOPT_SACK_LEN > end_opt) {
+ (void) strcat(line, "|");
+ break;
+ }
+ (void) sprintf(options, " %u-%u",
+ GET_UINT32(sack_opt),
+ GET_UINT32(sack_opt + 4));
+ (void) strcat(line, options);
+ sack_opt += TCPOPT_SACK_LEN;
+ sack_len -= TCPOPT_SACK_LEN;
+ }
+ break;
+ default:
+ (void) sprintf(options, "unknown %d", opt[0]);
+ (void) strcat(line, options);
+ break;
+ }
+ if (len <= 0) {
+ (void) sprintf(options, "optlen %d", len);
+ (void) strcat(line, options);
+ break;
+ }
+ opt += len;
+ optlen -= len;
+ if (optlen > 0) {
+ (void) strcat(line, ",");
+ }
+ }
+ (void) strcat(line, ">");
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tftp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tftp.c
new file mode 100644
index 0000000..2196454
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_tftp.c
@@ -0,0 +1,178 @@
+/*
+ * 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 <arpa/tftp.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+char *tftperror();
+char *show_type();
+
+int
+interpret_tftp(int flags, struct tftphdr *tftp, int fraglen)
+{
+ char *name, *mode;
+ extern int src_port, dst_port;
+ int blocksize = fraglen - 4;
+
+ switch (ntohs(tftp->th_opcode)) {
+ case RRQ:
+ case WRQ:
+ add_transient(src_port, interpret_tftp);
+ break;
+ case ERROR:
+ del_transient(src_port);
+ break;
+ }
+
+ if (flags & F_SUM) {
+ switch (ntohs(tftp->th_opcode)) {
+ case RRQ:
+ name = (char *)&tftp->th_stuff;
+ mode = name + (strlen(name) + 1);
+ (void) sprintf(get_sum_line(),
+ "TFTP Read \"%s\" (%s)", name, mode);
+ break;
+ case WRQ:
+ name = (char *)&tftp->th_stuff;
+ mode = name + (strlen(name) + 1);
+ (void) sprintf(get_sum_line(),
+ "TFTP Write \"%s\" (%s)", name, mode);
+ break;
+ case DATA:
+ (void) sprintf(get_sum_line(),
+ "TFTP Data block %d (%d bytes)%s",
+ ntohs(tftp->th_block),
+ blocksize,
+ blocksize < 512 ? " (last block)":"");
+ break;
+ case ACK:
+ (void) sprintf(get_sum_line(),
+ "TFTP Ack block %d",
+ ntohs(tftp->th_block));
+ break;
+ case ERROR:
+ (void) sprintf(get_sum_line(),
+ "TFTP Error: %s",
+ tftperror(ntohs(tftp->th_code)));
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+
+ show_header("TFTP: ", "Trivial File Transfer Protocol", fraglen);
+ show_space();
+ (void) sprintf(get_line((char *)(uintptr_t)tftp->th_opcode -
+ dlc_header, 2),
+ "Opcode = %d (%s)",
+ ntohs(tftp->th_opcode),
+ show_type(ntohs(tftp->th_opcode)));
+
+ switch (ntohs(tftp->th_opcode)) {
+ case RRQ:
+ case WRQ:
+ name = (char *)&tftp->th_stuff;
+ mode = name + (strlen(name) + 1);
+ (void) sprintf(
+ get_line(name - dlc_header, strlen(name) + 1),
+ "File name = \"%s\"",
+ name);
+ (void) sprintf(
+ get_line(mode - dlc_header, strlen(mode) + 1),
+ "Transfer mode = %s",
+ mode);
+ break;
+
+ case DATA:
+ (void) sprintf(
+ get_line((char *)(uintptr_t)tftp->th_block -
+ dlc_header, 2), "Data block = %d%s",
+ ntohs(tftp->th_block),
+ blocksize < 512 ? " (last block)":"");
+ (void) sprintf(get_line((char *)(uintptr_t)tftp->th_data -
+ dlc_header, blocksize),
+ "[ %d bytes of data ]",
+ blocksize);
+ break;
+
+ case ACK:
+ (void) sprintf(get_line((char *)(uintptr_t)tftp->th_block -
+ dlc_header, 2), "Acknowledge block = %d",
+ ntohs(tftp->th_block));
+ break;
+
+ case ERROR:
+ (void) sprintf(get_line((char *)(uintptr_t)tftp->th_code -
+ dlc_header, 2), "Error = %d (%s)",
+ ntohs(tftp->th_code),
+ tftperror(ntohs(tftp->th_code)));
+ (void) sprintf(get_line((char *)(uintptr_t)tftp->th_data -
+ dlc_header, strlen(tftp->th_data) + 1),
+ "Error string = \"%s\"", tftp->th_data);
+ }
+ }
+
+ return (fraglen);
+}
+
+char *
+show_type(t)
+ int t;
+{
+ switch (t) {
+ case RRQ: return ("read request");
+ case WRQ: return ("write request");
+ case DATA: return ("data packet");
+ case ACK: return ("acknowledgement");
+ case ERROR: return ("error");
+ }
+ return ("?");
+}
+
+char *
+tftperror(code)
+ unsigned short code;
+{
+ static char buf[128];
+
+ switch (code) {
+ case EUNDEF: return ("not defined");
+ case ENOTFOUND: return ("file not found");
+ case EACCESS: return ("access violation");
+ case ENOSPACE: return ("disk full or allocation exceeded");
+ case EBADOP: return ("illegal TFTP operation");
+ case EBADID: return ("unknown transfer ID");
+ case EEXISTS: return ("file already exists");
+ case ENOUSER: return ("no such user");
+ }
+ (void) sprintf(buf, "%d", code);
+
+ return (buf);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_trill.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_trill.c
new file mode 100644
index 0000000..51876bc
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_trill.c
@@ -0,0 +1,117 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ethernet.h>
+#include <sys/vlan.h>
+#include <net/trill.h>
+
+#include <snoop.h>
+
+int
+interpret_trill(int flags, struct ether_header **e, char *data, int *alen)
+{
+ trill_header_t *trillhdr;
+ struct ether_header *inner_ethhdr;
+ struct ether_vlan_header *inner_ethvlanhdr;
+ uint16_t ethertype;
+ int dlen = *alen;
+ size_t optslen;
+ size_t trillhdrlen;
+
+ if (dlen < sizeof (trill_header_t)) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "TRILL (short packet)");
+ return (0);
+ }
+
+ trillhdr = (trill_header_t *)data;
+ optslen = GET_TRILL_OPTS_LEN(trillhdr) * sizeof (uint32_t);
+
+ if (flags & F_DTAIL) {
+ show_header("TRILL: ", "TRILL Data Frame", dlen);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Egress nickname = %d",
+ ntohs(trillhdr->th_egressnick));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Ingress nickname = %d",
+ ntohs(trillhdr->th_ingressnick));
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Hop count = %d", trillhdr->th_hopcount);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Multi-destination = %d", trillhdr->th_multidest);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Options Len = %d bytes", optslen);
+ show_trailer();
+ }
+
+ trillhdrlen = sizeof (trill_header_t) + optslen;
+
+ if (dlen < trillhdrlen) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "TRILL (options truncated)");
+ return (0);
+ }
+
+ dlen -= trillhdrlen;
+
+ if (dlen < sizeof (struct ether_header)) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "TRILL (missing required inner MAC)");
+ return (0);
+ }
+
+ inner_ethhdr = (struct ether_header *)(data + trillhdrlen);
+ if (inner_ethhdr->ether_type != htons(ETHERTYPE_VLAN)) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "TRILL (inner VLAN missing; ethertype %X)",
+ ntohs(inner_ethhdr->ether_type));
+ return (0);
+ }
+
+ inner_ethvlanhdr = (struct ether_vlan_header *)inner_ethhdr;
+ ethertype = ntohs(inner_ethvlanhdr->ether_type);
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "TRILL D:%d S:%d HC:%d M:%d O:%d L:%d VLAN:%d %s",
+ ntohs(trillhdr->th_egressnick),
+ ntohs(trillhdr->th_ingressnick),
+ trillhdr->th_hopcount,
+ trillhdr->th_multidest,
+ optslen,
+ dlen, VLAN_ID(inner_ethvlanhdr->ether_tci),
+ print_ethertype(ethertype));
+ }
+
+ *alen = dlen;
+ *e = inner_ethhdr;
+ return (ethertype);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_udp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_udp.c
new file mode 100644
index 0000000..e4ee824
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_udp.c
@@ -0,0 +1,124 @@
+/*
+ * 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 <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <netinet/udp.h>
+#include "snoop.h"
+
+extern char *dlc_header;
+
+int
+interpret_udp(int flags, struct udphdr *udp, int iplen, int fraglen)
+{
+ char *data;
+ int udplen;
+ int sunrpc;
+ char *pname;
+ char buff [32];
+
+ if (fraglen < sizeof (struct udphdr))
+ return (fraglen); /* incomplete header */
+
+ data = (char *)udp + sizeof (struct udphdr);
+ udplen = ntohs((ushort_t)udp->uh_ulen) - sizeof (struct udphdr);
+ fraglen -= sizeof (struct udphdr);
+ if (fraglen > udplen)
+ fraglen = udplen;
+
+ if (flags & F_SUM) {
+ (void) sprintf(get_sum_line(),
+ "UDP D=%d S=%d LEN=%d",
+ ntohs(udp->uh_dport),
+ ntohs(udp->uh_sport),
+ ntohs((ushort_t)udp->uh_ulen));
+ }
+
+ sunrpc = !reservedport(IPPROTO_UDP, ntohs(udp->uh_dport)) &&
+ !reservedport(IPPROTO_UDP, ntohs(udp->uh_sport)) &&
+ valid_rpc(data, udplen);
+
+ if (flags & F_DTAIL) {
+ show_header("UDP: ", "UDP Header", udplen);
+ show_space();
+ (void) sprintf(get_line((char *)(uintptr_t)udp->uh_sport -
+ dlc_header, 1), "Source port = %d", ntohs(udp->uh_sport));
+
+ if (sunrpc) {
+ pname = "(Sun RPC)";
+ } else {
+ pname = getportname(IPPROTO_UDP, ntohs(udp->uh_dport));
+ if (pname == NULL) {
+ pname = "";
+ } else {
+ (void) sprintf(buff, "(%s)", pname);
+ pname = buff;
+ }
+ }
+ (void) sprintf(get_line((char *)(uintptr_t)udp->uh_dport -
+ dlc_header, 1), "Destination port = %d %s",
+ ntohs(udp->uh_dport), pname);
+ (void) sprintf(get_line((char *)(uintptr_t)udp->uh_ulen -
+ dlc_header, 1), "Length = %d %s",
+ ntohs((ushort_t)udp->uh_ulen),
+ udplen > fraglen ?
+ "(Not all data contained in this fragment)"
+ : "");
+ (void) sprintf(get_line((char *)(uintptr_t)udp->uh_sum -
+ dlc_header, 1), "Checksum = %04X %s",
+ ntohs(udp->uh_sum),
+ udp->uh_sum == 0 ? "(no checksum)" : "");
+ show_space();
+ }
+
+
+ /* go to the next protocol layer */
+
+ if (!interpret_reserved(flags, IPPROTO_UDP,
+ ntohs(udp->uh_sport),
+ ntohs(udp->uh_dport),
+ data, fraglen)) {
+ if (fraglen > 0 && sunrpc)
+ interpret_rpc(flags, data, fraglen, IPPROTO_UDP);
+ }
+
+ return (fraglen);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_vlan.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_vlan.h
new file mode 100644
index 0000000..068bcb5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_vlan.h
@@ -0,0 +1,57 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SNOOP_VLAN_H
+#define _SNOOP_VLAN_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/ethernet.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The offset in bytes, in a VLAN tagged packet, from the
+ * ethernet header ethertype (which is ETHERTYPE_VLAN) to
+ * the encapsulated ethertype.
+ */
+#define ENCAP_ETHERTYPE_OFF (offsetof(struct ether_vlan_header, ether_type) -\
+ offsetof(struct ether_vlan_header, ether_tpid))
+
+/*
+ * The offset in bytes, from the beginning of an ethernet header,
+ * to the VLAN ID.
+ */
+#define VLAN_ID_OFFSET (offsetof(struct ether_vlan_header, ether_tci) -\
+ offsetof(struct ether_vlan_header, ether_dhost))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SNOOP_VLAN_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_zip.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_zip.c
new file mode 100644
index 0000000..b1093b0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_zip.c
@@ -0,0 +1,407 @@
+/*
+ * 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 (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <at.h>
+#include <snoop.h>
+
+char *print_macaddr(uint8_t *, int);
+
+static char *zip_flags(char);
+static char *zip_flags_long(char);
+
+void
+interpret_ddp_zip(int flags, struct zip_hdr *zip, int len)
+{
+ int cnt;
+ uint16_t net;
+ uint16_t range;
+ uint8_t *p;
+ char zone[33];
+ char defzone[60] = "";
+ char mcast[50] = "";
+ uint8_t gniflags;
+ uint8_t *tail = (uint8_t *)zip + len;
+
+ if (flags & F_SUM) {
+ if (len < sizeof (struct zip_hdr))
+ goto out;
+
+ switch (zip->zip_func) {
+ case ZIP_QUERY:
+ cnt = zip->zip_netcnt;
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP Query CNT = %d", cnt);
+ break;
+ case ZIP_REPLY:
+ case ZIP_EXT_REPLY:
+ cnt = zip->zip_netcnt;
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP Reply CNT = %d", cnt);
+ break;
+ case ZIP_GET_NET_INFO:
+ p = &zip->zip_func;
+
+ if ((p+6 > tail) || (p+7+p[6] > tail))
+ goto out;
+
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP GNI Zone = \"%.*s\"", p[6], &p[7]);
+ break;
+ case ZIP_GET_NET_INFO_REPLY:
+ p = &zip->zip_func;
+
+ gniflags = p[1];
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP GNI Rep Flags 0x%x (%s)",
+ gniflags, zip_flags(gniflags));
+ break;
+ default:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP CMD = %d", zip->zip_func);
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ZIP: ", "ZIP Header", len);
+ show_space();
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Length = %d", len);
+
+ if (len < sizeof (struct zip_hdr))
+ goto out;
+
+ switch (zip->zip_func) {
+ case ZIP_QUERY:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Query, Network count = %d", zip->zip_netcnt);
+ cnt = zip->zip_netcnt;
+ p = (uint8_t *)(zip + 1);
+ while (cnt--) {
+ if (p+2 > tail)
+ goto out;
+ net = get_short(p);
+ p += 2;
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "Net = %d", net);
+ }
+ break;
+ case ZIP_REPLY:
+ case ZIP_EXT_REPLY:
+ cnt = zip->zip_netcnt;
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Reply, Network count = %d", cnt);
+
+ p = (uint8_t *)(zip + 1);
+ while (cnt--) {
+ if (p+2 > tail)
+ goto out;
+ net = get_short(p);
+ p += 2;
+ if (p+1 > tail || (&p[1] + p[0]) > tail)
+ goto out;
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Network = %d, Zone = \"%.*s\"",
+ net, p[0], &p[1]);
+ p += p[0] + 1;
+ }
+ break;
+ case ZIP_GET_NET_INFO:
+ p = &zip->zip_func;
+ if (p+1 > tail || (&p[1] + p[0]) > tail)
+ goto out;
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "GetNetInfo Zone = \"%.*s\"", p[0], &p[1]);
+ break;
+ case ZIP_GET_NET_INFO_REPLY:
+ p = &zip->zip_func;
+ if (p+5 > tail)
+ goto out;
+ gniflags = p[1];
+ net = get_short(&p[2]);
+ range = get_short(&p[4]);
+
+ if (p+7 > tail || (&p[7] + p[6]) > tail)
+ goto out;
+ (void) snprintf(zone, sizeof (zone),
+ "%.*s", p[6], &p[7]);
+ p = &p[7] + p[6];
+
+ if ((gniflags & ZIP_FLG_USEBRC) == 0) {
+ if (p+1 > tail || (&p[1] + p[0]) > tail)
+ goto out;
+ (void) snprintf(mcast, sizeof (mcast),
+ "Multicast address = %s",
+ print_macaddr(&p[1], p[0]));
+ }
+
+ if (gniflags & ZIP_FLG_ZINV) {
+ p = &p[1] + p[0];
+ if (p+1 > tail || (&p[1] + p[0]) > tail)
+ goto out;
+ (void) snprintf(defzone, sizeof (defzone),
+ "Default Zone = \"%.*s\"",
+ p[0], &p[1]);
+ }
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "GetNetInfo Reply, Flags 0x%x (%s)",
+ gniflags, zip_flags_long(gniflags));
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Network number = %d-%d", net, range);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Zone = \"%s\"", zone);
+
+ if (mcast[0])
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "%s", mcast);
+
+ if (defzone[0])
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "%s", defzone);
+
+ break;
+ case ZIP_NOTIFY:
+ p = &zip->zip_func;
+ if (p+5 > tail)
+ goto out;
+
+ gniflags = p[1];
+ net = get_short(&p[2]);
+ range = get_short(&p[4]);
+
+ if (p+7 > tail || (&p[7] + p[6]) > tail)
+ goto out;
+ (void) snprintf(zone, sizeof (zone),
+ "%.*s", p[6], &p[7]);
+ p = &p[7] + p[6];
+
+ if ((gniflags & ZIP_FLG_USEBRC) == 0) {
+ if (p+1 > tail || (&p[1] + p[0]) > tail)
+ goto out;
+ (void) snprintf(mcast, sizeof (mcast),
+ "New Multicast address = %s",
+ print_macaddr(&p[1], p[0]));
+ }
+
+ if (p+1 > tail || (&p[1] + p[0]) > tail)
+ goto out;
+
+ p = &p[1] + p[0];
+
+ if (p+1 > tail || (&p[1] + p[0]) > tail)
+ goto out;
+
+ (void) snprintf(defzone, sizeof (defzone),
+ "New Default Zone = \"%.*s\"",
+ p[0], &p[1]);
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Notify, Flags 0x%x (%s)",
+ gniflags, zip_flags_long(gniflags));
+
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Old Zone = \"%s\"", zone);
+
+ if (mcast[0])
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "%s", mcast);
+
+ if (defzone[0])
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(), "%s", defzone);
+
+ break;
+ default:
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "Op = %d", zip->zip_func);
+ break;
+ }
+ }
+ return;
+out:
+ if (flags & F_SUM)
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP (short packet)");
+ if (flags & F_DTAIL)
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "ZIP (short packet)");
+}
+
+static char *
+zip_flags(char flags)
+{
+ static char buf[50];
+ char *p = buf;
+ char *tail = &buf[sizeof (buf)];
+
+ buf[0] = '\0';
+
+ if (flags & ZIP_FLG_ZINV)
+ p += snprintf(p, tail-p, "IZ");
+
+ if (flags & ZIP_FLG_USEBRC)
+ p += snprintf(p, tail-p, p == buf ? "UB" : " UB");
+
+ if (flags & ZIP_FLG_ONEZ)
+ (void) snprintf(p, tail-p, p == buf ? "OOZ" : " OOZ");
+
+ return (buf);
+}
+
+static char *
+zip_flags_long(char flags)
+{
+ static char buf[50];
+ char *p = buf;
+ char *tail = &buf[sizeof (buf)];
+
+ buf[0] = '\0';
+
+ if (flags & ZIP_FLG_ZINV)
+ p += snprintf(p, tail-p, "ZoneInvalid");
+
+ if (flags & ZIP_FLG_USEBRC)
+ p += snprintf(p, tail-p,
+ p == buf ? "UseBroadcast" : " UseBroadcast");
+
+ if (flags & ZIP_FLG_ONEZ)
+ (void) snprintf(p, tail-p,
+ p == buf ? "OnlyOneZone" : " OnlyOneZone");
+
+ return (buf);
+}
+
+void
+interpret_atp_zip(int flags, struct atp_hdr *atp, int len)
+{
+ int cnt;
+ uint8_t *data;
+ uint8_t *tail = (uint8_t *)(atp+1) + len;
+
+ if (flags & F_SUM) {
+ if (len < 0) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP (short packet)");
+ return;
+ }
+
+ switch (atp_fun(atp->atp_ctrl)) {
+ case ATP_TREQ:
+ switch (atp->atp_user[0]) {
+ case ZIP_ATP_GETMYZONE:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP GetMyZone");
+ break;
+
+ case ZIP_ATP_GETZONELIST:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP GetZoneList");
+ break;
+
+ case ZIP_ATP_GETLOCALZONES:
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP GetLocalZones");
+ break;
+ }
+ break;
+ case ATP_TRESP:
+ cnt = get_short(&atp->atp_user[2]);
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "ZIP ZoneReply, Cnt = %d", cnt);
+
+ break;
+ }
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("ZIP: ", "ZIP Header", len);
+ show_space();
+
+ if (len < 0) {
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "ZIP (short packet)");
+ return;
+ }
+
+ switch (atp_fun(atp->atp_ctrl)) {
+ case ATP_TREQ:
+ switch (atp->atp_user[0]) {
+ case ZIP_ATP_GETMYZONE:
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "GetMyZone, Start Index = %d",
+ get_short(&atp->atp_user[2]));
+ break;
+ case ZIP_ATP_GETZONELIST:
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "GetZoneList, Start Index = %d",
+ get_short(&atp->atp_user[2]));
+ break;
+ case ZIP_ATP_GETLOCALZONES:
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "GetLocalZones, Start Index = %d",
+ get_short(&atp->atp_user[2]));
+ break;
+ }
+ break;
+ case ATP_TRESP:
+ cnt = get_short(&atp->atp_user[2]);
+ (void) snprintf(get_line(0, 0), get_line_remain(),
+ "ZoneReply, Number of Zones = %d, Length = %d",
+ cnt, len);
+
+ data = (uint8_t *)atp + DDPHDR_SIZE + ATPHDR_SIZE;
+
+ while (cnt--) {
+ if (data > tail ||
+ (&data[1] + data[0]) > tail) {
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "ZoneReply (short packet)");
+ return;
+ }
+ (void) snprintf(get_line(0, 0),
+ get_line_remain(),
+ "Zone = \"%.*s\"", data[0], &data[1]);
+ data += data[0] + 1;
+ }
+ break;
+ }
+ }
+}