summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2014-06-14 18:52:21 +0400
committerIgor Pashev <pashev.igor@gmail.com>2014-06-14 18:53:23 +0400
commit88101adac56be10a45a8bb6e30ea372a7ab82a44 (patch)
treeaee2fd534a7188062e56d0335d3932ce031ea050
downloadsnoop-88101adac56be10a45a8bb6e30ea372a7ab82a44.tar.gz
Initial illumos copyillumos/2014-02-13illumos
commit 484ad3ba6a529a2471a98577d59d8ed49c7dd2c7 Author: David Höppner <0xffea@gmail.com> Date: Thu Feb 13 09:42:44 2014 +0000 4587 snoop misdecodes DHCPv6 DHCPV6_DUID_LL identifiers Reviewed by: Sebastien Roy <sebastien.roy@delphix.com> Reviewed by: Marcel Telka <marcel@telka.sk> Reviewed by: Dan McDonald <danmcd@omniti.com> Approved by: Robert Mustacchi <rm@joyent.com>
-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
-rw-r--r--usr/src/man/man1m/snoop.1m1182
78 files changed, 52142 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;
+ }
+ }
+}
diff --git a/usr/src/man/man1m/snoop.1m b/usr/src/man/man1m/snoop.1m
new file mode 100644
index 0000000..ca969e2
--- /dev/null
+++ b/usr/src/man/man1m/snoop.1m
@@ -0,0 +1,1182 @@
+'\" te
+.\" Copyright (C) 2009, Sun Microsystems, Inc. All Rights Reserved
+.\" 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]
+.TH SNOOP 1M "Feb 18, 2009"
+.SH NAME
+snoop \- capture and inspect network packets
+.SH SYNOPSIS
+.LP
+.nf
+\fBsnoop\fR [\fB-aqrCDINPSvV\fR] [\fB-t\fR [r | a | d]] [\fB-c\fR \fImaxcount\fR]
+ [\fB-d\fR \fIdevice\fR] [\fB-i\fR \fIfilename\fR] [\fB-n\fR \fIfilename\fR] [\fB-o\fR \fIfilename\fR]
+ [\fB-p\fR \fIfirst\fR [, \fIlast\fR]] [\fB-s\fR \fIsnaplen\fR] [\fB-x\fR \fIoffset\fR [, \fIlength\fR]]
+ [\fIexpression\fR]
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+From a datalink or IP interface, \fBsnoop\fR captures packets and displays
+their contents. If the datalink or IP interface is not specified, \fBsnoop\fR
+will pick a datalink to use, giving priority to datalinks that have been
+plumbed for IP traffic. \fBsnoop\fR uses the \fBpfmod\fR(7M) and
+\fBbufmod\fR(7M) STREAMS modules to provide efficient capture of packets from
+the network. Captured packets can be displayed as they are received or saved to
+a file (which is \fIRFC 1761\fR-compliant) for later inspection.
+.sp
+.LP
+\fBsnoop\fR can display packets in a single-line summary form or in verbose
+multi-line forms. In summary form, with the exception of certain VLAN packets,
+only the data pertaining to the highest level protocol is displayed. If a
+packet has a VLAN header and its VLAN ID is non-zero, then \fBsnoop\fR will
+show that the packet is VLAN tagged. For example, an \fBNFS\fR packet will have
+only \fBNFS\fR information displayed. Except for VLAN information under the
+condition just described, the underlying \fBRPC\fR, \fBUDP\fR, \fBIP\fR, and
+Ethernet frame information is suppressed, but can be displayed if either of the
+verbose options are chosen.
+.sp
+.LP
+In the absence of a name service, such as LDAP or NIS, \fBsnoop\fR displays
+host names as numeric IP addresses.
+.sp
+.LP
+\fBsnoop\fR requires an interactive interface.
+.SH OPTIONS
+.sp
+.ne 2
+.na
+\fB\fB-C\fR\fR
+.ad
+.sp .6
+.RS 4n
+List the code generated from the filter expression for either the kernel packet
+filter, or \fBsnoop\fR's own filter.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-D\fR\fR
+.ad
+.sp .6
+.RS 4n
+Display number of packets dropped during capture on the summary line.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-N\fR\fR
+.ad
+.sp .6
+.RS 4n
+Create an \fBIP\fR address-to-name file from a capture file. This must be set
+together with the \fB-i\fR option that names a capture file. The
+address-to-name file has the same name as the capture file with \fB\&.names\fR
+appended. This file records the \fBIP\fR address to hostname mapping at the
+capture site and increases the portability of the capture file. Generate a
+\fB\&.names\fR file if the capture file is to be analyzed elsewhere. Packets
+are not displayed when this flag is used.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-I\fR \fIinterface\fR\fR
+.ad
+.sp .6
+.RS 4n
+Capture IP packets from the network using the IP interface specified by
+\fIinterface\fR, for example, \fBlo0\fR. The \fBifconfig\fR(1M) command can be
+used to list available IP interfaces. The \fB-I\fR and \fB-d\fR options are
+mutually exclusive.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-P\fR\fR
+.ad
+.sp .6
+.RS 4n
+Capture packets in non-promiscuous mode. Only broadcast, multicast, or packets
+addressed to the host machine will be seen.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-S\fR\fR
+.ad
+.sp .6
+.RS 4n
+Display size of the entire link layer frame in bytes on the summary line.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-V\fR\fR
+.ad
+.sp .6
+.RS 4n
+Verbose summary mode. This is halfway between summary mode and verbose mode in
+degree of verbosity. Instead of displaying just the summary line for the
+highest level protocol in a packet, it displays a summary line for each
+protocol layer in the packet. For instance, for an \fBNFS\fR packet it will
+display a line each for the \fBETHER\fR, \fBIP\fR, \fBUDP\fR, \fBRPC\fR and
+\fBNFS\fR layers. Verbose summary mode output may be easily piped through
+\fBgrep\fR to extract packets of interest. For example, to view only \fBRPC\fR
+summary lines, enter the following: \fBexample#\fR \fBsnoop -i rpc.cap -V |
+grep RPC\fR
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-a\fR\fR
+.ad
+.sp .6
+.RS 4n
+Listen to packets on \fB/dev/audio\fR (warning: can be noisy).
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-c\fR \fImaxcount\fR\fR
+.ad
+.sp .6
+.RS 4n
+Quit after capturing \fImaxcount\fR packets. Otherwise keep capturing until
+there is no disk space left or until interrupted with Control-C.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-d\fR \fIdatalink\fR\fR
+.ad
+.sp .6
+.RS 4n
+Capture link-layer packets from the network using the DLPI datalink specified
+by \fIdatalink\fR, for example, \fBbge0\fR or \fBnet0\fR. The \fBdladm\fR(1M)
+\fBshow-link\fR subcommand can be used to list available datalinks. The
+\fB-d\fR and \fB-I\fR options are mutually exclusive.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-i\fR \fIfilename\fR\fR
+.ad
+.sp .6
+.RS 4n
+Display packets previously captured in \fIfilename\fR. Without this option,
+\fBsnoop\fR reads packets from the network interface. If a
+\fIfilename\fR\fB\&.names\fR file is present, it is automatically loaded into
+the \fBsnoop\fR \fBIP\fR address-to-name mapping table (See \fB-N\fR flag).
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-n\fR \fIfilename\fR\fR
+.ad
+.sp .6
+.RS 4n
+Use \fIfilename\fR as an \fBIP\fR address-to-name mapping table. This file must
+have the same format as the \fB/etc/hosts\fR file (IP address followed by the
+hostname).
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-o\fR \fIfilename\fR\fR
+.ad
+.sp .6
+.RS 4n
+Save captured packets in \fIfilename\fR as they are captured. (This
+\fIfilename\fR is referred to as the "capture file".) The format of the capture
+file is RFC 1761-compliant. During packet capture, a count of the number of
+packets saved in the file is displayed. If you wish just to count packets
+without saving to a file, name the file \fB/dev/null\fR.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-p\fR \fIfirst\fR [ , \fBlast\fR ]\fR
+.ad
+.sp .6
+.RS 4n
+Select one or more packets to be displayed from a capture file. The \fIfirst\fR
+packet in the file is packet number 1.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-q\fR\fR
+.ad
+.sp .6
+.RS 4n
+When capturing network packets into a file, do not display the packet count.
+This can improve packet capturing performance.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-r\fR\fR
+.ad
+.sp .6
+.RS 4n
+Do not resolve the \fBIP\fR address to the symbolic name. This prevents
+\fBsnoop\fR from generating network traffic while capturing and displaying
+packets. However, if the \fB-n\fR option is used, and an address is found in
+the mapping file, its corresponding name will be used.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-s\fR \fIsnaplen\fR\fR
+.ad
+.sp .6
+.RS 4n
+Truncate each packet after \fIsnaplen\fR bytes. Usually the whole packet is
+captured. This option is useful if only certain packet header information is
+required. The packet truncation is done within the kernel giving better
+utilization of the streams packet buffer. This means less chance of dropped
+packets due to buffer overflow during periods of high traffic. It also saves
+disk space when capturing large traces to a capture file. To capture only
+\fBIP\fR headers (no options) use a \fIsnaplen\fR of 34. For \fBUDP\fR use 42,
+and for \fBTCP\fR use 54. You can capture \fBRPC\fR headers with a
+\fIsnaplen\fR of 80 bytes. \fBNFS\fR headers can be captured in 120 bytes.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-t\fR [ \fBr\fR | \fBa\fR | \fBd\fR ]\fR
+.ad
+.sp .6
+.RS 4n
+Time-stamp presentation. Time-stamps are accurate to within 4 microseconds. The
+default is for times to be presented in \fBd\fR (delta) format (the time since
+receiving the previous packet). Option \fBa\fR (absolute) gives wall-clock
+time. Option \fBr\fR (relative) gives time relative to the first packet
+displayed. This can be used with the \fB-p\fR option to display time relative
+to any selected packet.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-v\fR\fR
+.ad
+.sp .6
+.RS 4n
+Verbose mode. Print packet headers in lots of detail. This display consumes
+many lines per packet and should be used only on selected packets.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB\fR\fB-x\fR\fIoffset\fR [ , \fIlength\fR]\fR
+.ad
+.sp .6
+.RS 4n
+Display packet data in hexadecimal and \fBASCII\fR format. The \fIoffset\fR and
+\fIlength\fR values select a portion of the packet to be displayed. To display
+the whole packet, use an \fIoffset\fR of 0. If a \fIlength\fR value is not
+provided, the rest of the packet is displayed.
+.RE
+
+.SH OPERANDS
+.sp
+.ne 2
+.na
+\fB\fIexpression\fR\fR
+.ad
+.sp .6
+.RS 4n
+Select packets either from the network or from a capture file. Only packets for
+which the expression is true will be selected. If no expression is provided it
+is assumed to be true.
+.sp
+Given a filter expression, \fBsnoop\fR generates code for either the kernel
+packet filter or for its own internal filter. If capturing packets with the
+network interface, code for the kernel packet filter is generated. This filter
+is implemented as a streams module, upstream of the buffer module. The buffer
+module accumulates packets until it becomes full and passes the packets on to
+\fBsnoop\fR. The kernel packet filter is very efficient, since it rejects
+unwanted packets in the kernel before they reach the packet buffer or
+\fBsnoop\fR. The kernel packet filter has some limitations in its
+implementation; it is possible to construct filter expressions that it cannot
+handle. In this event, \fBsnoop\fR tries to split the filter and do as much
+filtering in the kernel as possible. The remaining filtering is done by the
+packet filter for \fBsnoop\fR. The \fB-C\fR flag can be used to view generated
+code for either the packet filter for the kernel or the packet filter for
+\fBsnoop\fR. If packets are read from a capture file using the \fB-i\fR option,
+only the packet filter for \fBsnoop\fR is used.
+.sp
+A filter \fIexpression\fR consists of a series of one or more boolean
+primitives that may be combined with boolean operators (\fBAND\fR, \fBOR\fR,
+and \fBNOT\fR). Normal precedence rules for boolean operators apply. Order of
+evaluation of these operators may be controlled with parentheses. Since
+parentheses and other filter expression characters are known to the shell, it
+is often necessary to enclose the filter expression in quotes. Refer to for
+information about setting up more efficient filters.
+.sp
+The primitives are:
+.sp
+.ne 2
+.na
+\fB\fBhost\fR \fIhostname\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the source or destination address is that of \fBhostname\fR. The
+\fIhostname\fR argument may be a literal address. The keyword \fBhost\fR may be
+omitted if the name does not conflict with the name of another expression
+primitive. For example, \fBpinky\fR selects packets transmitted to or received
+from the host \fBpinky\fR, whereas \fBpinky and dinky\fR selects packets
+exchanged between hosts \fBpinky AND dinky\fR.
+.sp
+The type of address used depends on the primitive which precedes the \fBhost\fR
+primitive. The possible qualifiers are \fBinet\fR, \fBinet6\fR, \fBether\fR, or
+none. These three primitives are discussed below. Having none of the primitives
+present is equivalent to "inet host hostname or inet6 host hostname". In other
+words, snoop tries to filter on all IP addresses associated with hostname.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fIinet\fR or \fIinet6\fR\fR
+.ad
+.sp .6
+.RS 4n
+A qualifier that modifies the \fBhost\fR primitive that follows. If it is
+\fIinet\fR, then \fBsnoop\fR tries to filter on all IPv4 addresses returned
+from a name lookup. If it is \fIinet6\fR, \fBsnoop\fR tries to filter on all
+IPv6 addresses returned from a name lookup.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fIipaddr\fR, \fIatalkaddr\fR, or \fIetheraddr\fR\fR
+.ad
+.sp .6
+.RS 4n
+Literal addresses, \fBIP\fR dotted, AppleTalk dotted, and Ethernet colon are
+recognized. For example,
+.RS +4
+.TP
+.ie t \(bu
+.el o
+"\fB172.16.40.13\fR" matches all packets with that \fBIP\fR
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+"\fB2::9255:a00:20ff:fe73:6e35\fR" matches all packets with that IPv6 address
+as source or destination;
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+"\fB65281.13\fR" matches all packets with that AppleTalk address;
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+"\fB8:0:20:f:b1:51\fR" matches all packets with the Ethernet address as source
+or destination.
+.RE
+An Ethernet address beginning with a letter is interpreted as a hostname. To
+avoid this, prepend a zero when specifying the address. For example, if the
+Ethernet address is \fBaa:0:45:23:52:44\fR, then specify it by add a leading
+zero to make it \fB0aa:0:45:23:52:44\fR.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBfrom\fR or \fBsrc\fR\fR
+.ad
+.sp .6
+.RS 4n
+A qualifier that modifies the following \fBhost\fR, \fBnet\fR, \fIipaddr\fR,
+\fIatalkaddr\fR, \fIetheraddr\fR, \fBport\fR or \fBrpc\fR primitive to match
+just the source address, port, or \fBRPC\fR reply.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBto\fR or \fBdst\fR\fR
+.ad
+.sp .6
+.RS 4n
+A qualifier that modifies the following \fBhost\fR, \fBnet\fR, \fIipaddr\fR,
+\fIatalkaddr\fR, \fIetheraddr\fR, \fBport\fR or \fBrpc\fR primitive to match
+just the destination address, port, or \fBRPC\fR call.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBether\fR\fR
+.ad
+.sp .6
+.RS 4n
+A qualifier that modifies the following \fBhost\fR primitive to resolve a name
+to an Ethernet address. Normally, \fBIP\fR address matching is performed. This
+option is not supported on media such as IPoIB (IP over InfiniBand).
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBethertype\fR \fInumber\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the Ethernet type field has value \fInumber\fR. If \fInumber\fR is not
+0x8100 (VLAN) and the packet is VLAN tagged, then the expression will match the
+encapsulated Ethernet type.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBip\fR, \fBip6\fR, \fBarp\fR, \fBrarp\fR, \fBpppoed\fR, \fBpppoes\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is of the appropriate ethertype.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBvlan\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet has \fBethertype\fR VLAN and the VLAN ID is not zero.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBvlan-id\fR \fIid\fR\fR
+.ad
+.sp .6
+.RS 4n
+True for packets of ethertype VLAN with the id \fIid\fR.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBpppoe\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the ethertype of the packet is either \fBpppoed\fR or \fBpppoes\fR.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBbroadcast\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is a broadcast packet. Equivalent to \fBether[2:4] =
+0xffffffff\fR for Ethernet. This option is not supported on media such as IPoIB
+(IP over InfiniBand).
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBmulticast\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is a multicast packet. Equivalent to "\fBether[0] & 1 =
+1\fR" on Ethernet. This option is not supported on media such as IPoIB (IP over
+InfiniBand).
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBbootp\fR, \fBdhcp\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is an unfragmented IPv4 UDP packet with either a source port
+of \fBBOOTPS (67)\fR and a destination port of \fBBOOTPC (68)\fR, or a source
+port of \fBBOOTPC (68)\fR and a destination of \fBBOOTPS (67)\fR.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBdhcp6\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is an unfragmented IPv6 UDP packet with either a source port
+of \fBDHCPV6-SERVER\fR (547) and a destination port of \fBDHCPV6-CLIENT\fR
+(546), or a source port of \fBDHCPV6-CLIENT\fR (546) and a destination of
+\fBDHCPV6-SERVER\fR (547).
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBapple\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is an Apple Ethertalk packet. Equivalent to "\fBethertype
+0x809b or ethertype 0x80f3\fR".
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBdecnet\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is a \fBDECNET\fR packet.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBgreater\fR \fIlength\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is longer than \fIlength\fR.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBless\fR \fIlength\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is shorter than \fIlength\fR.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBudp\fR, \fBtcp\fR, \fBicmp\fR, \fBicmp6\fR, \fBah\fR, \fBesp\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the \fBIP\fR or IPv6 protocol is of the appropriate type.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBnet\fR \fInet\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if either the \fBIP\fR source or destination address has a network number
+of \fInet\fR. The \fBfrom\fR or \fBto\fR qualifier may be used to select
+packets for which the network number occurs only in the source or destination
+address.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBport\fR \fIport\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if either the source or destination port is \fIport\fR. The \fIport\fR may
+be either a port number or name from \fB/etc/services\fR. The \fBtcp\fR or
+\fBudp\fR primitives may be used to select \fBTCP\fR or \fBUDP\fR ports only.
+The \fBfrom\fR or \fBto\fR qualifier may be used to select packets for which
+the \fIport\fR occurs only as the source or destination.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBrpc\fR \fIprog\fR [ , \fIvers\fR [ , \fBproc\fR ] ]\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is an \fBRPC\fR call or reply packet for the protocol
+identified by \fIprog\fR. The \fIprog\fR may be either the name of an \fBRPC\fR
+protocol from \fB/etc/rpc\fR or a program number. The \fIvers\fR and \fBproc\fR
+may be used to further qualify the program \fIversion\fR and \fIprocedure\fR
+number, for example, \fBrpc nfs,2,0\fR selects all calls and replies for the
+\fBNFS\fR null procedure. The \fBto\fR or \fBfrom\fR qualifier may be used to
+select either call or reply packets only.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBzone\fR \fIzoneid\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if \fIzoneid\fR matches either the source or destination \fIzoneid\fR of a
+packet received on an \fBipnet\fR device.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBldap\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is an \fBLDAP\fR packet on port 389.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBgateway\fR \fIhost\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet used \fIhost\fR as a gateway, that is, the Ethernet source
+or destination address was for \fIhost\fR but not the \fBIP\fR address.
+Equivalent to "\fBether host\fR \fIhost\fR and not host \fIhost\fR".
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBnofrag\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is unfragmented or is the first in a series of \fBIP\fR
+fragments. Equivalent to \fBip[6:2] & 0x1fff = 0\fR.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fIexpr\fR \fIrelop\fR \fIexpr\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the relation holds, where \fIrelop\fR is one of \fB>\fR, \fB<\fR,
+\fB>=\fR, \fB<=\fR, \fB=\fR, \fB!=\fR, and \fBexpr\fR is an arithmetic
+expression composed of numbers, packet field selectors, the \fBlength\fR
+primitive, and arithmetic operators \fB+\fR, \fB\(mi\fR, \fB*\fR, \fB&\fR,
+\fB|\fR, \fB^\fR, and \fB%\fR. The arithmetic operators within \fBexpr\fR are
+evaluated before the relational operator and normal precedence rules apply
+between the arithmetic operators, such as multiplication before addition.
+Parentheses may be used to control the order of evaluation. To use the value of
+a field in the packet use the following syntax:
+.sp
+.in +2
+.nf
+\fIbase\fR[\fBexpr\fR [\fB:\fR \fBsize\fR ] ]
+.fi
+.in -2
+.sp
+
+where \fBexpr\fR evaluates the value of an offset into the packet from a
+\fIbase\fR offset which may be \fBether\fR, \fBip\fR, \fBip6\fR, \fBudp\fR,
+\fBtcp\fR, or \fBicmp\fR. The \fBsize\fR value specifies the size of the field.
+If not given, 1 is assumed. Other legal values are 2 and 4. For example,
+.sp
+.in +2
+.nf
+ether[0] & 1 = 1
+.fi
+.in -2
+
+is equivalent to \fBmulticast\fR
+.sp
+.in +2
+.nf
+ether[2:4] = 0xffffffff
+.fi
+.in -2
+
+is equivalent to \fBbroadcast\fR.
+.sp
+.in +2
+.nf
+ip[ip[0] & 0xf * 4 : 2] = 2049
+.fi
+.in -2
+
+is equivalent to \fBudp[0:2] = 2049\fR
+.sp
+.in +2
+.nf
+ip[0] & 0xf > 5
+.fi
+.in -2
+
+selects \fBIP\fR packets with options.
+.sp
+.in +2
+.nf
+ip[6:2] & 0x1fff = 0
+.fi
+.in -2
+
+eliminates \fBIP\fR fragments.
+.sp
+.in +2
+.nf
+udp and ip[6:2]&0x1fff = 0 and udp[6:2] != 0
+.fi
+.in -2
+
+finds all packets with \fBUDP\fR checksums.
+.sp
+The \fBlength\fR primitive may be used to obtain the length of the packet. For
+instance "\fBlength > 60\fR" is equivalent to "\fBgreater 60\fR", and
+"\fBether[length \(mi 1]\fR" obtains the value of the last byte in a packet.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBand\fR\fR
+.ad
+.sp .6
+.RS 4n
+Perform a logical \fBAND\fR operation between two boolean values. The \fBAND\fR
+operation is implied by the juxtaposition of two boolean expressions, for
+example "\fBdinky pinky\fR" is the same as "\fBdinky AND pinky\fR".
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBor\fR or \fB,\fR\fR
+.ad
+.sp .6
+.RS 4n
+Perform a logical \fBOR\fR operation between two boolean values. A comma may be
+used instead, for example, "\fBdinky,pinky\fR" is the same as "\fBdinky OR
+pinky\fR".
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBnot\fR or \fB!\fR\fR
+.ad
+.sp .6
+.RS 4n
+Perform a logical \fBNOT\fR operation on the following boolean value. This
+operator is evaluated before \fBAND\fR or OR.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBslp\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is an \fBSLP\fR packet.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBsctp\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is an \fBSCTP\fR packet.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBospf\fR\fR
+.ad
+.sp .6
+.RS 4n
+True if the packet is an \fBOSPF\fR packet.
+.RE
+
+.RE
+
+.SH EXAMPLES
+.LP
+\fBExample 1 \fRUsing the \fBsnoop\fR Command
+.sp
+.LP
+Capture all packets and display them as they are received:
+
+.sp
+.in +2
+.nf
+example# \fBsnoop\fR
+.fi
+.in -2
+.sp
+
+.sp
+.LP
+Capture packets with host \fBfunky\fR as either the source or destination and
+display them as they are received:
+
+.sp
+.in +2
+.nf
+example# \fBsnoop funky\fR
+.fi
+.in -2
+.sp
+
+.sp
+.LP
+Capture packets between \fBfunky\fR and \fBpinky\fR and save them to a file.
+Then inspect the packets using times (in seconds) relative to the first
+captured packet:
+
+.sp
+.in +2
+.nf
+example# \fBsnoop -o cap funky pinky\fR
+example# \fBsnoop -i cap -t r | more\fR
+.fi
+.in -2
+.sp
+
+.sp
+.LP
+To look at selected packets in another capture file:
+
+.sp
+.in +2
+.nf
+example# \fBsnoop -i pkts -p 99,108\fR
+ 99 0.0027 boutique -> sunroof NFS C GETATTR FH=8E6
+100 0.0046 sunroof -> boutique NFS R GETATTR OK
+101 0.0080 boutique -> sunroof NFS C RENAME FH=8E6C MTra00192 to .nfs08
+102 0.0102 marmot -> viper NFS C LOOKUP FH=561E screen.r.13.i386
+103 0.0072 viper -> marmot NFS R LOOKUP No such file or directory
+104 0.0085 bugbomb -> sunroof RLOGIN C PORT=1023 h
+105 0.0005 kandinsky -> sparky RSTAT C Get Statistics
+106 0.0004 beeblebrox -> sunroof NFS C GETATTR FH=0307
+107 0.0021 sparky -> kandinsky RSTAT R
+108 0.0073 office -> jeremiah NFS C READ FH=2584 at 40960 for 8192
+.fi
+.in -2
+.sp
+
+.sp
+.LP
+To look at packet 101 in more detail:
+
+.sp
+.in +2
+.nf
+example# \fBsnoop -i pkts -v -p101\fR
+ETHER: ----- Ether Header -----
+ETHER:
+ETHER: Packet 101 arrived at 16:09:53.59
+ETHER: Packet size = 210 bytes
+ETHER: Destination = 8:0:20:1:3d:94, Sun
+ETHER: Source = 8:0:69:1:5f:e, Silicon Graphics
+ETHER: Ethertype = 0800 (IP)
+ETHER:
+IP: ----- IP Header -----
+IP:
+IP: Version = 4, header length = 20 bytes
+IP: Type of service = 00
+IP: ..0. .... = routine
+IP: ...0 .... = normal delay
+IP: .... 0... = normal throughput
+IP: .... .0.. = normal reliability
+IP: Total length = 196 bytes
+IP: Identification 19846
+IP: Flags = 0X
+IP: .0.. .... = may fragment
+IP: ..0. .... = more fragments
+IP: Fragment offset = 0 bytes
+IP: Time to live = 255 seconds/hops
+IP: Protocol = 17 (UDP)
+IP: Header checksum = 18DC
+IP: Source address = 172.16.40.222, boutique
+IP: Destination address = 172.16.40.200, sunroof
+IP:
+UDP: ----- UDP Header -----
+UDP:
+UDP: Source port = 1023
+UDP: Destination port = 2049 (Sun RPC)
+UDP: Length = 176
+UDP: Checksum = 0
+UDP:
+RPC: ----- SUN RPC Header -----
+RPC:
+RPC: Transaction id = 665905
+RPC: Type = 0 (Call)
+RPC: RPC version = 2
+RPC: Program = 100003 (NFS), version = 2, procedure = 1
+RPC: Credentials: Flavor = 1 (Unix), len = 32 bytes
+RPC: Time = 06-Mar-90 07:26:58
+RPC: Hostname = boutique
+RPC: Uid = 0, Gid = 1
+RPC: Groups = 1
+RPC: Verifier : Flavor = 0 (None), len = 0 bytes
+RPC:
+NFS: ----- SUN NFS -----
+NFS:
+NFS: Proc = 11 (Rename)
+NFS: File handle = 000016430000000100080000305A1C47
+NFS: 597A0000000800002046314AFC450000
+NFS: File name = MTra00192
+NFS: File handle = 000016430000000100080000305A1C47
+NFS: 597A0000000800002046314AFC450000
+NFS: File name = .nfs08
+NFS:
+.fi
+.in -2
+.sp
+
+.sp
+.LP
+To view just the \fBNFS\fR packets between \fBsunroof\fR and \fBboutique\fR:
+
+.sp
+.in +2
+.nf
+example# \fBsnoop -i pkts rpc nfs and sunroof and boutique\fR
+1 0.0000 boutique -> sunroof NFS C GETATTR FH=8E6C
+2 0.0046 sunroof -> boutique NFS R GETATTR OK
+3 0.0080 boutique -> sunroof NFS C RENAME FH=8E6C MTra00192 to .nfs08
+.fi
+.in -2
+.sp
+
+.sp
+.LP
+To save these packets to a new capture file:
+
+.sp
+.in +2
+.nf
+example# \fBsnoop -i pkts -o pkts.nfs rpc nfs sunroof boutique\fR
+.fi
+.in -2
+.sp
+
+.sp
+.LP
+To view encapsulated packets, there will be an indicator of encapsulation:
+
+.sp
+.in +2
+.nf
+example# \fBsnoop ip-in-ip\fR
+sunroof -> boutique ICMP Echo request (1 encap)
+.fi
+.in -2
+.sp
+
+.sp
+.LP
+If -V is used on an encapsulated packet:
+
+.sp
+.in +2
+.nf
+example# \fBsnoop -V ip-in-ip\fR
+sunroof -> boutique ETHER Type=0800 (IP), size = 118 bytes
+sunroof -> boutique IP D=172.16.40.222 S=172.16.40.200 LEN=104, ID=27497
+sunroof -> boutique IP D=10.1.1.2 S=10.1.1.1 LEN=84, ID=27497
+sunroof -> boutique ICMP Echo request
+.fi
+.in -2
+.sp
+
+.LP
+\fBExample 2 \fRSetting Up A More Efficient Filter
+.sp
+.LP
+To set up a more efficient filter, the following filters should be used toward
+the end of the expression, so that the first part of the expression can be set
+up in the kernel: \fBgreater\fR, \fBless\fR, \fBport\fR, \fBrpc\fR,
+\fBnofrag\fR, and \fBrelop\fR. The presence of \fBOR\fR makes it difficult to
+split the filtering when using these primitives that cannot be set in the
+kernel. Instead, use parentheses to enforce the primitives that should be
+\fBOR\fR'd.
+
+.sp
+.LP
+To capture packets between \fBfunky\fR and \fBpinky\fR of type \fBtcp\fR or
+\fBudp\fR on \fBport\fR 80:
+
+.sp
+.in +2
+.nf
+example# \fBsnoop funky and pinky and port 80 and tcp or udp\fR
+.fi
+.in -2
+.sp
+
+.sp
+.LP
+Since the primitive \fBport\fR cannot be handled by the kernel filter, and
+there is also an \fBOR\fR in the expression, a more efficient way to filter is
+to move the \fBOR\fR to the end of the expression and to use parentheses to
+enforce the \fBOR\fR between \fBtcp\fR and \fBudp\fR:
+
+.sp
+.in +2
+.nf
+example# \fBsnoop funky and pinky and (tcp or udp) and port 80\fR
+.fi
+.in -2
+.sp
+
+.SH EXIT STATUS
+.sp
+.ne 2
+.na
+\fB\fB0\fR\fR
+.ad
+.RS 5n
+Successful completion.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB1\fR\fR
+.ad
+.RS 5n
+An error occurred.
+.RE
+
+.SH FILES
+.sp
+.ne 2
+.na
+\fB\fB/dev/audio\fR\fR
+.ad
+.RS 17n
+Symbolic link to the system's primary audio device.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB/dev/null\fR\fR
+.ad
+.RS 17n
+The null file.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB/etc/hosts\fR\fR
+.ad
+.RS 17n
+Host name database.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB/etc/rpc\fR\fR
+.ad
+.RS 17n
+RPC program number data base.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB/etc/services\fR\fR
+.ad
+.RS 17n
+Internet services and aliases.
+.RE
+
+.SH SEE ALSO
+.sp
+.LP
+\fBdladm\fR(1M), \fBifconfig\fR(1M), \fBnetstat\fR(1M), \fBhosts\fR(4),
+\fBrpc\fR(4), \fBservices\fR(4), \fBattributes\fR(5), \fBaudio\fR(7I),
+\fBipnet\fR(7D), \fBbufmod\fR(7M), \fBdlpi\fR(7P), \fBpfmod\fR(7M)
+.sp
+.LP
+Callaghan, B. and Gilligan, R. \fIRFC 1761, Snoop Version 2 Packet Capture File
+Format\fR. Network Working Group. February 1995.
+.SH WARNINGS
+.sp
+.LP
+The processing overhead is much higher for real-time packet interpretation.
+Consequently, the packet drop count may be higher. For more reliable capture,
+output raw packets to a file using the \fB-o\fR option and analyze the packets
+offline.
+.sp
+.LP
+Unfiltered packet capture imposes a heavy processing load on the host computer,
+particularly if the captured packets are interpreted real-time. This processing
+load further increases if verbose options are used. Since heavy use of
+\fBsnoop\fR may deny computing resources to other processes, it should not be
+used on production servers. Heavy use of \fBsnoop\fR should be restricted to a
+dedicated computer.
+.sp
+.LP
+\fBsnoop\fR does not reassemble \fBIP\fR fragments. Interpretation of higher
+level protocol halts at the end of the first \fBIP\fR fragment.
+.sp
+.LP
+\fBsnoop\fR may generate extra packets as a side-effect of its use. For example
+it may use a network name service (NIS or NIS+) to convert \fBIP\fR addresses
+to host names for display. Capturing into a file for later display can be used
+to postpone the address-to-name mapping until after the capture session is
+complete. Capturing into an NFS-mounted file may also generate extra packets.
+.sp
+.LP
+Setting the \fBsnaplen\fR (\fB-s\fR option) to small values may remove header
+information that is needed to interpret higher level protocols. The exact
+cutoff value depends on the network and protocols being used. For \fBNFS\fR
+Version 2 traffic using \fBUDP\fR on 10 Mb/s Ethernet, do not set \fBsnaplen\fR
+less than 150 bytes. For \fBNFS\fR Version 3 traffic using \fBTCP\fR on 100
+Mb/s Ethernet, \fBsnaplen\fR should be 250 bytes or more.
+.sp
+.LP
+\fBsnoop\fR requires information from an \fBRPC\fR request to fully interpret
+an \fBRPC\fR reply. If an \fBRPC\fR reply in a capture file or packet range
+does not have a request preceding it, then only the \fBRPC\fR reply header will
+be displayed.