diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2014-06-14 18:52:21 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2014-06-14 18:53:23 +0400 |
commit | 88101adac56be10a45a8bb6e30ea372a7ab82a44 (patch) | |
tree | aee2fd534a7188062e56d0335d3932ce031ea050 /usr/src/cmd/cmd-inet | |
download | snoop-illumos.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>
Diffstat (limited to 'usr/src/cmd/cmd-inet')
77 files changed, 50960 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile new file mode 100644 index 0000000..1d408bc --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile @@ -0,0 +1,80 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END + + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +PROG= snoop +OBJS= nfs4_xdr.o snoop.o snoop_aarp.o snoop_adsp.o snoop_aecho.o \ + snoop_apple.o snoop_arp.o snoop_atp.o snoop_bparam.o \ + snoop_bpdu.o \ + snoop_capture.o snoop_dhcp.o snoop_dhcpv6.o snoop_display.o \ + snoop_dns.o snoop_ether.o \ + snoop_filter.o snoop_http.o snoop_icmp.o snoop_igmp.o snoop_ip.o \ + snoop_ipaddr.o snoop_ipsec.o snoop_isis.o \ + snoop_ldap.o snoop_mip.o snoop_mount.o \ + snoop_nbp.o snoop_netbios.o snoop_nfs.o snoop_nfs3.o snoop_nfs4.o \ + snoop_nfs_acl.o snoop_nis.o snoop_nlm.o snoop_ntp.o \ + snoop_pf.o snoop_ospf.o snoop_ospf6.o snoop_pmap.o snoop_ppp.o \ + snoop_pppoe.o snoop_rip.o snoop_rip6.o snoop_rpc.o snoop_rpcprint.o \ + snoop_rpcsec.o snoop_rport.o snoop_rquota.o snoop_rstat.o snoop_rtmp.o \ + snoop_sctp.o snoop_slp.o snoop_smb.o snoop_socks.o snoop_solarnet.o \ + snoop_tcp.o snoop_tftp.o snoop_trill.o snoop_udp.o snoop_zip.o + +SRCS= $(OBJS:.o=.c) +HDRS= snoop.h snoop_mip.h at.h snoop_ospf.h snoop_ospf6.h + +include ../../../Makefile.cmd + +CPPFLAGS += -I. -I$(SRC)/common/net/dhcp \ + -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 +LDLIBS += -ldhcputil -ldlpi -lsocket -lnsl -ltsol +LDFLAGS += $(MAPFILE.NGB:%=-M%) +CERRWARN += -_gcc=-Wno-switch +CERRWARN += -_gcc=-Wno-implicit-function-declaration +CERRWARN += -_gcc=-Wno-uninitialized +CERRWARN += -_gcc=-Wno-clobbered +CERRWARN += -_gcc=-Wno-unused-value +CERRWARN += -_gcc=-Wno-parentheses +CERRWARN += -_gcc=-Wno-unused-variable +CERRWARN += -_gcc=-Wno-unused-label +CERRWARN += -_gcc=-Wno-unused-function + +.KEEP_STATE: + +.PARALLEL: $(OBJS) + +all: $(PROG) + +$(PROG): $(OBJS) $(MAPFILE.NGB) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +install: all $(ROOTUSRSBINPROG) + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +include ../../../Makefile.targ diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/at.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/at.h new file mode 100644 index 0000000..e98072f --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/at.h @@ -0,0 +1,206 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + * + */ + +#ifndef _AT_H +#define _AT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * There is a lot of alignment problems in AppleTalk packets. + * This is the reason some of the headers use uint8_t arrays instead of the + * natural datatype. + */ + +/* AARP */ + +#define AARP_REQ 1 +#define AARP_RESP 2 +#define AARP_PROBE 3 + + +/* DDP */ + +struct ddp_hdr { + uint8_t ddp_hop_len; + uint8_t ddp_len_lo; + uint16_t ddp_cksum; + uint16_t ddp_dest_net; + uint16_t ddp_src_net; + uint8_t ddp_dest_id; + uint8_t ddp_src_id; + uint8_t ddp_dest_sock; + uint8_t ddp_src_sock; + uint8_t ddp_type; +}; + +#define ddp_pad(x) ((x)->ddp_hop_len & 0xc0) +#define ddp_hop(x) (((x)->ddp_hop_len >> 2) & 0xf) +#define ddp_len(x) ((((x)->ddp_hop_len & 0x3) << 8) + (x)->ddp_len_lo) + +#define DDPHDR_SIZE 13 + +#define DDP_TYPE_RTMPRQ 5 +#define DDP_TYPE_RTMPRESP 1 +#define DDP_TYPE_NBP 2 +#define DDP_TYPE_ATP 3 +#define DDP_TYPE_AEP 4 +#define DDP_TYPE_ZIP 6 +#define DDP_TYPE_ADSP 7 + + +/* AECHO */ + +#define AEP_REQ 1 +#define AEP_REPLY 2 + +/* NBP */ + +struct nbp_hdr { + uint8_t ddphdr[DDPHDR_SIZE]; + uint8_t nbp_fun_cnt; + uint8_t nbp_id; +}; + +#define NBP_BRRQ 1 +#define NBP_LKUP 2 +#define NBP_LKUP_REPLY 3 +#define NBP_FWDREQ 4 + + +/* ZIP */ + +struct zip_hdr { + uint8_t ddphdr[DDPHDR_SIZE]; + uint8_t zip_func; + uint8_t zip_netcnt; +}; + +#define ZIP_QUERY 1 +#define ZIP_REPLY 2 +#define ZIP_GET_NET_INFO 5 +#define ZIP_GET_NET_INFO_REPLY 6 +#define ZIP_NOTIFY 7 +#define ZIP_EXT_REPLY 8 + +#define ZIP_ATP_GETMYZONE 7 +#define ZIP_ATP_GETZONELIST 8 +#define ZIP_ATP_GETLOCALZONES 9 + +#define ZIP_FLG_ONEZ 0x20 +#define ZIP_FLG_USEBRC 0x40 +#define ZIP_FLG_ZINV 0x80 + + +/* ATP */ + +struct atp_hdr { + uint8_t ddphdr[DDPHDR_SIZE]; + uint8_t atp_ctrl; + uint8_t atp_seq; + uint8_t atp_tid[2]; + uint8_t atp_user[4]; +}; + +#define ATPHDR_SIZE 8 + +#define atp_fun(x) (((x) >> 6) & 0x3) +#define atp_tmo(x) ((x) & 0x7) + +#define ATP_TREQ 1 +#define ATP_TRESP 2 +#define ATP_TREL 3 +#define ATP_FLG_STS 0x08 +#define ATP_FLG_EOM 0x10 +#define ATP_FLG_XO 0x20 + + +#define NODE_ID_BROADCAST 0xff + +struct ddp_adsphdr { + uint8_t ddphdr[DDPHDR_SIZE]; + uint8_t ad_connid[2]; /* short */ + uint8_t ad_fbseq[4]; /* long */ + uint8_t ad_nrseq[4]; /* long */ + uint8_t ad_rcvwin[2]; /* short */ + uint8_t ad_desc; +}; + +#define AD_CTRL 0x80 +#define AD_ACKREQ 0x40 +#define AD_EOM 0x20 +#define AD_ATT 0x10 +#define AD_CTRL_MASK 0x0f + +#define AD_CREQ 0x81 /* Open Conn Request */ +#define AD_CACK 0x82 /* Open Conn Ack */ +#define AD_CREQ_ACK 0x83 /* Open Conn Req+Ack */ +#define AD_CDENY 0x84 /* Open Conn Denial */ + +struct ddp_adsp_att { + struct ddp_adsphdr ad; + uint8_t ad_att_code[2]; /* short */ +}; + +struct ddp_adsp_open { + struct ddp_adsphdr ad; + uint8_t ad_version[2]; /* short */ + uint8_t ad_dconnid[2]; /* short */ + uint8_t ad_attseq[4]; /* long */ +}; + +#define RTMP_REQ 1 +#define RTMP_RDR_SH 2 /* Route Data Request, split horizon */ +#define RTMP_RDR_NSH 3 /* Route Data Request, no split horizon */ + +#define RTMP_DIST_MASK 0x1f +#define RTMP_EXTEND 0x80 +#define RTMP_FILLER 0x82 + + +uint16_t get_short(uint8_t *); +uint32_t get_long(uint8_t *); + +extern void interpret_aarp(int, char *, int); +extern void interpret_at(int, struct ddp_hdr *, int); +extern void interpret_nbp(int, struct nbp_hdr *, int); +extern void interpret_rtmp(int, struct ddp_hdr *, int); +extern void interpret_aecho(int, struct ddp_hdr *, int); +extern void interpret_atp(int, struct ddp_hdr *, int); +extern void interpret_adsp(int, struct ddp_adsphdr *, int); +extern void interpret_ddp_zip(int, struct zip_hdr *, int); +extern void interpret_atp_zip(int, struct atp_hdr *, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _AT_H */ diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/fakewin.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fakewin.h new file mode 100644 index 0000000..d075e0b --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fakewin.h @@ -0,0 +1,71 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1993 by Sun Microsystems, Inc. + */ + +#ifndef _FAKEWIN_H +#define _FAKEWIN_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file defines appropriate macros so that + * we can use the same codebase for Unix, DOS, and Windows. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WINDOWS +#include <tklib.h> + +#define malloc _fmalloc +#define calloc _fcalloc +#define free _ffree +#define strdup _fstrdup +#define strcpy _fstrcpy +#define strcmp _fstrcmp +#define strchr _fstrchr +#define sprintf wsprintf +#define vsprintf wvsprintf +#define memcpy _fmemcpy +#define strlen _fstrlen +#else +#define LPSTR char * +#endif + +#if !defined(_WINDOWS) && !defined(_MSDOS) +#define _TKFAR +#endif + +#ifndef _WINDOWS +#define _TKPASCAL +#define __export +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !_FAKEWIN_H */ diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw.h new file mode 100644 index 0000000..3274623 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw.h @@ -0,0 +1,147 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1993 by Sun Microsystems, Inc. + */ + +#ifndef _FW_H +#define _FW_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <rpc/rpc.h> +#include "fakewin.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Header file for the framework. + */ +#define CTXTLEN 1024 + +struct Op_arg_item { + char _TKFAR *name; + char _TKFAR *value; + struct Op_arg_item _TKFAR *next; +}; +typedef struct Op_arg_item Op_arg_item; + +struct Op_row_link { + Op_arg_item _TKFAR *first; + Op_arg_item _TKFAR *last; + struct Op_row_link _TKFAR *next; +}; +typedef struct Op_row_link Op_row_link; + +struct Op_arg { + Op_row_link _TKFAR *first; + Op_row_link _TKFAR *last; + Op_row_link _TKFAR *curr; + Op_arg_item _TKFAR *cura; + u_long rows; + bool_t xdr_flag; +}; +typedef struct Op_arg Op_arg; + +enum Fw_err { + FW_ERR_NONE = 0, + FW_ERR_FW = 1, + FW_ERR_OP = 2 +}; +typedef enum Fw_err Fw_err; + +struct Op_err { + Fw_err type; + u_long code; + bool_t xdr_flag; + char _TKFAR *message; +}; +typedef struct Op_err Op_err; + +typedef char invk_context[CTXTLEN]; + +struct invk_result { + Op_err _TKFAR *err; + Op_arg _TKFAR *arg; + bool_t eof; +}; +typedef struct invk_result invk_result; + +struct invk_request { + char _TKFAR *category; + char _TKFAR *op; + char _TKFAR *vers; + char _TKFAR *locale; + u_long threshold; + invk_context context; + Op_arg _TKFAR *arg; +}; +typedef struct invk_request invk_request; + +struct more_request { + invk_context context; + u_long threshold; +}; +typedef struct more_request more_request; + +struct kill_request { + invk_context context; +}; +typedef struct kill_request kill_request; + +#define FW_KV_DELIM "=" +#define FW_KV_DELIM_CH '=' +#define FW_VK_DELIM "\n" +#define FW_VK_DELIM_CH '\n'; +#define FW_INPUT_VERS_VAL 1 +#define FW_INPUT_VERS_STR "1" +#define FW_OUTPUT_VERS_VAL 1 +#define FW_OUTPUT_VERS_STR "1" +#define FW_INPUT_VERS_KEY "_SUNW_AO_INPUT_VERS" +#define FW_OUTPUT_VERS_KEY "_SUNW_AO_OUTPUT_VERS" +#define FW_ROW_MARKER_KEY "_SUNW_AO_BEGIN_ROW" +#define FW_ROW_MARKER FW_ROW_MARKER_KEY FW_KV_DELIM FW_OUTPUT_VERS_STR \ + FW_VK_DELIM +#define FW_INPUT_VERS FW_INPUT_VERS_KEY FW_KV_DELIM FW_INPUT_VERS_STR \ + FW_VK_DELIM +#define FW_OUTPUT_VERS FW_OUTPUT_VERS_KEY FW_KV_DELIM FW_OUTPUT_VERS_STR \ + FW_VK_DELIM +#define FW_ERR_MSG_MAX 2047 +#define FW_UNIX_USER "UU" + +#define FW_SUCCESS 0 +#define FW_ERROR -1 +#define FW_TIMEOUT -2 + +#define SN_LOCALE_PATH_VAR "_SN_LOCALE_PATH" +#define SN_UNAME_VAR "_SN_UNAME" +#define SN_UID_VAR "_SN_UID" + +#include "fw_lib.h" + +#ifdef __cplusplus +} +#endif + +#endif /* !_FW_H */ diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_lib.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_lib.h new file mode 100644 index 0000000..36e7e40 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_lib.h @@ -0,0 +1,96 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1993 by Sun Microsystems, Inc. + */ + +#ifndef _FW_LIB_H +#define _FW_LIB_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __STDC__ + +extern short net_invoke(char _TKFAR *, char _TKFAR *, char _TKFAR *, + char _TKFAR *, u_long, u_long, char _TKFAR *, + invk_context, Op_arg _TKFAR * _TKFAR *, Op_err _TKFAR * _TKFAR *, ...); +extern short net_more(u_long, u_long, invk_context, Op_arg _TKFAR * _TKFAR *, + Op_err _TKFAR * _TKFAR *); +extern short net_end(u_long, invk_context, Op_err _TKFAR * _TKFAR *); +extern Op_arg _TKFAR *net_arg_init(void); +extern short net_arg_set(char _TKFAR *, ...); +extern short net_arg_markrow(void); +extern short net_arg_get(Op_arg _TKFAR *, char _TKFAR *, + char _TKFAR * _TKFAR *); +extern short net_arg_getnext(Op_arg _TKFAR *, char _TKFAR * _TKFAR *, + char _TKFAR * _TKFAR *); +extern short net_arg_nextrow(Op_arg _TKFAR *); +extern short net_arg_rowstart(Op_arg _TKFAR *); +extern short net_arg_reset(Op_arg _TKFAR *); +extern void net_arg_free(Op_arg _TKFAR *); +extern void net_err_free(Op_err _TKFAR *); +extern Op_arg _TKFAR *new_Op_arg(void); +extern void free_Op_arg(Op_arg _TKFAR *); +extern void free_Op_err(Op_err _TKFAR *); +extern short append_Op_arg(Op_arg _TKFAR *, char _TKFAR *, char _TKFAR *); +extern void fw_err_set(Op_err _TKFAR * _TKFAR *, Fw_err, u_long, ...); + +#ifdef _WINDOWS +extern void net_cleanup(void); +#endif + +#else + +extern short net_invoke(); +extern short net_more(); +extern short net_end(); +extern Op_arg _TKFAR *net_arg_init(); +extern short net_arg_set(); +extern short net_arg_markrow(); +extern short net_arg_get(); +extern short net_arg_getnext(); +extern short net_arg_nextrow(); +extern short net_arg_rowstart(); +extern short net_arg_reset(); +extern void net_arg_free(); +extern void net_err_free(); +extern Op_arg _TKFAR *new_Op_arg(); +extern void free_Op_arg(); +extern void free_Op_err(); +extern short append_Op_arg(); +extern void fw_err_set(); + +#ifdef _WINDOWS +extern void net_cleanup(); +#endif + +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* !_FW_LIB_H */ diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_rpc.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_rpc.h new file mode 100644 index 0000000..b44f3cd --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/fw_rpc.h @@ -0,0 +1,78 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1993 by Sun Microsystems, Inc. + */ + +/* + * This file contains definitions which are only of interest to the actual + * service daemon and client stubs. Normal framework users will not include + * this file. + */ + +#ifndef _FW_RPC_H +#define _FW_RPC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FW_NO_CONTEXT "Plead no context" + +#define FW_PROG ((unsigned long)(150006)) +#define FW_VERSION ((unsigned long)(1)) + +#define FW_INVOKE ((unsigned long)(1)) +#define FW_MORE ((unsigned long)(2)) +#define FW_KILL ((unsigned long)(3)) + + +/* the xdr functions */ + +extern bool_t _TKFAR _TKPASCAL xdr_Op_arg(XDR _TKFAR *, Op_arg _TKFAR *); +extern bool_t _TKFAR _TKPASCAL xdr_Fw_err(XDR _TKFAR *, Fw_err _TKFAR *); +extern bool_t _TKFAR _TKPASCAL xdr_Op_err(XDR _TKFAR *, Op_err _TKFAR *); +extern bool_t _TKFAR _TKPASCAL xdr_invk_context(XDR _TKFAR *, invk_context); +extern bool_t _TKFAR _TKPASCAL xdr_invk_result(XDR _TKFAR *, + invk_result _TKFAR *); +extern bool_t _TKFAR _TKPASCAL xdr_invk_request(XDR _TKFAR *, + invk_request _TKFAR *); +extern bool_t _TKFAR _TKPASCAL xdr_more_request(XDR _TKFAR *, + more_request _TKFAR *); +extern bool_t _TKFAR _TKPASCAL xdr_kill_request(XDR _TKFAR *, + kill_request _TKFAR *); + +#ifdef _WINDOWS +extern void thunk_xdrs(void); +extern void unthunk_xdrs(void); +extern FARPROC lp_xdr_invk_request, lp_xdr_invk_result; +extern FARPROC lp_xdr_more_request, lp_xdr_kill_request; +extern FARPROC lp_xdr_Op_err, lp_xdr_Op_arg; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !_FW_RPC_H */ diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/inc.flg b/usr/src/cmd/cmd-inet/usr.sbin/snoop/inc.flg new file mode 100644 index 0000000..d7246ea --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/inc.flg @@ -0,0 +1,30 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +find_files "s.*" usr/src/common/net/dhcp diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/nfs4_xdr.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/nfs4_xdr.c new file mode 100644 index 0000000..0ee7a6c --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/nfs4_xdr.c @@ -0,0 +1,2972 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file was initially generated using rpcgen. The rpcgen-erated + * code used tail recursion to implement linked lists which resulted + * in various crashes due to blown stacks. If the NFS4 protocol changes + * be sure to either use the NFS4-friendly rpcgen (doesn't use tail + * recursion) or do the xdr by hand. + * + * CAUTION: This file is kept in sync with it's uts counterpart: + * + * usr/src/uts/common/fs/nfs/nfs4_xdr.c + * + * However, it is not an exact copy. NEVER copy uts's nfs4_xdr.c + * directly over this file. Changes from the uts version must be + * integrated by hand into this file. + */ + +#include <rpcsvc/nfs4_prot.h> +#include <nfs/nfs4.h> +#include <malloc.h> + +#define IGNORE_RDWR_DATA + +extern int nfs4_skip_bytes; + +bool_t +xdr_nfs_ftype4(register XDR *xdrs, nfs_ftype4 *objp) +{ + + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfsstat4(register XDR *xdrs, nfsstat4 *objp) +{ + + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_bitmap4(register XDR *xdrs, bitmap4 *objp) +{ + + if (!xdr_array(xdrs, (char **)&objp->bitmap4_val, + (uint_t *)&objp->bitmap4_len, ~0, + sizeof (uint32_t), (xdrproc_t)xdr_uint32_t)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_offset4(register XDR *xdrs, offset4 *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_count4(register XDR *xdrs, count4 *objp) +{ + + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_length4(register XDR *xdrs, length4 *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_clientid4(register XDR *xdrs, clientid4 *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_seqid4(register XDR *xdrs, seqid4 *objp) +{ + + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_utf8string(register XDR *xdrs, utf8string *objp) +{ + + if (!xdr_bytes(xdrs, (char **)&objp->utf8string_val, + (uint_t *)&objp->utf8string_len, NFS4_MAX_UTF8STRING)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_component4(register XDR *xdrs, component4 *objp) +{ + + if (!xdr_utf8string(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_pathname4(register XDR *xdrs, pathname4 *objp) +{ + + if (!xdr_array(xdrs, (char **)&objp->pathname4_val, + (uint_t *)&objp->pathname4_len, NFS4_MAX_PATHNAME4, + sizeof (component4), (xdrproc_t)xdr_component4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfs_lockid4(register XDR *xdrs, nfs_lockid4 *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfs_cookie4(register XDR *xdrs, nfs_cookie4 *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_linktext4(register XDR *xdrs, linktext4 *objp) +{ + + if (!xdr_utf8string(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_sec_oid4(register XDR *xdrs, sec_oid4 *objp) +{ + + if (!xdr_bytes(xdrs, (char **)&objp->sec_oid4_val, + (uint_t *)&objp->sec_oid4_len, NFS4_MAX_SECOID4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_qop4(register XDR *xdrs, qop4 *objp) +{ + + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_mode4(register XDR *xdrs, mode4 *objp) +{ + + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_changeid4(register XDR *xdrs, changeid4 *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_verifier4(register XDR *xdrs, verifier4 objp) +{ + + if (!xdr_opaque(xdrs, objp, NFS4_VERIFIER_SIZE)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfstime4(register XDR *xdrs, nfstime4 *objp) +{ + + if (!xdr_int64_t(xdrs, &objp->seconds)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->nseconds)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_time_how4(register XDR *xdrs, time_how4 *objp) +{ + + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_settime4(register XDR *xdrs, settime4 *objp) +{ + + if (!xdr_time_how4(xdrs, &objp->set_it)) + return (FALSE); + switch (objp->set_it) { + case SET_TO_CLIENT_TIME4: + if (!xdr_nfstime4(xdrs, &objp->settime4_u.time)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_nfs_fh4(register XDR *xdrs, nfs_fh4 *objp) +{ + + if (!xdr_bytes(xdrs, (char **)&objp->nfs_fh4_val, + (uint_t *)&objp->nfs_fh4_len, NFS4_FHSIZE)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fsid4(register XDR *xdrs, fsid4 *objp) +{ + + if (!xdr_uint64_t(xdrs, &objp->major)) + return (FALSE); + if (!xdr_uint64_t(xdrs, &objp->minor)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fs_location4(register XDR *xdrs, fs_location4 *objp) +{ + + if (!xdr_array(xdrs, (char **)&objp->server.server_val, + (uint_t *)&objp->server.server_len, ~0, + sizeof (utf8string), (xdrproc_t)xdr_utf8string)) + return (FALSE); + if (!xdr_pathname4(xdrs, &objp->rootpath)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fs_locations4(register XDR *xdrs, fs_locations4 *objp) +{ + + if (!xdr_pathname4(xdrs, &objp->fs_root)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->locations.locations_val, + (uint_t *)&objp->locations.locations_len, ~0, + sizeof (fs_location4), (xdrproc_t)xdr_fs_location4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_acetype4(register XDR *xdrs, acetype4 *objp) +{ + + if (!xdr_u_int(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_aceflag4(register XDR *xdrs, aceflag4 *objp) +{ + + if (!xdr_u_int(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_acemask4(register XDR *xdrs, acemask4 *objp) +{ + + if (!xdr_u_int(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfsace4(register XDR *xdrs, nfsace4 *objp) +{ + if (!xdr_acetype4(xdrs, &objp->type)) + return (FALSE); + if (!xdr_aceflag4(xdrs, &objp->flag)) + return (FALSE); + if (!xdr_acemask4(xdrs, &objp->access_mask)) + return (FALSE); + if (xdrs->x_op == XDR_DECODE) { + objp->who.utf8string_val = NULL; + objp->who.utf8string_len = 0; + } + return (xdr_bytes(xdrs, (char **)&objp->who.utf8string_val, + (uint_t *)&objp->who.utf8string_len, + NFS4_MAX_UTF8STRING)); +} + +bool_t +xdr_specdata4(register XDR *xdrs, specdata4 *objp) +{ + + if (!xdr_uint32_t(xdrs, &objp->specdata1)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->specdata2)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_supported_attrs(register XDR *xdrs, fattr4_supported_attrs *objp) +{ + + if (!xdr_bitmap4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_type(register XDR *xdrs, fattr4_type *objp) +{ + + if (!xdr_nfs_ftype4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_fh_expire_type(register XDR *xdrs, fattr4_fh_expire_type *objp) +{ + + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_change(register XDR *xdrs, fattr4_change *objp) +{ + + if (!xdr_changeid4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_size(register XDR *xdrs, fattr4_size *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_link_support(register XDR *xdrs, fattr4_link_support *objp) +{ + + if (!xdr_bool(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_symlink_support(register XDR *xdrs, fattr4_symlink_support *objp) +{ + + if (!xdr_bool(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_named_attr(register XDR *xdrs, fattr4_named_attr *objp) +{ + + if (!xdr_bool(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_fsid(register XDR *xdrs, fattr4_fsid *objp) +{ + + if (!xdr_fsid4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_unique_handles(register XDR *xdrs, fattr4_unique_handles *objp) +{ + + if (!xdr_bool(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_lease_time(register XDR *xdrs, fattr4_lease_time *objp) +{ + + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_rdattr_error(register XDR *xdrs, fattr4_rdattr_error *objp) +{ + + if (!xdr_nfsstat4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_acl(register XDR *xdrs, fattr4_acl *objp) +{ + + if (!xdr_array(xdrs, (char **)&objp->fattr4_acl_val, + (uint_t *)&objp->fattr4_acl_len, ~0, + sizeof (nfsace4), (xdrproc_t)xdr_nfsace4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_aclsupport(register XDR *xdrs, fattr4_aclsupport *objp) +{ + + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_archive(register XDR *xdrs, fattr4_archive *objp) +{ + + if (!xdr_bool(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_cansettime(register XDR *xdrs, fattr4_cansettime *objp) +{ + + if (!xdr_bool(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_case_insensitive(register XDR *xdrs, fattr4_case_insensitive *objp) +{ + + if (!xdr_bool(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_case_preserving(register XDR *xdrs, fattr4_case_preserving *objp) +{ + + if (!xdr_bool(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_chown_restricted(register XDR *xdrs, fattr4_chown_restricted *objp) +{ + + if (!xdr_bool(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_fileid(register XDR *xdrs, fattr4_fileid *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_files_avail(register XDR *xdrs, fattr4_files_avail *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_filehandle(register XDR *xdrs, fattr4_filehandle *objp) +{ + + if (!xdr_nfs_fh4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_files_free(register XDR *xdrs, fattr4_files_free *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_files_total(register XDR *xdrs, fattr4_files_total *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_fs_locations(register XDR *xdrs, fattr4_fs_locations *objp) +{ + + if (!xdr_fs_locations4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_hidden(register XDR *xdrs, fattr4_hidden *objp) +{ + + if (!xdr_bool(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_homogeneous(register XDR *xdrs, fattr4_homogeneous *objp) +{ + + if (!xdr_bool(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_maxfilesize(register XDR *xdrs, fattr4_maxfilesize *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_maxlink(register XDR *xdrs, fattr4_maxlink *objp) +{ + + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_maxname(register XDR *xdrs, fattr4_maxname *objp) +{ + + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_maxread(register XDR *xdrs, fattr4_maxread *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_maxwrite(register XDR *xdrs, fattr4_maxwrite *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_mimetype(register XDR *xdrs, fattr4_mimetype *objp) +{ + + if (!xdr_utf8string(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_mode(register XDR *xdrs, fattr4_mode *objp) +{ + + if (!xdr_mode4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_mounted_on_fileid(register XDR *xdrs, fattr4_mounted_on_fileid *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_no_trunc(register XDR *xdrs, fattr4_no_trunc *objp) +{ + + if (!xdr_bool(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_numlinks(register XDR *xdrs, fattr4_numlinks *objp) +{ + + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_owner(register XDR *xdrs, fattr4_owner *objp) +{ + + if (!xdr_utf8string(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_owner_group(register XDR *xdrs, fattr4_owner_group *objp) +{ + + if (!xdr_utf8string(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_quota_avail_hard(register XDR *xdrs, fattr4_quota_avail_hard *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_quota_avail_soft(register XDR *xdrs, fattr4_quota_avail_soft *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_quota_used(register XDR *xdrs, fattr4_quota_used *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_rawdev(register XDR *xdrs, fattr4_rawdev *objp) +{ + + if (!xdr_specdata4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_space_avail(register XDR *xdrs, fattr4_space_avail *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_space_free(register XDR *xdrs, fattr4_space_free *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_space_total(register XDR *xdrs, fattr4_space_total *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_space_used(register XDR *xdrs, fattr4_space_used *objp) +{ + + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_system(register XDR *xdrs, fattr4_system *objp) +{ + + if (!xdr_bool(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_time_access(register XDR *xdrs, fattr4_time_access *objp) +{ + + if (!xdr_nfstime4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_time_access_set(register XDR *xdrs, fattr4_time_access_set *objp) +{ + + if (!xdr_settime4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_time_backup(register XDR *xdrs, fattr4_time_backup *objp) +{ + + if (!xdr_nfstime4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_time_create(register XDR *xdrs, fattr4_time_create *objp) +{ + + if (!xdr_nfstime4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_time_delta(register XDR *xdrs, fattr4_time_delta *objp) +{ + + if (!xdr_nfstime4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_time_metadata(register XDR *xdrs, fattr4_time_metadata *objp) +{ + + if (!xdr_nfstime4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_time_modify(register XDR *xdrs, fattr4_time_modify *objp) +{ + + if (!xdr_nfstime4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_time_modify_set(register XDR *xdrs, fattr4_time_modify_set *objp) +{ + + if (!xdr_settime4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_attrlist4(register XDR *xdrs, attrlist4 *objp) +{ + + if (!xdr_bytes(xdrs, (char **)&objp->attrlist4_val, + (uint_t *)&objp->attrlist4_len, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4(register XDR *xdrs, fattr4 *objp) +{ + + if (!xdr_bitmap4(xdrs, &objp->attrmask)) + return (FALSE); + if (!xdr_attrlist4(xdrs, &objp->attr_vals)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_change_info4(register XDR *xdrs, change_info4 *objp) +{ + + if (!xdr_bool(xdrs, &objp->atomic)) + return (FALSE); + if (!xdr_changeid4(xdrs, &objp->before)) + return (FALSE); + if (!xdr_changeid4(xdrs, &objp->after)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_clientaddr4(register XDR *xdrs, clientaddr4 *objp) +{ + + if (!xdr_string(xdrs, &objp->r_netid, ~0)) + return (FALSE); + if (!xdr_string(xdrs, &objp->r_addr, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_cb_client4(register XDR *xdrs, cb_client4 *objp) +{ + + if (!xdr_uint32_t(xdrs, &objp->cb_program)) + return (FALSE); + if (!xdr_clientaddr4(xdrs, &objp->cb_location)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_stateid4(register XDR *xdrs, stateid4 *objp) +{ + + if (!xdr_uint32_t(xdrs, &objp->seqid)) + return (FALSE); + if (!xdr_opaque(xdrs, objp->other, 12)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfs_client_id4(register XDR *xdrs, nfs_client_id4 *objp) +{ + + if (!xdr_verifier4(xdrs, objp->verifier)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->id.id_val, + (uint_t *)&objp->id.id_len, NFS4_OPAQUE_LIMIT)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_open_owner4(register XDR *xdrs, open_owner4 *objp) +{ + + if (!xdr_clientid4(xdrs, &objp->clientid)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->owner.owner_val, + (uint_t *)&objp->owner.owner_len, NFS4_OPAQUE_LIMIT)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_lock_owner4(register XDR *xdrs, lock_owner4 *objp) +{ + + if (!xdr_clientid4(xdrs, &objp->clientid)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->owner.owner_val, + (uint_t *)&objp->owner.owner_len, NFS4_OPAQUE_LIMIT)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfs_lock_type4(register XDR *xdrs, nfs_lock_type4 *objp) +{ + + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_ACCESS4args(register XDR *xdrs, ACCESS4args *objp) +{ + + if (!xdr_uint32_t(xdrs, &objp->access)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_ACCESS4resok(register XDR *xdrs, ACCESS4resok *objp) +{ + + if (!xdr_uint32_t(xdrs, &objp->supported)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->access)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_ACCESS4res(register XDR *xdrs, ACCESS4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_ACCESS4resok(xdrs, &objp->ACCESS4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_CLOSE4args(register XDR *xdrs, CLOSE4args *objp) +{ + + if (!xdr_seqid4(xdrs, &objp->seqid)) + return (FALSE); + if (!xdr_stateid4(xdrs, &objp->open_stateid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CLOSE4res(register XDR *xdrs, CLOSE4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_stateid4(xdrs, &objp->CLOSE4res_u.open_stateid)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_COMMIT4args(register XDR *xdrs, COMMIT4args *objp) +{ + + if (!xdr_offset4(xdrs, &objp->offset)) + return (FALSE); + if (!xdr_count4(xdrs, &objp->count)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_COMMIT4resok(register XDR *xdrs, COMMIT4resok *objp) +{ + + if (!xdr_verifier4(xdrs, objp->writeverf)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_COMMIT4res(register XDR *xdrs, COMMIT4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_COMMIT4resok(xdrs, &objp->COMMIT4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_createtype4(register XDR *xdrs, createtype4 *objp) +{ + + if (!xdr_nfs_ftype4(xdrs, &objp->type)) + return (FALSE); + switch (objp->type) { + case NF4LNK: + if (!xdr_linktext4(xdrs, &objp->createtype4_u.linkdata)) + return (FALSE); + break; + case NF4BLK: + case NF4CHR: + if (!xdr_specdata4(xdrs, &objp->createtype4_u.devdata)) + return (FALSE); + break; + case NF4SOCK: + case NF4FIFO: + case NF4DIR: + break; + } + return (TRUE); +} + +bool_t +xdr_CREATE4args(register XDR *xdrs, CREATE4args *objp) +{ + + if (!xdr_createtype4(xdrs, &objp->objtype)) + return (FALSE); + if (!xdr_component4(xdrs, &objp->objname)) + return (FALSE); + if (!xdr_fattr4(xdrs, &objp->createattrs)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CREATE4resok(register XDR *xdrs, CREATE4resok *objp) +{ + + if (!xdr_change_info4(xdrs, &objp->cinfo)) + return (FALSE); + if (!xdr_bitmap4(xdrs, &objp->attrset)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CREATE4res(register XDR *xdrs, CREATE4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_CREATE4resok(xdrs, &objp->CREATE4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_DELEGPURGE4args(register XDR *xdrs, DELEGPURGE4args *objp) +{ + + if (!xdr_clientid4(xdrs, &objp->clientid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_DELEGPURGE4res(register XDR *xdrs, DELEGPURGE4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_DELEGRETURN4args(register XDR *xdrs, DELEGRETURN4args *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->deleg_stateid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_DELEGRETURN4res(register XDR *xdrs, DELEGRETURN4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_GETATTR4args(register XDR *xdrs, GETATTR4args *objp) +{ + + if (!xdr_bitmap4(xdrs, &objp->attr_request)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_GETATTR4resok(register XDR *xdrs, GETATTR4resok *objp) +{ + + if (!xdr_fattr4(xdrs, &objp->obj_attributes)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_GETATTR4res(register XDR *xdrs, GETATTR4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_GETATTR4resok(xdrs, &objp->GETATTR4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_GETFH4resok(register XDR *xdrs, GETFH4resok *objp) +{ + + if (!xdr_nfs_fh4(xdrs, &objp->object)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_GETFH4res(register XDR *xdrs, GETFH4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_GETFH4resok(xdrs, &objp->GETFH4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_LINK4args(register XDR *xdrs, LINK4args *objp) +{ + + if (!xdr_component4(xdrs, &objp->newname)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_LINK4resok(register XDR *xdrs, LINK4resok *objp) +{ + + if (!xdr_change_info4(xdrs, &objp->cinfo)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_LINK4res(register XDR *xdrs, LINK4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_LINK4resok(xdrs, &objp->LINK4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_open_to_lock_owner4(register XDR *xdrs, open_to_lock_owner4 *objp) +{ + + if (!xdr_seqid4(xdrs, &objp->open_seqid)) + return (FALSE); + if (!xdr_stateid4(xdrs, &objp->open_stateid)) + return (FALSE); + if (!xdr_seqid4(xdrs, &objp->lock_seqid)) + return (FALSE); + if (!xdr_lock_owner4(xdrs, &objp->lock_owner)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_exist_lock_owner4(register XDR *xdrs, exist_lock_owner4 *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->lock_stateid)) + return (FALSE); + if (!xdr_seqid4(xdrs, &objp->lock_seqid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_locker4(register XDR *xdrs, locker4 *objp) +{ + + if (!xdr_bool(xdrs, &objp->new_lock_owner)) + return (FALSE); + switch (objp->new_lock_owner) { + case TRUE: + if (!xdr_open_to_lock_owner4(xdrs, &objp->locker4_u.open_owner)) + return (FALSE); + break; + case FALSE: + if (!xdr_exist_lock_owner4(xdrs, &objp->locker4_u.lock_owner)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_LOCK4args(register XDR *xdrs, LOCK4args *objp) +{ + + if (!xdr_nfs_lock_type4(xdrs, &objp->locktype)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->reclaim)) + return (FALSE); + if (!xdr_offset4(xdrs, &objp->offset)) + return (FALSE); + if (!xdr_length4(xdrs, &objp->length)) + return (FALSE); + if (!xdr_locker4(xdrs, &objp->locker)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_LOCK4denied(register XDR *xdrs, LOCK4denied *objp) +{ + + if (!xdr_offset4(xdrs, &objp->offset)) + return (FALSE); + if (!xdr_length4(xdrs, &objp->length)) + return (FALSE); + if (!xdr_nfs_lock_type4(xdrs, &objp->locktype)) + return (FALSE); + if (!xdr_lock_owner4(xdrs, &objp->owner)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_LOCK4resok(register XDR *xdrs, LOCK4resok *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->lock_stateid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_LOCK4res(register XDR *xdrs, LOCK4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_LOCK4resok(xdrs, &objp->LOCK4res_u.resok4)) + return (FALSE); + break; + case NFS4ERR_DENIED: + if (!xdr_LOCK4denied(xdrs, &objp->LOCK4res_u.denied)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_LOCKT4args(register XDR *xdrs, LOCKT4args *objp) +{ + + if (!xdr_nfs_lock_type4(xdrs, &objp->locktype)) + return (FALSE); + if (!xdr_offset4(xdrs, &objp->offset)) + return (FALSE); + if (!xdr_length4(xdrs, &objp->length)) + return (FALSE); + if (!xdr_lock_owner4(xdrs, &objp->owner)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_LOCKT4res(register XDR *xdrs, LOCKT4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4ERR_DENIED: + if (!xdr_LOCK4denied(xdrs, &objp->LOCKT4res_u.denied)) + return (FALSE); + break; + case NFS4_OK: + break; + } + return (TRUE); +} + +bool_t +xdr_LOCKU4args(register XDR *xdrs, LOCKU4args *objp) +{ + + if (!xdr_nfs_lock_type4(xdrs, &objp->locktype)) + return (FALSE); + if (!xdr_seqid4(xdrs, &objp->seqid)) + return (FALSE); + if (!xdr_stateid4(xdrs, &objp->lock_stateid)) + return (FALSE); + if (!xdr_offset4(xdrs, &objp->offset)) + return (FALSE); + if (!xdr_length4(xdrs, &objp->length)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_LOCKU4res(register XDR *xdrs, LOCKU4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_stateid4(xdrs, &objp->LOCKU4res_u.lock_stateid)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_LOOKUP4args(register XDR *xdrs, LOOKUP4args *objp) +{ + + if (!xdr_component4(xdrs, &objp->objname)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_LOOKUP4res(register XDR *xdrs, LOOKUP4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_LOOKUPP4res(register XDR *xdrs, LOOKUPP4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_NVERIFY4args(register XDR *xdrs, NVERIFY4args *objp) +{ + + if (!xdr_fattr4(xdrs, &objp->obj_attributes)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_NVERIFY4res(register XDR *xdrs, NVERIFY4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_createmode4(register XDR *xdrs, createmode4 *objp) +{ + + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_createhow4(register XDR *xdrs, createhow4 *objp) +{ + + if (!xdr_createmode4(xdrs, &objp->mode)) + return (FALSE); + switch (objp->mode) { + case UNCHECKED4: + case GUARDED4: + if (!xdr_fattr4(xdrs, &objp->createhow4_u.createattrs)) + return (FALSE); + break; + case EXCLUSIVE4: + if (!xdr_verifier4(xdrs, objp->createhow4_u.createverf)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_opentype4(register XDR *xdrs, opentype4 *objp) +{ + + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_openflag4(register XDR *xdrs, openflag4 *objp) +{ + + if (!xdr_opentype4(xdrs, &objp->opentype)) + return (FALSE); + switch (objp->opentype) { + case OPEN4_CREATE: + if (!xdr_createhow4(xdrs, &objp->openflag4_u.how)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_limit_by4(register XDR *xdrs, limit_by4 *objp) +{ + + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfs_modified_limit4(register XDR *xdrs, nfs_modified_limit4 *objp) +{ + + if (!xdr_uint32_t(xdrs, &objp->num_blocks)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->bytes_per_block)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfs_space_limit4(register XDR *xdrs, nfs_space_limit4 *objp) +{ + + if (!xdr_limit_by4(xdrs, &objp->limitby)) + return (FALSE); + switch (objp->limitby) { + case NFS_LIMIT_SIZE: + if (!xdr_uint64_t(xdrs, &objp->nfs_space_limit4_u.filesize)) + return (FALSE); + break; + case NFS_LIMIT_BLOCKS: + if (!xdr_nfs_modified_limit4(xdrs, &objp->nfs_space_limit4_u. + mod_blocks)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_open_delegation_type4(register XDR *xdrs, open_delegation_type4 *objp) +{ + + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_open_claim_type4(register XDR *xdrs, open_claim_type4 *objp) +{ + + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_open_claim_delegate_cur4(register XDR *xdrs, open_claim_delegate_cur4 *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->delegate_stateid)) + return (FALSE); + if (!xdr_component4(xdrs, &objp->file)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_open_claim4(register XDR *xdrs, open_claim4 *objp) +{ + + if (!xdr_open_claim_type4(xdrs, &objp->claim)) + return (FALSE); + switch (objp->claim) { + case CLAIM_NULL: + if (!xdr_component4(xdrs, &objp->open_claim4_u.file)) + return (FALSE); + break; + case CLAIM_PREVIOUS: + if (!xdr_open_delegation_type4(xdrs, &objp->open_claim4_u. + delegate_type)) + return (FALSE); + break; + case CLAIM_DELEGATE_CUR: + if (!xdr_open_claim_delegate_cur4(xdrs, &objp->open_claim4_u. + delegate_cur_info)) + return (FALSE); + break; + case CLAIM_DELEGATE_PREV: + if (!xdr_component4(xdrs, &objp->open_claim4_u. + file_delegate_prev)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_OPEN4args(register XDR *xdrs, OPEN4args *objp) +{ + + if (!xdr_seqid4(xdrs, &objp->seqid)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->share_access)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->share_deny)) + return (FALSE); + if (!xdr_open_owner4(xdrs, &objp->owner)) + return (FALSE); + if (!xdr_openflag4(xdrs, &objp->openhow)) + return (FALSE); + if (!xdr_open_claim4(xdrs, &objp->claim)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_open_read_delegation4(register XDR *xdrs, open_read_delegation4 *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->stateid)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->recall)) + return (FALSE); + if (!xdr_nfsace4(xdrs, &objp->permissions)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_open_write_delegation4(register XDR *xdrs, open_write_delegation4 *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->stateid)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->recall)) + return (FALSE); + if (!xdr_nfs_space_limit4(xdrs, &objp->space_limit)) + return (FALSE); + if (!xdr_nfsace4(xdrs, &objp->permissions)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_open_delegation4(register XDR *xdrs, open_delegation4 *objp) +{ + + if (!xdr_open_delegation_type4(xdrs, &objp->delegation_type)) + return (FALSE); + switch (objp->delegation_type) { + case OPEN_DELEGATE_NONE: + break; + case OPEN_DELEGATE_READ: + if (!xdr_open_read_delegation4(xdrs, &objp->open_delegation4_u. + read)) + return (FALSE); + break; + case OPEN_DELEGATE_WRITE: + if (!xdr_open_write_delegation4(xdrs, &objp->open_delegation4_u. + write)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_OPEN4resok(register XDR *xdrs, OPEN4resok *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->stateid)) + return (FALSE); + if (!xdr_change_info4(xdrs, &objp->cinfo)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->rflags)) + return (FALSE); + if (!xdr_bitmap4(xdrs, &objp->attrset)) + return (FALSE); + if (!xdr_open_delegation4(xdrs, &objp->delegation)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_OPEN4res(register XDR *xdrs, OPEN4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_OPEN4resok(xdrs, &objp->OPEN4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_OPENATTR4args(register XDR *xdrs, OPENATTR4args *objp) +{ + + if (!xdr_bool(xdrs, &objp->createdir)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_OPENATTR4res(register XDR *xdrs, OPENATTR4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_OPEN_CONFIRM4args(register XDR *xdrs, OPEN_CONFIRM4args *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->open_stateid)) + return (FALSE); + if (!xdr_seqid4(xdrs, &objp->seqid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_OPEN_CONFIRM4resok(register XDR *xdrs, OPEN_CONFIRM4resok *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->open_stateid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_OPEN_CONFIRM4res(register XDR *xdrs, OPEN_CONFIRM4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_OPEN_CONFIRM4resok(xdrs, &objp->OPEN_CONFIRM4res_u. + resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_OPEN_DOWNGRADE4args(register XDR *xdrs, OPEN_DOWNGRADE4args *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->open_stateid)) + return (FALSE); + if (!xdr_seqid4(xdrs, &objp->seqid)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->share_access)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->share_deny)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_OPEN_DOWNGRADE4resok(register XDR *xdrs, OPEN_DOWNGRADE4resok *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->open_stateid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_OPEN_DOWNGRADE4res(register XDR *xdrs, OPEN_DOWNGRADE4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_OPEN_DOWNGRADE4resok(xdrs, &objp->OPEN_DOWNGRADE4res_u. + resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_PUTFH4args(register XDR *xdrs, PUTFH4args *objp) +{ + + if (!xdr_nfs_fh4(xdrs, &objp->object)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_PUTFH4res(register XDR *xdrs, PUTFH4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_PUTPUBFH4res(register XDR *xdrs, PUTPUBFH4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_PUTROOTFH4res(register XDR *xdrs, PUTROOTFH4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_READ4args(register XDR *xdrs, READ4args *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->stateid)) + return (FALSE); + if (!xdr_offset4(xdrs, &objp->offset)) + return (FALSE); + if (!xdr_count4(xdrs, &objp->count)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_READ4resok(register XDR *xdrs, READ4resok *objp) +{ + + if (!xdr_bool(xdrs, &objp->eof)) + return (FALSE); + +#ifdef IGNORE_RDWR_DATA + /* + * Try to get length of read, and if that + * fails, default to 0. Don't return FALSE + * because the other read info will not be + * displayed. + */ + objp->data.data_val = NULL; + if (!xdr_u_int(xdrs, &objp->data.data_len)) + objp->data.data_len = 0; + nfs4_skip_bytes = objp->data.data_len; +#else + if (!xdr_bytes(xdrs, (char **)&objp->data.data_val, + (uint_t *)&objp->data.data_len, ~0)) + return (FALSE); +#endif + return (TRUE); +} + +bool_t +xdr_READ4res(register XDR *xdrs, READ4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_READ4resok(xdrs, &objp->READ4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_READDIR4args(register XDR *xdrs, READDIR4args *objp) +{ + + if (!xdr_nfs_cookie4(xdrs, &objp->cookie)) + return (FALSE); + if (!xdr_verifier4(xdrs, objp->cookieverf)) + return (FALSE); + if (!xdr_count4(xdrs, &objp->dircount)) + return (FALSE); + if (!xdr_count4(xdrs, &objp->maxcount)) + return (FALSE); + if (!xdr_bitmap4(xdrs, &objp->attr_request)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_entry4(register XDR *xdrs, entry4 *objp) +{ + + entry4 *tmp_entry4; + bool_t more_data = TRUE; + bool_t first_objp = TRUE; + + while (more_data) { + + if (!xdr_nfs_cookie4(xdrs, &objp->cookie)) + return (FALSE); + if (!xdr_component4(xdrs, &objp->name)) + return (FALSE); + if (!xdr_fattr4(xdrs, &objp->attrs)) + return (FALSE); + + if (xdrs->x_op == XDR_DECODE) { + + void bzero(); + + if (!xdr_bool(xdrs, &more_data)) + return (FALSE); + + if (!more_data) { + objp->nextentry = NULL; + break; + } + + objp->nextentry = (entry4 *) + mem_alloc(sizeof (entry4)); + if (objp->nextentry == NULL) + return (NULL); + bzero(objp->nextentry, sizeof (entry4)); + objp = objp->nextentry; + + } else if (xdrs->x_op == XDR_ENCODE) { + objp = objp->nextentry; + if (!objp) + more_data = FALSE; + + if (!xdr_bool(xdrs, &more_data)) + return (FALSE); + } else { + tmp_entry4 = objp; + objp = objp->nextentry; + if (!objp) + more_data = FALSE; + if (!xdr_bool(xdrs, &more_data)) + return (FALSE); + if (!first_objp) + mem_free(tmp_entry4, sizeof (entry4)); + else + first_objp = FALSE; + } + } + return (TRUE); +} + +bool_t +xdr_dirlist4(register XDR *xdrs, dirlist4 *objp) +{ + + if (!xdr_pointer(xdrs, (char **)&objp->entries, sizeof (entry4), + (xdrproc_t)xdr_entry4)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->eof)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_READDIR4resok(register XDR *xdrs, READDIR4resok *objp) +{ + + if (!xdr_verifier4(xdrs, objp->cookieverf)) + return (FALSE); + if (!xdr_dirlist4(xdrs, &objp->reply)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_READDIR4res(register XDR *xdrs, READDIR4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_READDIR4resok(xdrs, &objp->READDIR4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_READLINK4resok(register XDR *xdrs, READLINK4resok *objp) +{ + + if (!xdr_linktext4(xdrs, &objp->link)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_READLINK4res(register XDR *xdrs, READLINK4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_READLINK4resok(xdrs, &objp->READLINK4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_REMOVE4args(register XDR *xdrs, REMOVE4args *objp) +{ + + if (!xdr_component4(xdrs, &objp->target)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_REMOVE4resok(register XDR *xdrs, REMOVE4resok *objp) +{ + + if (!xdr_change_info4(xdrs, &objp->cinfo)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_REMOVE4res(register XDR *xdrs, REMOVE4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_REMOVE4resok(xdrs, &objp->REMOVE4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_RENAME4args(register XDR *xdrs, RENAME4args *objp) +{ + + if (!xdr_component4(xdrs, &objp->oldname)) + return (FALSE); + if (!xdr_component4(xdrs, &objp->newname)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_RENAME4resok(register XDR *xdrs, RENAME4resok *objp) +{ + + if (!xdr_change_info4(xdrs, &objp->source_cinfo)) + return (FALSE); + if (!xdr_change_info4(xdrs, &objp->target_cinfo)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_RENAME4res(register XDR *xdrs, RENAME4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_RENAME4resok(xdrs, &objp->RENAME4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_RENEW4args(register XDR *xdrs, RENEW4args *objp) +{ + + if (!xdr_clientid4(xdrs, &objp->clientid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_RENEW4res(register XDR *xdrs, RENEW4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_RESTOREFH4res(register XDR *xdrs, RESTOREFH4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SAVEFH4res(register XDR *xdrs, SAVEFH4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SECINFO4args(register XDR *xdrs, SECINFO4args *objp) +{ + + if (!xdr_component4(xdrs, &objp->name)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_rpc_gss_svc_t(register XDR *xdrs, rpc_gss_svc_t *objp) +{ + + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_rpcsec_gss_info(register XDR *xdrs, rpcsec_gss_info *objp) +{ + + if (!xdr_sec_oid4(xdrs, &objp->oid)) + return (FALSE); + if (!xdr_qop4(xdrs, &objp->qop)) + return (FALSE); + if (!xdr_rpc_gss_svc_t(xdrs, &objp->service)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_secinfo4(register XDR *xdrs, secinfo4 *objp) +{ + + if (!xdr_uint32_t(xdrs, &objp->flavor)) + return (FALSE); + switch (objp->flavor) { + case RPCSEC_GSS: + if (!xdr_rpcsec_gss_info(xdrs, &objp->secinfo4_u.flavor_info)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_SECINFO4resok(register XDR *xdrs, SECINFO4resok *objp) +{ + + if (!xdr_array(xdrs, (char **)&objp->SECINFO4resok_val, + (uint_t *)&objp->SECINFO4resok_len, ~0, + sizeof (secinfo4), (xdrproc_t)xdr_secinfo4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SECINFO4res(register XDR *xdrs, SECINFO4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_SECINFO4resok(xdrs, &objp->SECINFO4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_SETATTR4args(register XDR *xdrs, SETATTR4args *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->stateid)) + return (FALSE); + if (!xdr_fattr4(xdrs, &objp->obj_attributes)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SETATTR4res(register XDR *xdrs, SETATTR4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + if (!xdr_bitmap4(xdrs, &objp->attrsset)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SETCLIENTID4args(register XDR *xdrs, SETCLIENTID4args *objp) +{ + + if (!xdr_nfs_client_id4(xdrs, &objp->client)) + return (FALSE); + if (!xdr_cb_client4(xdrs, &objp->callback)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->callback_ident)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SETCLIENTID4resok(register XDR *xdrs, SETCLIENTID4resok *objp) +{ + + if (!xdr_clientid4(xdrs, &objp->clientid)) + return (FALSE); + if (!xdr_verifier4(xdrs, objp->setclientid_confirm)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SETCLIENTID4res(register XDR *xdrs, SETCLIENTID4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_SETCLIENTID4resok(xdrs, &objp->SETCLIENTID4res_u. + resok4)) + return (FALSE); + break; + case NFS4ERR_CLID_INUSE: + if (!xdr_clientaddr4(xdrs, &objp->SETCLIENTID4res_u. + client_using)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_SETCLIENTID_CONFIRM4args(register XDR *xdrs, SETCLIENTID_CONFIRM4args *objp) +{ + + if (!xdr_clientid4(xdrs, &objp->clientid)) + return (FALSE); + if (!xdr_verifier4(xdrs, objp->setclientid_confirm)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SETCLIENTID_CONFIRM4res(register XDR *xdrs, SETCLIENTID_CONFIRM4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_VERIFY4args(register XDR *xdrs, VERIFY4args *objp) +{ + + if (!xdr_fattr4(xdrs, &objp->obj_attributes)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_VERIFY4res(register XDR *xdrs, VERIFY4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_stable_how4(register XDR *xdrs, stable_how4 *objp) +{ + + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_WRITE4args(register XDR *xdrs, WRITE4args *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->stateid)) + return (FALSE); + if (!xdr_offset4(xdrs, &objp->offset)) + return (FALSE); + if (!xdr_stable_how4(xdrs, &objp->stable)) + return (FALSE); + +#ifdef IGNORE_RDWR_DATA + /* + * try to get length of write, and if that + * fails, default to 0. Don't return FALSE + * because the other write info will not be + * displayed (write stateid). + */ + objp->data.data_val = NULL; + if (!xdr_u_int(xdrs, &objp->data.data_len)) + objp->data.data_len = 0; + nfs4_skip_bytes = objp->data.data_len; +#else + if (!xdr_bytes(xdrs, (char **)&objp->data.data_val, + (uint_t *)&objp->data.data_len, ~0)) + return (FALSE); +#endif + return (TRUE); +} + +bool_t +xdr_WRITE4resok(register XDR *xdrs, WRITE4resok *objp) +{ + + if (!xdr_count4(xdrs, &objp->count)) + return (FALSE); + if (!xdr_stable_how4(xdrs, &objp->committed)) + return (FALSE); + if (!xdr_verifier4(xdrs, objp->writeverf)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_WRITE4res(register XDR *xdrs, WRITE4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_WRITE4resok(xdrs, &objp->WRITE4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_RELEASE_LOCKOWNER4args(register XDR *xdrs, RELEASE_LOCKOWNER4args *objp) +{ + + if (!xdr_lock_owner4(xdrs, &objp->lock_owner)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_RELEASE_LOCKOWNER4res(register XDR *xdrs, RELEASE_LOCKOWNER4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_ILLEGAL4res(register XDR *xdrs, ILLEGAL4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfs_opnum4(register XDR *xdrs, nfs_opnum4 *objp) +{ + + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfs_argop4(register XDR *xdrs, nfs_argop4 *objp) +{ + nfs4_skip_bytes = 0; + if (!xdr_nfs_opnum4(xdrs, &objp->argop)) + return (FALSE); + switch (objp->argop) { + case OP_ACCESS: + if (!xdr_ACCESS4args(xdrs, &objp->nfs_argop4_u.opaccess)) + return (FALSE); + break; + case OP_CLOSE: + if (!xdr_CLOSE4args(xdrs, &objp->nfs_argop4_u.opclose)) + return (FALSE); + break; + case OP_COMMIT: + if (!xdr_COMMIT4args(xdrs, &objp->nfs_argop4_u.opcommit)) + return (FALSE); + break; + case OP_CREATE: + if (!xdr_CREATE4args(xdrs, &objp->nfs_argop4_u.opcreate)) + return (FALSE); + break; + case OP_DELEGPURGE: + if (!xdr_DELEGPURGE4args(xdrs, &objp->nfs_argop4_u. + opdelegpurge)) + return (FALSE); + break; + case OP_DELEGRETURN: + if (!xdr_DELEGRETURN4args(xdrs, &objp->nfs_argop4_u. + opdelegreturn)) + return (FALSE); + break; + case OP_GETATTR: + if (!xdr_GETATTR4args(xdrs, &objp->nfs_argop4_u. + opgetattr)) + return (FALSE); + break; + case OP_GETFH: + break; + case OP_LINK: + if (!xdr_LINK4args(xdrs, &objp->nfs_argop4_u.oplink)) + return (FALSE); + break; + case OP_LOCK: + if (!xdr_LOCK4args(xdrs, &objp->nfs_argop4_u.oplock)) + return (FALSE); + break; + case OP_LOCKT: + if (!xdr_LOCKT4args(xdrs, &objp->nfs_argop4_u.oplockt)) + return (FALSE); + break; + case OP_LOCKU: + if (!xdr_LOCKU4args(xdrs, &objp->nfs_argop4_u.oplocku)) + return (FALSE); + break; + case OP_LOOKUP: + if (!xdr_LOOKUP4args(xdrs, &objp->nfs_argop4_u.oplookup)) + return (FALSE); + break; + case OP_LOOKUPP: + break; + case OP_NVERIFY: + if (!xdr_NVERIFY4args(xdrs, &objp->nfs_argop4_u.opnverify)) + return (FALSE); + break; + case OP_OPEN: + if (!xdr_OPEN4args(xdrs, &objp->nfs_argop4_u.opopen)) + return (FALSE); + break; + case OP_OPENATTR: + if (!xdr_OPENATTR4args(xdrs, &objp->nfs_argop4_u.opopenattr)) + return (FALSE); + break; + case OP_OPEN_CONFIRM: + if (!xdr_OPEN_CONFIRM4args(xdrs, &objp->nfs_argop4_u. + opopen_confirm)) + return (FALSE); + break; + case OP_OPEN_DOWNGRADE: + if (!xdr_OPEN_DOWNGRADE4args(xdrs, &objp->nfs_argop4_u. + opopen_downgrade)) + return (FALSE); + break; + case OP_PUTFH: + if (!xdr_PUTFH4args(xdrs, &objp->nfs_argop4_u.opputfh)) + return (FALSE); + break; + case OP_PUTPUBFH: + break; + case OP_PUTROOTFH: + break; + case OP_READ: + if (!xdr_READ4args(xdrs, &objp->nfs_argop4_u.opread)) + return (FALSE); + break; + case OP_READDIR: + if (!xdr_READDIR4args(xdrs, &objp->nfs_argop4_u.opreaddir)) + return (FALSE); + break; + case OP_READLINK: + break; + case OP_REMOVE: + if (!xdr_REMOVE4args(xdrs, &objp->nfs_argop4_u.opremove)) + return (FALSE); + break; + case OP_RENAME: + if (!xdr_RENAME4args(xdrs, &objp->nfs_argop4_u.oprename)) + return (FALSE); + break; + case OP_RENEW: + if (!xdr_RENEW4args(xdrs, &objp->nfs_argop4_u.oprenew)) + return (FALSE); + break; + case OP_RESTOREFH: + break; + case OP_SAVEFH: + break; + case OP_SECINFO: + if (!xdr_SECINFO4args(xdrs, &objp->nfs_argop4_u.opsecinfo)) + return (FALSE); + break; + case OP_SETATTR: + if (!xdr_SETATTR4args(xdrs, &objp->nfs_argop4_u.opsetattr)) + return (FALSE); + break; + case OP_SETCLIENTID: + if (!xdr_SETCLIENTID4args(xdrs, &objp->nfs_argop4_u. + opsetclientid)) + return (FALSE); + break; + case OP_SETCLIENTID_CONFIRM: + if (!xdr_SETCLIENTID_CONFIRM4args(xdrs, &objp->nfs_argop4_u. + opsetclientid_confirm)) + return (FALSE); + break; + case OP_VERIFY: + if (!xdr_VERIFY4args(xdrs, &objp->nfs_argop4_u.opverify)) + return (FALSE); + break; + case OP_WRITE: + if (!xdr_WRITE4args(xdrs, &objp->nfs_argop4_u.opwrite)) + return (FALSE); + break; + case OP_RELEASE_LOCKOWNER: + if (!xdr_RELEASE_LOCKOWNER4args(xdrs, + &objp->nfs_argop4_u.oprelease_lockowner)) + return (FALSE); + break; + case OP_ILLEGAL: + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_nfs_resop4(register XDR *xdrs, nfs_resop4 *objp) +{ + nfs4_skip_bytes = 0; + if (!xdr_nfs_opnum4(xdrs, &objp->resop)) + return (FALSE); + switch (objp->resop) { + case OP_ACCESS: + if (!xdr_ACCESS4res(xdrs, &objp->nfs_resop4_u.opaccess)) + return (FALSE); + break; + case OP_CLOSE: + if (!xdr_CLOSE4res(xdrs, &objp->nfs_resop4_u.opclose)) + return (FALSE); + break; + case OP_COMMIT: + if (!xdr_COMMIT4res(xdrs, &objp->nfs_resop4_u.opcommit)) + return (FALSE); + break; + case OP_CREATE: + if (!xdr_CREATE4res(xdrs, &objp->nfs_resop4_u.opcreate)) + return (FALSE); + break; + case OP_DELEGPURGE: + if (!xdr_DELEGPURGE4res(xdrs, &objp->nfs_resop4_u.opdelegpurge)) + return (FALSE); + break; + case OP_DELEGRETURN: + if (!xdr_DELEGRETURN4res(xdrs, &objp->nfs_resop4_u. + opdelegreturn)) + return (FALSE); + break; + case OP_GETATTR: + if (!xdr_GETATTR4res(xdrs, &objp->nfs_resop4_u.opgetattr)) + return (FALSE); + break; + case OP_GETFH: + if (!xdr_GETFH4res(xdrs, &objp->nfs_resop4_u.opgetfh)) + return (FALSE); + break; + case OP_LINK: + if (!xdr_LINK4res(xdrs, &objp->nfs_resop4_u.oplink)) + return (FALSE); + break; + case OP_LOCK: + if (!xdr_LOCK4res(xdrs, &objp->nfs_resop4_u.oplock)) + return (FALSE); + break; + case OP_LOCKT: + if (!xdr_LOCKT4res(xdrs, &objp->nfs_resop4_u.oplockt)) + return (FALSE); + break; + case OP_LOCKU: + if (!xdr_LOCKU4res(xdrs, &objp->nfs_resop4_u.oplocku)) + return (FALSE); + break; + case OP_LOOKUP: + if (!xdr_LOOKUP4res(xdrs, &objp->nfs_resop4_u.oplookup)) + return (FALSE); + break; + case OP_LOOKUPP: + if (!xdr_LOOKUPP4res(xdrs, &objp->nfs_resop4_u.oplookupp)) + return (FALSE); + break; + case OP_NVERIFY: + if (!xdr_NVERIFY4res(xdrs, &objp->nfs_resop4_u.opnverify)) + return (FALSE); + break; + case OP_OPEN: + if (!xdr_OPEN4res(xdrs, &objp->nfs_resop4_u.opopen)) + return (FALSE); + break; + case OP_OPENATTR: + if (!xdr_OPENATTR4res(xdrs, &objp->nfs_resop4_u.opopenattr)) + return (FALSE); + break; + case OP_OPEN_CONFIRM: + if (!xdr_OPEN_CONFIRM4res(xdrs, &objp->nfs_resop4_u. + opopen_confirm)) + return (FALSE); + break; + case OP_OPEN_DOWNGRADE: + if (!xdr_OPEN_DOWNGRADE4res(xdrs, &objp->nfs_resop4_u. + opopen_downgrade)) + return (FALSE); + break; + case OP_PUTFH: + if (!xdr_PUTFH4res(xdrs, &objp->nfs_resop4_u.opputfh)) + return (FALSE); + break; + case OP_PUTPUBFH: + if (!xdr_PUTPUBFH4res(xdrs, &objp->nfs_resop4_u.opputpubfh)) + return (FALSE); + break; + case OP_PUTROOTFH: + if (!xdr_PUTROOTFH4res(xdrs, &objp->nfs_resop4_u.opputrootfh)) + return (FALSE); + break; + case OP_READ: + if (!xdr_READ4res(xdrs, &objp->nfs_resop4_u.opread)) + return (FALSE); + break; + case OP_READDIR: + if (!xdr_READDIR4res(xdrs, &objp->nfs_resop4_u.opreaddir)) + return (FALSE); + break; + case OP_READLINK: + if (!xdr_READLINK4res(xdrs, &objp->nfs_resop4_u.opreadlink)) + return (FALSE); + break; + case OP_REMOVE: + if (!xdr_REMOVE4res(xdrs, &objp->nfs_resop4_u.opremove)) + return (FALSE); + break; + case OP_RENAME: + if (!xdr_RENAME4res(xdrs, &objp->nfs_resop4_u.oprename)) + return (FALSE); + break; + case OP_RENEW: + if (!xdr_RENEW4res(xdrs, &objp->nfs_resop4_u.oprenew)) + return (FALSE); + break; + case OP_RESTOREFH: + if (!xdr_RESTOREFH4res(xdrs, &objp->nfs_resop4_u.oprestorefh)) + return (FALSE); + break; + case OP_SAVEFH: + if (!xdr_SAVEFH4res(xdrs, &objp->nfs_resop4_u.opsavefh)) + return (FALSE); + break; + case OP_SECINFO: + if (!xdr_SECINFO4res(xdrs, &objp->nfs_resop4_u.opsecinfo)) + return (FALSE); + break; + case OP_SETATTR: + if (!xdr_SETATTR4res(xdrs, &objp->nfs_resop4_u.opsetattr)) + return (FALSE); + break; + case OP_SETCLIENTID: + if (!xdr_SETCLIENTID4res(xdrs, &objp->nfs_resop4_u. + opsetclientid)) + return (FALSE); + break; + case OP_SETCLIENTID_CONFIRM: + if (!xdr_SETCLIENTID_CONFIRM4res(xdrs, &objp->nfs_resop4_u. + opsetclientid_confirm)) + return (FALSE); + break; + case OP_VERIFY: + if (!xdr_VERIFY4res(xdrs, &objp->nfs_resop4_u.opverify)) + return (FALSE); + break; + case OP_WRITE: + if (!xdr_WRITE4res(xdrs, &objp->nfs_resop4_u.opwrite)) + return (FALSE); + break; + case OP_RELEASE_LOCKOWNER: + if (!xdr_RELEASE_LOCKOWNER4res(xdrs, + &objp->nfs_resop4_u.oprelease_lockowner)) + return (FALSE); + break; + case OP_ILLEGAL: + if (!xdr_ILLEGAL4res(xdrs, &objp->nfs_resop4_u.opillegal)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_COMPOUND4args(register XDR *xdrs, COMPOUND4args *objp) +{ + + if (!xdr_utf8string(xdrs, &objp->tag)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->minorversion)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->argarray.argarray_val, + (uint_t *)&objp->argarray.argarray_len, ~0, + sizeof (nfs_argop4), (xdrproc_t)xdr_nfs_argop4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_COMPOUND4res(register XDR *xdrs, COMPOUND4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + if (!xdr_utf8string(xdrs, &objp->tag)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->resarray.resarray_val, + (uint_t *)&objp->resarray.resarray_len, ~0, + sizeof (nfs_resop4), (xdrproc_t)xdr_nfs_resop4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_GETATTR4args(register XDR *xdrs, CB_GETATTR4args *objp) +{ + + if (!xdr_nfs_fh4(xdrs, &objp->fh)) + return (FALSE); + if (!xdr_bitmap4(xdrs, &objp->attr_request)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_GETATTR4resok(register XDR *xdrs, CB_GETATTR4resok *objp) +{ + + if (!xdr_fattr4(xdrs, &objp->obj_attributes)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_GETATTR4res(register XDR *xdrs, CB_GETATTR4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + switch (objp->status) { + case NFS4_OK: + if (!xdr_CB_GETATTR4resok(xdrs, &objp->CB_GETATTR4res_u.resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_CB_RECALL4args(register XDR *xdrs, CB_RECALL4args *objp) +{ + + if (!xdr_stateid4(xdrs, &objp->stateid)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->truncate)) + return (FALSE); + if (!xdr_nfs_fh4(xdrs, &objp->fh)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_RECALL4res(register XDR *xdrs, CB_RECALL4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_ILLEGAL4res(register XDR *xdrs, CB_ILLEGAL4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfs_cb_opnum4(register XDR *xdrs, nfs_cb_opnum4 *objp) +{ + + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfs_cb_argop4(register XDR *xdrs, nfs_cb_argop4 *objp) +{ + + if (!xdr_u_int(xdrs, &objp->argop)) + return (FALSE); + switch (objp->argop) { + case OP_CB_GETATTR: + if (!xdr_CB_GETATTR4args(xdrs, &objp->nfs_cb_argop4_u. + opcbgetattr)) + return (FALSE); + break; + case OP_CB_RECALL: + if (!xdr_CB_RECALL4args(xdrs, &objp->nfs_cb_argop4_u. + opcbrecall)) + return (FALSE); + break; + case OP_CB_ILLEGAL: + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_nfs_cb_resop4(register XDR *xdrs, nfs_cb_resop4 *objp) +{ + + if (!xdr_u_int(xdrs, &objp->resop)) + return (FALSE); + switch (objp->resop) { + case OP_CB_GETATTR: + if (!xdr_CB_GETATTR4res(xdrs, &objp->nfs_cb_resop4_u. + opcbgetattr)) + return (FALSE); + break; + case OP_CB_RECALL: + if (!xdr_CB_RECALL4res(xdrs, &objp->nfs_cb_resop4_u.opcbrecall)) + return (FALSE); + break; + case OP_CB_ILLEGAL: + if (!xdr_CB_ILLEGAL4res(xdrs, + &objp->nfs_cb_resop4_u.opcbillegal)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_CB_COMPOUND4args(register XDR *xdrs, CB_COMPOUND4args *objp) +{ + + if (!xdr_utf8string(xdrs, &objp->tag)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->minorversion)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->callback_ident)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->argarray.argarray_val, + (uint_t *)&objp->argarray.argarray_len, ~0, + sizeof (nfs_cb_argop4), (xdrproc_t)xdr_nfs_cb_argop4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_COMPOUND4res(register XDR *xdrs, CB_COMPOUND4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + if (!xdr_utf8string(xdrs, &objp->tag)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->resarray.resarray_val, + (uint_t *)&objp->resarray.resarray_len, ~0, + sizeof (nfs_cb_resop4), (xdrproc_t)xdr_nfs_cb_resop4)) + return (FALSE); + return (TRUE); +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/ntp.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/ntp.h new file mode 100644 index 0000000..71809d4 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/ntp.h @@ -0,0 +1,533 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * University Copyright- Copyright (c) 1982, 1986, 1988 + * The Regents of the University of California + * All Rights Reserved + * + * University Acknowledgment- Portions of this document are derived from + * software developed by the University of California, Berkeley, and its + * contributors. + */ + +#ifndef _NTP_H +#define _NTP_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* $Source: /usr/users/louie/ntp/RCS/ntp.h,v $ */ +/* $Revision: 3.4.1.5 $ $Date: 89/04/10 15:55:42 $ */ + +/* + * $Log: ntp.h,v $ + * Revision 3.4.1.5 89/04/10 15:55:42 louie + * Provide default value for number of bits/byte if not defined. Compute the + * Window shift mask inside of conditional code on XTAL so we get the correct + * value if configured without a crystal controled clock (!!) + * + * Revision 3.4.1.4 89/03/31 16:34:50 louie + * Add bit in flags which allow a peer to be synced to. Changed a char to a bit + * field so that it is always signed. + * + * Revision 3.4.1.3 89/03/29 12:26:18 louie + * Removed some unused #defines. Replaced MAXSTRATUM with NTP_INFIN per new + * spec. The variable 'mode' in the peer structure has been renamed 'hmode' + * per the new spec. + * + * Revision 3.4.1.2 89/03/22 18:28:18 louie + * patch3: Use new RCS headers. + * + * Revision 3.4.1.1 89/03/20 00:02:53 louie + * 1 + * + * Revision 3.4 89/03/17 18:37:00 louie + * Latest test release. + * + * Revision 3.3.1.1 89/03/17 18:23:49 louie + * Change CLOCK_FACTOR to be a power of 2. + * + * Revision 3.3 89/03/15 14:19:36 louie + * New baseline for next release. + * + * Revision 3.2.1.2 89/03/15 13:46:52 louie + * The version number for that particular flavor of ntpd <--> ntpdc interaction + * is now defined by NTPDC_VERSION. The packet format for the ntpdc program + * has changed slightly to improve robustness when dealing with multiple packets + * of status data. + * + * Revision 3.2.1.1 89/03/09 17:11:24 louie + * patch1: Updated constants, which were previously in incorrect units. + * + * Revision 3.2 89/03/07 18:21:45 louie + * New version of UNIX NTP daemon and software based on the 6 March 1989 + * draft of the new NTP protocol specification. This version doesn't + * implement authentication, and accepts and send only NTP Version 1 + * packets. + * + * Revision 3.1.1.1 89/02/15 08:54:42 louie + * *** empty log message *** + * + * + * Revision 3.1 89/01/30 14:43:07 louie + * Second UNIX NTP test release. + * + * Revision 3.0 88/12/12 16:01:07 louie + * Test release of new UNIX NTP software. This version should conform to the + * revised NTP protocol specification. + * + */ + +#ifndef FD_SET +#define NFDBITS 32 +#define FD_SETSIZE 32 +#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) +#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) +#define FD_ZERO(p) bzero((char *)(p), sizeof (*(p))) +#endif + +#ifndef NBBY +#define NBBY 8 /* number of bits per byte */ +#endif + +#define MAXNETIF 10 + +struct intf { + int fd; + char *name; + struct sockaddr_in sin; + struct sockaddr_in bcast; + struct sockaddr_in mask; + int uses; + int if_flags; +}; +extern struct intf addrs[]; +extern int nintf; + +/* + * Definitions for the masses + */ +#define JAN_1970 2208988800U /* 1970 - 1900 in seconds */ + +/* + * Daemon specific (ntpd.c) + */ +#define SHIFT_MASK 0xff /* number of intervals to wait */ + +#ifndef WAYTOOBIG +#define WAYTOOBIG 1000.0 /* Too many seconds to correct, something is */ + /* really wrong */ +#endif + +#ifndef XTAL +#define XTAL 1 /* crystal controlled clock by default */ +#endif + +#ifndef NTPINITFILE +#define NTPINITFILE "/etc/ntp.conf" +#endif + +struct list { + struct ntp_peer *head; + struct ntp_peer *tail; + int members; +}; + +#define STRMCMP(a, cond, b) \ + (((a) == UNSPECIFIED ? NTP_INFIN+1 : a) cond \ + ((b) == UNSPECIFIED ? NTP_INFIN+1 : (b))) + + +/* + * Definitions outlined in the NTP spec + */ +#define NTP_VERSION 1 +#define NTP_PORT 123 /* for ref only (see /etc/services) */ +#define NTP_INFIN 15 +#define NTP_MAXAGE 86400 +#define NTP_MAXSKW 0.01 /* seconds */ +#define NTP_MINDIST 0.02 /* seconds */ +#define NTP_MINPOLL 6 /* (64) seconds between messages */ +#define NTP_MAXPOLL 10 /* (1024) secs to poll */ +#define NTP_WINDOW 8 /* size of shift register */ +#define NTP_MAXWGT 8 /* maximum allowable dispersion */ +#define NTP_MAXLIST 5 /* max size of selection list */ +#define NTP_MAXSTRA 2 /* max number of strata in selection list */ +#define X_NTP_CANDIDATES 64 /* number of peers to consider when doing */ + /* clock selection */ +#define NTP_SELECT 0.75 /* weight used to compute dispersion */ + +#define PEER_MAXDISP 64.0 /* Maximum dispersion */ +#define PEER_THRESHOLD 0.5 /* dispersion threshold */ +#define PEER_FILTER 0.5 /* filter weight */ + +#if XTAL == 0 +#define PEER_SHIFT 4 +#define NTP_WINDOW_SHIFT_MASK 0x0f +#else +#define PEER_SHIFT 8 +#define NTP_WINDOW_SHIFT_MASK 0xff +#endif + + +/* + * 5.1 Uniform Phase Adjustments + * Clock parameters + */ +#define CLOCK_UPDATE 8 /* update interval (1<<CLOCK_UPDATE secs) */ +#if XTAL +#define CLOCK_ADJ 2 /* adjustment interval (1<<CLOCK_ADJ secs) */ +#define CLOCK_PHASE 8 /* phase shift */ +#define CLOCK_MAX 0.128 /* maximum aperture (milliseconds) */ +#else +#define CLOCK_ADJ 0 +#define CLOCK_PHASE 6 /* phase shift */ +#define CLOCK_MAX 0.512 /* maximum aperture (milliseconds) */ +#endif +#define CLOCK_FREQ 10 /* frequency shift */ +#define CLOCK_TRACK 8 +#define CLOCK_COMP 4 +#define CLOCK_FACTOR 18 + +/* + * Structure definitions for NTP fixed point values + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Integer Part | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Fraction Part | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Integer Part | Fraction Part | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct l_fixedpt { + ulong_t int_part; + ulong_t fraction; +}; + +struct s_fixedpt { + ushort_t int_part; + ushort_t fraction; +}; + +/* + * ================= Table 3.3. Packet Variables ================= + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |LI | VN | Mode| Stratum | Poll | Precision | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Synchronizing Distance | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Synchronizing Dispersion | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reference Clock Identifier | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * | Reference Timestamp (64 bits) | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * | Originate Timestamp (64 bits) | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * | Receive Timestamp (64 bits) | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * | Transmit Timestamp (64 bits) | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Encryption Keyid (32 bits, when A bit set) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * | Message Authentication Code/MAC (when A bit set) | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +#define MAC_OCTETS_DES 8 +#define MAC_OCTETS_MD5 16 +#define MAC_OCTETS_MIN MAC_OCTETS_DES +#define MAC_OCTETS_MAX MAC_OCTETS_MD5 +#define AUTH_OCTETS_V3 (MAC_OCTETS_MAX + sizeof (uint32_t)) + +struct ntpdata { + uchar_t li_vn_mode; /* contains leap indicator, version and mode */ + uchar_t stratum; /* Stratum level */ + uchar_t ppoll; /* poll value */ + int precision:8; + struct s_fixedpt distance; + struct s_fixedpt dispersion; + ulong_t refid; + struct l_fixedpt reftime; + struct l_fixedpt org; + struct l_fixedpt rec; + struct l_fixedpt xmt; + uint32_t keyid; + uchar_t mac[MAC_OCTETS_MAX]; +}; + +#define LEN_PKT_NOMAC (sizeof (struct ntpdata) - AUTH_OCTETS_V3) + +/* + * Leap Second Codes (high order two bits) + */ +#define NO_WARNING 0x00 /* no warning */ +#define PLUS_SEC 0x40 /* add a second (61 seconds) */ +#define MINUS_SEC 0x80 /* minus a second (59 seconds) */ +#define ALARM 0xc0 /* alarm condition (clock unsynchronized) */ + +/* + * Clock Status Bits that Encode Version + */ +#define NTPVERSION_1 0x08 +#define VERSIONMASK 0x38 +#define LEAPMASK 0xc0 +#define NTPMODEMASK 0x07 + +/* + * Code values + */ +#define MODE_UNSPEC 0 /* unspecified */ +#define MODE_SYM_ACT 1 /* symmetric active */ +#define MODE_SYM_PAS 2 /* symmetric passive */ +#define MODE_CLIENT 3 /* client */ +#define MODE_SERVER 4 /* server */ +#define MODE_BROADCAST 5 /* broadcast */ +#define MODE_CONTROL 6 /* control */ +#define MODE_PRIVATE 7 /* private */ + +/* + * Stratum Definitions + */ +#define UNSPECIFIED 0 +#define PRIM_REF 1 /* radio clock */ +#define INFO_QUERY 62 /* **** THIS implementation dependent **** */ +#define INFO_REPLY 63 /* **** THIS implementation dependent **** */ + + +/* ================= table 3.2 Peer Variables ================= */ +struct ntp_peer { + struct ntp_peer *next, *prev; + struct sockaddr_in src; /* both peer.srcadr and peer.srcport */ + int flags; /* local flags */ +#define PEER_FL_CONFIG 1 +#define PEER_FL_AUTHENABLE 2 +#define PEER_FL_SYNC 0x1000 /* peer can bet sync'd to */ +#define PEER_FL_BCAST 0x2000 /* broadcast peer */ +#define PEER_FL_SELECTED 0x8000 /* actually used by query routine */ + + int sock; /* index into sockets to derive */ + /* peer.dstadr and peer.dstport */ + uchar_t leap; /* receive */ + uchar_t hmode; /* receive */ + uchar_t stratum; /* receive */ + uchar_t ppoll; /* receive */ + uchar_t hpoll; /* poll update */ + short precision; /* receive */ + struct s_fixedpt distance; /* receive */ + struct s_fixedpt dispersion; /* receive */ + ulong_t refid; /* receive */ + struct l_fixedpt reftime; /* receive */ + struct l_fixedpt org; /* receive, clear */ + struct l_fixedpt rec; /* receive, clear */ + struct l_fixedpt xmt; /* transmit, clear */ + ulong_t reach; /* receive, transmit, clear */ + ulong_t valid; /* packet, transmit, clear */ + ulong_t timer; /* receive, transmit, poll update */ + long stopwatch; /* <<local>> for timing */ + /* + * first order offsets + */ + struct filter { + short samples; /* <<local>> */ + double offset[PEER_SHIFT]; + double delay[PEER_SHIFT]; + } filter; /* filter, clear */ + + double estdelay; /* filter */ + double estoffset; /* filter */ + double estdisp; /* filter */ + + ulong_t pkt_sent; /* <<local>> */ + ulong_t pkt_rcvd; /* <<local>> */ + ulong_t pkt_dropped; /* <<local>> */ +}; + +/* ================= table 3.1: System Variables ================= */ + +struct sysdata { /* procedure */ + uchar_t leap; /* clock update */ + uchar_t stratum; /* clock update */ + short precision; /* system */ + struct s_fixedpt distance; /* clock update */ + struct s_fixedpt dispersion; /* clock update */ + ulong_t refid; /* clock update */ + struct l_fixedpt reftime; /* clock update */ + int hold; /* clock update */ + struct ntp_peer *peer; /* selection */ + int maxpeers; /* <<local>> */ + uchar_t filler; /* put here for %&*%$$ SUNs */ +}; + +#define NTPDC_VERSION 2 + +/* + * These structures are used to pass information to the ntpdc (control) + * program. They are unique to this implementation and not part of the + * NTP specification. + */ +struct clockinfo { + ulong_t net_address; + ulong_t my_address; + ushort_t port; + ushort_t flags; + ulong_t pkt_sent; + ulong_t pkt_rcvd; + ulong_t pkt_dropped; + ulong_t timer; + uchar_t leap; + uchar_t stratum; + uchar_t ppoll; + int precision:8; + + uchar_t hpoll; + uchar_t filler1; + ushort_t reach; + + long estdisp; /* scaled by 1000 */ + long estdelay; /* in milliseconds */ + long estoffset; /* in milliseconds */ + ulong_t refid; + struct l_fixedpt reftime; + struct info_filter { + short index; + short filler; + long offset[PEER_SHIFT]; /* in milliseconds */ + long delay[PEER_SHIFT]; /* in milliseconds */ + } info_filter; +}; + +struct ntpinfo { + uchar_t version; + uchar_t type; /* request type (stratum in ntp packets) */ + uchar_t count; /* number of entries in this packet */ + uchar_t seq; /* sequence number of this packet */ + + uchar_t npkts; /* total number of packets */ + uchar_t peers; + uchar_t fill3; + uchar_t fill4; +}; + +/* + * From usr/src/cmd/xntpd/include/ntp_control.h: + * Definition of a mode 6 packet. + */ +struct ntp_control { + uchar_t li_vn_mode; /* leap, version, mode */ + uchar_t r_m_e_op; /* response, more, error, opcode */ + ushort_t sequence; /* sequence number of request */ + ushort_t status; /* status word for association */ + ushort_t associd; /* association ID */ + ushort_t offset; /* offset of this batch of data */ + ushort_t count; /* count of data in this packet */ + uchar_t data[1]; /* data + auth */ +}; + +#define NTPC_DATA_MAXLEN (480 + AUTH_OCTETS_V3) + +/* + * Decoding for the r_m_e_op field + */ +#define CTL_RESPONSE 0x80 +#define CTL_ERROR 0x40 +#define CTL_MORE 0x20 +#define CTL_OP_MASK 0x1f + +/* + * Opcodes + */ +#define CTL_OP_UNSPEC 0 +#define CTL_OP_READSTAT 1 +#define CTL_OP_READVAR 2 +#define CTL_OP_WRITEVAR 3 +#define CTL_OP_READCLOCK 4 +#define CTL_OP_WRITECLOCK 5 +#define CTL_OP_SETTRAP 6 +#define CTL_OP_ASYNCMSG 7 +#define CTL_OP_UNSETTRAP 31 + +/* + * From usr/src/cmd/xntpd/include/ntp_request.h: + * A mode 7 packet is used exchanging data between an NTP server + * and a client for purposes other than time synchronization, e.g. + * monitoring, statistics gathering and configuration. A mode 7 + * packet has the following format: + */ + +struct ntp_private { + uchar_t rm_vn_mode; /* response, more, version, mode */ + uchar_t auth_seq; /* key, sequence number */ + uchar_t implementation; /* implementation number */ + uchar_t request; /* request number */ + ushort_t err_nitems; /* error code/number of data items */ + ushort_t mbz_itemsize; /* item size */ + char data[1]; /* data area */ +}; + +#define RESP_BIT 0x80 +#define MORE_BIT 0x40 +#define INFO_VERSION(rm_vn_mode) ((uchar_t)(((rm_vn_mode)>>3) & 0x7)) +#define INFO_MODE(rm_vn_mode) ((rm_vn_mode) & 0x7) + +#define AUTH_BIT 0x80 +#define INFO_SEQ(auth_seq) ((auth_seq) & 0x7f) + +#define INFO_ERR(err_nitems) ((ushort_t)((ntohs(err_nitems) >> 12) & 0xf)) +#define INFO_NITEMS(err_nitems) ((ushort_t)(ntohs(err_nitems) & 0xfff)) + +#define INFO_ITEMSIZE(mbz_itemsize) (ntohs(mbz_itemsize) & 0xfff) + +#ifdef __cplusplus +} +#endif + +#endif /* _NTP_H */ diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/slp.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/slp.h new file mode 100644 index 0000000..b01c5ea --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/slp.h @@ -0,0 +1,146 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1998 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#ifndef _SNOOP_SLP_H +#define _SNOOP_SLP_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Structs and definitions for the snoop SLP interpreter only + * (This code is not used by the SLP library). + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct slpv1_hdr { + unsigned char vers; + unsigned char function; + unsigned short length; + unsigned char flags; + unsigned char dialect; + unsigned char language[2]; + unsigned short charset; + unsigned short xid; +}; + +struct slpv2_hdr { + unsigned char vers; + unsigned char function; + unsigned char l1, l2, l3; + unsigned char flags; + unsigned char reserved; + unsigned char o1, o2, o3; + unsigned short xid; +}; + +/* + * flags + */ +#define V1_OVERFLOW 0x80 +#define V1_MONOLINGUAL 0x40 +#define V1_URL_AUTH 0x20 +#define V1_ATTR_AUTH 0x10 +#define V1_FRESH_REG 0x08 + +#define V2_OVERFLOW 0x80 +#define V2_FRESH 0x40 +#define V2_MCAST 0x20 + +/* + * packet types + */ + +#define V1_SRVREQ 1 +#define V1_SRVRPLY 2 +#define V1_SRVREG 3 +#define V1_SRVDEREG 4 +#define V1_SRVACK 5 +#define V1_ATTRRQST 6 +#define V1_ATTRRPLY 7 +#define V1_DAADVERT 8 +#define V1_SRVTYPERQST 9 +#define V1_SRVTYPERPLY 10 + +#define V2_SRVRQST 1 +#define V2_SRVRPLY 2 +#define V2_SRVREG 3 +#define V2_SRVDEREG 4 +#define V2_SRVACK 5 +#define V2_ATTRRQST 6 +#define V2_ATTRRPLY 7 +#define V2_DAADVERT 8 +#define V2_SRVTYPERQST 9 +#define V2_SRVTYPERPLY 10 +#define V2_SAADVERT 11 + +/* + * extended packet types + */ +#define SCOPERQST 65 +#define SCOPERPLY 66 +#define DARQST 67 +#define DARPLY 68 +#define DASTRIKE 69 + + +/* + * error codes + */ + +#define OK 0x0000 +#define LANG_NOT_SUPPORTED 0x0001 +#define PROTOCOL_PARSE_ERR 0x0002 +#define INVALID_REGISTRATION 0x0003 +#define SCOPE_NOT_SUPPORTED 0x0004 +#define CHARSET_NOT_UNDERSTOOD 0x0005 +#define AUTHENTICATION_UNKNOWN 0x0005 +#define AUTHENTICATION_INVALID 0x0006 +#define V2_AUTHENTICATION_ABSENT 0x0006 +#define V2_AUTHENTICATION_FAILED 0x0007 +#define V2_VER_NOT_SUPPORTED 0x0009 +#define NOT_SUPPORTED_YET 0x000a +#define V2_INTERNAL_ERROR 0x000a +#define REQUEST_TIMED_OUT 0x000b +#define V2_DA_BUSY_NOW 0x000b +#define COULD_NOT_INIT_NET_RESOURCES 0x000c +#define V2_OPTION_NOT_UNDERSTOOD 0x000c +#define COULD_NOT_ALLOCATE_MEMORY 0x000d +#define V2_INVALID_UPDATE 0x000d +#define PARAMETER_BAD 0x000e +#define V2_RQST_NOT_SUPPORTED 0x000e +#define INVALID_LIFETIME 0x000f + +#define INTERNAL_NET_ERROR 0x000f +#define INTERNAL_SYSTEM_ERROR 0x0010 + +#ifdef __cplusplus +} +#endif + +#endif /* _SNOOP_SLP_H */ diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c new file mode 100644 index 0000000..097dd6e --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c @@ -0,0 +1,1127 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stropts.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <stdarg.h> +#include <setjmp.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/time.h> +#include <signal.h> +#include <sys/mman.h> +#include <assert.h> +#include <sys/sysmacros.h> + +#include <sys/socket.h> +#include <sys/pfmod.h> +#include <net/if.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <netdb.h> + +#include "snoop.h" + +static int snaplen; + +/* Global error recovery variables */ +sigjmp_buf jmp_env, ojmp_env; /* error recovery jmp buf */ +int snoop_nrecover; /* number of recoveries on curr pkt */ +int quitting; /* user termination flag */ + +static struct snoop_handler *snoop_hp; /* global alarm handler head */ +static struct snoop_handler *snoop_tp; /* global alarm handler tail */ +static time_t snoop_nalarm; /* time of next alarm */ + +/* protected interpreter output areas */ +#define MAXSUM 8 +#define REDZONE 64 +static char *sumline[MAXSUM]; +static char *detail_line; +static char *line; +static char *encap; + +static int audio; +int maxcount; /* maximum no of packets to capture */ +int count; /* count of packets captured */ +static int sumcount; +int x_offset = -1; +int x_length = 0x7fffffff; +FILE *namefile; +boolean_t Pflg; +boolean_t Iflg; +boolean_t qflg; +boolean_t rflg; +#ifdef DEBUG +boolean_t zflg; +#endif +struct Pf_ext_packetfilt pf; + +static int vlanid = 0; + +static void usage(void); +static void snoop_sigrecover(int sig, siginfo_t *info, void *p); +static char *protmalloc(size_t); +static void resetperm(void); + +int +main(int argc, char **argv) +{ + int c; + int filter = 0; + int flags = F_SUM; + struct Pf_ext_packetfilt *fp = NULL; + char *icapfile = NULL; + char *ocapfile = NULL; + boolean_t nflg = B_FALSE; + boolean_t Nflg = B_FALSE; + int Cflg = 0; + boolean_t Uflg = B_FALSE; + int first = 1; + int last = 0x7fffffff; + boolean_t use_kern_pf; + char *p, *p2; + char names[MAXPATHLEN + 1]; + char self[MAXHOSTNAMELEN + 1]; + char *argstr = NULL; + void (*proc)(); + char *audiodev; + int ret; + struct sigaction sigact; + stack_t sigstk; + char *output_area; + int nbytes; + char *datalink = NULL; + dlpi_handle_t dh; + + names[0] = '\0'; + /* + * Global error recovery: Prepare for interpreter failures + * with corrupted packets or confused interpreters. + * Allocate protected output and stack areas, with generous + * red-zones. + */ + nbytes = (MAXSUM + 3) * (MAXLINE + REDZONE); + output_area = protmalloc(nbytes); + if (output_area == NULL) { + perror("Warning: mmap"); + exit(1); + } + + /* Allocate protected output areas */ + for (ret = 0; ret < MAXSUM; ret++) { + sumline[ret] = (char *)output_area; + output_area += (MAXLINE + REDZONE); + } + detail_line = output_area; + output_area += MAXLINE + REDZONE; + line = output_area; + output_area += MAXLINE + REDZONE; + encap = output_area; + output_area += MAXLINE + REDZONE; + + /* Initialize an alternate signal stack to increase robustness */ + if ((sigstk.ss_sp = (char *)malloc(SIGSTKSZ+REDZONE)) == NULL) { + perror("Warning: malloc"); + exit(1); + } + sigstk.ss_size = SIGSTKSZ; + sigstk.ss_flags = 0; + if (sigaltstack(&sigstk, (stack_t *)NULL) < 0) { + perror("Warning: sigaltstack"); + exit(1); + } + + /* Initialize a master signal handler */ + sigact.sa_handler = NULL; + sigact.sa_sigaction = snoop_sigrecover; + (void) sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_ONSTACK|SA_SIGINFO; + + /* Register master signal handler */ + if (sigaction(SIGHUP, &sigact, (struct sigaction *)NULL) < 0) { + perror("Warning: sigaction"); + exit(1); + } + if (sigaction(SIGINT, &sigact, (struct sigaction *)NULL) < 0) { + perror("Warning: sigaction"); + exit(1); + } + if (sigaction(SIGQUIT, &sigact, (struct sigaction *)NULL) < 0) { + perror("Warning: sigaction"); + exit(1); + } + if (sigaction(SIGILL, &sigact, (struct sigaction *)NULL) < 0) { + perror("Warning: sigaction"); + exit(1); + } + if (sigaction(SIGTRAP, &sigact, (struct sigaction *)NULL) < 0) { + perror("Warning: sigaction"); + exit(1); + } + if (sigaction(SIGIOT, &sigact, (struct sigaction *)NULL) < 0) { + perror("Warning: sigaction"); + exit(1); + } + if (sigaction(SIGEMT, &sigact, (struct sigaction *)NULL) < 0) { + perror("Warning: sigaction"); + exit(1); + } + if (sigaction(SIGFPE, &sigact, (struct sigaction *)NULL) < 0) { + perror("Warning: sigaction"); + exit(1); + } + if (sigaction(SIGBUS, &sigact, (struct sigaction *)NULL) < 0) { + perror("Warning: sigaction"); + exit(1); + } + if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) < 0) { + perror("Warning: sigaction"); + exit(1); + } + if (sigaction(SIGSYS, &sigact, (struct sigaction *)NULL) < 0) { + perror("Warning: sigaction"); + exit(1); + } + if (sigaction(SIGALRM, &sigact, (struct sigaction *)NULL) < 0) { + perror("Warning: sigaction"); + exit(1); + } + if (sigaction(SIGTERM, &sigact, (struct sigaction *)NULL) < 0) { + perror("Warning: sigaction"); + exit(1); + } + + /* Prepare for failure during program initialization/exit */ + if (sigsetjmp(jmp_env, 1)) { + exit(1); + } + (void) setvbuf(stdout, NULL, _IOLBF, BUFSIZ); + + while ((c = getopt(argc, argv, "at:CPDSi:o:Nn:s:d:I:vVp:f:c:x:U?rqz")) + != EOF) { + switch (c) { + case 'a': + audiodev = getenv("AUDIODEV"); + if (audiodev == NULL) + audiodev = "/dev/audio"; + audio = open(audiodev, O_WRONLY); + if (audio < 0) { + pr_err("Audio device %s: %m", + audiodev); + exit(1); + } + break; + case 't': + flags |= F_TIME; + switch (*optarg) { + case 'r': flags |= F_RTIME; break; + case 'a': flags |= F_ATIME; break; + case 'd': break; + default: usage(); + } + break; + case 'I': + if (datalink != NULL) + usage(); + Iflg = B_TRUE; + datalink = optarg; + break; + case 'P': + Pflg = B_TRUE; + break; + case 'D': + flags |= F_DROPS; + break; + case 'S': + flags |= F_LEN; + break; + case 'i': + icapfile = optarg; + break; + case 'o': + ocapfile = optarg; + break; + case 'N': + Nflg = B_TRUE; + break; + case 'n': + nflg = B_TRUE; + (void) strlcpy(names, optarg, MAXPATHLEN); + break; + case 's': + snaplen = atoi(optarg); + break; + case 'd': + if (Iflg) + usage(); + datalink = optarg; + break; + case 'v': + flags &= ~(F_SUM); + flags |= F_DTAIL; + break; + case 'V': + flags |= F_ALLSUM; + break; + case 'p': + p = optarg; + p2 = strpbrk(p, ",:-"); + if (p2 == NULL) { + first = last = atoi(p); + } else { + *p2++ = '\0'; + first = atoi(p); + last = atoi(p2); + } + break; + case 'f': + (void) gethostname(self, MAXHOSTNAMELEN); + p = strchr(optarg, ':'); + if (p) { + *p = '\0'; + if (strcmp(optarg, self) == 0 || + strcmp(p+1, self) == 0) + (void) fprintf(stderr, + "Warning: cannot capture packets from %s\n", + self); + *p = ' '; + } else if (strcmp(optarg, self) == 0) + (void) fprintf(stderr, + "Warning: cannot capture packets from %s\n", + self); + argstr = optarg; + break; + case 'x': + p = optarg; + p2 = strpbrk(p, ",:-"); + if (p2 == NULL) { + x_offset = atoi(p); + x_length = -1; + } else { + *p2++ = '\0'; + x_offset = atoi(p); + x_length = atoi(p2); + } + break; + case 'c': + maxcount = atoi(optarg); + break; + case 'C': + Cflg = B_TRUE; + break; + case 'q': + qflg = B_TRUE; + break; + case 'r': + rflg = B_TRUE; + break; + case 'U': + Uflg = B_TRUE; + break; +#ifdef DEBUG + case 'z': + zflg = B_TRUE; + break; +#endif /* DEBUG */ + case '?': + default: + usage(); + } + } + + if (argc > optind) + argstr = (char *)concat_args(&argv[optind], argc - optind); + + /* + * Need to know before we decide on filtering method some things + * about the interface. So, go ahead and do part of the initialization + * now so we have that data. Note that if no datalink is specified, + * open_datalink() selects one and returns it. In an ideal world, + * it might be nice if the "correct" interface for the filter + * requested was chosen, but that's too hard. + */ + if (!icapfile) { + use_kern_pf = open_datalink(&dh, datalink); + } else { + use_kern_pf = B_FALSE; + cap_open_read(icapfile); + + if (!nflg) { + names[0] = '\0'; + (void) strlcpy(names, icapfile, MAXPATHLEN); + (void) strlcat(names, ".names", MAXPATHLEN); + } + } + + if (Uflg) + use_kern_pf = B_FALSE; + + /* attempt to read .names file if it exists before filtering */ + if ((!Nflg) && names[0] != '\0') { + if (access(names, F_OK) == 0) { + load_names(names); + } else if (nflg) { + (void) fprintf(stderr, "%s not found\n", names); + exit(1); + } + } + + if (argstr) { + if (use_kern_pf) { + ret = pf_compile(argstr, Cflg); + switch (ret) { + case 0: + filter++; + compile(argstr, Cflg); + break; + case 1: + fp = &pf; + break; + case 2: + fp = &pf; + filter++; + break; + } + } else { + filter++; + compile(argstr, Cflg); + } + + if (Cflg) + exit(0); + } + + if (flags & F_SUM) + flags |= F_WHO; + + /* + * If the -o flag is set then capture packets + * directly to a file. Don't attempt to + * interpret them on the fly (F_NOW). + * Note: capture to file is much less likely + * to drop packets since we don't spend cpu + * cycles running through the interpreters + * and possibly hanging in address-to-name + * mappings through the name service. + */ + if (ocapfile) { + cap_open_write(ocapfile); + proc = cap_write; + } else { + flags |= F_NOW; + proc = process_pkt; + } + + + /* + * If the -i flag is set then get packets from + * the log file which has been previously captured + * with the -o option. + */ + if (icapfile) { + names[0] = '\0'; + (void) strlcpy(names, icapfile, MAXPATHLEN); + (void) strlcat(names, ".names", MAXPATHLEN); + + if (Nflg) { + namefile = fopen(names, "w"); + if (namefile == NULL) { + perror(names); + exit(1); + } + flags = 0; + (void) fprintf(stderr, + "Creating name file %s\n", names); + } + + if (flags & F_DTAIL) + flags = F_DTAIL; + else + flags |= F_NUM | F_TIME; + + resetperm(); + cap_read(first, last, filter, proc, flags); + + if (Nflg) + (void) fclose(namefile); + + } else { + const int chunksize = 8 * 8192; + struct timeval timeout; + + /* + * If listening to packets on audio + * then set the buffer timeout down + * to 1/10 sec. A higher value + * makes the audio "bursty". + */ + if (audio) { + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + } else { + timeout.tv_sec = 1; + timeout.tv_usec = 0; + } + + init_datalink(dh, snaplen, chunksize, &timeout, fp); + if (! qflg && ocapfile) + show_count(); + resetperm(); + net_read(dh, chunksize, filter, proc, flags); + dlpi_close(dh); + + if (!(flags & F_NOW)) + (void) printf("\n"); + } + + if (ocapfile) + cap_close(); + + return (0); +} + +static int tone[] = { +0x076113, 0x153333, 0x147317, 0x144311, 0x147315, 0x050353, 0x037103, 0x051106, +0x157155, 0x142723, 0x133273, 0x134664, 0x051712, 0x024465, 0x026447, 0x072473, +0x136715, 0x126257, 0x135256, 0x047344, 0x034476, 0x027464, 0x036062, 0x133334, +0x127256, 0x130660, 0x136262, 0x040724, 0x016446, 0x025437, 0x137171, 0x127672, +0x124655, 0x134654, 0x032741, 0x021447, 0x037450, 0x125675, 0x127650, 0x077277, +0x046514, 0x036077, 0x035471, 0x147131, 0x136272, 0x162720, 0x166151, 0x037527, +}; + +/* + * Make a sound on /dev/audio according to the length of the packet. The + * tone data was ripped from /usr/share/audio/samples/au/bark.au. The + * amount of waveform used is a function of packet length e.g. a series + * of small packets is heard as clicks, whereas a series of NFS packets in + * an 8k read sounds like a "WHAAAARP". + */ +void +click(len) + int len; +{ + len /= 8; + len = len ? len : 4; + + if (audio) { + (void) write(audio, tone, len); + } +} + +/* Display a count of packets */ +void +show_count() +{ + static int prev = -1; + + if (count == prev) + return; + + prev = count; + (void) fprintf(stderr, "\r%d ", count); +} + +#define ENCAP_LEN 16 /* Hold "(NN encap)" */ + +/* + * Display data that's external to the packet. + * This constitutes the first half of the summary + * line display. + */ +void +show_pktinfo(flags, num, src, dst, ptvp, tvp, drops, len) + int flags, num, drops, len; + char *src, *dst; + struct timeval *ptvp, *tvp; +{ + struct tm *tm; + static struct timeval tvp0; + int sec, usec; + char *lp = line; + int i, start; + + if (flags & F_NUM) { + (void) sprintf(lp, "%3d ", num); + lp += strlen(lp); + } + tm = localtime(&tvp->tv_sec); + + if (flags & F_TIME) { + if (flags & F_ATIME) { + (void) sprintf(lp, "%d:%02d:%d.%05d ", + tm->tm_hour, tm->tm_min, tm->tm_sec, + (int)tvp->tv_usec / 10); + lp += strlen(lp); + } else { + if (flags & F_RTIME) { + if (tvp0.tv_sec == 0) { + tvp0.tv_sec = tvp->tv_sec; + tvp0.tv_usec = tvp->tv_usec; + } + ptvp = &tvp0; + } + sec = tvp->tv_sec - ptvp->tv_sec; + usec = tvp->tv_usec - ptvp->tv_usec; + if (usec < 0) { + usec += 1000000; + sec -= 1; + } + (void) sprintf(lp, "%3d.%05d ", sec, usec / 10); + lp += strlen(lp); + } + } + + if ((flags & F_SUM) && !(flags & F_ALLSUM) && (vlanid != 0)) { + (void) snprintf(lp, MAXLINE, "VLAN#%i: ", vlanid); + lp += strlen(lp); + } + + if (flags & F_WHO) { + (void) sprintf(lp, "%12s -> %-12s ", src, dst); + lp += strlen(lp); + } + + if (flags & F_DROPS) { + (void) sprintf(lp, "drops: %d ", drops); + lp += strlen(lp); + } + + if (flags & F_LEN) { + (void) sprintf(lp, "length: %4d ", len); + lp += strlen(lp); + } + + if (flags & F_SUM) { + if (flags & F_ALLSUM) + (void) printf("________________________________\n"); + + start = flags & F_ALLSUM ? 0 : sumcount - 1; + (void) sprintf(encap, " (%d encap)", total_encap_levels - 1); + (void) printf("%s%s%s\n", line, sumline[start], + ((flags & F_ALLSUM) || (total_encap_levels == 1)) ? "" : + encap); + + for (i = start + 1; i < sumcount; i++) + (void) printf("%s%s\n", line, sumline[i]); + + sumcount = 0; + } + + if (flags & F_DTAIL) { + (void) printf("%s\n\n", detail_line); + detail_line[0] = '\0'; + } +} + +/* + * The following three routines are called back + * from the interpreters to display their stuff. + * The theory is that when snoop becomes a window + * based tool we can just supply a new version of + * get_sum_line and get_detail_line and not have + * to touch the interpreters at all. + */ +char * +get_sum_line() +{ + int tsumcount = sumcount; + + if (sumcount >= MAXSUM) { + sumcount = 0; /* error recovery */ + pr_err( + "get_sum_line: sumline overflow (sumcount=%d, MAXSUM=%d)\n", + tsumcount, MAXSUM); + } + + sumline[sumcount][0] = '\0'; + return (sumline[sumcount++]); +} + +/*ARGSUSED*/ +char * +get_detail_line(off, len) + int off, len; +{ + if (detail_line[0]) { + (void) printf("%s\n", detail_line); + detail_line[0] = '\0'; + } + return (detail_line); +} + +/* + * This function exists to make sure that VLAN information is + * prepended to summary lines displayed. The problem this function + * solves is how to display VLAN information while in summary mode. + * Each interpretor uses the get_sum_line and get_detail_line functions + * to get a character buffer to display information to the user. + * get_sum_line is the important one here. Each call to get_sum_line + * gets a buffer which stores one line of information. In summary mode, + * the last line generated is the line printed. Instead of changing each + * interpreter to add VLAN information to the summary line, the ethernet + * interpreter changes to call this function and set an ID. If the ID is not + * zero and snoop is in default summary mode, snoop displays the + * VLAN information at the beginning of the output line. Otherwise, + * no VLAN information is displayed. + */ +void +set_vlan_id(int id) +{ + vlanid = id; +} + +/* + * Print an error. + * Works like printf (fmt string and variable args) + * except that it will substitute an error message + * for a "%m" string (like syslog) and it calls + * long_jump - it doesn't return to where it was + * called from - it goes to the last setjmp(). + */ +/* VARARGS1 */ +void +pr_err(const char *fmt, ...) +{ + va_list ap; + char buf[1024], *p2; + const char *p1; + + (void) strcpy(buf, "snoop: "); + p2 = buf + strlen(buf); + + /* + * Note that we terminate the buffer with '\n' and '\0'. + */ + for (p1 = fmt; *p1 != '\0' && p2 < buf + sizeof (buf) - 2; p1++) { + if (*p1 == '%' && *(p1+1) == 'm') { + const char *errstr; + + if ((errstr = strerror(errno)) != NULL) { + *p2 = '\0'; + (void) strlcat(buf, errstr, sizeof (buf)); + p2 += strlen(p2); + } + p1++; + } else { + *p2++ = *p1; + } + } + if (p2 > buf && *(p2-1) != '\n') + *p2++ = '\n'; + *p2 = '\0'; + + va_start(ap, fmt); + /* LINTED: E_SEC_PRINTF_VAR_FMT */ + (void) vfprintf(stderr, buf, ap); + va_end(ap); + snoop_sigrecover(-1, NULL, NULL); /* global error recovery */ +} + +/* + * Store a copy of linkname associated with the DLPI handle. + * Save errno before closing the dlpi handle so that the + * correct error value is used if 'err' is a system error. + */ +void +pr_errdlpi(dlpi_handle_t dh, const char *cmd, int err) +{ + int save_errno = errno; + char linkname[DLPI_LINKNAME_MAX]; + + (void) strlcpy(linkname, dlpi_linkname(dh), sizeof (linkname)); + + dlpi_close(dh); + errno = save_errno; + + pr_err("%s on \"%s\": %s", cmd, linkname, dlpi_strerror(err)); +} + +/* + * Ye olde usage proc + * PLEASE keep this up to date! + * Naive users *love* this stuff. + */ +static void +usage(void) +{ + (void) fprintf(stderr, "\nUsage: snoop\n"); + (void) fprintf(stderr, + "\t[ -a ] # Listen to packets on audio\n"); + (void) fprintf(stderr, + "\t[ -d link ] # Listen on named link\n"); + (void) fprintf(stderr, + "\t[ -s snaplen ] # Truncate packets\n"); + (void) fprintf(stderr, + "\t[ -I IP interface ] # Listen on named IP interface\n"); + (void) fprintf(stderr, + "\t[ -c count ] # Quit after count packets\n"); + (void) fprintf(stderr, + "\t[ -P ] # Turn OFF promiscuous mode\n"); + (void) fprintf(stderr, + "\t[ -D ] # Report dropped packets\n"); + (void) fprintf(stderr, + "\t[ -S ] # Report packet size\n"); + (void) fprintf(stderr, + "\t[ -i file ] # Read previously captured packets\n"); + (void) fprintf(stderr, + "\t[ -o file ] # Capture packets in file\n"); + (void) fprintf(stderr, + "\t[ -n file ] # Load addr-to-name table from file\n"); + (void) fprintf(stderr, + "\t[ -N ] # Create addr-to-name table\n"); + (void) fprintf(stderr, + "\t[ -t r|a|d ] # Time: Relative, Absolute or Delta\n"); + (void) fprintf(stderr, + "\t[ -v ] # Verbose packet display\n"); + (void) fprintf(stderr, + "\t[ -V ] # Show all summary lines\n"); + (void) fprintf(stderr, + "\t[ -p first[,last] ] # Select packet(s) to display\n"); + (void) fprintf(stderr, + "\t[ -x offset[,length] ] # Hex dump from offset for length\n"); + (void) fprintf(stderr, + "\t[ -C ] # Print packet filter code\n"); + (void) fprintf(stderr, + "\t[ -q ] # Suppress printing packet count\n"); + (void) fprintf(stderr, + "\t[ -r ] # Do not resolve address to name\n"); + (void) fprintf(stderr, + "\n\t[ filter expression ]\n"); + (void) fprintf(stderr, "\nExample:\n"); + (void) fprintf(stderr, "\tsnoop -o saved host fred\n\n"); + (void) fprintf(stderr, "\tsnoop -i saved -tr -v -p19\n"); + exit(1); +} + +/* + * sdefault: default global alarm handler. Causes the current packet + * to be skipped. + */ +static void +sdefault(void) +{ + snoop_nrecover = SNOOP_MAXRECOVER; +} + +/* + * snoop_alarm: register or unregister an alarm handler to be called after + * s_sec seconds. Because snoop wasn't written to tolerate random signal + * delivery, periodic SIGALRM delivery (or SA_RESTART) cannot be used. + * + * s_sec argument of 0 seconds unregisters the handler. + * s_handler argument of NULL registers default handler sdefault(), or + * unregisters all signal handlers (for error recovery). + * + * Variables must be volatile to force the compiler to not optimize + * out the signal blocking. + */ +/*ARGSUSED*/ +int +snoop_alarm(int s_sec, void (*s_handler)()) +{ + volatile time_t now; + volatile time_t nalarm = 0; + volatile struct snoop_handler *sh = NULL; + volatile struct snoop_handler *hp, *tp, *next; + volatile sigset_t s_mask; + volatile int ret = -1; + + (void) sigemptyset((sigset_t *)&s_mask); + (void) sigaddset((sigset_t *)&s_mask, SIGALRM); + if (s_sec < 0) + return (-1); + + /* register an alarm handler */ + now = time(NULL); + if (s_sec) { + sh = malloc(sizeof (struct snoop_handler)); + sh->s_time = now + s_sec; + if (s_handler == NULL) + s_handler = sdefault; + sh->s_handler = s_handler; + sh->s_next = NULL; + (void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL); + if (snoop_hp == NULL) { + snoop_hp = snoop_tp = (struct snoop_handler *)sh; + + snoop_nalarm = sh->s_time; + (void) alarm(sh->s_time - now); + } else { + snoop_tp->s_next = (struct snoop_handler *)sh; + snoop_tp = (struct snoop_handler *)sh; + + if (sh->s_time < snoop_nalarm) { + snoop_nalarm = sh->s_time; + (void) alarm(sh->s_time - now); + } + } + (void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL); + + return (0); + } + + /* unregister an alarm handler */ + (void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL); + tp = (struct snoop_handler *)&snoop_hp; + for (hp = snoop_hp; hp; hp = next) { + next = hp->s_next; + if (s_handler == NULL || hp->s_handler == s_handler) { + ret = 0; + tp->s_next = hp->s_next; + if (snoop_tp == hp) { + if (tp == (struct snoop_handler *)&snoop_hp) + snoop_tp = NULL; + else + snoop_tp = (struct snoop_handler *)tp; + } + free((void *)hp); + } else { + if (nalarm == 0 || nalarm > hp->s_time) + nalarm = now < hp->s_time ? hp->s_time : + now + 1; + tp = hp; + } + } + /* + * Stop or adjust timer + */ + if (snoop_hp == NULL) { + snoop_nalarm = 0; + (void) alarm(0); + } else if (nalarm > 0 && nalarm < snoop_nalarm) { + snoop_nalarm = nalarm; + (void) alarm(nalarm - now); + } + + (void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL); + return (ret); +} + +/* + * snoop_recover: reset snoop's output area, and any internal variables, + * to allow continuation. + * XXX: make this an interface such that each interpreter can + * register a reset routine. + */ +void +snoop_recover(void) +{ + int i; + + /* Error recovery: reset output_area and associated variables */ + for (i = 0; i < MAXSUM; i++) + sumline[i][0] = '\0'; + detail_line[0] = '\0'; + line[0] = '\0'; + encap[0] = '\0'; + sumcount = 0; + + /* stacking/unstacking cannot be relied upon */ + encap_levels = 0; + total_encap_levels = 1; + + /* remove any pending timeouts */ + (void) snoop_alarm(0, NULL); +} + +/* + * snoop_sigrecover: global sigaction routine to manage recovery + * from catastrophic interpreter failures while interpreting + * corrupt trace files/packets. SIGALRM timeouts, program errors, + * and user termination are all handled. In the case of a corrupt + * packet or confused interpreter, the packet will be skipped, and + * execution will continue in scan(). + * + * Global alarm handling (see snoop_alarm()) is managed here. + * + * Variables must be volatile to force the compiler to not optimize + * out the signal blocking. + */ +/*ARGSUSED*/ +static void +snoop_sigrecover(int sig, siginfo_t *info, void *p) +{ + volatile time_t now; + volatile time_t nalarm = 0; + volatile struct snoop_handler *hp; + + /* + * Invoke any registered alarms. This involves first calculating + * the time for the next alarm, setting it up, then progressing + * through handler invocations. Note that since handlers may + * use siglongjmp(), in the worst case handlers may be serviced + * at a later time. + */ + if (sig == SIGALRM) { + now = time(NULL); + /* Calculate next alarm time */ + for (hp = snoop_hp; hp; hp = hp->s_next) { + if (hp->s_time) { + if ((hp->s_time - now) > 0) { + if (nalarm == 0 || nalarm > hp->s_time) + nalarm = now < hp->s_time ? + hp->s_time : now + 1; + } + } + } + /* Setup next alarm */ + if (nalarm) { + snoop_nalarm = nalarm; + (void) alarm(nalarm - now); + } else { + snoop_nalarm = 0; + } + + /* Invoke alarm handlers (may not return) */ + for (hp = snoop_hp; hp; hp = hp->s_next) { + if (hp->s_time) { + if ((now - hp->s_time) >= 0) { + hp->s_time = 0; /* only invoke once */ + if (hp->s_handler) + hp->s_handler(); + } + } + } + } else { + snoop_nrecover++; + } + + /* + * Exit if a signal has occurred after snoop has begun the process + * of quitting. + */ + if (quitting) + exit(1); + + /* + * If an alarm handler has timed out, and snoop_nrecover has + * reached SNOOP_MAXRECOVER, skip to the next packet. + * + * If any other signal has occurred, and snoop_nrecover has + * reached SNOOP_MAXRECOVER, give up. + */ + if (sig == SIGALRM) { + if (ioctl(STDOUT_FILENO, I_CANPUT, 0) == 0) { + /* + * We've stalled on output, which is not a critical + * failure. Reset the recovery counter so we do not + * consider this a persistent failure, and return so + * we do not skip this packet. + */ + snoop_nrecover = 0; + return; + } + if (snoop_nrecover >= SNOOP_MAXRECOVER) { + (void) fprintf(stderr, + "snoop: WARNING: skipping from packet %d\n", + count); + snoop_nrecover = 0; + } else { + /* continue trying */ + return; + } + } else if (snoop_nrecover >= SNOOP_MAXRECOVER) { + (void) fprintf(stderr, + "snoop: ERROR: cannot recover from packet %d\n", count); + exit(1); + } + +#ifdef DEBUG + (void) fprintf(stderr, "snoop_sigrecover(%d, %p, %p)\n", sig, info, p); +#endif /* DEBUG */ + + /* + * Prepare to quit. This allows final processing to occur + * after first terminal interruption. + */ + if (sig == SIGTERM || sig == SIGHUP || sig == SIGINT) { + quitting = 1; + return; + } else if (sig != -1 && sig != SIGALRM) { + /* Inform user that snoop has taken a fault */ + (void) fprintf(stderr, + "WARNING: received signal %d from packet %d\n", + sig, count); + } + + /* Reset interpreter variables */ + snoop_recover(); + + /* Continue in scan() with the next packet */ + siglongjmp(jmp_env, 1); + /*NOTREACHED*/ +} + +/* + * Protected malloc for global error recovery: prepare for interpreter + * failures with corrupted packets or confused interpreters. Dynamically + * allocate `nbytes' bytes, and sandwich it between two PROT_NONE pages to + * catch writes outside of the allocated region. + */ +static char * +protmalloc(size_t nbytes) +{ + caddr_t start; + int psz = sysconf(_SC_PAGESIZE); + + nbytes = P2ROUNDUP(nbytes, psz); + start = mmap(NULL, nbytes + psz * 2, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON, -1, 0); + if (start == MAP_FAILED) { + perror("Error: protmalloc: mmap"); + return (NULL); + } + assert(IS_P2ALIGNED(start, psz)); + if (mprotect(start, 1, PROT_NONE) == -1) + perror("Warning: mprotect"); + + start += psz; + if (mprotect(start + nbytes, 1, PROT_NONE) == -1) + perror("Warning: mprotect"); + + return (start); +} + +/* + * resetperm - reduce security vulnerabilities by resetting + * owner/group/permissions. Always attempt setuid() - if we have + * permission to drop our privilege level, do so. + */ +void +resetperm(void) +{ + if (geteuid() == 0) { + (void) setgid(GID_NOBODY); + (void) setuid(UID_NOBODY); + } +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h new file mode 100644 index 0000000..e4f1825 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h @@ -0,0 +1,355 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + */ + +#ifndef _SNOOP_H +#define _SNOOP_H + +#include <rpc/types.h> +#include <sys/pfmod.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/bufmod.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp6.h> +#include <net/pppoe.h> +#include <libdlpi.h> +#include <note.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Flags to control packet info display + */ +#define F_NOW 0x00000001 /* display in realtime */ +#define F_SUM 0x00000002 /* display summary line */ +#define F_ALLSUM 0x00000004 /* display all summary lines */ +#define F_DTAIL 0x00000008 /* display detail lines */ +#define F_TIME 0x00000010 /* display time */ +#define F_ATIME 0x00000020 /* display absolute time */ +#define F_RTIME 0x00000040 /* display relative time */ +#define F_DROPS 0x00000080 /* display drops */ +#define F_LEN 0x00000100 /* display pkt length */ +#define F_NUM 0x00000200 /* display pkt number */ +#define F_WHO 0x00000400 /* display src/dst */ + +#define MAXLINE (1088) /* max len of detail line */ + +/* + * The RPC XID cache structure. + * When analyzing RPC protocols we + * have to cache the xid of the RPC + * request together with the program + * number, proc, version etc since this + * information is missing in the reply + * packet. Using the xid in the reply + * we can lookup this previously stashed + * information in the cache. + * + * For RPCSEC_GSS flavor, some special processing is + * needed for the argument interpretation based on its + * control procedure and service type. This information + * is stored in the cache table during interpretation of + * the rpc header and will be referenced later when the rpc + * argument is interpreted. + */ +#define XID_CACHE_SIZE 256 +struct cache_struct { + int xid_num; /* RPC transaction id */ + int xid_frame; /* Packet number */ + int xid_prog; /* RPC program number */ + int xid_vers; /* RPC version number */ + int xid_proc; /* RPC procedure number */ + unsigned int xid_gss_proc; /* control procedure */ + int xid_gss_service; /* none, integ, priv */ +} xid_cache[XID_CACHE_SIZE]; + + +/* + * The following macros advance the pointer passed to them. They + * assume they are given a char *. + */ +#define GETINT8(v, ptr) { \ + (v) = (*(ptr)++); \ +} + +#define GETINT16(v, ptr) { \ + (v) = *(ptr)++ << 8; \ + (v) |= *(ptr)++; \ +} + +#define GETINT32(v, ptr) { \ + (v) = *(ptr)++ << 8; \ + (v) |= *(ptr)++; (v) <<= 8; \ + (v) |= *(ptr)++; (v) <<= 8; \ + (v) |= *(ptr)++; \ +} + +/* + * Used to print nested protocol layers. For example, an ip datagram included + * in an icmp error, or a PPP packet included in an LCP protocol reject.. + */ +extern char *prot_nest_prefix; + +extern char *get_sum_line(void); +extern char *get_detail_line(int, int); +extern int want_packet(uchar_t *, int, int); +extern void set_vlan_id(int); +extern struct timeval prev_time; +extern void process_pkt(struct sb_hdr *, char *, int, int); +extern char *getflag(int, int, char *, char *); +extern void show_header(char *, char *, int); +extern void show_count(void); +extern void xdr_init(char *, int); +extern char *get_line(int, int); +extern int get_line_remain(void); +extern char getxdr_char(void); +extern char showxdr_char(char *); +extern uchar_t getxdr_u_char(void); +extern uchar_t showxdr_u_char(char *); +extern short getxdr_short(void); +extern short showxdr_short(char *); +extern ushort_t getxdr_u_short(void); +extern ushort_t showxdr_u_short(char *); +extern long getxdr_long(void); +extern long showxdr_long(char *); +extern ulong_t getxdr_u_long(void); +extern ulong_t showxdr_u_long(char *); +extern longlong_t getxdr_longlong(void); +extern longlong_t showxdr_longlong(char *); +extern u_longlong_t getxdr_u_longlong(void); +extern u_longlong_t showxdr_u_longlong(char *); +extern char *getxdr_opaque(char *, int); +extern char *getxdr_string(char *, int); +extern char *showxdr_string(int, char *); +extern char *getxdr_bytes(uint_t *); +extern void xdr_skip(int); +extern int getxdr_pos(void); +extern void setxdr_pos(int); +extern char *getxdr_context(char *, int); +extern char *showxdr_context(char *); +extern enum_t getxdr_enum(void); +extern void show_space(void); +extern void show_trailer(void); +extern char *getxdr_date(void); +extern char *showxdr_date(char *); +extern char *getxdr_date_ns(void); +char *format_time(int64_t sec, uint32_t nsec); +extern char *showxdr_date_ns(char *); +extern char *getxdr_hex(int); +extern char *showxdr_hex(int, char *); +extern bool_t getxdr_bool(void); +extern bool_t showxdr_bool(char *); +extern char *concat_args(char **, int); +extern int pf_compile(char *, int); +extern void compile(char *, int); +extern void load_names(char *); +extern void cap_write(struct sb_hdr *, char *, int, int); +extern void cap_open_read(const char *); +extern void cap_open_write(const char *); +extern void cap_read(int, int, int, void (*)(), int); +extern void cap_close(void); +extern boolean_t open_datalink(dlpi_handle_t *, const char *); +extern void init_datalink(dlpi_handle_t, ulong_t, ulong_t, struct timeval *, + struct Pf_ext_packetfilt *); +extern void net_read(dlpi_handle_t, size_t, int, void (*)(), int); +extern void click(int); +extern void show_pktinfo(int, int, char *, char *, struct timeval *, + struct timeval *, int, int); +extern void show_line(char *); +/*PRINTFLIKE1*/ +extern void show_printf(char *fmt, ...) + __PRINTFLIKE(1); +extern char *getxdr_time(void); +extern char *showxdr_time(char *); +extern char *addrtoname(int, const void *); +extern char *show_string(const char *, int, int); +extern void pr_err(const char *, ...); +extern void pr_errdlpi(dlpi_handle_t, const char *, int); +extern void check_retransmit(char *, ulong_t); +extern char *nameof_prog(int); +extern char *getproto(int); +extern uint8_t print_ipv6_extensions(int, uint8_t **, uint8_t *, int *, int *); +extern void protoprint(int, int, ulong_t, int, int, int, char *, int); +extern char *getportname(int, in_port_t); + +extern void interpret_arp(int, struct arphdr *, int); +extern void interpret_bparam(int, int, int, int, int, char *, int); +extern void interpret_dns(int, int, const uchar_t *, int, int); +extern void interpret_mount(int, int, int, int, int, char *, int); +extern void interpret_nfs(int, int, int, int, int, char *, int); +extern void interpret_nfs3(int, int, int, int, int, char *, int); +extern void interpret_nfs4(int, int, int, int, int, char *, int); +extern void interpret_nfs4_cb(int, int, int, int, int, char *, int); +extern void interpret_nfs_acl(int, int, int, int, int, char *, int); +extern void interpret_nis(int, int, int, int, int, char *, int); +extern void interpret_nisbind(int, int, int, int, int, char *, int); +extern void interpret_nlm(int, int, int, int, int, char *, int); +extern void interpret_pmap(int, int, int, int, int, char *, int); +extern int interpret_reserved(int, int, in_port_t, in_port_t, char *, int); +extern void interpret_rquota(int, int, int, int, int, char *, int); +extern void interpret_rstat(int, int, int, int, int, char *, int); +extern void interpret_solarnet_fw(int, int, int, int, int, char *, int); +extern void interpret_ldap(int, char *, int, int, int); +extern void interpret_icmp(int, struct icmp *, int, int); +extern void interpret_icmpv6(int, icmp6_t *, int, int); +extern int interpret_ip(int, const struct ip *, int); +extern int interpret_ipv6(int, const ip6_t *, int); +extern int interpret_ppp(int, uchar_t *, int); +extern int interpret_pppoe(int, poep_t *, int); +struct tcphdr; +extern int interpret_tcp(int, struct tcphdr *, int, int); +struct udphdr; +extern int interpret_udp(int, struct udphdr *, int, int); +extern int interpret_esp(int, uint8_t *, int, int); +extern int interpret_ah(int, uint8_t *, int, int); +struct sctp_hdr; +extern void interpret_sctp(int, struct sctp_hdr *, int, int); +extern void interpret_mip_cntrlmsg(int, uchar_t *, int); +struct dhcp; +extern int interpret_dhcp(int, struct dhcp *, int); +extern int interpret_dhcpv6(int, const uint8_t *, int); +struct tftphdr; +extern int interpret_tftp(int, struct tftphdr *, int); +extern int interpret_http(int, char *, int); +struct ntpdata; +extern int interpret_ntp(int, struct ntpdata *, int); +extern void interpret_netbios_ns(int, uchar_t *, int); +extern void interpret_netbios_datagram(int, uchar_t *, int); +extern void interpret_netbios_ses(int, uchar_t *, int); +extern void interpret_slp(int, char *, int); +struct rip; +extern int interpret_rip(int, struct rip *, int); +struct rip6; +extern int interpret_rip6(int, struct rip6 *, int); +extern int interpret_socks_call(int, char *, int); +extern int interpret_socks_reply(int, char *, int); +extern int interpret_trill(int, struct ether_header **, char *, int *); +extern int interpret_isis(int, char *, int, boolean_t); +extern int interpret_bpdu(int, char *, int); +extern void init_ldap(void); +extern boolean_t arp_for_ether(char *, struct ether_addr *); +extern char *ether_ouiname(uint32_t); +extern char *tohex(char *p, int len); +extern char *printether(struct ether_addr *); +extern char *print_ethertype(int); +extern const char *arp_htype(int); +extern int valid_rpc(char *, int); + +/* + * Describes characteristics of the Media Access Layer. + * The mac_type is one of the supported DLPI media + * types (see <sys/dlpi.h>). + * The mtu_size is the size of the largest frame. + * network_type_offset is where the network type + * is located in the link layer header. + * The header length is returned by a function to + * allow for variable header size - for ethernet it's + * just a constant 14 octets. + * The interpreter is the function that "knows" how + * to interpret the frame. + * try_kernel_filter tells snoop to first try a kernel + * filter (because the header size is fixed, or if it could + * be of variable size where the variable size is easy for a kernel + * filter to handle, for example, Ethernet and VLAN tags) + * and only use a user space filter if the filter expression + * cannot be expressed in kernel space. + */ +typedef uint_t (interpreter_fn_t)(int, char *, int, int); +typedef uint_t (headerlen_fn_t)(char *, size_t); +typedef struct interface { + uint_t mac_type; + uint_t mtu_size; + uint_t network_type_offset; + size_t network_type_len; + uint_t network_type_ip; + uint_t network_type_ipv6; + headerlen_fn_t *header_len; + interpreter_fn_t *interpreter; + boolean_t try_kernel_filter; +} interface_t; + +extern interface_t INTERFACES[], *interface; +extern char *dlc_header; +extern char *src_name, *dst_name; +extern char *prot_prefix; +extern char *prot_nest_prefix; +extern char *prot_title; + +/* Keep track of how many nested IP headers we have. */ +extern unsigned int encap_levels, total_encap_levels; + +extern int quitting; +extern boolean_t Iflg, Pflg, rflg; + +/* + * Global error recovery routine: used to reset snoop variables after + * catastrophic failure. + */ +void snoop_recover(void); + +/* + * Global alarm handler structure for managing multiple alarms within + * snoop. + */ +typedef struct snoop_handler { + struct snoop_handler *s_next; /* next alarm handler */ + time_t s_time; /* time to fire */ + void (*s_handler)(); /* alarm handler */ +} snoop_handler_t; + +#define SNOOP_MAXRECOVER 20 /* maxium number of recoveries */ +#define SNOOP_ALARM_GRAN 3 /* alarm() timeout multiplier */ + +/* + * Global alarm handler management routine. + */ +extern int snoop_alarm(int s_sec, void (*s_handler)()); + +/* + * The next two definitions do not take into account the length + * of the underlying link header. In order to use them, you must + * add link_header_len to them. The reason it is not done here is + * that later these macros are used to initialize a table. + */ +#define IPV4_TYPE_HEADER_OFFSET 9 +#define IPV6_TYPE_HEADER_OFFSET 6 + +#ifdef __cplusplus +} +#endif + +#endif /* _SNOOP_H */ diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aarp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aarp.c new file mode 100644 index 0000000..32133ee --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aarp.c @@ -0,0 +1,143 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1991-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <at.h> +#include <snoop.h> + +static char *printat(uint8_t *); + +static char *aarp_opname[] = { + "", + "AARP Request", + "AARP Reply", + "AARP Probe", +}; + +void +interpret_aarp(int flags, char *data, int alen) +{ + /* LINTED */ + struct ether_arp *ap = (struct ether_arp *)data; + + extern char *dst_name; + + if (flags & F_SUM) { + if (alen < sizeof (struct ether_arp)) { + (void) snprintf(get_sum_line(), MAXLINE, + "AARP (short packet)"); + return; + } + + switch (ntohs(ap->arp_op)) { + case AARP_REQ: + (void) snprintf(get_sum_line(), MAXLINE, + "AARP C Who is %s ?", + printat(ap->arp_tpa)); + break; + case AARP_RESP: + (void) snprintf(get_sum_line(), MAXLINE, + "AARP R %s is %s", + printat(ap->arp_spa), + printether((struct ether_addr *)&ap->arp_sha)); + dst_name = printat(ap->arp_tpa); + break; + case AARP_PROBE: + (void) snprintf(get_sum_line(), MAXLINE, + "AARP Probe %s ?", + printat(ap->arp_tpa)); + break; + } + } + + if (flags & F_DTAIL) { + show_header("AARP: ", "AARP Frame", alen); + show_space(); + + if (alen < sizeof (struct ether_arp)) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "AARP (short packet)"); + return; + } + + (void) snprintf(get_line(0, 0), get_line_remain(), + "Hardware type = %d", + ntohs(ap->arp_hrd)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Protocol type = %04X (%s)", + ntohs(ap->arp_pro), + print_ethertype(ntohs(ap->arp_pro))); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Length of hardware address = %d bytes", + ap->arp_hln); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Length of protocol address = %d bytes", + ap->arp_pln); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Opcode %d (%s)", + ntohs(ap->arp_op), + aarp_opname[ntohs(ap->arp_op)]); + + if (ntohs(ap->arp_hrd) == ARPHRD_ETHER && + ntohs(ap->arp_pro) == ETHERTYPE_AT) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "Sender's hardware address = %s", + printether((struct ether_addr *)&ap->arp_sha)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Sender's protocol address = %s", + printat(ap->arp_spa)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Target hardware address = %s", + (ntohs(ap->arp_op) == AARP_REQ || + ntohs(ap->arp_op) == AARP_PROBE) ? "?" : + printether((struct ether_addr *)&ap->arp_tha)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Target protocol address = %s", + ntohs(ap->arp_op) == REVARP_REQUEST ? "?" : + printat(ap->arp_tpa)); + } + show_trailer(); + } +} + +static char * +printat(uint8_t *p) +{ + static char buf[16]; + + (void) snprintf(buf, sizeof (buf), "%d.%d", get_short(&p[1]), p[3]); + return (buf); +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_adsp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_adsp.c new file mode 100644 index 0000000..e771837 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_adsp.c @@ -0,0 +1,142 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1991-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <sys/types.h> + +#include <at.h> +#include <snoop.h> + +static char *adsp_ctrl(uint8_t); + +void +interpret_adsp(int flags, struct ddp_adsphdr *adp, int len) +{ + struct ddp_adsp_open *apo; + + if (flags & F_SUM) { + if (len < sizeof (struct ddp_adsphdr)) { + (void) snprintf(get_sum_line(), MAXLINE, + "ADSP (short packet)"); + return; + } + (void) snprintf(get_sum_line(), MAXLINE, + "ADSP ConnID=%u (%s)", + get_short(adp->ad_connid), + adsp_ctrl(adp->ad_desc)); + } + + if (flags & F_DTAIL) { + show_header("ADSP: ", "ADSP Header", + len - sizeof (struct ddp_adsphdr)); + show_space(); + + if (len < sizeof (struct ddp_adsphdr)) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "(short packet)"); + return; + } + + (void) snprintf(get_line(0, 0), get_line_remain(), + "ConnID = %u, ByteSeq = %u, RecvSeq = %u", + get_short(adp->ad_connid), + get_long(adp->ad_fbseq), + get_long(adp->ad_nrseq)); + + (void) snprintf(get_line(0, 0), get_line_remain(), + "RcvWin = %u, Ctrl = 0x%x (%s)", + get_short(adp->ad_rcvwin), + adp->ad_desc, + adsp_ctrl(adp->ad_desc)); + + switch (adp->ad_desc) { + case AD_CREQ: /* open requests */ + case AD_CACK: + case AD_CREQ_ACK: + case AD_CDENY: + apo = (struct ddp_adsp_open *)adp; + if (len < sizeof (struct ddp_adsp_open)) { + (void) snprintf(get_line(0, 0), + get_line_remain(), + "(short packet)"); + return; + } + (void) snprintf(get_line(0, 0), get_line_remain(), + "Dest ConnID = %u, AttRcvSeq = %u", + get_short(apo->ad_dconnid), + get_long(apo->ad_attseq)); + break; + } + + if (adp->ad_desc & AD_ATT) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "AttCode = 0x%x", + get_short(((struct ddp_adsp_att *)adp)-> + ad_att_code)); + } + } +} + +static char *adsp_ctrl_msg[] = { + "Probe/Ack", + "OpenConnReq", + "OpenConnAck", + "OpenConnReq+Ack", + "OpenConnDeny", + "CloseConnAdv", + "ForwReset", + "ForwReset Ack", + "RetransAdv", + "9", "10", "11", "12", "13", "14", "15", +}; + +static char * +adsp_ctrl(uint8_t ctrl) +{ + static char buf[50]; + char *p = buf; + char *tail = &buf[sizeof (buf)]; + + if (ctrl & AD_ACKREQ) + p += snprintf(p, tail-p, "AckReq"); + + if (ctrl & AD_EOM) { + p += snprintf(p, tail-p, p == buf ? "EOM" : " EOM"); + } + + if (ctrl & AD_ATT) { + p += snprintf(p, tail-p, p == buf ? "Att" : " Att"); + } + + if (ctrl & AD_CTRL) { + (void) snprintf(p, tail-p, "%s%s", p == buf ? "" : " ", + adsp_ctrl_msg[ctrl & AD_CTRL_MASK]); + } + + return (buf); +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aecho.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aecho.c new file mode 100644 index 0000000..348f6a9 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_aecho.c @@ -0,0 +1,72 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <sys/types.h> + +#include <at.h> +#include <snoop.h> + +void +interpret_aecho(int flags, struct ddp_hdr *ddp, int len) +{ + char *data; + + data = (char *)ddp + DDPHDR_SIZE; + + if (flags & F_SUM) { + if (len < DDPHDR_SIZE + 1) { + (void) snprintf(get_sum_line(), MAXLINE, + "AECHO (short packet)"); + return; + } + + (void) snprintf(get_sum_line(), MAXLINE, + "AECHO F=%s LEN=%d", + *data == AEP_REQ ? "Request" : "Reply", + len); + } + + if (flags & F_DTAIL) { + if (len < DDPHDR_SIZE + 1) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "AECHO (short packet)"); + return; + } + + show_header("AECHO: ", "AECHO Header", len); + show_space(); + + (void) snprintf(get_line(0, 0), get_line_remain(), + "Func = %d (%s)", + data[0], + data[0] == AEP_REQ ? "Request" : "Reply"); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Length = %d", len); + } +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_apple.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_apple.c new file mode 100644 index 0000000..bf6a6b4 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_apple.c @@ -0,0 +1,210 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <at.h> +#include <snoop.h> + +extern char *src_name, *dst_name; + +struct socktable { + int pt_num; + char *pt_short; +}; + +static struct socktable pt_ddp[] = { + {1, "RTMP"}, + {2, "NIS"}, + {4, "Echoer"}, + {6, "ZIS"}, + {0, NULL}, +}; + +static struct socktable pt_ddp_types[] = { + {1, "RTMP Resp"}, + {2, "NBP"}, + {3, "ATP"}, + {4, "AEP"}, + {5, "RTMP Req"}, + {6, "ZIP"}, + {7, "ADSP"}, + {0, NULL}, +}; + +static char * +apple_ddp_type(struct socktable *p, uint16_t port) +{ + for (; p->pt_num != 0; p++) { + if (port == p->pt_num) + return (p->pt_short); + } + return (NULL); +} + +/* + * return the short at p, regardless of alignment + */ + +uint16_t +get_short(uint8_t *p) +{ + return (p[0] << 8 | p[1]); +} + +/* + * return the long at p, regardless of alignment + */ +uint32_t +get_long(uint8_t *p) +{ + return (p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]); +} + +/* + * format a MAC address + */ + +char * +print_macaddr(uint8_t *ha, int len) +{ + static char buf[128]; + char *p = buf; + + while (len-- != 0) { + p += snprintf(p, sizeof (buf) - (p - buf), + len > 0 ? "%x:" : "%x", *ha++); + } + return (buf); +} + +/* ARGSUSED */ +void +interpret_at(int flags, struct ddp_hdr *ddp, int len) +{ + int ddplen; + char *pname; + char buff [32]; + static char src_buf[16]; + static char dst_buf[16]; + + if (ddp_pad(ddp) != 0) + return; /* unknown AppleTalk proto */ + + ddplen = ddp_len(ddp); + + (void) snprintf(src_buf, sizeof (src_buf), + "%u.%u", ntohs(ddp->ddp_src_net), ddp->ddp_src_id); + src_name = src_buf; + + (void) snprintf(dst_buf, sizeof (dst_buf), + "%u.%u", ntohs(ddp->ddp_dest_net), ddp->ddp_dest_id); + if (ddp->ddp_dest_id == NODE_ID_BROADCAST) + dst_name = "(broadcast)"; + else + dst_name = dst_buf; + + if (flags & F_SUM) { + (void) snprintf(get_sum_line(), MAXLINE, + "DDP S=%u.%u:%u D=%u.%u:%u LEN=%d", + ntohs(ddp->ddp_src_net), + ddp->ddp_src_id, + ddp->ddp_src_sock, + ntohs(ddp->ddp_dest_net), + ddp->ddp_dest_id, + ddp->ddp_dest_sock, + ddp_len(ddp)); + } + + if (flags & F_DTAIL) { + show_header("DDP: ", "DDP Header", ddplen - DDPHDR_SIZE); + show_space(); + pname = apple_ddp_type(pt_ddp, ddp->ddp_src_sock); + if (pname == NULL) { + pname = ""; + } else { + (void) snprintf(buff, sizeof (buff), "(%s)", pname); + pname = buff; + } + + (void) snprintf(get_line(0, 0), get_line_remain(), + "Source = %s, Socket = %u %s", + src_name, ddp->ddp_src_sock, pname); + pname = apple_ddp_type(pt_ddp, ddp->ddp_dest_sock); + if (pname == NULL) { + pname = ""; + } else { + (void) snprintf(buff, sizeof (buff), "(%s)", pname); + pname = buff; + } + (void) snprintf(get_line(0, 0), get_line_remain(), + "Destination = %s, Socket = %u %s", + dst_name, ddp->ddp_dest_sock, pname); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Hop count = %d", + ddp_hop(ddp)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Length = %d", + ddp_len(ddp)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Checksum = %04x %s", + ntohs(ddp->ddp_cksum), + ddp->ddp_cksum == 0 ? "(no checksum)" : ""); + (void) snprintf(get_line(0, 0), get_line_remain(), + "DDP type = %d (%s)", + ddp->ddp_type, + apple_ddp_type(pt_ddp_types, ddp->ddp_type)); + show_space(); + } + + + /* go to the next protocol layer */ + + switch (ddp->ddp_type) { + case DDP_TYPE_NBP: + interpret_nbp(flags, (struct nbp_hdr *)ddp, ddplen); + break; + case DDP_TYPE_AEP: + interpret_aecho(flags, ddp, ddplen); + break; + case DDP_TYPE_ATP: + interpret_atp(flags, ddp, ddplen); + break; + case DDP_TYPE_ZIP: + interpret_ddp_zip(flags, (struct zip_hdr *)ddp, ddplen); + break; + case DDP_TYPE_ADSP: + interpret_adsp(flags, (struct ddp_adsphdr *)ddp, ddplen); + break; + case DDP_TYPE_RTMPRQ: + case DDP_TYPE_RTMPRESP: + interpret_rtmp(flags, ddp, ddplen); + break; + } +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_arp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_arp.c new file mode 100644 index 0000000..c41507c --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_arp.c @@ -0,0 +1,263 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/errno.h> +#include <setjmp.h> +#include <sys/socket.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#include <netdb.h> +#include <net/if_types.h> + +#include "snoop.h" + +extern char *dlc_header; +extern jmp_buf xdr_err; + +static char *printip(unsigned char *); +static char *addrtoname_align(unsigned char *); + +static char unarp_addr[] = "Unknown"; +char *opname[] = { + "", + "ARP Request", + "ARP Reply", + "REVARP Request", + "REVARP Reply", +}; + +void +interpret_arp(int flags, struct arphdr *ap, int alen) +{ + char *line; + extern char *src_name, *dst_name; + unsigned char *sip, *tip, *sha, *tha; + char *smacbuf = NULL, *dmacbuf = NULL; + int maclen; + ushort_t arpop; + boolean_t is_ip = B_FALSE; + + /* + * Check that at least the generic ARP header was received. + */ + if (sizeof (struct arphdr) > alen) + goto short_packet; + + arpop = ntohs(ap->ar_op); + maclen = ap->ar_hln; + if (ntohs(ap->ar_pro) == ETHERTYPE_IP) + is_ip = B_TRUE; + + sha = (unsigned char *)(ap + 1); + sip = sha + maclen; + tha = sip + ap->ar_pln; + tip = tha + maclen; + + /* + * Check that the protocol/hardware addresses were received. + */ + if ((tip + ap->ar_pln) > ((unsigned char *)ap + alen)) + goto short_packet; + + if (maclen == 0) { + smacbuf = dmacbuf = unarp_addr; + } else { + if (((flags & F_DTAIL) && is_ip) || (arpop == ARPOP_REPLY)) { + smacbuf = _link_ntoa(sha, NULL, maclen, IFT_OTHER); + if (smacbuf == NULL) + pr_err("Warning: malloc failure"); + } + + if (((flags & F_DTAIL) && is_ip) || (arpop == + REVARP_REQUEST) || (arpop == REVARP_REPLY)) { + dmacbuf = _link_ntoa(tha, NULL, maclen, IFT_OTHER); + if (dmacbuf == NULL) + pr_err("Warning: malloc failure"); + } + } + + src_name = addrtoname_align(sip); + + if (flags & F_SUM) { + + line = get_sum_line(); + + switch (arpop) { + case ARPOP_REQUEST: + (void) snprintf(line, MAXLINE, "ARP C Who is %s ?", + printip(tip)); + break; + case ARPOP_REPLY: + (void) snprintf(line, MAXLINE, "ARP R %s is %s", + printip(sip), smacbuf); + dst_name = addrtoname_align(tip); + break; + case REVARP_REQUEST: + (void) snprintf(line, MAXLINE, "RARP C Who is %s ?", + dmacbuf); + break; + case REVARP_REPLY: + (void) snprintf(line, MAXLINE, "RARP R %s is %s", + dmacbuf, printip(tip)); + dst_name = addrtoname_align(tip); + break; + } + } + + if (flags & F_DTAIL) { + show_header("ARP: ", "ARP/RARP Frame", alen); + show_space(); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Hardware type = %d (%s)", ntohs(ap->ar_hrd), + arp_htype(ntohs(ap->ar_hrd))); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Protocol type = %04x (%s)", ntohs(ap->ar_pro), + print_ethertype(ntohs(ap->ar_pro))); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Length of hardware address = %d bytes", ap->ar_hln); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Length of protocol address = %d bytes", ap->ar_pln); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Opcode %d (%s)", arpop, + (arpop > REVARP_REPLY) ? opname[0] : opname[arpop]); + + if (is_ip) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "Sender's hardware address = %s", smacbuf); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Sender's protocol address = %s", + printip(sip)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Target hardware address = %s", + arpop == ARPOP_REQUEST ? "?" : dmacbuf); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Target protocol address = %s", + arpop == REVARP_REQUEST ? "?" : + printip(tip)); + } + show_trailer(); + } + + if (maclen != 0) { + free(smacbuf); + free(dmacbuf); + } + return; + +short_packet: + if (flags & F_SUM) { + (void) snprintf(get_sum_line(), MAXLINE, + "ARP (short packet)"); + } else if (flags & F_DTAIL) { + show_header("ARP: ", "ARP/RARP Frame", alen); + show_space(); + (void) snprintf(get_line(0, 0), get_line_remain(), + "ARP (short packet)"); + } +} + +char * +printip(unsigned char *p) +{ + static char buff[MAXHOSTNAMELEN + 32]; + char *ap, *np; + struct in_addr a; + + memcpy(&a, p, 4); + ap = (char *)inet_ntoa(a); + np = (char *)addrtoname(AF_INET, &a); + (void) snprintf(buff, MAXHOSTNAMELEN, "%s, %s", ap, np); + return (buff); +} + +char * +addrtoname_align(unsigned char *p) +{ + struct in_addr a; + + memcpy(&a, p, 4); + return ((char *)addrtoname(AF_INET, &a)); +} + +/* + * These numbers are assigned by the IANA. See the arp-parameters registry. + * Only those values that are used within Solaris have #defines. + */ +const char * +arp_htype(int t) +{ + switch (t) { + case ARPHRD_ETHER: + return ("Ethernet (10Mb)"); + case 2: + return ("Experimental Ethernet (3MB)"); + case 3: + return ("Amateur Radio AX.25"); + case 4: + return ("Proteon ProNET Token Ring"); + case 5: + return ("Chaos"); + case ARPHRD_IEEE802: + return ("IEEE 802"); + case 7: + return ("ARCNET"); + case 8: + return ("Hyperchannel"); + case 9: + return ("Lanstar"); + case 10: + return ("Autonet"); + case 11: + return ("LocalTalk"); + case 12: + return ("LocalNet"); + case 13: + return ("Ultra Link"); + case 14: + return ("SMDS"); + case ARPHRD_FRAME: + return ("Frame Relay"); + case ARPHRD_ATM: + return ("ATM"); + case ARPHRD_HDLC: + return ("HDLC"); + case ARPHRD_FC: + return ("Fibre Channel"); + case ARPHRD_IPATM: + return ("IP-ATM"); + case ARPHRD_TUNNEL: + return ("Tunnel"); + case ARPHRD_IB: + return ("IPIB"); + }; + return ("UNKNOWN"); +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_atp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_atp.c new file mode 100644 index 0000000..31808ec --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_atp.c @@ -0,0 +1,128 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <sys/types.h> + +#include <at.h> +#include <snoop.h> + +static char *atp_ci(uint8_t); + +static char *atp_trel[8] = { + "30s", + "1m", + "2m", + "4m", + "8m", + "(undef 5)", + "(undef 6)", + "(undef 7)" +}; + +void +interpret_atp(int flags, struct ddp_hdr *ddp, int len) +{ + struct atp_hdr *atp = (struct atp_hdr *)ddp; + int atplen = len - (DDPHDR_SIZE + ATPHDR_SIZE); + + if (flags & F_SUM) { + if (atplen < 0) { + (void) snprintf(get_sum_line(), MAXLINE, + "ATP (short packet)"); + return; + } + (void) snprintf(get_sum_line(), MAXLINE, + "ATP (%s), TID=%d, L=%d", + atp_ci(atp->atp_ctrl), + get_short((uint8_t *)&atp->atp_tid), + len); + } + + if (flags & F_DTAIL) { + show_header("ATP: ", "ATP Header", 8); + show_space(); + + if (atplen < 0) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "ATP (short packet)"); + return; + } + (void) snprintf(get_line(0, 0), get_line_remain(), + "Length = %d", len); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Ctrl = 0x%x (%s), bitmap/seq = 0x%x", + atp->atp_ctrl, + atp_ci(atp->atp_ctrl), + atp->atp_seq); + (void) snprintf(get_line(0, 0), get_line_remain(), + "TID = %d, user bytes 0x%x 0x%x 0x%x 0x%x", + get_short((uint8_t *)&atp->atp_tid), + atp->atp_user[0], atp->atp_user[1], + atp->atp_user[2], atp->atp_user[3]); + show_space(); + } + + if (ddp->ddp_dest_sock == DDP_TYPE_ZIP || + ddp->ddp_src_sock == DDP_TYPE_ZIP) + interpret_atp_zip(flags, atp, atplen); +} + +static char * +atp_ci(uint8_t ci) +{ + static char buf[50]; + char *p = buf; + char *to = NULL; + char *tail = &buf[sizeof (buf)]; + + switch (atp_fun(ci)) { + case ATP_TREQ: + p += snprintf(p, tail-p, "TReq"); + to = atp_trel[atp_tmo(ci)]; + break; + case ATP_TRESP: + p += snprintf(p, tail-p, "TResp"); + break; + case ATP_TREL: + p += snprintf(p, tail-p, "TRel"); + break; + } + + p += snprintf(p, tail-p, ci & ATP_FLG_XO ? " XO" : " ALO"); + + if (ci & ATP_FLG_EOM) + p += snprintf(p, tail-p, " EOM"); + + if (ci & ATP_FLG_STS) + p += snprintf(p, tail-p, " STS"); + + if (to != NULL) + (void) snprintf(p, tail-p, " %s", to); + return (buf); +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bparam.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bparam.c new file mode 100644 index 0000000..5d1be71 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bparam.c @@ -0,0 +1,220 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1991, 1999 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#ident "%Z%%M% %I% %E% SMI" /* SunOS */ + +#include <sys/types.h> +#include <sys/errno.h> +#include <setjmp.h> +#include <string.h> + +#include <netinet/in.h> +#include <rpc/types.h> +#include <rpc/rpc.h> +#include <rpc/xdr.h> +#include <rpc/auth.h> +#include <rpc/clnt.h> +#include <rpc/rpc_msg.h> +#include <rpcsvc/bootparam_prot.h> +#include "snoop.h" + +extern char *dlc_header; +extern jmp_buf xdr_err; + +static void show_address(char *); +static char *sum_address(void); + +static char *procnames_short[] = { + "Null", /* 0 */ + "WHOAMI?", /* 1 */ + "GETFILE", /* 2 */ +}; + +static char *procnames_long[] = { + "Null procedure", /* 0 */ + "Who am I?", /* 1 */ + "Get file name", /* 2 */ +}; + +#define MAXPROC 2 + +void +interpret_bparam(flags, type, xid, vers, proc, data, len) + int flags, type, xid, vers, proc; + char *data; + int len; +{ + char *line; + char buff[MAX_PATH_LEN + 1]; + char buff2[MAX_MACHINE_NAME + 1]; + + if (proc < 0 || proc > MAXPROC) + return; + + if (flags & F_SUM) { + if (setjmp(xdr_err)) { + return; + } + + line = get_sum_line(); + + if (type == CALL) { + (void) sprintf(line, + "BPARAM C %s", + procnames_short[proc]); + line += strlen(line); + + switch (proc) { + case BOOTPARAMPROC_WHOAMI: + (void) sprintf(line, " %s", + sum_address()); + break; + case BOOTPARAMPROC_GETFILE: + (void) getxdr_string(buff, + MAX_MACHINE_NAME); + (void) sprintf(line, " %s", + getxdr_string(buff, + MAX_FILEID)); + break; + } + + check_retransmit(line, xid); + } else { + (void) sprintf(line, "BPARAM R %s ", + procnames_short[proc]); + line += strlen(line); + switch (proc) { + case BOOTPARAMPROC_WHOAMI: + (void) getxdr_string(buff, + MAX_MACHINE_NAME); + (void) getxdr_string(buff2, + MAX_MACHINE_NAME); + (void) sprintf(line, "%s in %s", + buff, buff2); + break; + case BOOTPARAMPROC_GETFILE: + (void) getxdr_string(buff, + MAX_MACHINE_NAME); + (void) sum_address(); + (void) sprintf(line, "File=%s", + getxdr_string(buff, + MAX_PATH_LEN)); + break; + } + } + } + + if (flags & F_DTAIL) { + show_header("BPARAM: ", "Boot Parameters", len); + show_space(); + if (setjmp(xdr_err)) { + return; + } + (void) sprintf(get_line(0, 0), + "Proc = %d (%s)", + proc, procnames_long[proc]); + + if (type == CALL) { + switch (proc) { + case BOOTPARAMPROC_WHOAMI: + show_address("Client address"); + break; + + case BOOTPARAMPROC_GETFILE: + (void) showxdr_string(MAX_MACHINE_NAME, + "Hostname = %s"); + (void) showxdr_string(MAX_FILEID, + "File = %s"); + break; + } + } else { + switch (proc) { + case BOOTPARAMPROC_WHOAMI: + (void) showxdr_string(MAX_MACHINE_NAME, + "Client name = %s"); + (void) showxdr_string(MAX_MACHINE_NAME, + "Domain name = %s"); + show_address("Router addr"); + break; + + case BOOTPARAMPROC_GETFILE: + (void) showxdr_string(MAX_MACHINE_NAME, + "Server name = %s"); + show_address("Server addr"); + (void) showxdr_string(MAX_PATH_LEN, + "Server file = %s"); + break; + } + } + + show_trailer(); + } +} + +static char * +sum_address() +{ + struct in_addr host; + extern char *inet_ntoa(); + int atype; + + atype = getxdr_u_long(); + if (atype != IP_ADDR_TYPE) + return ("?"); + + host.S_un.S_un_b.s_b1 = getxdr_char(); + host.S_un.S_un_b.s_b2 = getxdr_char(); + host.S_un.S_un_b.s_b3 = getxdr_char(); + host.S_un.S_un_b.s_b4 = getxdr_char(); + + return (inet_ntoa(host)); +} + +static void +show_address(label) + char *label; +{ + struct in_addr host; + extern char *inet_ntoa(); + int atype; + + atype = getxdr_u_long(); + if (atype == IP_ADDR_TYPE) { + host.S_un.S_un_b.s_b1 = getxdr_char(); + host.S_un.S_un_b.s_b2 = getxdr_char(); + host.S_un.S_un_b.s_b3 = getxdr_char(); + host.S_un.S_un_b.s_b4 = getxdr_char(); + + (void) sprintf(get_line(0, 0), + "%s = %s (%s)", + label, + inet_ntoa(host), + addrtoname(AF_INET, &host)); + } else { + (void) sprintf(get_line(0, 0), + "Router addr = ? (type not known)"); + } +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bpdu.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bpdu.c new file mode 100644 index 0000000..89aa775 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_bpdu.c @@ -0,0 +1,95 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ethernet.h> + +#include <snoop.h> + +struct conf_bpdu { + uchar_t cb_protid[2]; /* Protocol Identifier */ + uchar_t cb_protvers; /* Protocol Version Identifier */ + uchar_t cb_type; /* BPDU Type */ + uchar_t cb_flags; /* BPDU Flags */ + uchar_t cb_rootid[8]; /* Root Identifier */ + uchar_t cb_rootcost[4]; /* Root Path Cost */ + uchar_t cb_bridgeid[8]; /* Bridge Identifier */ + uchar_t cb_portid[2]; /* Port Identifier */ + uchar_t cb_messageage[2]; /* Message Age */ + uchar_t cb_maxage[2]; /* Max Age */ + uchar_t cb_hello[2]; /* Hello Time */ + uchar_t cb_fwddelay[2]; /* Forward Delay */ +}; + +#define BPDU_TYPE_CONF 0 +#define BPDU_TYPE_RCONF 2 +#define BPDU_TYPE_TCNOTIF 0x80 + +int +interpret_bpdu(int flags, char *data, int dlen) +{ + struct conf_bpdu *cb; + const char *pdutype; + + if (dlen < 4) { + (void) snprintf(get_sum_line(), MAXLINE, + "BPDU (short packet)"); + return (0); + } + + cb = (struct conf_bpdu *)data; + + if (flags & F_SUM) { + (void) snprintf(get_sum_line(), MAXLINE, + "Bridge PDU T:%d L:%d", cb->cb_type, dlen); + } + + if (flags & F_DTAIL) { + show_header("Bridge-PDU: ", + "Bridge PDU Frame", dlen); + show_space(); + switch (cb->cb_type) { + case BPDU_TYPE_CONF: + pdutype = "Configuration"; + break; + case BPDU_TYPE_RCONF: + pdutype = "Rapid Configuration"; + break; + case BPDU_TYPE_TCNOTIF: + pdutype = "TC Notification"; + break; + default: + pdutype = "?"; + break; + } + (void) snprintf(get_line(0, 0), get_line_remain(), + "PDU type = %d (%s)", cb->cb_type, pdutype); + show_trailer(); + } + return (0); +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c new file mode 100644 index 0000000..ab6bc29 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c @@ -0,0 +1,766 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2012 Milan Jurik. All rights reserved. + */ + +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <fcntl.h> +#include <setjmp.h> +#include <sys/types.h> +#include <sys/signal.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <sys/pfmod.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/bufmod.h> + +#include <unistd.h> +#include <stropts.h> +#include <stdlib.h> +#include <ctype.h> +#include <values.h> +#include <libdlpi.h> + +#include "snoop.h" + +/* + * Old header format. + * Actually two concatenated structs: nit_bufhdr + nit_head + */ +struct ohdr { + /* nit_bufhdr */ + int o_msglen; + int o_totlen; + /* nit_head */ + struct timeval o_time; + int o_drops; + int o_len; +}; + +static void scan(char *, int, int, int, int, void (*)(), int, int, int); +void convert_to_network(); +void convert_from_network(); +static void convert_old(struct ohdr *); +extern sigjmp_buf jmp_env, ojmp_env; +static char *bufp; /* pointer to read buffer */ + +static int strioctl(int, int, int, int, void *); + +enum { DWA_NONE, DWA_EXISTS, DWA_PLUMBED }; + +typedef struct dlpi_walk_arg { + char dwa_linkname[MAXLINKNAMELEN]; + int dwa_type; /* preference type above */ + int dwa_s4; /* IPv4 socket */ + int dwa_s6; /* IPv6 socket */ +} dlpi_walk_arg_t; + +static boolean_t +select_datalink(const char *linkname, void *arg) +{ + struct lifreq lifr; + dlpi_walk_arg_t *dwap = arg; + int s4 = dwap->dwa_s4; + int s6 = dwap->dwa_s6; + + (void) strlcpy(dwap->dwa_linkname, linkname, MAXLINKNAMELEN); + dwap->dwa_type = DWA_EXISTS; + + /* + * See if it's plumbed by IP. We prefer such links because they're + * more likely to have interesting traffic. + */ + bzero(&lifr, sizeof (lifr)); + (void) strlcpy(lifr.lifr_name, linkname, LIFNAMSIZ); + if ((s4 != -1 && ioctl(s4, SIOCGLIFFLAGS, &lifr) != -1) || + (s6 != -1 && ioctl(s6, SIOCGLIFFLAGS, &lifr) != -1)) { + dwap->dwa_type = DWA_PLUMBED; + return (B_TRUE); + } + return (B_FALSE); +} + +/* + * Open `linkname' in raw/passive mode (see dlpi_open(3DLPI)). If `linkname' + * is NULL, pick a datalink as per snoop(1M). Also gather some information + * about the datalink useful for building the proper packet filters. + */ +boolean_t +open_datalink(dlpi_handle_t *dhp, const char *linkname) +{ + int retval; + int flags = DLPI_PASSIVE | DLPI_RAW; + dlpi_walk_arg_t dwa; + dlpi_info_t dlinfo; + + if (linkname == NULL) { + /* + * Select a datalink to use by default. Prefer datalinks that + * are plumbed by IP. + */ + bzero(&dwa, sizeof (dwa)); + dwa.dwa_s4 = socket(AF_INET, SOCK_DGRAM, 0); + dwa.dwa_s6 = socket(AF_INET6, SOCK_DGRAM, 0); + dlpi_walk(select_datalink, &dwa, 0); + (void) close(dwa.dwa_s4); + (void) close(dwa.dwa_s6); + + if (dwa.dwa_type == DWA_NONE) + pr_err("no datalinks found"); + if (dwa.dwa_type == DWA_EXISTS) { + (void) fprintf(stderr, "snoop: WARNING: " + "no datalinks plumbed for IP traffic\n"); + } + linkname = dwa.dwa_linkname; + } + if (Iflg) + flags |= DLPI_DEVIPNET; + if (Iflg || strcmp(linkname, "lo0") == 0) + flags |= DLPI_IPNETINFO; + if ((retval = dlpi_open(linkname, dhp, flags)) != DLPI_SUCCESS) { + pr_err("cannot open \"%s\": %s", linkname, + dlpi_strerror(retval)); + } + + if ((retval = dlpi_info(*dhp, &dlinfo, 0)) != DLPI_SUCCESS) + pr_errdlpi(*dhp, "dlpi_info failed", retval); + + for (interface = &INTERFACES[0]; interface->mac_type != -1; interface++) + if (interface->mac_type == dlinfo.di_mactype) + break; + + /* allow limited functionality even if interface isn't known */ + if (interface->mac_type == -1) { + (void) fprintf(stderr, "snoop: WARNING: Mac Type = %x " + "not supported\n", dlinfo.di_mactype); + } + + return (interface->try_kernel_filter); +} + +/* + * Initialize `dh' for packet capture using the provided arguments. + */ +void +init_datalink(dlpi_handle_t dh, ulong_t snaplen, ulong_t chunksize, + struct timeval *timeout, struct Pf_ext_packetfilt *fp) +{ + int retv; + int netfd; + + retv = dlpi_bind(dh, DLPI_ANY_SAP, NULL); + if (retv != DLPI_SUCCESS) + pr_errdlpi(dh, "cannot bind on", retv); + + if (Iflg) { + (void) fprintf(stderr, "Using device ipnet/%s ", + dlpi_linkname(dh)); + } else { + (void) fprintf(stderr, "Using device %s ", dlpi_linkname(dh)); + } + + /* + * If Pflg not set - use physical level + * promiscuous mode. Otherwise - just SAP level. + */ + if (!Pflg) { + (void) fprintf(stderr, "(promiscuous mode)\n"); + retv = dlpi_promiscon(dh, DL_PROMISC_PHYS); + if (retv != DLPI_SUCCESS) { + pr_errdlpi(dh, "promiscuous mode(physical) failed", + retv); + } + } else { + (void) fprintf(stderr, "(non promiscuous)\n"); + retv = dlpi_promiscon(dh, DL_PROMISC_MULTI); + if (retv != DLPI_SUCCESS) { + pr_errdlpi(dh, "promiscuous mode(multicast) failed", + retv); + } + } + + retv = dlpi_promiscon(dh, DL_PROMISC_SAP); + if (retv != DLPI_SUCCESS) + pr_errdlpi(dh, "promiscuous mode(SAP) failed", retv); + + netfd = dlpi_fd(dh); + + if (fp) { + /* + * push and configure the packet filtering module + */ + if (ioctl(netfd, I_PUSH, "pfmod") < 0) + pr_errdlpi(dh, "cannot push \"pfmod\"", DL_SYSERR); + + if (strioctl(netfd, PFIOCSETF, -1, sizeof (*fp), + (char *)fp) < 0) + pr_errdlpi(dh, "PFIOCSETF", DL_SYSERR); + } + + if (ioctl(netfd, I_PUSH, "bufmod") < 0) + pr_errdlpi(dh, "cannot push \"bufmod\"", DL_SYSERR); + + if (strioctl(netfd, SBIOCSTIME, -1, sizeof (struct timeval), + (char *)timeout) < 0) + pr_errdlpi(dh, "SBIOCSTIME", DL_SYSERR); + + if (strioctl(netfd, SBIOCSCHUNK, -1, sizeof (uint_t), + (char *)&chunksize) < 0) + pr_errdlpi(dh, "SBIOCGCHUNK", DL_SYSERR); + + if (strioctl(netfd, SBIOCSSNAP, -1, sizeof (uint_t), + (char *)&snaplen) < 0) + pr_errdlpi(dh, "SBIOCSSNAP", DL_SYSERR); + + /* + * Flush the read queue, to get rid of anything that + * accumulated before the device reached its final configuration. + */ + if (ioctl(netfd, I_FLUSH, FLUSHR) < 0) + pr_errdlpi(dh, "cannot flush \"I_FLUSH\"", DL_SYSERR); +} + +/* + * Read packets from the network. init_datalink() is called in + * here to set up the network interface for reading of + * raw ethernet packets in promiscuous mode into a buffer. + * Packets are read and either written directly to a file + * or interpreted for display on the fly. + */ +void +net_read(dlpi_handle_t dh, size_t chunksize, int filter, void (*proc)(), + int flags) +{ + int retval; + extern int count; + size_t msglen; + + count = 0; + + /* allocate a read buffer */ + bufp = malloc(chunksize); + if (bufp == NULL) + pr_err("no memory for %d buffer", chunksize); + + /* + * read frames + */ + for (;;) { + msglen = chunksize; + retval = dlpi_recv(dh, NULL, NULL, bufp, &msglen, -1, NULL); + + if (retval != DLPI_SUCCESS || quitting) + break; + + if (msglen != 0) + scan(bufp, msglen, filter, 0, 0, proc, 0, 0, flags); + } + + free(bufp); + + if (!quitting) + pr_errdlpi(dh, "network read failed", retval); +} + +#ifdef DEBUG +/* + * corrupt: simulate packet corruption for debugging interpreters + */ +void +corrupt(volatile char *pktp, volatile char *pstop, char *buf, + volatile char *bufstop) +{ + int c; + int i; + int p; + int li = rand() % (pstop - pktp - 1) + 1; + volatile char *pp = pktp; + volatile char *pe = bufstop < pstop ? bufstop : pstop; + + if (pktp < buf || pktp > bufstop) + return; + + for (pp = pktp; pp < pe; pp += li) { + c = ((pe - pp) < li ? pe - pp : li); + i = (rand() % c)>>1; + while (--i > 0) { + p = (rand() % c); + pp[p] = (unsigned char)(rand() & 0xFF); + } + } +} +#endif /* DEBUG */ + +static void +scan(char *buf, int len, int filter, int cap, int old, void (*proc)(), + int first, int last, int flags) +{ + volatile char *bp, *bufstop; + volatile struct sb_hdr *hdrp; + volatile struct sb_hdr nhdr, *nhdrp; + volatile char *pktp; + volatile struct timeval last_timestamp; + volatile int header_okay; + extern int count, maxcount; + extern int snoop_nrecover; +#ifdef DEBUG + extern int zflg; +#endif /* DEBUG */ + + proc(0, 0, 0); + bufstop = buf + len; + + /* + * + * Loop through each packet in the buffer + */ + last_timestamp.tv_sec = 0; + (void) memcpy((char *)ojmp_env, (char *)jmp_env, sizeof (jmp_env)); + for (bp = buf; bp < bufstop; bp += nhdrp->sbh_totlen) { + /* + * Gracefully exit if user terminates + */ + if (quitting) + break; + /* + * Global error recocery: Prepare to continue when a corrupt + * packet or header is encountered. + */ + if (sigsetjmp(jmp_env, 1)) { + goto err; + } + + header_okay = 0; + hdrp = (struct sb_hdr *)bp; + nhdrp = hdrp; + pktp = (char *)hdrp + sizeof (*hdrp); + + /* + * If reading a capture file + * convert the headers from network + * byte order (for little-endians like X86) + */ + if (cap) { + /* + * If the packets come from an old + * capture file, convert the header. + */ + if (old) { + convert_old((struct ohdr *)hdrp); + } + + nhdrp = &nhdr; + + nhdrp->sbh_origlen = ntohl(hdrp->sbh_origlen); + nhdrp->sbh_msglen = ntohl(hdrp->sbh_msglen); + nhdrp->sbh_totlen = ntohl(hdrp->sbh_totlen); + nhdrp->sbh_drops = ntohl(hdrp->sbh_drops); + nhdrp->sbh_timestamp.tv_sec = + ntohl(hdrp->sbh_timestamp.tv_sec); + nhdrp->sbh_timestamp.tv_usec = + ntohl(hdrp->sbh_timestamp.tv_usec); + } + + /* Enhanced check for valid header */ + + if ((nhdrp->sbh_totlen == 0) || + (bp + nhdrp->sbh_totlen) < bp || + (bp + nhdrp->sbh_totlen) > bufstop || + (nhdrp->sbh_origlen == 0) || + (bp + nhdrp->sbh_origlen) < bp || + (nhdrp->sbh_msglen == 0) || + (bp + nhdrp->sbh_msglen) < bp || + (bp + nhdrp->sbh_msglen) > bufstop || + (nhdrp->sbh_msglen > nhdrp->sbh_origlen) || + (nhdrp->sbh_totlen < nhdrp->sbh_msglen) || + (nhdrp->sbh_timestamp.tv_sec == 0)) { + if (cap) { + (void) fprintf(stderr, "(warning) bad packet " + "header in capture file"); + } else { + (void) fprintf(stderr, "(warning) bad packet " + "header in buffer"); + } + (void) fprintf(stderr, " offset %d: length=%d\n", + bp - buf, nhdrp->sbh_totlen); + goto err; + } + + /* + * Check for incomplete packet. We are conservative here, + * since we don't know how good the checking is in other + * parts of the code. We pass a partial packet, with + * a warning. + */ + if (pktp + nhdrp->sbh_msglen > bufstop) { + (void) fprintf(stderr, "truncated packet buffer\n"); + nhdrp->sbh_msglen = bufstop - pktp; + } + +#ifdef DEBUG + if (zflg) + corrupt(pktp, pktp + nhdrp->sbh_msglen, buf, bufstop); +#endif /* DEBUG */ + + header_okay = 1; + if (!filter || + want_packet((uchar_t *)pktp, + nhdrp->sbh_msglen, + nhdrp->sbh_origlen)) { + count++; + + /* + * Start deadman timer for interpreter processing + */ + (void) snoop_alarm(SNOOP_ALARM_GRAN*SNOOP_MAXRECOVER, + NULL); + + encap_levels = 0; + if (!cap || count >= first) + proc(nhdrp, pktp, count, flags); + + if (cap && count >= last) { + (void) snoop_alarm(0, NULL); + break; + } + + if (maxcount && count >= maxcount) { + (void) fprintf(stderr, "%d packets captured\n", + count); + exit(0); + } + + snoop_nrecover = 0; /* success */ + (void) snoop_alarm(0, NULL); + last_timestamp = hdrp->sbh_timestamp; /* save stamp */ + } + continue; +err: + /* + * Corruption has been detected. Reset errors. + */ + snoop_recover(); + + /* + * packet header was apparently okay. Continue. + */ + if (header_okay) + continue; + + /* + * Otherwise try to scan forward to the next packet, using + * the last known timestamp if it is available. + */ + nhdrp = &nhdr; + nhdrp->sbh_totlen = 0; + if (last_timestamp.tv_sec == 0) { + bp += sizeof (int); + } else { + for (bp += sizeof (int); bp <= bufstop; + bp += sizeof (int)) { + hdrp = (struct sb_hdr *)bp; + /* An approximate timestamp located */ + if ((hdrp->sbh_timestamp.tv_sec >> 8) == + (last_timestamp.tv_sec >> 8)) + break; + } + } + } + /* reset jmp_env for program exit */ + (void) memcpy((char *)jmp_env, (char *)ojmp_env, sizeof (jmp_env)); + proc(0, -1, 0); +} + +/* + * Called if nwrite() encounters write problems. + */ +static void +cap_write_error(const char *msgtype) +{ + (void) fprintf(stderr, + "snoop: cannot write %s to capture file: %s\n", + msgtype, strerror(errno)); + exit(1); +} + +/* + * Writes target buffer to the open file descriptor. Upon detection of a short + * write, an attempt to process the remaining bytes occurs until all anticipated + * bytes are written. An error status is returned to indicate any serious write + * failures. + */ +static int +nwrite(int fd, const void *buffer, size_t buflen) +{ + size_t nwritten; + ssize_t nbytes = 0; + const char *buf = buffer; + + for (nwritten = 0; nwritten < buflen; nwritten += nbytes) { + nbytes = write(fd, &buf[nwritten], buflen - nwritten); + if (nbytes == -1) + return (-1); + if (nbytes == 0) { + errno = EIO; + return (-1); + } + } + return (0); +} + +/* + * Routines for opening, closing, reading and writing + * a capture file of packets saved with the -o option. + */ +static int capfile_out; + +/* + * The snoop capture file has a header to identify + * it as a capture file and record its version. + * A file without this header is assumed to be an + * old format snoop file. + * + * A version 1 header looks like this: + * + * 0 1 2 3 4 5 6 7 8 9 10 11 + * +---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | s | n | o | o | p | \0| \0| \0| version | data + * +---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | word 0 | word 1 | word 2 | + * + * + * A version 2 header adds a word that identifies the MAC type. + * This allows for capture files from FDDI etc. + * + * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | s | n | o | o | p | \0| \0| \0| version | MAC type | data + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | word 0 | word 1 | word 2 | word 3 + * + */ +static const char *snoop_id = "snoop\0\0\0"; +static const int snoop_idlen = 8; +static const int snoop_version = 2; + +void +cap_open_write(const char *name) +{ + int vers; + + capfile_out = open(name, O_CREAT | O_TRUNC | O_RDWR, 0666); + if (capfile_out < 0) + pr_err("%s: %m", name); + + vers = htonl(snoop_version); + if (nwrite(capfile_out, snoop_id, snoop_idlen) == -1) + cap_write_error("snoop_id"); + + if (nwrite(capfile_out, &vers, sizeof (int)) == -1) + cap_write_error("version"); +} + + +void +cap_close(void) +{ + (void) close(capfile_out); +} + +static char *cap_buffp = NULL; +static int cap_len = 0; +static int cap_new; + +void +cap_open_read(const char *name) +{ + struct stat st; + int cap_vers; + int *word; + int device_mac_type = -1; + int capfile_in; + + capfile_in = open(name, O_RDONLY); + if (capfile_in < 0) + pr_err("couldn't open %s: %m", name); + + if (fstat(capfile_in, &st) < 0) + pr_err("couldn't stat %s: %m", name); + cap_len = st.st_size; + + cap_buffp = mmap(0, cap_len, PROT_READ, MAP_PRIVATE, capfile_in, 0); + (void) close(capfile_in); + if ((int)cap_buffp == -1) + pr_err("couldn't mmap %s: %m", name); + + /* Check if new snoop capture file format */ + + cap_new = bcmp(cap_buffp, snoop_id, snoop_idlen) == 0; + + /* + * If new file - check version and + * set buffer pointer to point at first packet + */ + if (cap_new) { + cap_vers = ntohl(*(int *)(cap_buffp + snoop_idlen)); + cap_buffp += snoop_idlen + sizeof (int); + cap_len -= snoop_idlen + sizeof (int); + + switch (cap_vers) { + case 1: + device_mac_type = DL_ETHER; + break; + + case 2: + device_mac_type = ntohl(*((int *)cap_buffp)); + cap_buffp += sizeof (int); + cap_len -= sizeof (int); + break; + + default: + pr_err("capture file: %s: Version %d unrecognized\n", + name, cap_vers); + } + + for (interface = &INTERFACES[0]; interface->mac_type != -1; + interface++) + if (interface->mac_type == device_mac_type) + break; + + if (interface->mac_type == -1) + pr_err("Mac Type = %x is not supported\n", + device_mac_type); + } else { + /* Use heuristic to check if it's an old-style file */ + + device_mac_type = DL_ETHER; + word = (int *)cap_buffp; + + if (!((word[0] < 1600 && word[1] < 1600) && + (word[0] < word[1]) && + (word[2] > 610000000 && word[2] < 770000000))) + pr_err("not a capture file: %s", name); + + /* Change protection so's we can fix the headers */ + + if (mprotect(cap_buffp, cap_len, PROT_READ | PROT_WRITE) < 0) + pr_err("mprotect: %s: %m", name); + } +} + +void +cap_read(int first, int last, int filter, void (*proc)(), int flags) +{ + extern int count; + + count = 0; + + scan(cap_buffp, cap_len, filter, 1, !cap_new, proc, first, last, flags); + + (void) munmap(cap_buffp, cap_len); +} + +/* ARGSUSED */ +void +cap_write(struct sb_hdr *hdrp, char *pktp, int num, int flags) +{ + int pktlen, mac; + static int first = 1; + struct sb_hdr nhdr; + extern boolean_t qflg; + + if (hdrp == NULL) + return; + + if (first) { + first = 0; + mac = htonl(interface->mac_type); + if (nwrite(capfile_out, &mac, sizeof (int)) == -1) + cap_write_error("mac_type"); + } + + pktlen = hdrp->sbh_totlen - sizeof (*hdrp); + + /* + * Convert sb_hdr to network byte order + */ + nhdr.sbh_origlen = htonl(hdrp->sbh_origlen); + nhdr.sbh_msglen = htonl(hdrp->sbh_msglen); + nhdr.sbh_totlen = htonl(hdrp->sbh_totlen); + nhdr.sbh_drops = htonl(hdrp->sbh_drops); + nhdr.sbh_timestamp.tv_sec = htonl(hdrp->sbh_timestamp.tv_sec); + nhdr.sbh_timestamp.tv_usec = htonl(hdrp->sbh_timestamp.tv_usec); + + if (nwrite(capfile_out, &nhdr, sizeof (nhdr)) == -1) + cap_write_error("packet header"); + + if (nwrite(capfile_out, pktp, pktlen) == -1) + cap_write_error("packet"); + + if (! qflg) + show_count(); +} + +/* + * Convert a packet header from + * old to new format. + */ +static void +convert_old(struct ohdr *ohdrp) +{ + struct sb_hdr nhdr; + + nhdr.sbh_origlen = ohdrp->o_len; + nhdr.sbh_msglen = ohdrp->o_msglen; + nhdr.sbh_totlen = ohdrp->o_totlen; + nhdr.sbh_drops = ohdrp->o_drops; + nhdr.sbh_timestamp = ohdrp->o_time; + + *(struct sb_hdr *)ohdrp = nhdr; +} + +static int +strioctl(int fd, int cmd, int timout, int len, void *dp) +{ + struct strioctl sioc; + int rc; + + sioc.ic_cmd = cmd; + sioc.ic_timout = timout; + sioc.ic_len = len; + sioc.ic_dp = dp; + rc = ioctl(fd, I_STR, &sioc); + + if (rc < 0) + return (rc); + else + return (sioc.ic_len); +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcp.c new file mode 100644 index 0000000..7857da7 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcp.c @@ -0,0 +1,746 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sysmacros.h> +#include <netinet/in.h> +#include <netinet/dhcp.h> +#include <arpa/inet.h> +#include <dhcp_inittab.h> +#include <dhcp_symbol.h> +#include "snoop.h" + +static const char *show_msgtype(unsigned char); +static int show_options(unsigned char *, int); +static void display_ip(int, char *, char *, unsigned char **); +static void display_ascii(char *, char *, unsigned char **); +static void display_number(char *, char *, unsigned char **); +static void display_ascii_hex(char *, unsigned char **); +static unsigned char bootmagic[] = BOOTMAGIC; /* rfc 1048 */ + +static char *option_types[] = { +"", /* 0 */ +"Subnet Mask", /* 1 */ +"UTC Time Offset", /* 2 */ +"Router", /* 3 */ +"RFC868 Time Servers", /* 4 */ +"IEN 116 Name Servers", /* 5 */ +"DNS Servers", /* 6 */ +"UDP LOG Servers", /* 7 */ +"RFC 865 Cookie Servers", /* 8 */ +"RFC 1179 Line Printer Servers (LPR)", /* 9 */ +"Impress Servers", /* 10 */ +"RFC 887 Resource Location Servers", /* 11 */ +"Client Hostname", /* 12 */ +"Boot File size in 512 byte Blocks", /* 13 */ +"Merit Dump File", /* 14 */ +"DNS Domain Name", /* 15 */ +"SWAP Server", /* 16 */ +"Client Root Path", /* 17 */ +"BOOTP options extensions path", /* 18 */ +"IP Forwarding Flag", /* 19 */ +"NonLocal Source Routing Flag", /* 20 */ +"Policy Filters for NonLocal Routing", /* 21 */ +"Maximum Datagram Reassembly Size", /* 22 */ +"Default IP Time To Live", /* 23 */ +"Path MTU Aging Timeout", /* 24 */ +"Path MTU Size Plateau Table", /* 25 */ +"Interface MTU Size", /* 26 */ +"All Subnets are Local Flag", /* 27 */ +"Broadcast Address", /* 28 */ +"Perform Mask Discovery Flag", /* 29 */ +"Mask Supplier Flag", /* 30 */ +"Perform Router Discovery Flag", /* 31 */ +"Router Solicitation Address", /* 32 */ +"Static Routes", /* 33 */ +"Trailer Encapsulation Flag", /* 34 */ +"ARP Cache Timeout Seconds", /* 35 */ +"Ethernet Encapsulation Flag", /* 36 */ +"TCP Default Time To Live", /* 37 */ +"TCP Keepalive Interval Seconds", /* 38 */ +"TCP Keepalive Garbage Flag", /* 39 */ +"NIS Domainname", /* 40 */ +"NIS Servers", /* 41 */ +"Network Time Protocol Servers", /* 42 */ +"Vendor Specific Options", /* 43 */ +"NetBIOS RFC 1001/1002 Name Servers", /* 44 */ +"NetBIOS Datagram Dist. Servers", /* 45 */ +"NetBIOS Node Type", /* 46 */ +"NetBIOS Scope", /* 47 */ +"X Window Font Servers", /* 48 */ +"X Window Display Manager Servers", /* 49 */ +"Requested IP Address", /* 50 */ +"IP Address Lease Time", /* 51 */ +"Option Field Overload Flag", /* 52 */ +"DHCP Message Type", /* 53 */ +"DHCP Server Identifier", /* 54 */ +"Option Request List", /* 55 */ +"Error Message", /* 56 */ +"Maximum DHCP Message Size", /* 57 */ +"Renewal (T1) Time Value", /* 58 */ +"Rebinding (T2) Time Value", /* 59 */ +"Client Class Identifier =", /* 60 */ +"Client Identifier =", /* 61 */ +"Netware IP Domain =", /* 62 */ +"Netware IP Options =", /* 63 */ +"TFTP Server Name", /* 66 */ +"Option BootFile Name", /* 67 */ +"Mobile IP Agents", /* 68 */ +"Simple Mail (SMTP) Servers", /* 69 */ +"Post Office (POP3) Servers", /* 70 */ +"Net News (NNTP) Servers", /* 71 */ +"WorldWideWeb Servers", /* 72 */ +"Finger Servers", /* 73 */ +"Internet Relay Chat (IRC) Servers", /* 74 */ +"StreetTalk Servers", /* 75 */ +"StreetTalk Directory Assist. Servers", /* 76 */ +"User Class Identifier", /* 77 */ +}; + +#define OPTIONS_ARRAY_SIZE 78 + +int +interpret_dhcp(int flags, struct dhcp *dp, int len) +{ + if (flags & F_SUM) { + if ((memcmp(dp->cookie, bootmagic, sizeof (bootmagic)) == 0) && + (len >= BASE_PKT_SIZE + 3) && + dp->options[0] == CD_DHCP_TYPE) { + (void) sprintf(get_sum_line(), + "DHCP/BOOTP %s", show_msgtype(dp->options[2])); + } else { + switch (ntohs(dp->op)) { + case BOOTREQUEST: + (void) sprintf(get_sum_line(), + "DHCP/BOOTP BOOTREQUEST"); + break; + case BOOTREPLY: + (void) sprintf(get_sum_line(), + "DHCP/BOOTP BOOTREPLY"); + break; + } + } + } + if (flags & F_DTAIL) { + show_header("DHCP: ", "Dynamic Host Configuration Protocol", + len); + show_space(); + (void) sprintf(get_line((char *)(uintptr_t)dp->htype - + dlc_header, 1), + "Hardware address type (htype) = %d (%s)", dp->htype, + arp_htype(dp->htype)); + (void) sprintf(get_line((char *)(uintptr_t)dp->hlen - + dlc_header, 1), + "Hardware address length (hlen) = %d octets", dp->hlen); + (void) sprintf(get_line((char *)(uintptr_t)dp->hops - + dlc_header, 1), + "Relay agent hops = %d", dp->hops); + (void) sprintf(get_line((char *)(uintptr_t)dp->xid - + dlc_header, 4), + "Transaction ID = 0x%x", ntohl(dp->xid)); + (void) sprintf(get_line((char *)(uintptr_t)dp->secs - + dlc_header, 2), + "Time since boot = %d seconds", ntohs(dp->secs)); + (void) sprintf(get_line((char *)(uintptr_t)dp->flags - + dlc_header, 2), + "Flags = 0x%.4x", ntohs(dp->flags)); + (void) sprintf(get_line((char *)&dp->ciaddr - dlc_header, 4), + "Client address (ciaddr) = %s", inet_ntoa(dp->ciaddr)); + (void) sprintf(get_line((char *)&dp->yiaddr - dlc_header, 4), + "Your client address (yiaddr) = %s", + inet_ntoa(dp->yiaddr)); + (void) sprintf(get_line((char *)&dp->siaddr - dlc_header, 4), + "Next server address (siaddr) = %s", + inet_ntoa(dp->siaddr)); + (void) sprintf(get_line((char *)&dp->giaddr - dlc_header, 4), + "Relay agent address (giaddr) = %s", + inet_ntoa(dp->giaddr)); + if (dp->htype == 1) { + (void) sprintf(get_line((char *)dp->chaddr - + dlc_header, dp->hlen), + "Client hardware address (chaddr) = %.2X:%.2X:%.2X:%.2X:%.2X:%.2X", + dp->chaddr[0], + dp->chaddr[1], + dp->chaddr[2], + dp->chaddr[3], + dp->chaddr[4], + dp->chaddr[5]); + } + /* + * Check cookie, process options + */ + if (memcmp(dp->cookie, bootmagic, sizeof (bootmagic)) != 0) { + (void) sprintf(get_line(0, 0), + "Unrecognized cookie: 0x%.2X%.2X%.2X%.2X\n", + dp->cookie[0], + dp->cookie[1], + dp->cookie[2], + dp->cookie[3]); + return (0); + } + show_space(); + show_header("DHCP: ", "(Options) field options", len); + show_space(); + switch (show_options(dp->options, (len - BASE_PKT_SIZE))) { + case 0: + /* No option overloading */ + if (*(unsigned char *)(dp->sname) != '\0') { + (void) sprintf(get_line(0, 0), + "Server Name = %s", dp->sname); + } + if (*(unsigned char *)(dp->file) != '\0') { + (void) sprintf(get_line(0, 0), + "Boot File Name = %s", dp->file); + } + break; + case 1: + /* file field used */ + if (*(unsigned char *)(dp->sname) != '\0') { + (void) sprintf(get_line(0, 0), + "Server Name = %s", dp->sname); + } + show_space(); + show_header("DHCP: ", "(File) field options", len); + show_space(); + (void) show_options(dp->file, 128); + break; + case 2: + /* sname field used for options */ + if (*(unsigned char *)(dp->file) != '\0') { + (void) sprintf(get_line(0, 0), + "Boot File Name = %s", dp->file); + } + show_space(); + show_header("DHCP: ", "(Sname) field options", len); + show_space(); + (void) show_options(dp->sname, 64); + break; + case 3: + show_space(); + show_header("DHCP: ", "(File) field options", len); + show_space(); + (void) show_options(dp->file, 128); + show_space(); + show_header("DHCP: ", "(Sname) field options", len); + show_space(); + (void) show_options(dp->sname, 64); + break; + }; + } + return (len); +} + +static int +show_options(unsigned char *cp, int len) +{ + char *prmpt; + unsigned char *end, *vend; + unsigned char *start, save; + int items, i; + int nooverload = 0; + ushort_t s_buf; + struct in_addr tmp; + char scratch[128]; + dhcp_symbol_t *entry; + char *decoded_opt; + int opt_len; + + start = cp; + end = (unsigned char *)cp + len; + + while (start < end) { + if (*start == CD_PAD) { + start++; + continue; + } + if (*start == CD_END) + break; /* done */ + + save = *start++; + switch (save) { + /* Network order IP address(es) */ + case CD_SUBNETMASK: + case CD_ROUTER_SOLICIT_SERV: + case CD_BROADCASTADDR: + case CD_REQUESTED_IP_ADDR: + case CD_SERVER_ID: + /* Single IP address */ + if (*start != 4) { + (void) sprintf(get_line(0, 0), + "Error: Bad %s", option_types[save]); + } else { + start++; + display_ip(1, "%s = %s", option_types[save], + &start); + } + break; + case CD_ROUTER: + case CD_TIMESERV: + case CD_IEN116_NAME_SERV: + case CD_DNSSERV: + case CD_LOG_SERV: + case CD_COOKIE_SERV: + case CD_LPR_SERV: + case CD_IMPRESS_SERV: + case CD_RESOURCE_SERV: + case CD_SWAP_SERV: + case CD_NIS_SERV: + case CD_NTP_SERV: + case CD_NETBIOS_NAME_SERV: + case CD_NETBIOS_DIST_SERV: + case CD_XWIN_FONT_SERV: + case CD_XWIN_DISP_SERV: + case CD_MOBILE_IP_AGENT: + case CD_SMTP_SERVS: + case CD_POP3_SERVS: + case CD_NNTP_SERVS: + case CD_WWW_SERVS: + case CD_FINGER_SERVS: + case CD_IRC_SERVS: + case CD_STREETTALK_SERVS: + case CD_STREETTALK_DA_SERVS: + /* Multiple IP addresses */ + if ((*start % 4) != 0) { + (void) sprintf(get_line(0, 0), + "Error: Bad %s address", + option_types[save]); + } else { + items = *start++ / 4; + display_ip(items, "%s at = %s", + option_types[save], &start); + } + break; + case CD_TFTP_SERV_NAME: + case CD_HOSTNAME: + case CD_DUMP_FILE: + case CD_DNSDOMAIN: + case CD_ROOT_PATH: + case CD_NIS_DOMAIN: + case CD_NETBIOS_SCOPE: + case CD_MESSAGE: + case CD_OPT_BOOTFILE_NAME: + case CD_USER_CLASS_ID: + /* Ascii strings */ + display_ascii("%s = %s", option_types[save], &start); + break; + case CD_TIMEOFFSET: + case CD_IPTTL: + case CD_PATH_MTU_TIMEOUT: + case CD_ARP_TIMEOUT: + case CD_TCP_TTL: + case CD_TCP_KALIVE_INTVL: + case CD_T1_TIME: + case CD_T2_TIME: + case CD_LEASE_TIME: + /* Number: seconds */ + display_number("%s = %d seconds", option_types[save], + &start); + break; + case CD_IP_FORWARDING_ON: + case CD_NON_LCL_ROUTE_ON: + case CD_ALL_SUBNETS_LCL_ON: + case CD_MASK_DISCVRY_ON: + case CD_MASK_SUPPLIER_ON: + case CD_ROUTER_DISCVRY_ON: + case CD_TRAILER_ENCAPS_ON: + case CD_ETHERNET_ENCAPS_ON: + case CD_TCP_KALIVE_GRBG_ON: + /* Number: hex flag */ + display_number("%s flag = 0x%x", option_types[save], + &start); + break; + case CD_MAXIPSIZE: + case CD_MTU: + case CD_MAX_DHCP_SIZE: + /* Number: bytes */ + display_number("%s = %d bytes", option_types[save], + &start); + break; + case CD_CLASS_ID: + case CD_CLIENT_ID: + case CD_NW_IP_DOMAIN: + case CD_NW_IP_OPTIONS: + /* Hex ascii strings */ + display_ascii_hex(option_types[save], &start); + break; + case CD_BOOT_SIZE: + display_number("%s = %d 512 byte blocks", + "Boot file size", &start); + break; + case CD_POLICY_FILTER: + if ((*start % 8) != 0) { + (void) sprintf(get_line(0, 0), + "Error: Bad Policy Filter option"); + } else { + items = *start++ / 8; + for (i = 0; i < items; i++) { + display_ip(1, + "%s = %s", + "Policy Destination", + &start); + display_ip(1, "%s = %s", "Mask", + &start); + } + } + break; + case CD_PATH_MTU_TABLE_SZ: + if (*start % 2 != 0) { + (void) sprintf(get_line(0, 0), + "Error: Bad Path MTU Table"); + } else { + (void) sprintf(get_line(0, 0), + "\tPath MTU Plateau Table:"); + (void) sprintf(get_line(0, 0), + "\t======================="); + items = *start / sizeof (ushort_t); + ++start; + for (i = 0; i < items; i++) { + if (IS_P2ALIGNED(start, + sizeof (ushort_t))) { + /* LINTED: improper alignment */ + s_buf = *(ushort_t *)start; + } else { + memcpy((char *)&s_buf, + start, sizeof (short)); + } + (void) sprintf(get_line(0, 0), + "\t\tEntry %d:\t\t%d", i, + ntohs(s_buf)); + start += sizeof (ushort_t); + } + } + break; + case CD_STATIC_ROUTE: + if ((*start % 8) != 0) { + (void) sprintf(get_line(0, 0), + "Error: Bad Static Route option: %d", + *start); + } else { + items = *start++ / 8; + for (i = 0; i < items; i++) { + memcpy((char *)&tmp, start, + sizeof (struct in_addr)); + (void) strcpy(scratch, inet_ntoa(tmp)); + start += sizeof (ulong_t); + memcpy((char *)&tmp, start, + sizeof (struct in_addr)); + (void) sprintf(get_line(0, 0), + "Static route from %s to %s", + scratch, inet_ntoa(tmp)); + start += sizeof (ulong_t); + } + } + break; + case CD_VENDOR_SPEC: + i = *start++; + (void) sprintf(get_line(0, 0), + "Vendor-specific Options (%d total octets):", i); + /* + * We don't know what these things are, so just + * display the option number, length, and value + * (hex). + */ + vend = (uchar_t *)((uchar_t *)start + i); + while (start < vend && *start != CD_END) { + if (*start == CD_PAD) { + start++; + continue; + } + (void) sprintf(scratch, + "\t(%.2d) %.2d octets", *start, + *(uchar_t *)((uchar_t *)start + 1)); + start++; + display_ascii_hex(scratch, &start); + } + start = vend; /* in case CD_END found */ + break; + case CD_NETBIOS_NODE_TYPE: + if (*start != 1) { + (void) sprintf(get_line(0, 0), + "Error: Bad '%s' parameter", + option_types[CD_NETBIOS_NODE_TYPE]); + } else { + char *type; + start++; + switch (*start) { + case 0x1: + type = "Broadcast Node"; + break; + case 0x2: + type = "Point To Point Node"; + break; + case 0x4: + type = "Mixed Mode Node"; + break; + case 0x8: + type = "Hybrid Node"; + break; + default: + type = "??? Node"; + break; + }; + (void) sprintf(get_line(0, 0), + "%s = %s (%d)", + option_types[CD_NETBIOS_NODE_TYPE], + type, *start); + start++; + } + break; + case CD_OPTION_OVERLOAD: + if (*start != 1) { + (void) sprintf(get_line(0, 0), + "Bad Option Overload value."); + } else { + start++; + nooverload = *start++; + } + break; + case CD_DHCP_TYPE: + if (*start < 1 || *start > 7) { + (void) sprintf(get_line(0, 0), + "Bad DHCP Message Type."); + } else { + start++; + (void) sprintf(get_line(0, 0), + "Message type = %s", + show_msgtype(*start)); + start++; + } + break; + case CD_REQUEST_LIST: + opt_len = *start++; + (void) sprintf(get_line(0, 0), + "Requested Options:"); + for (i = 0; i < opt_len; i++) { + entry = NULL; + if (*start < OPTIONS_ARRAY_SIZE) { + prmpt = option_types[*start]; + } else { + entry = inittab_getbycode( + ITAB_CAT_STANDARD|ITAB_CAT_SITE, + ITAB_CONS_SNOOP, *start); + if (entry == NULL) { + if (*start >= DHCP_SITE_OPT && + *start <= DHCP_END_SITE) { + prmpt = "Site Option"; + } else { + prmpt = "Unrecognized " + "Option"; + } + } else { + prmpt = entry->ds_name; + } + } + (void) sprintf(get_line(0, 0), + "\t%2d (%s)", *start, prmpt); + start++; + free(entry); + } + break; + default: + opt_len = *start++; + entry = inittab_getbycode( + ITAB_CAT_STANDARD|ITAB_CAT_SITE, + ITAB_CONS_SNOOP, save); + if (entry == NULL) { + if (save >= DHCP_SITE_OPT && + save <= DHCP_END_SITE) + prmpt = "Site"; + else + prmpt = "Unrecognized"; + decoded_opt = NULL; + } else { + if (save < OPTIONS_ARRAY_SIZE) { + prmpt = option_types[save]; + } else { + prmpt = entry->ds_name; + } + decoded_opt = inittab_decode(entry, start, + opt_len, B_TRUE); + } + if (decoded_opt == NULL) { + (void) sprintf(get_line(0, 0), + "%s Option = %d, length = %d octets", + prmpt, save, opt_len); + start--; + display_ascii_hex("\tValue =", &start); + } else { + (void) sprintf(get_line(0, 0), "%s = %s", prmpt, + decoded_opt); + start += opt_len; + free(decoded_opt); + } + free(entry); + break; + }; + } + return (nooverload); +} + +static const char * +show_msgtype(unsigned char type) +{ + /* + * note: the ordering here allows direct indexing of the table + * based on the RFC2131 packet type value passed in. + */ + + static const char *types[] = { + "BOOTP", + "DHCPDISCOVER", "DHCPOFFER", "DHCPREQUEST", "DHCPDECLINE", + "DHCPACK", "DHCPNAK", "DHCPRELEASE", "DHCPINFORM" + }; + + if (type >= (sizeof (types) / sizeof (*types)) || types[type] == NULL) + return ("UNKNOWN"); + + return (types[type]); +} + +static void +display_ip(int items, char *fmt, char *msg, unsigned char **opt) +{ + struct in_addr tmp; + int i; + + for (i = 0; i < items; i++) { + memcpy((char *)&tmp, *opt, sizeof (struct in_addr)); + (void) sprintf(get_line(0, 0), fmt, msg, inet_ntoa(tmp)); + *opt += 4; + } +} + +static void +display_ascii(char *fmt, char *msg, unsigned char **opt) +{ + static unsigned char buf[256]; + int len = **opt; + unsigned char slen = len; + + if (len >= sizeof (buf)) + len = sizeof (buf) - 1; + (*opt)++; + memcpy(buf, *opt, len); + *(unsigned char *)(buf + len) = '\0'; + (void) sprintf(get_line(0, 0), fmt, msg, buf); + (*opt) += slen; +} + +static void +display_number(char *fmt, char *msg, unsigned char **opt) +{ + int len = **opt; + unsigned long l_buf = 0; + unsigned short s_buf = 0; + + if (len > 4) { + (*opt)++; + (void) sprintf(get_line(0, 0), fmt, msg, 0xdeadbeef); + return; + } + switch (len) { + case sizeof (uchar_t): + (*opt)++; + (void) sprintf(get_line(0, 0), fmt, msg, **opt); + break; + case sizeof (ushort_t): + (*opt)++; + if (IS_P2ALIGNED(*opt, sizeof (ushort_t))) + /* LINTED: improper alignment */ + s_buf = *(unsigned short *)*opt; + else + memcpy((char *)&s_buf, *opt, len); + (void) sprintf(get_line(0, 0), fmt, msg, ntohs(s_buf)); + break; + case sizeof (ulong_t): + (*opt)++; + if (IS_P2ALIGNED(*opt, sizeof (ulong_t))) + /* LINTED: improper alignment */ + l_buf = *(unsigned long *)*opt; + else + memcpy((char *)&l_buf, *opt, len); + (void) sprintf(get_line(0, 0), fmt, msg, ntohl(l_buf)); + break; + } + (*opt) += len; +} + +static void +display_ascii_hex(char *msg, unsigned char **opt) +{ + int printable; + char buffer[512]; + char *line, *tmp, *ap, *fmt; + int i, len = **opt; + + line = get_line(0, 0); + + (*opt)++; + + if (len >= 255) { + (void) sprintf(line, "\t%s <TOO LONG>", msg); + return; + } + + for (printable = 1, tmp = (char *)(*opt), ap = buffer; + tmp < (char *)&((*opt)[len]); tmp++) { + if (isprint(*tmp)) + *ap++ = *tmp; + else { + *ap++ = '.'; + printable = 0; + } + } + *ap = '\0'; + + if (!printable) { + for (tmp = (char *)(*opt), ap = buffer; + (tmp < (char *)&((*opt)[len])) && ((ap + 5) < &buffer[512]); + tmp++) { + ap += sprintf(ap, "0x%02X ", *(uchar_t *)(tmp)); + } + /* Truncate the trailing space */ + *(--ap) = '\0'; + /* More bytes to print in hex but no space in buffer */ + if (tmp < (char *)&((*opt)[len])) { + i = ap - buffer; + buffer[i - 1] = '.'; + buffer[i - 2] = '.'; + buffer[i - 3] = '.'; + } + fmt = "%s\t%s (unprintable)"; + } else { + fmt = "%s\t\"%s\""; + } + (*opt) += len; + (void) sprintf(line, fmt, msg, buffer); +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcpv6.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcpv6.c new file mode 100644 index 0000000..0e7c1be --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcpv6.c @@ -0,0 +1,1051 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Dynamic Host Configuration Protocol version 6, for IPv6. Supports + * RFCs 3315, 3319, 3646, 3898, 4075, 4242, 4280, 4580, 4649, and 4704. + */ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/dhcp6.h> +#include <arpa/inet.h> +#include <dhcp_impl.h> +#include <dhcp_inittab.h> + +#include "snoop.h" + +static const char *mtype_to_str(uint8_t); +static const char *option_to_str(uint8_t); +static const char *duidtype_to_str(uint16_t); +static const char *status_to_str(uint16_t); +static const char *entr_to_str(uint32_t); +static const char *reconf_to_str(uint8_t); +static const char *authproto_to_str(uint8_t); +static const char *authalg_to_str(uint8_t, uint8_t); +static const char *authrdm_to_str(uint8_t); +static const char *cwhat_to_str(uint8_t); +static const char *catype_to_str(uint8_t); +static void show_hex(const uint8_t *, int, const char *); +static void show_ascii(const uint8_t *, int, const char *); +static void show_address(const char *, const void *); +static void show_options(const uint8_t *, int); + +int +interpret_dhcpv6(int flags, const uint8_t *data, int len) +{ + int olen = len; + char *line, *lstart; + dhcpv6_relay_t d6r; + dhcpv6_message_t d6m; + uint_t optlen; + uint16_t statuscode; + + if (len <= 0) { + (void) strlcpy(get_sum_line(), "DHCPv6?", MAXLINE); + return (0); + } + if (flags & F_SUM) { + uint_t ias; + dhcpv6_option_t *d6o; + in6_addr_t link, peer; + char linkstr[INET6_ADDRSTRLEN]; + char peerstr[INET6_ADDRSTRLEN]; + + line = lstart = get_sum_line(); + line += snprintf(line, MAXLINE, "DHCPv6 %s", + mtype_to_str(data[0])); + if (data[0] == DHCPV6_MSG_RELAY_FORW || + data[0] == DHCPV6_MSG_RELAY_REPL) { + if (len < sizeof (d6r)) { + (void) strlcpy(line, "?", + MAXLINE - (line - lstart)); + return (olen); + } + /* Not much in DHCPv6 is aligned. */ + (void) memcpy(&d6r, data, sizeof (d6r)); + (void) memcpy(&link, d6r.d6r_linkaddr, sizeof (link)); + (void) memcpy(&peer, d6r.d6r_peeraddr, sizeof (peer)); + line += snprintf(line, MAXLINE - (line - lstart), + " HC=%d link=%s peer=%s", d6r.d6r_hop_count, + inet_ntop(AF_INET6, &link, linkstr, + sizeof (linkstr)), + inet_ntop(AF_INET6, &peer, peerstr, + sizeof (peerstr))); + data += sizeof (d6r); + len -= sizeof (d6r); + } else { + if (len < sizeof (d6m)) { + (void) strlcpy(line, "?", + MAXLINE - (line - lstart)); + return (olen); + } + (void) memcpy(&d6m, data, sizeof (d6m)); + line += snprintf(line, MAXLINE - (line - lstart), + " xid=%x", DHCPV6_GET_TRANSID(&d6m)); + data += sizeof (d6m); + len -= sizeof (d6m); + } + ias = 0; + d6o = NULL; + while ((d6o = dhcpv6_find_option(data, len, d6o, + DHCPV6_OPT_IA_NA, NULL)) != NULL) + ias++; + if (ias > 0) + line += snprintf(line, MAXLINE - (line - lstart), + " IAs=%u", ias); + d6o = dhcpv6_find_option(data, len, NULL, + DHCPV6_OPT_STATUS_CODE, &optlen); + optlen -= sizeof (*d6o); + if (d6o != NULL && optlen >= sizeof (statuscode)) { + (void) memcpy(&statuscode, d6o + 1, + sizeof (statuscode)); + line += snprintf(line, MAXLINE - (line - lstart), + " status=%u", ntohs(statuscode)); + optlen -= sizeof (statuscode); + if (optlen > 0) { + line += snprintf(line, + MAXLINE - (line - lstart), " \"%.*s\"", + optlen, (char *)(d6o + 1) + 2); + } + } + d6o = dhcpv6_find_option(data, len, NULL, + DHCPV6_OPT_RELAY_MSG, &optlen); + optlen -= sizeof (*d6o); + if (d6o != NULL && optlen >= 1) { + line += snprintf(line, MAXLINE - (line - lstart), + " relay=%s", mtype_to_str(*(uint8_t *)(d6o + 1))); + } + } else if (flags & F_DTAIL) { + show_header("DHCPv6: ", + "Dynamic Host Configuration Protocol Version 6", len); + show_space(); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Message type (msg-type) = %u (%s)", data[0], + mtype_to_str(data[0])); + if (data[0] == DHCPV6_MSG_RELAY_FORW || + data[0] == DHCPV6_MSG_RELAY_REPL) { + if (len < sizeof (d6r)) { + (void) strlcpy(get_line(0, 0), "Truncated", + get_line_remain()); + return (olen); + } + (void) memcpy(&d6r, data, sizeof (d6r)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Hop count = %u", d6r.d6r_hop_count); + show_address("Link address", d6r.d6r_linkaddr); + show_address("Peer address", d6r.d6r_peeraddr); + data += sizeof (d6r); + len -= sizeof (d6r); + } else { + if (len < sizeof (d6m)) { + (void) strlcpy(get_line(0, 0), "Truncated", + get_line_remain()); + return (olen); + } + (void) memcpy(&d6m, data, sizeof (d6m)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Transaction ID = %x", DHCPV6_GET_TRANSID(&d6m)); + data += sizeof (d6m); + len -= sizeof (d6m); + } + show_space(); + show_options(data, len); + show_space(); + } + return (olen); +} + +static const char * +mtype_to_str(uint8_t mtype) +{ + switch (mtype) { + case DHCPV6_MSG_SOLICIT: + return ("Solicit"); + case DHCPV6_MSG_ADVERTISE: + return ("Advertise"); + case DHCPV6_MSG_REQUEST: + return ("Request"); + case DHCPV6_MSG_CONFIRM: + return ("Confirm"); + case DHCPV6_MSG_RENEW: + return ("Renew"); + case DHCPV6_MSG_REBIND: + return ("Rebind"); + case DHCPV6_MSG_REPLY: + return ("Reply"); + case DHCPV6_MSG_RELEASE: + return ("Release"); + case DHCPV6_MSG_DECLINE: + return ("Decline"); + case DHCPV6_MSG_RECONFIGURE: + return ("Reconfigure"); + case DHCPV6_MSG_INFO_REQ: + return ("Information-Request"); + case DHCPV6_MSG_RELAY_FORW: + return ("Relay-Forward"); + case DHCPV6_MSG_RELAY_REPL: + return ("Relay-Reply"); + default: + return ("Unknown"); + } +} + +static const char * +option_to_str(uint8_t mtype) +{ + switch (mtype) { + case DHCPV6_OPT_CLIENTID: + return ("Client Identifier"); + case DHCPV6_OPT_SERVERID: + return ("Server Identifier"); + case DHCPV6_OPT_IA_NA: + return ("Identity Association for Non-temporary Addresses"); + case DHCPV6_OPT_IA_TA: + return ("Identity Association for Temporary Addresses"); + case DHCPV6_OPT_IAADDR: + return ("IA Address"); + case DHCPV6_OPT_ORO: + return ("Option Request"); + case DHCPV6_OPT_PREFERENCE: + return ("Preference"); + case DHCPV6_OPT_ELAPSED_TIME: + return ("Elapsed Time"); + case DHCPV6_OPT_RELAY_MSG: + return ("Relay Message"); + case DHCPV6_OPT_AUTH: + return ("Authentication"); + case DHCPV6_OPT_UNICAST: + return ("Server Unicast"); + case DHCPV6_OPT_STATUS_CODE: + return ("Status Code"); + case DHCPV6_OPT_RAPID_COMMIT: + return ("Rapid Commit"); + case DHCPV6_OPT_USER_CLASS: + return ("User Class"); + case DHCPV6_OPT_VENDOR_CLASS: + return ("Vendor Class"); + case DHCPV6_OPT_VENDOR_OPT: + return ("Vendor-specific Information"); + case DHCPV6_OPT_INTERFACE_ID: + return ("Interface-Id"); + case DHCPV6_OPT_RECONF_MSG: + return ("Reconfigure Message"); + case DHCPV6_OPT_RECONF_ACC: + return ("Reconfigure Accept"); + case DHCPV6_OPT_SIP_NAMES: + return ("SIP Servers Domain Name List"); + case DHCPV6_OPT_SIP_ADDR: + return ("SIP Servers IPv6 Address List"); + case DHCPV6_OPT_DNS_ADDR: + return ("DNS Recursive Name Server"); + case DHCPV6_OPT_DNS_SEARCH: + return ("Domain Search List"); + case DHCPV6_OPT_IA_PD: + return ("Identity Association for Prefix Delegation"); + case DHCPV6_OPT_IAPREFIX: + return ("IA_PD Prefix"); + case DHCPV6_OPT_NIS_SERVERS: + return ("Network Information Service Servers"); + case DHCPV6_OPT_NIS_DOMAIN: + return ("Network Information Service Domain Name"); + case DHCPV6_OPT_SNTP_SERVERS: + return ("Simple Network Time Protocol Servers"); + case DHCPV6_OPT_INFO_REFTIME: + return ("Information Refresh Time"); + case DHCPV6_OPT_BCMCS_SRV_D: + return ("BCMCS Controller Domain Name List"); + case DHCPV6_OPT_BCMCS_SRV_A: + return ("BCMCS Controller IPv6 Address"); + case DHCPV6_OPT_GEOCONF_CVC: + return ("Civic Location"); + case DHCPV6_OPT_REMOTE_ID: + return ("Relay Agent Remote-ID"); + case DHCPV6_OPT_SUBSCRIBER: + return ("Relay Agent Subscriber-ID"); + case DHCPV6_OPT_CLIENT_FQDN: + return ("Client FQDN"); + default: + return ("Unknown"); + } +} + +static const char * +duidtype_to_str(uint16_t dtype) +{ + switch (dtype) { + case DHCPV6_DUID_LLT: + return ("Link-layer Address Plus Time"); + case DHCPV6_DUID_EN: + return ("Enterprise Number"); + case DHCPV6_DUID_LL: + return ("Link-layer Address"); + default: + return ("Unknown"); + } +} + +static const char * +status_to_str(uint16_t status) +{ + switch (status) { + case DHCPV6_STAT_SUCCESS: + return ("Success"); + case DHCPV6_STAT_UNSPECFAIL: + return ("Failure, reason unspecified"); + case DHCPV6_STAT_NOADDRS: + return ("No addresses for IAs"); + case DHCPV6_STAT_NOBINDING: + return ("Client binding unavailable"); + case DHCPV6_STAT_NOTONLINK: + return ("Prefix not on link"); + case DHCPV6_STAT_USEMCAST: + return ("Use multicast"); + case DHCPV6_STAT_NOPREFIX: + return ("No prefix available"); + default: + return ("Unknown"); + } +} + +static const char * +entr_to_str(uint32_t entr) +{ + switch (entr) { + case DHCPV6_SUN_ENT: + return ("Sun Microsystems"); + default: + return ("Unknown"); + } +} + +static const char * +reconf_to_str(uint8_t msgtype) +{ + switch (msgtype) { + case DHCPV6_RECONF_RENEW: + return ("Renew"); + case DHCPV6_RECONF_INFO: + return ("Information-request"); + default: + return ("Unknown"); + } +} + +static const char * +authproto_to_str(uint8_t aproto) +{ + switch (aproto) { + case DHCPV6_PROTO_DELAYED: + return ("Delayed"); + case DHCPV6_PROTO_RECONFIG: + return ("Reconfigure Key"); + default: + return ("Unknown"); + } +} + +static const char * +authalg_to_str(uint8_t aproto, uint8_t aalg) +{ + switch (aproto) { + case DHCPV6_PROTO_DELAYED: + case DHCPV6_PROTO_RECONFIG: + switch (aalg) { + case DHCPV6_ALG_HMAC_MD5: + return ("HMAC-MD5 Signature"); + default: + return ("Unknown"); + } + break; + default: + return ("Unknown"); + } +} + +static const char * +authrdm_to_str(uint8_t ardm) +{ + switch (ardm) { + case DHCPV6_RDM_MONOCNT: + return ("Monotonic Counter"); + default: + return ("Unknown"); + } +} + +static const char * +cwhat_to_str(uint8_t what) +{ + switch (what) { + case DHCPV6_CWHAT_SERVER: + return ("Server"); + case DHCPV6_CWHAT_NETWORK: + return ("Network"); + case DHCPV6_CWHAT_CLIENT: + return ("Client"); + default: + return ("Unknown"); + } +} + +static const char * +catype_to_str(uint8_t catype) +{ + switch (catype) { + case CIVICADDR_LANG: + return ("Language; RFC 2277"); + case CIVICADDR_A1: + return ("National division (state)"); + case CIVICADDR_A2: + return ("County"); + case CIVICADDR_A3: + return ("City"); + case CIVICADDR_A4: + return ("City division"); + case CIVICADDR_A5: + return ("Neighborhood"); + case CIVICADDR_A6: + return ("Street group"); + case CIVICADDR_PRD: + return ("Leading street direction"); + case CIVICADDR_POD: + return ("Trailing street suffix"); + case CIVICADDR_STS: + return ("Street suffix or type"); + case CIVICADDR_HNO: + return ("House number"); + case CIVICADDR_HNS: + return ("House number suffix"); + case CIVICADDR_LMK: + return ("Landmark"); + case CIVICADDR_LOC: + return ("Additional location information"); + case CIVICADDR_NAM: + return ("Name/occupant"); + case CIVICADDR_PC: + return ("Postal Code/ZIP"); + case CIVICADDR_BLD: + return ("Building"); + case CIVICADDR_UNIT: + return ("Unit/apt/suite"); + case CIVICADDR_FLR: + return ("Floor"); + case CIVICADDR_ROOM: + return ("Room number"); + case CIVICADDR_TYPE: + return ("Place type"); + case CIVICADDR_PCN: + return ("Postal community name"); + case CIVICADDR_POBOX: + return ("Post office box"); + case CIVICADDR_ADDL: + return ("Additional code"); + case CIVICADDR_SEAT: + return ("Seat/desk"); + case CIVICADDR_ROAD: + return ("Primary road or street"); + case CIVICADDR_RSEC: + return ("Road section"); + case CIVICADDR_RBRA: + return ("Road branch"); + case CIVICADDR_RSBR: + return ("Road sub-branch"); + case CIVICADDR_SPRE: + return ("Street name pre-modifier"); + case CIVICADDR_SPOST: + return ("Street name post-modifier"); + case CIVICADDR_SCRIPT: + return ("Script"); + default: + return ("Unknown"); + } +} + +static void +show_hex(const uint8_t *data, int len, const char *name) +{ + char buffer[16 * 3 + 1]; + int nlen; + int i; + char sep; + + nlen = strlen(name); + sep = '='; + while (len > 0) { + for (i = 0; i < 16 && i < len; i++) + (void) snprintf(buffer + 3 * i, 4, " %02x", *data++); + (void) snprintf(get_line(0, 0), get_line_remain(), "%*s %c%s", + nlen, name, sep, buffer); + name = ""; + sep = ' '; + len -= i; + } +} + +static void +show_ascii(const uint8_t *data, int len, const char *name) +{ + char buffer[64], *bp; + int nlen; + int i; + char sep; + + nlen = strlen(name); + sep = '='; + while (len > 0) { + bp = buffer; + for (i = 0; i < sizeof (buffer) - 4 && len > 0; len--) { + if (!isascii(*data) || !isprint(*data)) + bp += snprintf(bp, 5, "\\%03o", *data++); + else + *bp++; + } + *bp = '\0'; + (void) snprintf(get_line(0, 0), get_line_remain(), + "%*s %c \"%s\"", nlen, name, sep, buffer); + sep = ' '; + name = ""; + } +} + +static void +show_address(const char *addrname, const void *aptr) +{ + char *hname; + char addrstr[INET6_ADDRSTRLEN]; + in6_addr_t addr; + + (void) memcpy(&addr, aptr, sizeof (in6_addr_t)); + (void) inet_ntop(AF_INET6, &addr, addrstr, sizeof (addrstr)); + hname = addrtoname(AF_INET6, &addr); + if (strcmp(hname, addrstr) == 0) { + (void) snprintf(get_line(0, 0), get_line_remain(), "%s = %s", + addrname, addrstr); + } else { + (void) snprintf(get_line(0, 0), get_line_remain(), + "%s = %s (%s)", addrname, addrstr, hname); + } +} + +static void +nest_options(const uint8_t *data, uint_t olen, char *prefix, char *title) +{ + char *str, *oldnest, *oldprefix; + + if (olen <= 0) + return; + oldprefix = prot_prefix; + oldnest = prot_nest_prefix; + str = malloc(strlen(prot_nest_prefix) + strlen(prot_prefix) + 1); + if (str == NULL) { + prot_nest_prefix = prot_prefix; + } else { + (void) sprintf(str, "%s%s", prot_nest_prefix, prot_prefix); + prot_nest_prefix = str; + } + show_header(prefix, title, 0); + show_options(data, olen); + free(str); + prot_prefix = oldprefix; + prot_nest_prefix = oldnest; +} + +static void +show_options(const uint8_t *data, int len) +{ + dhcpv6_option_t d6o; + uint_t olen, retlen; + uint16_t val16; + uint16_t type; + uint32_t val32; + const uint8_t *ostart; + char *str, *sp; + char *oldnest; + + /* + * Be very careful with negative numbers; ANSI signed/unsigned + * comparison doesn't work as expected. + */ + while (len >= (signed)sizeof (d6o)) { + (void) memcpy(&d6o, data, sizeof (d6o)); + d6o.d6o_code = ntohs(d6o.d6o_code); + d6o.d6o_len = olen = ntohs(d6o.d6o_len); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Option Code = %u (%s)", d6o.d6o_code, + option_to_str(d6o.d6o_code)); + ostart = data += sizeof (d6o); + len -= sizeof (d6o); + if (olen > len) { + (void) strlcpy(get_line(0, 0), "Option truncated", + get_line_remain()); + olen = len; + } + switch (d6o.d6o_code) { + case DHCPV6_OPT_CLIENTID: + case DHCPV6_OPT_SERVERID: + if (olen < sizeof (val16)) + break; + (void) memcpy(&val16, data, sizeof (val16)); + data += sizeof (val16); + olen -= sizeof (val16); + type = ntohs(val16); + (void) snprintf(get_line(0, 0), get_line_remain(), + " DUID Type = %u (%s)", type, + duidtype_to_str(type)); + if (type == DHCPV6_DUID_LLT || type == DHCPV6_DUID_LL) { + if (olen < sizeof (val16)) + break; + (void) memcpy(&val16, data, sizeof (val16)); + data += sizeof (val16); + olen -= sizeof (val16); + val16 = ntohs(val16); + (void) snprintf(get_line(0, 0), + get_line_remain(), + " Hardware Type = %u (%s)", val16, + arp_htype(val16)); + } + if (type == DHCPV6_DUID_LLT) { + time_t timevalue; + + if (olen < sizeof (val32)) + break; + (void) memcpy(&val32, data, sizeof (val32)); + data += sizeof (val32); + olen -= sizeof (val32); + timevalue = ntohl(val32) + DUID_TIME_BASE; + (void) snprintf(get_line(0, 0), + get_line_remain(), + " Time = %lu (%.24s)", ntohl(val32), + ctime(&timevalue)); + } + if (type == DHCPV6_DUID_EN) { + if (olen < sizeof (val32)) + break; + (void) memcpy(&val32, data, sizeof (val32)); + data += sizeof (val32); + olen -= sizeof (val32); + val32 = ntohl(val32); + (void) snprintf(get_line(0, 0), + get_line_remain(), + " Enterprise Number = %lu (%s)", val32, + entr_to_str(val32)); + } + if (olen == 0) + break; + if ((str = malloc(olen * 3)) == NULL) + pr_err("interpret_dhcpv6: no mem"); + sp = str + snprintf(str, 3, "%02x", *data++); + while (--olen > 0) { + *sp++ = (type == DHCPV6_DUID_LLT || + type == DHCPV6_DUID_LL) ? ':' : ' '; + sp = sp + snprintf(sp, 3, "%02x", *data++); + } + (void) snprintf(get_line(0, 0), get_line_remain(), + (type == DHCPV6_DUID_LLT || + type == DHCPV6_DUID_LL) ? + " Link Layer Address = %s" : + " Identifier = %s", str); + free(str); + break; + case DHCPV6_OPT_IA_NA: + case DHCPV6_OPT_IA_PD: { + dhcpv6_ia_na_t d6in; + + if (olen < sizeof (d6in) - sizeof (d6o)) + break; + (void) memcpy(&d6in, data - sizeof (d6o), + sizeof (d6in)); + data += sizeof (d6in) - sizeof (d6o); + olen -= sizeof (d6in) - sizeof (d6o); + (void) snprintf(get_line(0, 0), get_line_remain(), + " IAID = %u", ntohl(d6in.d6in_iaid)); + (void) snprintf(get_line(0, 0), get_line_remain(), + " T1 (renew) = %u seconds", ntohl(d6in.d6in_t1)); + (void) snprintf(get_line(0, 0), get_line_remain(), + " T2 (rebind) = %u seconds", ntohl(d6in.d6in_t2)); + nest_options(data, olen, "IA: ", + "Identity Association"); + break; + } + case DHCPV6_OPT_IA_TA: { + dhcpv6_ia_ta_t d6it; + + if (olen < sizeof (d6it) - sizeof (d6o)) + break; + (void) memcpy(&d6it, data - sizeof (d6o), + sizeof (d6it)); + data += sizeof (d6it) - sizeof (d6o); + olen -= sizeof (d6it) - sizeof (d6o); + (void) snprintf(get_line(0, 0), get_line_remain(), + " IAID = %u", ntohl(d6it.d6it_iaid)); + nest_options(data, olen, "IA: ", + "Identity Association"); + break; + } + case DHCPV6_OPT_IAADDR: { + dhcpv6_iaaddr_t d6ia; + + if (olen < sizeof (d6ia) - sizeof (d6o)) + break; + (void) memcpy(&d6ia, data - sizeof (d6o), + sizeof (d6ia)); + data += sizeof (d6ia) - sizeof (d6o); + olen -= sizeof (d6ia) - sizeof (d6o); + show_address(" Address", &d6ia.d6ia_addr); + (void) snprintf(get_line(0, 0), get_line_remain(), + " Preferred lifetime = %u seconds", + ntohl(d6ia.d6ia_preflife)); + (void) snprintf(get_line(0, 0), get_line_remain(), + " Valid lifetime = %u seconds", + ntohl(d6ia.d6ia_vallife)); + nest_options(data, olen, "ADDR: ", "Address"); + break; + } + case DHCPV6_OPT_ORO: + while (olen >= sizeof (val16)) { + (void) memcpy(&val16, data, sizeof (val16)); + val16 = ntohs(val16); + (void) snprintf(get_line(0, 0), + get_line_remain(), + " Requested Option Code = %u (%s)", val16, + option_to_str(val16)); + data += sizeof (val16); + olen -= sizeof (val16); + } + break; + case DHCPV6_OPT_PREFERENCE: + if (olen > 0) { + (void) snprintf(get_line(0, 0), + get_line_remain(), + *data == 255 ? + " Preference = %u (immediate)" : + " Preference = %u", *data); + } + break; + case DHCPV6_OPT_ELAPSED_TIME: + if (olen == sizeof (val16)) { + (void) memcpy(&val16, data, sizeof (val16)); + val16 = ntohs(val16); + (void) snprintf(get_line(0, 0), + get_line_remain(), + " Elapsed Time = %u.%02u seconds", + val16 / 100, val16 % 100); + } + break; + case DHCPV6_OPT_RELAY_MSG: + if (olen > 0) { + oldnest = prot_nest_prefix; + prot_nest_prefix = prot_prefix; + retlen = interpret_dhcpv6(F_DTAIL, data, olen); + prot_prefix = prot_nest_prefix; + prot_nest_prefix = oldnest; + } + break; + case DHCPV6_OPT_AUTH: { + dhcpv6_auth_t d6a; + + if (olen < DHCPV6_AUTH_SIZE - sizeof (d6o)) + break; + (void) memcpy(&d6a, data - sizeof (d6o), + DHCPV6_AUTH_SIZE); + data += DHCPV6_AUTH_SIZE - sizeof (d6o); + olen += DHCPV6_AUTH_SIZE - sizeof (d6o); + (void) snprintf(get_line(0, 0), get_line_remain(), + " Protocol = %u (%s)", d6a.d6a_proto, + authproto_to_str(d6a.d6a_proto)); + (void) snprintf(get_line(0, 0), get_line_remain(), + " Algorithm = %u (%s)", d6a.d6a_alg, + authalg_to_str(d6a.d6a_proto, d6a.d6a_alg)); + (void) snprintf(get_line(0, 0), get_line_remain(), + " Replay Detection Method = %u (%s)", d6a.d6a_rdm, + authrdm_to_str(d6a.d6a_rdm)); + show_hex(d6a.d6a_replay, sizeof (d6a.d6a_replay), + " RDM Data"); + if (olen > 0) + show_hex(data, olen, " Auth Info"); + break; + } + case DHCPV6_OPT_UNICAST: + if (olen >= sizeof (in6_addr_t)) + show_address(" Server Address", data); + break; + case DHCPV6_OPT_STATUS_CODE: + if (olen < sizeof (val16)) + break; + (void) memcpy(&val16, data, sizeof (val16)); + val16 = ntohs(val16); + (void) snprintf(get_line(0, 0), get_line_remain(), + " Status Code = %u (%s)", val16, + status_to_str(val16)); + data += sizeof (val16); + olen -= sizeof (val16); + if (olen > 0) + (void) snprintf(get_line(0, 0), + get_line_remain(), " Text = \"%.*s\"", + olen, data); + break; + case DHCPV6_OPT_VENDOR_CLASS: + if (olen < sizeof (val32)) + break; + (void) memcpy(&val32, data, sizeof (val32)); + data += sizeof (val32); + olen -= sizeof (val32); + val32 = ntohl(val32); + (void) snprintf(get_line(0, 0), get_line_remain(), + " Enterprise Number = %lu (%s)", val32, + entr_to_str(val32)); + /* FALLTHROUGH */ + case DHCPV6_OPT_USER_CLASS: + while (olen >= sizeof (val16)) { + (void) memcpy(&val16, data, sizeof (val16)); + data += sizeof (val16); + olen -= sizeof (val16); + val16 = ntohs(val16); + if (val16 > olen) { + (void) strlcpy(get_line(0, 0), + " Truncated class", + get_line_remain()); + val16 = olen; + } + show_hex(data, olen, " Class"); + data += val16; + olen -= val16; + } + break; + case DHCPV6_OPT_VENDOR_OPT: { + dhcpv6_option_t sd6o; + + if (olen < sizeof (val32)) + break; + (void) memcpy(&val32, data, sizeof (val32)); + data += sizeof (val32); + olen -= sizeof (val32); + val32 = ntohl(val32); + (void) snprintf(get_line(0, 0), get_line_remain(), + " Enterprise Number = %lu (%s)", val32, + entr_to_str(val32)); + while (olen >= sizeof (sd6o)) { + (void) memcpy(&sd6o, data, sizeof (sd6o)); + sd6o.d6o_code = ntohs(sd6o.d6o_code); + sd6o.d6o_len = ntohs(sd6o.d6o_len); + (void) snprintf(get_line(0, 0), + get_line_remain(), + " Vendor Option Code = %u", d6o.d6o_code); + data += sizeof (d6o); + olen -= sizeof (d6o); + if (sd6o.d6o_len > olen) { + (void) strlcpy(get_line(0, 0), + " Vendor Option truncated", + get_line_remain()); + sd6o.d6o_len = olen; + } + if (sd6o.d6o_len > 0) { + show_hex(data, sd6o.d6o_len, + " Data"); + data += sd6o.d6o_len; + olen -= sd6o.d6o_len; + } + } + break; + } + case DHCPV6_OPT_REMOTE_ID: + if (olen < sizeof (val32)) + break; + (void) memcpy(&val32, data, sizeof (val32)); + data += sizeof (val32); + olen -= sizeof (val32); + val32 = ntohl(val32); + (void) snprintf(get_line(0, 0), get_line_remain(), + " Enterprise Number = %lu (%s)", val32, + entr_to_str(val32)); + /* FALLTHROUGH */ + case DHCPV6_OPT_INTERFACE_ID: + case DHCPV6_OPT_SUBSCRIBER: + if (olen > 0) + show_hex(data, olen, " ID"); + break; + case DHCPV6_OPT_RECONF_MSG: + if (olen > 0) { + (void) snprintf(get_line(0, 0), + get_line_remain(), + " Message Type = %u (%s)", *data, + reconf_to_str(*data)); + } + break; + case DHCPV6_OPT_SIP_NAMES: + case DHCPV6_OPT_DNS_SEARCH: + case DHCPV6_OPT_NIS_DOMAIN: + case DHCPV6_OPT_BCMCS_SRV_D: { + dhcp_symbol_t *symp; + char *sp2; + + symp = inittab_getbycode( + ITAB_CAT_STANDARD | ITAB_CAT_V6, ITAB_CONS_SNOOP, + d6o.d6o_code); + if (symp != NULL) { + str = inittab_decode(symp, data, olen, B_TRUE); + if (str != NULL) { + sp = str; + do { + sp2 = strchr(sp, ' '); + if (sp2 != NULL) + *sp2++ = '\0'; + (void) snprintf(get_line(0, 0), + get_line_remain(), + " Name = %s", sp); + } while ((sp = sp2) != NULL); + free(str); + } + free(symp); + } + break; + } + case DHCPV6_OPT_SIP_ADDR: + case DHCPV6_OPT_DNS_ADDR: + case DHCPV6_OPT_NIS_SERVERS: + case DHCPV6_OPT_SNTP_SERVERS: + case DHCPV6_OPT_BCMCS_SRV_A: + while (olen >= sizeof (in6_addr_t)) { + show_address(" Address", data); + data += sizeof (in6_addr_t); + olen -= sizeof (in6_addr_t); + } + break; + case DHCPV6_OPT_IAPREFIX: { + dhcpv6_iaprefix_t d6ip; + + if (olen < DHCPV6_IAPREFIX_SIZE - sizeof (d6o)) + break; + (void) memcpy(&d6ip, data - sizeof (d6o), + DHCPV6_IAPREFIX_SIZE); + data += DHCPV6_IAPREFIX_SIZE - sizeof (d6o); + olen -= DHCPV6_IAPREFIX_SIZE - sizeof (d6o); + show_address(" Prefix", d6ip.d6ip_addr); + (void) snprintf(get_line(0, 0), get_line_remain(), + " Preferred lifetime = %u seconds", + ntohl(d6ip.d6ip_preflife)); + (void) snprintf(get_line(0, 0), get_line_remain(), + " Valid lifetime = %u seconds", + ntohl(d6ip.d6ip_vallife)); + (void) snprintf(get_line(0, 0), get_line_remain(), + " Prefix length = %u", d6ip.d6ip_preflen); + nest_options(data, olen, "ADDR: ", "Address"); + break; + } + case DHCPV6_OPT_INFO_REFTIME: + if (olen < sizeof (val32)) + break; + (void) memcpy(&val32, data, sizeof (val32)); + (void) snprintf(get_line(0, 0), get_line_remain(), + " Refresh Time = %lu seconds", ntohl(val32)); + break; + case DHCPV6_OPT_GEOCONF_CVC: { + dhcpv6_civic_t d6c; + int solen; + + if (olen < DHCPV6_CIVIC_SIZE - sizeof (d6o)) + break; + (void) memcpy(&d6c, data - sizeof (d6o), + DHCPV6_CIVIC_SIZE); + data += DHCPV6_CIVIC_SIZE - sizeof (d6o); + olen -= DHCPV6_CIVIC_SIZE - sizeof (d6o); + (void) snprintf(get_line(0, 0), get_line_remain(), + " What Location = %u (%s)", d6c.d6c_what, + cwhat_to_str(d6c.d6c_what)); + (void) snprintf(get_line(0, 0), get_line_remain(), + " Country Code = %.*s", sizeof (d6c.d6c_cc), + d6c.d6c_cc); + while (olen >= 2) { + (void) snprintf(get_line(0, 0), + get_line_remain(), + " CA Element = %u (%s)", *data, + catype_to_str(*data)); + solen = data[1]; + data += 2; + olen -= 2; + if (solen > olen) { + (void) strlcpy(get_line(0, 0), + " CA Element truncated", + get_line_remain()); + solen = olen; + } + if (solen > 0) { + show_ascii(data, solen, " CA Data"); + data += solen; + olen -= solen; + } + } + break; + } + case DHCPV6_OPT_CLIENT_FQDN: { + dhcp_symbol_t *symp; + + if (olen == 0) + break; + (void) snprintf(get_line(0, 0), get_line_remain(), + " Flags = %02x", *data); + (void) snprintf(get_line(0, 0), get_line_remain(), + " %s", getflag(*data, DHCPV6_FQDNF_S, + "Perform AAAA RR updates", "No AAAA RR updates")); + (void) snprintf(get_line(0, 0), get_line_remain(), + " %s", getflag(*data, DHCPV6_FQDNF_O, + "Server override updates", + "No server override updates")); + (void) snprintf(get_line(0, 0), get_line_remain(), + " %s", getflag(*data, DHCPV6_FQDNF_N, + "Server performs no updates", + "Server performs updates")); + symp = inittab_getbycode( + ITAB_CAT_STANDARD | ITAB_CAT_V6, ITAB_CONS_SNOOP, + d6o.d6o_code); + if (symp != NULL) { + str = inittab_decode(symp, data, olen, B_TRUE); + if (str != NULL) { + (void) snprintf(get_line(0, 0), + get_line_remain(), + " FQDN = %s", str); + free(str); + } + free(symp); + } + break; + } + } + data = ostart + d6o.d6o_len; + len -= d6o.d6o_len; + } + if (len != 0) { + (void) strlcpy(get_line(0, 0), "Option entry truncated", + get_line_remain()); + } +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_display.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_display.c new file mode 100644 index 0000000..9bc4eb5 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_display.c @@ -0,0 +1,836 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <fcntl.h> +#include <string.h> +#include <sys/types.h> +#include <time.h> +#include <sys/time.h> +#include <sys/bufmod.h> +#include <setjmp.h> +#include <stdarg.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#include <rpc/types.h> +#include <rpc/xdr.h> +#include <inttypes.h> + +#include "snoop.h" + +char *dlc_header; +char *src_name, *dst_name; +int pi_frame; +int pi_time_hour; +int pi_time_min; +int pi_time_sec; +int pi_time_usec; + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +static void hexdump(char *, int); + +/* + * This routine invokes the packet interpreters + * on a packet. There's some messing around + * setting up a few packet-externals before + * starting with the ethernet interpreter. + * Yes, we assume here that all packets will + * be ethernet packets. + */ +void +process_pkt(struct sb_hdr *hdrp, char *pktp, int num, int flags) +{ + int drops, pktlen; + struct timeval *tvp; + struct tm *tm; + extern int x_offset; + extern int x_length; + int offset, length; + static struct timeval ptv; + + if (hdrp == NULL) + return; + + tvp = &hdrp->sbh_timestamp; + if (ptv.tv_sec == 0) + ptv = *tvp; + drops = hdrp->sbh_drops; + pktlen = hdrp->sbh_msglen; + if (pktlen <= 0) + return; + + /* set up externals */ + dlc_header = pktp; + pi_frame = num; + tm = localtime(&tvp->tv_sec); + pi_time_hour = tm->tm_hour; + pi_time_min = tm->tm_min; + pi_time_sec = tm->tm_sec; + pi_time_usec = tvp->tv_usec; + + src_name = "?"; + dst_name = "*"; + + click(hdrp->sbh_origlen); + + (*interface->interpreter)(flags, dlc_header, hdrp->sbh_msglen, + hdrp->sbh_origlen); + + show_pktinfo(flags, num, src_name, dst_name, &ptv, tvp, drops, + hdrp->sbh_origlen); + + if (x_offset >= 0) { + offset = MIN(x_offset, hdrp->sbh_msglen); + offset -= (offset % 2); /* round down */ + length = MIN(hdrp->sbh_msglen - offset, x_length); + + hexdump(dlc_header + offset, length); + } + + ptv = *tvp; +} + + +/* + * ************************************************************* + * The following routines constitute a library + * used by the packet interpreters to facilitate + * the display of packet data. This library + * of routines helps provide a consistent + * "look and feel". + */ + + +/* + * Display the value of a flag bit in + * a byte together with some text that + * corresponds to its value - whether + * true or false. + */ +char * +getflag(int val, int mask, char *s_true, char *s_false) +{ + static char buff[80]; + char *p; + int set; + + (void) strcpy(buff, ".... .... = "); + if (s_false == NULL) + s_false = s_true; + + for (p = &buff[8]; p >= buff; p--) { + if (*p == ' ') + p--; + if (mask & 0x1) { + set = val & mask & 0x1; + *p = set ? '1':'0'; + (void) strcat(buff, set ? s_true: s_false); + break; + } + mask >>= 1; + val >>= 1; + } + return (buff); +} + +XDR xdrm; +jmp_buf xdr_err; +int xdr_totlen; +char *prot_prefix; +char *prot_nest_prefix = ""; +char *prot_title; + +void +show_header(char *pref, char *str, int len) +{ + prot_prefix = pref; + prot_title = str; + (void) sprintf(get_detail_line(0, len), "%s%s----- %s -----", + prot_nest_prefix, pref, str); +} + +void +xdr_init(char *addr, int len) +{ + xdr_totlen = len; + xdrmem_create(&xdrm, addr, len, XDR_DECODE); +} + +/* Note: begin+end are ignored in get_detail_line */ +char * +get_line(int begin, int end) +{ + char *line; + + line = get_detail_line(begin, end); + (void) strcpy(line, prot_nest_prefix); + (void) strcat(line, prot_prefix); + return (line + strlen(line)); +} + +int +get_line_remain(void) +{ + return (MAXLINE - strlen(prot_nest_prefix) - strlen(prot_prefix)); +} + +void +show_line(char *str) +{ + (void) strlcpy(get_line(0, 0), str, get_line_remain()); +} + +void +show_printf(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + (void) vsnprintf(get_line(0, 0), get_line_remain(), fmt, ap); + va_end(ap); +} + +char +getxdr_char() +{ + char s; + + if (xdr_char(&xdrm, &s)) + return (s); + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +char +showxdr_char(char *fmt) +{ + int pos; char val; + + pos = getxdr_pos(); + val = getxdr_char(); + (void) sprintf(get_line(pos, getxdr_pos()), fmt, val); + return (val); +} + +uchar_t +getxdr_u_char() +{ + uchar_t s; + + if (xdr_u_char(&xdrm, &s)) + return (s); + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +uchar_t +showxdr_u_char(char *fmt) +{ + int pos; + uchar_t val; + + pos = getxdr_pos(); + val = getxdr_u_char(); + (void) sprintf(get_line(pos, getxdr_pos()), fmt, val); + return (val); +} + +short +getxdr_short() +{ + short s; + + if (xdr_short(&xdrm, &s)) + return (s); + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +short +showxdr_short(char *fmt) +{ + int pos; short val; + + pos = getxdr_pos(); + val = getxdr_short(); + (void) sprintf(get_line(pos, getxdr_pos()), fmt, val); + return (val); +} + +ushort_t +getxdr_u_short() +{ + ushort_t s; + + if (xdr_u_short(&xdrm, &s)) + return (s); + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +ushort_t +showxdr_u_short(char *fmt) +{ + int pos; + ushort_t val; + + pos = getxdr_pos(); + val = getxdr_u_short(); + (void) sprintf(get_line(pos, getxdr_pos()), fmt, val); + return (val); +} + +long +getxdr_long() +{ + long l; + + if (xdr_long(&xdrm, &l)) + return (l); + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +long +showxdr_long(char *fmt) +{ + int pos; long val; + + pos = getxdr_pos(); + val = getxdr_long(); + (void) sprintf(get_line(pos, getxdr_pos()), fmt, val); + return (val); +} + +ulong_t +getxdr_u_long() +{ + ulong_t l; + + if (xdr_u_long(&xdrm, &l)) + return (l); + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +ulong_t +showxdr_u_long(char *fmt) +{ + int pos; + ulong_t val; + + pos = getxdr_pos(); + val = getxdr_u_long(); + (void) sprintf(get_line(pos, getxdr_pos()), fmt, val); + return (val); +} + +longlong_t +getxdr_longlong() +{ + longlong_t l; + + if (xdr_longlong_t(&xdrm, &l)) + return (l); + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +longlong_t +showxdr_longlong(char *fmt) +{ + int pos; longlong_t val; + + pos = getxdr_pos(); + val = getxdr_longlong(); + (void) sprintf(get_line(pos, getxdr_pos()), fmt, val); + return (val); +} + +u_longlong_t +getxdr_u_longlong() +{ + u_longlong_t l; + + if (xdr_u_longlong_t(&xdrm, &l)) + return (l); + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +u_longlong_t +showxdr_u_longlong(char *fmt) +{ + int pos; u_longlong_t val; + + pos = getxdr_pos(); + val = getxdr_u_longlong(); + (void) sprintf(get_line(pos, getxdr_pos()), fmt, val); + return (val); +} + +bool_t +getxdr_bool() +{ + bool_t b; + + if (xdr_bool(&xdrm, &b)) + return (b); + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +bool_t +showxdr_bool(char *fmt) +{ + int pos; bool_t val; + + pos = getxdr_pos(); + val = getxdr_bool(); + (void) sprintf(get_line(pos, getxdr_pos()), fmt, + val ? "True" : "False"); + return (val); +} + +char * +getxdr_opaque(char *p, int len) +{ + if (xdr_opaque(&xdrm, p, len)) + return (p); + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +char * +getxdr_string(char *p, /* len+1 bytes or longer */ + int len) +{ + if (xdr_string(&xdrm, &p, len)) + return (p); + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +char * +showxdr_string(int len, /* XDR length */ + char *fmt) +{ + static int buff_len = 0; + static char *buff = NULL; + int pos; + + /* + * XDR strings don't necessarily have a trailing null over the + * wire. However, the XDR code will put one in for us. Make sure + * we have allocated room for it. + */ + len++; + + if ((len > buff_len) || (buff_len == 0)) { + if (buff) + free(buff); + if ((buff = (char *)malloc(len)) == NULL) + pr_err("showxdr_string: no mem"); + buff_len = len; + } + pos = getxdr_pos(); + getxdr_string(buff, len); + (void) strcpy(buff+60, "..."); + (void) sprintf(get_line(pos, getxdr_pos()), fmt, buff); + return (buff); +} + +char * +getxdr_bytes(uint_t *lenp) +{ + static char buff[1024]; + char *p = buff; + + if (xdr_bytes(&xdrm, &p, lenp, 1024)) + return (buff); + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +char * +getxdr_context(char *p, int len) +{ + ushort_t size; + + size = getxdr_u_short(); + if (((int)size > 0) && ((int)size < len) && getxdr_opaque(p, size)) + return (p); + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +char * +showxdr_context(char *fmt) +{ + ushort_t size; + static char buff[1024]; + int pos; + + pos = getxdr_pos(); + size = getxdr_u_short(); + if (((int)size > 0) && ((int)size < 1024) && + getxdr_opaque(buff, size)) { + (void) sprintf(get_line(pos, getxdr_pos()), fmt, buff); + return (buff); + } + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +enum_t +getxdr_enum() +{ + enum_t e; + + if (xdr_enum(&xdrm, &e)) + return (e); + longjmp(xdr_err, 1); + /* NOTREACHED */ +} + +void +xdr_skip(int delta) +{ + uint_t pos; + if (delta % 4 != 0 || delta < 0) + longjmp(xdr_err, 1); + /* Check for overflow */ + pos = xdr_getpos(&xdrm); + if ((pos + delta) < pos) + longjmp(xdr_err, 1); + /* xdr_setpos() checks for buffer overrun */ + if (xdr_setpos(&xdrm, pos + delta) == FALSE) + longjmp(xdr_err, 1); +} + +int +getxdr_pos() +{ + return (xdr_getpos(&xdrm)); +} + +void +setxdr_pos(int pos) +{ + xdr_setpos(&xdrm, pos); +} + +void +show_space() +{ + (void) get_line(0, 0); +} + +void +show_trailer() +{ + show_space(); +} + +char * +getxdr_date() +{ + time_t sec; + int usec; + static char buff[64]; + char *p; + struct tm my_time; /* private buffer to avoid collision */ + /* between gmtime and strftime */ + struct tm *tmp; + + sec = getxdr_long(); + usec = getxdr_long(); + if (sec == -1) + return ("-1 "); + + if (sec < 3600 * 24 * 365) { /* assume not a date */ + (void) sprintf(buff, "%d.%06d", sec, usec); + } else { + tmp = gmtime(&sec); + (void) memcpy(&my_time, tmp, sizeof (struct tm)); + strftime(buff, sizeof (buff), "%d-%h-%y %T.", &my_time); + p = buff + strlen(buff); + (void) sprintf(p, "%06d GMT", usec); + } + return (buff); +} + +char * +showxdr_date(char *fmt) +{ + int pos; + char *p; + + pos = getxdr_pos(); + p = getxdr_date(); + (void) sprintf(get_line(pos, getxdr_pos()), fmt, p); + return (p); +} + +char * +getxdr_date_ns(void) +{ + time_t sec, nsec; + + sec = getxdr_long(); + nsec = getxdr_long(); + if (sec == -1) + return ("-1 "); + else + return (format_time(sec, nsec)); +} + +/* + * Format the given time. + */ +char * +format_time(int64_t sec, uint32_t nsec) +{ + static char buff[64]; + char *p; + struct tm my_time; /* private buffer to avoid collision */ + /* between gmtime and strftime */ + struct tm *tmp; + + if (sec < 3600 * 24 * 365) { + /* assume not a date; includes negative times */ + (void) sprintf(buff, "%lld.%06d", sec, nsec); + } else if (sec > INT32_MAX) { + /* + * XXX No routines are available yet for formatting 64-bit + * times. + */ + (void) sprintf(buff, "%lld.%06d", sec, nsec); + } else { + time_t sec32 = (time_t)sec; + + tmp = gmtime(&sec32); + memcpy(&my_time, tmp, sizeof (struct tm)); + strftime(buff, sizeof (buff), "%d-%h-%y %T.", &my_time); + p = buff + strlen(buff); + (void) sprintf(p, "%09d GMT", nsec); + } + return (buff); +} + +char * +showxdr_date_ns(char *fmt) +{ + int pos; + char *p; + + pos = getxdr_pos(); + p = getxdr_date_ns(); + (void) sprintf(get_line(pos, getxdr_pos()), fmt, p); + return (p); +} + +char * +getxdr_time() +{ + time_t sec; + static char buff[64]; + struct tm my_time; /* private buffer to avoid collision */ + /* between gmtime and strftime */ + struct tm *tmp; + + sec = getxdr_long(); + if (sec == -1) + return ("-1 "); + + if (sec < 3600 * 24 * 365) { /* assume not a date */ + (void) sprintf(buff, "%d", sec); + } else { + tmp = gmtime(&sec); + memcpy(&my_time, tmp, sizeof (struct tm)); + strftime(buff, sizeof (buff), "%d-%h-%y %T", &my_time); + } + return (buff); +} + +char * +showxdr_time(char *fmt) +{ + int pos; + char *p; + + pos = getxdr_pos(); + p = getxdr_time(); + (void) sprintf(get_line(pos, getxdr_pos()), fmt, p); + return (p); +} + +char * +getxdr_hex(int len) +{ + int i, j; + static char hbuff[1024]; + char rbuff[1024]; + static char *hexstr = "0123456789ABCDEF"; + char toobig = 0; + + if (len == 0) { + hbuff[0] = '\0'; + return (hbuff); + } + if (len > 1024) + len = 1024; + if (len < 0 || xdr_opaque(&xdrm, rbuff, len) == FALSE) { + longjmp(xdr_err, 1); + } + + if (len * 2 > sizeof (hbuff)) { + toobig++; + len = sizeof (hbuff) / 2; + } + + j = 0; + for (i = 0; i < len; i++) { + hbuff[j++] = hexstr[rbuff[i] >> 4 & 0x0f]; + hbuff[j++] = hexstr[rbuff[i] & 0x0f]; + } + + if (toobig) { + hbuff[len * 2 - strlen("<Too Long>")] = '\0'; + strcat(hbuff, "<Too Long>"); + } else + hbuff[j] = '\0'; + + return (hbuff); +} + +char * +showxdr_hex(int len, char *fmt) +{ + int pos; + char *p; + + pos = getxdr_pos(); + p = getxdr_hex(len); + (void) sprintf(get_line(pos, getxdr_pos()), fmt, p); + return (p); +} + +static void +hexdump(char *data, int datalen) +{ + char *p; + ushort_t *p16 = (ushort_t *)data; + char *p8 = data; + int i, left, len; + int chunk = 16; /* 16 bytes per line */ + + printf("\n"); + + for (p = data; p < data + datalen; p += chunk) { + printf("\t%4d: ", p - data); + left = (data + datalen) - p; + len = MIN(chunk, left); + for (i = 0; i < (len / 2); i++) + printf("%04x ", ntohs(*p16++) & 0xffff); + if (len % 2) { + printf("%02x ", *((unsigned char *)p16)); + } + for (i = 0; i < (chunk - left) / 2; i++) + printf(" "); + + printf(" "); + for (i = 0; i < len; i++, p8++) + printf("%c", isprint(*p8) ? *p8 : '.'); + printf("\n"); + } + + printf("\n"); +} + +char * +show_string(const char *str, int dlen, int maxlen) +/* + * Prints len bytes from str enclosed in quotes. + * If len is negative, length is taken from strlen(str). + * No more than maxlen bytes will be printed. Longer + * strings are flagged with ".." after the closing quote. + * Non-printing characters are converted to C-style escape + * codes or octal digits. + */ +{ +#define TBSIZE 256 + static char tbuff[TBSIZE]; + const char *p; + char *pp; + int printable = 0; + int c, len; + + len = dlen > maxlen ? maxlen : dlen; + dlen = len; + + for (p = str, pp = tbuff; len; p++, len--) { + switch (c = *p & 0xFF) { + case '\n': (void) strcpy(pp, "\\n"); pp += 2; break; + case '\b': (void) strcpy(pp, "\\b"); pp += 2; break; + case '\t': (void) strcpy(pp, "\\t"); pp += 2; break; + case '\r': (void) strcpy(pp, "\\r"); pp += 2; break; + case '\f': (void) strcpy(pp, "\\f"); pp += 2; break; + default: + if (isascii(c) && isprint(c)) { + *pp++ = c; + printable++; + } else { + (void) snprintf(pp, TBSIZE - (pp - tbuff), + isdigit(*(p + 1)) ? + "\\%03o" : "\\%o", c); + pp += strlen(pp); + } + break; + } + *pp = '\0'; + /* + * Check for overflow of temporary buffer. Allow for + * the next character to be a \nnn followed by a trailing + * null. If not, then just bail with what we have. + */ + if (pp + 5 >= &tbuff[TBSIZE]) { + break; + } + } + return (printable > dlen / 2 ? tbuff : ""); +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c new file mode 100644 index 0000000..309d24a --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c @@ -0,0 +1,917 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/tiuser.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include "snoop.h" + +/* The string used to indent detail lines */ +#define DNS_INDENT " " +/* + * From RFC1035, the maximum size of a character-string is limited by the + * one octet length field. We add one character to that to make sure the + * result is terminated. + */ +#define MAX_CHAR_STRING_SIZE UCHAR_MAX + 1 + +/* private functions */ +static char *dns_opcode_string(uint_t opcode); +static char *dns_rcode_string(uint_t rcode); +static char *dns_type_string(uint_t type, int detail); +static char *dns_class_string(uint_t cls, int detail); +static size_t skip_question(const uchar_t *header, const uchar_t *data, + const uchar_t *data_end); +static size_t print_question(char *line, const uchar_t *header, + const uchar_t *data, const uchar_t *data_end, int detail); +static size_t print_answer(char *line, const uchar_t *header, + const uchar_t *data, const uchar_t *data_end, int detail); +static char *binary_string(char data); +static void print_ip(int af, char *line, const uchar_t *data, uint16_t len); +static const uchar_t *get_char_string(const uchar_t *data, char *charbuf, + uint16_t datalen); +static size_t print_char_string(char *line, const uchar_t *data, uint16_t len); +static const uchar_t *get_domain_name(const uchar_t *header, + const uchar_t *data, const uchar_t *data_end, char *namebuf, char *namend); +static size_t print_domain_name(char *line, const uchar_t *header, + const uchar_t *data, const uchar_t *data_end); + +void +interpret_dns(int flags, int proto, const uchar_t *data, int len, int port) +{ + typedef HEADER dns_header; + dns_header header; + char *line; + ushort_t id, qdcount, ancount, nscount, arcount; + ushort_t count; + const uchar_t *rrp; /* Resource Record Pointer. */ + const uchar_t *data_end; + const char *protostr; + char *protopfxstr; + char *protohdrstr; + + if (proto == IPPROTO_TCP) { + /* not supported now */ + return; + } + + if (port == IPPORT_DOMAIN) { + protostr = "DNS"; + protopfxstr = "DNS: "; + protohdrstr = "DNS Header"; + } else { + protostr = "MDNS"; + protopfxstr = "MDNS: "; + protohdrstr = "MDNS Header"; + } + + /* We need at least the header in order to parse a packet. */ + if (sizeof (dns_header) > len) { + return; + } + data_end = data + len; + /* + * Copy the header into a local structure for aligned access to + * each field. + */ + (void) memcpy(&header, data, sizeof (header)); + id = ntohs(header.id); + qdcount = ntohs(header.qdcount); + ancount = ntohs(header.ancount); + nscount = ntohs(header.nscount); + arcount = ntohs(header.arcount); + + if (flags & F_SUM) { + line = get_sum_line(); + line += sprintf(line, "%s %c ", + protostr, header.qr ? 'R' : 'C'); + + if (header.qr) { + /* answer */ + if (header.rcode == 0) { + /* reply is OK */ + rrp = data + sizeof (dns_header); + while (qdcount--) { + if (rrp >= data_end) { + return; + } + rrp += skip_question(data, + rrp, data_end); + } + /* the answers follow the questions */ + if (ancount > 0) { + (void) print_answer(line, + data, rrp, data_end, FALSE); + } + } else { + (void) sprintf(line, " Error: %d(%s)", + header.rcode, + dns_rcode_string(header.rcode)); + } + } else { + /* question */ + rrp = data + sizeof (dns_header); + if (rrp >= data_end) { + return; + } + (void) print_question(line, data, rrp, data_end, + FALSE); + } + } + if (flags & F_DTAIL) { + show_header(protopfxstr, protohdrstr, sizeof (dns_header)); + show_space(); + if (header.qr) { + /* answer */ + (void) snprintf(get_line(0, 0), get_line_remain(), + "Response ID = %d", id); + (void) snprintf(get_line(0, 0), get_line_remain(), + "%s%s%s", + header.aa ? "AA (Authoritative Answer) " : "", + header.tc ? "TC (TrunCation) " : "", + header.ra ? "RA (Recursion Available) ": ""); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Response Code: %d (%s)", + header.rcode, dns_rcode_string(header.rcode)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Reply to %d question(s)", qdcount); + } else { + /* question */ + (void) snprintf(get_line(0, 0), get_line_remain(), + "Query ID = %d", id); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Opcode: %s", dns_opcode_string(header.opcode)); + (void) snprintf(get_line(0, 0), get_line_remain(), + "%s%s", + header.tc ? "TC (TrunCation) " : "", + header.rd ? "RD (Recursion Desired) " : ""); + (void) snprintf(get_line(0, 0), get_line_remain(), + "%d question(s)", qdcount); + } + rrp = data + sizeof (dns_header); + count = 0; + while (qdcount--) { + if (rrp >= data_end) { + return; + } + count++; + rrp += print_question(get_line(0, 0), + data, rrp, data_end, TRUE); + show_space(); + } + /* Only answers should hold answers, but just in case */ + if (header.qr || ancount > 0) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "%d answer(s)", ancount); + count = 0; + while (ancount--) { + if (rrp >= data_end) { + return; + } + count++; + rrp += print_answer(get_line(0, 0), + data, rrp, data_end, TRUE); + show_space(); + } + } + /* Likewise only answers should hold NS records */ + if (header.qr || nscount > 0) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "%d name server resource(s)", nscount); + count = 0; + while (nscount--) { + if (rrp >= data_end) { + return; + } + count++; + rrp += print_answer(get_line(0, 0), data, + rrp, data_end, TRUE); + show_space(); + } + } + /* Additional section may hold an EDNS0 record. */ + if (header.qr || arcount > 0) { + (void) snprintf(get_line(0, 0), get_line_remain(), + "%d additional record(s)", arcount); + count = 0; + while (arcount-- && rrp < data_end) { + count++; + rrp += print_answer(get_line(0, 0), data, + rrp, data_end, TRUE); + show_space(); + } + } + } +} + + +static char * +dns_opcode_string(uint_t opcode) +{ + static char buffer[64]; + switch (opcode) { + case ns_o_query: return ("Query"); + case ns_o_iquery: return ("Inverse Query"); + case ns_o_status: return ("Status"); + default: + (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", + opcode); + return (buffer); + } +} + +static char * +dns_rcode_string(uint_t rcode) +{ + static char buffer[64]; + switch (rcode) { + case ns_r_noerror: return ("OK"); + case ns_r_formerr: return ("Format Error"); + case ns_r_servfail: return ("Server Fail"); + case ns_r_nxdomain: return ("Name Error"); + case ns_r_notimpl: return ("Unimplemented"); + case ns_r_refused: return ("Refused"); + case ns_r_badvers: return ("Bad Version"); /* EDNS rcode */ + default: + (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", rcode); + return (buffer); + } +} + +static char * +dns_type_string(uint_t type, int detail) +{ + static char buffer[64]; + switch (type) { + case ns_t_a: return (detail ? "Address" : "Addr"); + case ns_t_ns: return (detail ? "Authoritative Name Server" : "NS"); + case ns_t_cname: return (detail ? "Canonical Name" : "CNAME"); + case ns_t_soa: return (detail ? "Start Of a zone Authority" : "SOA"); + case ns_t_mb: return (detail ? "Mailbox domain name" : "MB"); + case ns_t_mg: return (detail ? "Mailbox Group member" : "MG"); + case ns_t_mr: return (detail ? "Mail Rename domain name" : "MR"); + case ns_t_null: return ("NULL"); + case ns_t_wks: return (detail ? "Well Known Service" : "WKS"); + case ns_t_ptr: return (detail ? "Domain Name Pointer" : "PTR"); + case ns_t_hinfo: return (detail ? "Host Information": "HINFO"); + case ns_t_minfo: + return (detail ? "Mailbox or maillist Info" : "MINFO"); + case ns_t_mx: return (detail ? "Mail Exchange" : "MX"); + case ns_t_txt: return (detail ? "Text strings" : "TXT"); + case ns_t_aaaa: return (detail ? "IPv6 Address" : "AAAA"); + case ns_t_opt: return (detail ? "EDNS0 option" : "OPT"); + case ns_t_axfr: return (detail ? "Transfer of entire zone" : "AXFR"); + case ns_t_mailb: + return (detail ? "Mailbox related records" : "MAILB"); + case ns_t_maila: return (detail ? "Mail agent RRs" : "MAILA"); + case ns_t_any: return (detail ? "All records" : "*"); + default: + (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", type); + return (buffer); + } +} + +static char * +dns_class_string(uint_t cls, int detail) +{ + static char buffer[64]; + switch (cls) { + case ns_c_in: return (detail ? "Internet" : "Internet"); + case ns_c_chaos: return (detail ? "CHAOS" : "CH"); + case ns_c_hs: return (detail ? "Hesiod" : "HS"); + case ns_c_any: return (detail ? "* (Any class)" : "*"); + default: + (void) snprintf(buffer, sizeof (buffer), "Unknown (%u)", cls); + return (buffer); + } +} + +static size_t +skip_question(const uchar_t *header, const uchar_t *data, + const uchar_t *data_end) +{ + const uchar_t *data_bak = data; + char dummy_buffer[NS_MAXDNAME]; + + data = get_domain_name(header, data, data_end, dummy_buffer, + dummy_buffer + sizeof (dummy_buffer)); + /* Skip the 32 bits of class and type that follow the domain name */ + data += sizeof (uint32_t); + return (data - data_bak); +} + +static size_t +print_question(char *line, const uchar_t *header, const uchar_t *data, + const uchar_t *data_end, int detail) +{ + const uchar_t *data_bak = data; + uint16_t type; + uint16_t cls; + + if (detail) { + line += snprintf(line, get_line_remain(), + DNS_INDENT "Domain Name: "); + } + data += print_domain_name(line, header, data, data_end); + + /* + * Make sure we don't run off the end of the packet by reading the + * type and class. + * + * The pointer subtraction on the left side of the following + * expression has a signed result of type ptrdiff_t, and the right + * side has an unsigned result of type size_t. We therefore need + * to cast the right side of the expression to be of the same + * signed type to keep the result of the pointer arithmetic to be + * automatically cast to an unsigned value. We do a similar cast + * in other similar expressions throughout this file. + */ + if ((data_end - data) < (ptrdiff_t)(2 * sizeof (uint16_t))) + return (data_end - data_bak); + + GETINT16(type, data); + GETINT16(cls, data); + + /* + * Multicast DNS re-uses the top bit of the class field + * in the question and answer sections. Unicast DNS only + * uses 1 (Internet), 3 and 4. Hence it is safe. The top + * order bit is always cleared here to display the rrclass in case + * of Multicast DNS packets. + */ + cls = cls & 0x7fff; + + if (detail) { + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "Class: %u (%s)", + cls, dns_class_string(cls, detail)); + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "Type: %u (%s)", type, + dns_type_string(type, detail)); + } else { + (void) sprintf(line + strlen(line), " %s %s \?", + dns_class_string(cls, detail), + dns_type_string(type, detail)); + } + return (data - data_bak); +} + +/* + * print_answer() is used to display the contents of a single resource + * record (RR) from either the answer, name server or additional + * section of the DNS packet. + * + * Input: + * *line: snoops output buffer. + * *header: start of the DNS packet, required for names and rcode. + * *data: location within header from where the RR starts. + * *data_end: where DNS data ends. + * detail: simple or verbose output. + * + * Returns: + * Pointer to next RR or data_end. + * + * Most RRs have the same top level format as defined in RFC 1035: + * + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | | + * / NAME / + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | TYPE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | CLASS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | TTL | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | RDLENGTH | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| + * / RDATA / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * However RFC 2671 introduced an exception to this rule + * with the "Extension Mechanisms for DNS" (EDNS0). + * When the type is 41 the remaining resource record format + * is: + * + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | TYPE = 41 | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | Sender's UDP payload size | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | Extended-rcode | Version | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | Zero | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | RDLENGTH | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| + * / RDATA / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + */ +static size_t +print_answer(char *line, const uchar_t *header, const uchar_t *data, + const uchar_t *data_end, int detail) +{ + const uchar_t *data_bak = data; + const uchar_t *data_next; + uint16_t type; + uint16_t cls; + int32_t ttl; + uint16_t rdlen; + uint32_t serial, refresh, retry, expire, minimum; + uint8_t protocol; + int linepos; + uint16_t preference; + /* declarations for EDNS follow */ + uint16_t size; /* Sender's UDP payload size */ + uint8_t xrcode; /* Extended-rcode */ + uint8_t ver; /* Version */ + uint16_t rcode; /* Extracted from the DNS header */ + union { /* DNS header overlay used for extraction */ + HEADER *head; + const uchar_t *raw; + } headptr; + + if (detail) { + line += snprintf(line, get_line_remain(), + DNS_INDENT "Domain Name: "); + } + data += print_domain_name(line, header, data, data_end); + + /* + * Next, get the record type, being careful to make sure we + * don't run off the end of the packet. + */ + if ((data_end - data) < (ptrdiff_t)(sizeof (type))) { + return (data_end - data_bak); + } + + GETINT16(type, data); + + if (type == ns_t_opt) { + /* + * Make sure we won't run off the end reading size, + * xrcode, version, zero and rdlen. + */ + if ((data_end - data) < + ((ptrdiff_t)(sizeof (size) + + sizeof (xrcode) + + sizeof (ver) + + sizeof (cls) /* zero */ + + sizeof (rdlen)))) { + return (data_end - data_bak); + } + + GETINT16(size, data); + GETINT8(xrcode, data); + /* + * The extended rcode represents the top half of the + * rcode which must be added to the rcode in the header. + */ + rcode = 0xff & (xrcode << 4); + headptr.raw = header; /* Overlay the header... */ + rcode += headptr.head->rcode; /* And pluck out the rcode. */ + + GETINT8(ver, data); + GETINT16(cls, data); /* zero */ + GETINT16(rdlen, data); + + if (detail) { + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "Type: %u (%s)", type, + dns_type_string(type, detail)); + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "UDP payload size: %u (0x%.4x)", + size, size); + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "Extended rcode: %u " + "(translates to %u (%s))", + xrcode, rcode, dns_rcode_string(rcode)); + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "EDNS0 Version: %u", ver); + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "zero: %u", cls); + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "Data length: %u", rdlen); + } else { + line += strlen(line); + line += sprintf(line, " %s UDP %u rc %d ver %u len %u", + dns_type_string(type, detail), size, rcode, ver, + rdlen); + } + + /* + * Make sure that rdlen is within data boundary. + */ + if (rdlen > data_end - data) + return (data_end - data_bak); + + /* Future OPT decode code goes here. */ + + data += rdlen; + return (data - data_bak); + } + + /* + * Make sure we don't run off the end of the packet by reading the + * class, ttl, and length. + */ + if ((data_end - data) < + ((ptrdiff_t)(sizeof (cls) + + sizeof (ttl) + + sizeof (rdlen)))) { + return (data_end - data_bak); + } + + GETINT16(cls, data); + + /* + * Multicast DNS re-uses the top bit of the class field + * in the question and answer sections. Unicast DNS only + * uses 1 (Internet), 3 and 4. Hence it is safe. The top + * order bit is always cleared here to display the rrclass in case + * of Multicast DNS packets. + */ + cls = cls & 0x7fff; + + if (detail) { + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "Class: %d (%s)", cls, + dns_class_string(cls, detail)); + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "Type: %d (%s)", type, + dns_type_string(type, detail)); + } else { + line += strlen(line); + line += sprintf(line, " %s %s ", + dns_class_string(cls, detail), + dns_type_string(type, detail)); + } + + GETINT32(ttl, data); + if (detail) { + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "TTL (Time To Live): %d", ttl); + } + + GETINT16(rdlen, data); + if (detail) { + line = get_line(0, 0); + line += snprintf(line, get_line_remain(), DNS_INDENT "%s: ", + dns_type_string(type, detail)); + } + + if (rdlen > data_end - data) + return (data_end - data_bak); + + switch (type) { + case ns_t_a: + print_ip(AF_INET, line, data, rdlen); + break; + case ns_t_aaaa: + print_ip(AF_INET6, line, data, rdlen); + break; + case ns_t_hinfo: + line += sprintf(line, "CPU: "); + data_next = data + print_char_string(line, data, rdlen); + if (data_next >= data_end) + break; + line += strlen(line); + line += sprintf(line, "OS: "); + (void) print_char_string(line, data_next, + rdlen - (data_next - data)); + break; + case ns_t_ns: + case ns_t_cname: + case ns_t_mb: + case ns_t_mg: + case ns_t_mr: + case ns_t_ptr: + (void) print_domain_name(line, header, data, data_end); + break; + case ns_t_mx: + data_next = data; + if (rdlen < sizeof (uint16_t)) + break; + GETINT16(preference, data_next); + if (detail) { + (void) print_domain_name(line, header, data_next, + data_end); + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "Preference: %u", preference); + } else { + (void) print_domain_name(line, header, data_next, + data_end); + } + break; + case ns_t_soa: + if (!detail) + break; + line = get_line(0, 0); + line += snprintf(line, get_line_remain(), + DNS_INDENT "MNAME (Server name): "); + data_next = data + print_domain_name(line, header, data, + data_end); + if (data_next >= data_end) + break; + line = get_line(0, 0); + line += snprintf(line, get_line_remain(), + DNS_INDENT "RNAME (Resposible mailbox): "); + data_next = data_next + + print_domain_name(line, header, data_next, data_end); + if ((data_end - data_next) < (ptrdiff_t)(5 * sizeof (uint32_t))) + break; + GETINT32(serial, data_next); + GETINT32(refresh, data_next); + GETINT32(retry, data_next); + GETINT32(expire, data_next); + GETINT32(minimum, data_next); + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "Serial: %u", serial); + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "Refresh: %u Retry: %u " + "Expire: %u Minimum: %u", + refresh, retry, expire, minimum); + break; + case ns_t_wks: + print_ip(AF_INET, line, data, rdlen); + if (!detail) + break; + data_next = data + sizeof (in_addr_t); + if (data_next >= data_end) + break; + GETINT8(protocol, data_next); + line = get_line(0, 0); + line += snprintf(line, get_line_remain(), + DNS_INDENT "Protocol: %u ", protocol); + switch (protocol) { + case IPPROTO_UDP: + (void) snprintf(line, get_line_remain(), "(UDP)"); + break; + case IPPROTO_TCP: + (void) snprintf(line, get_line_remain(), "(TCP)"); + break; + } + (void) snprintf(get_line(0, 0), get_line_remain(), + DNS_INDENT "Service bitmap:"); + (void) snprintf(line, get_line_remain(), + DNS_INDENT "0 8 16 24"); + linepos = 4; + while (data_next < data + rdlen) { + if (linepos == 4) { + line = get_line(0, 0); + line += snprintf(line, get_line_remain(), + DNS_INDENT); + linepos = 0; + } + line += snprintf(line, get_line_remain(), "%s", + binary_string(*data_next)); + linepos++; + data_next++; + } + break; + case ns_t_minfo: + if (!detail) + break; + line = get_line(0, 0); + line += snprintf(line, get_line_remain(), + DNS_INDENT "RMAILBX (Resposible mailbox): "); + data_next = data + print_domain_name(line, header, data, + data_end); + line = get_line(0, 0); + line += snprintf(line, get_line_remain(), + DNS_INDENT "EMAILBX (mailbox to receive err message): "); + data_next = data_next + print_domain_name(line, header, + data_next, data_end); + break; + } + data += rdlen; + return (data - data_bak); +} + +static char * +binary_string(char data) +{ + static char bstring[8 + 1]; + char *ptr; + int i; + ptr = bstring; + for (i = 0; i < 8; i++) { + *ptr++ = (data & 0x80) ? '1' : '0'; + data = data << 1; + } + *ptr = (char)0; + return (bstring); +} + +static void +print_ip(int af, char *line, const uchar_t *data, uint16_t len) +{ + in6_addr_t addr6; + in_addr_t addr4; + void *addr; + + switch (af) { + case AF_INET: + if (len != sizeof (in_addr_t)) + return; + addr = memcpy(&addr4, data, sizeof (addr4)); + break; + case AF_INET6: + if (len != sizeof (in6_addr_t)) + return; + addr = memcpy(&addr6, data, sizeof (addr6)); + break; + } + + (void) inet_ntop(af, addr, line, INET6_ADDRSTRLEN); +} + +/* + * charbuf is assumed to be of size MAX_CHAR_STRING_SIZE. + */ +static const uchar_t * +get_char_string(const uchar_t *data, char *charbuf, uint16_t datalen) +{ + int len; + char *name = charbuf; + int i = 0; + + /* + * From RFC1035, a character-string is a single length octet followed + * by that number of characters. + */ + if (datalen > 1) { + len = *data; + data++; + if (len > 0 && len < MAX_CHAR_STRING_SIZE) { + for (i = 0; i < len; i++, data++) + name[i] = *data; + } + } + name[i] = '\0'; + return (data); +} + +static size_t +print_char_string(char *line, const uchar_t *data, uint16_t len) +{ + char charbuf[MAX_CHAR_STRING_SIZE]; + const uchar_t *data_bak = data; + + data = get_char_string(data, charbuf, len); + (void) sprintf(line, "%s", charbuf); + return (data - data_bak); +} + +/* + * header: the entire message header, this is where we start to + * count the offset of the compression scheme + * data: the start of the domain name + * namebuf: user supplied buffer + * return: the next byte after what we have parsed + */ +static const uchar_t * +get_domain_name(const uchar_t *header, const uchar_t *data, + const uchar_t *data_end, char *namebuf, char *namend) +{ + uint8_t len; + char *name = namebuf; + + /* + * From RFC1035, a domain name is a sequence of labels, where each + * label consists of a length octet followed by that number of + * octets. The domain name terminates with the zero length octet + * for the null label of the root. + */ + + while (name < (namend - 1)) { + if ((data_end - data) < (ptrdiff_t)(sizeof (uint8_t))) { + /* The length octet is off the end of the packet. */ + break; + } + GETINT8(len, data); + if (len == 0) { + /* + * Domain names end with a length byte of zero, + * which represents the null label of the root. + */ + break; + } + /* + * test if we are using the compression scheme + */ + if ((len & 0xc0) == 0xc0) { + uint16_t offset; + const uchar_t *label_ptr; + + /* + * From RFC1035, message compression allows a + * domain name or a list of labels at the end of a + * domain name to be replaced with a pointer to a + * prior occurance of the same name. In this + * scheme, the pointer is a two octet sequence + * where the most significant two bits are set, and + * the remaining 14 bits are the offset from the + * start of the message of the next label. + */ + data--; + if ((data_end - data) < + (ptrdiff_t)(sizeof (uint16_t))) { + /* + * The offset octets aren't entirely + * contained within this pakcet. + */ + data = data_end; + break; + } + GETINT16(offset, data); + label_ptr = header + (offset & 0x3fff); + /* + * We must verify that the offset is valid by + * checking that it is less than the current data + * pointer and that it isn't off the end of the + * packet. + */ + if (label_ptr > data || label_ptr >= data_end) + break; + (void) get_domain_name(header, label_ptr, data_end, + name, namend); + return (data); + } else { + if (len > (data_end - data)) { + /* + * The label isn't entirely contained + * within the packet. Don't read it. The + * caller checks that the data pointer is + * not beyond the end after we've + * incremented it. + */ + data = data_end; + break; + } + while (len > 0 && name < (namend - 2)) { + *name = *data; + name++; + data++; + len--; + } + *name = '.'; + name++; + } + } + *name = '\0'; + return (data); +} + +static size_t +print_domain_name(char *line, const uchar_t *header, const uchar_t *data, + const uchar_t *data_end) +{ + char name[NS_MAXDNAME]; + const uchar_t *new_data; + + new_data = get_domain_name(header, data, data_end, name, + name + sizeof (name)); + + (void) sprintf(line, "%s", name); + return (new_data - data); +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c new file mode 100644 index 0000000..5c6bde0 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c @@ -0,0 +1,1802 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stddef.h> +#include <fcntl.h> +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/sysmacros.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#include <sys/ib/clients/ibd/ibd.h> +#include <sys/ethernet.h> +#include <sys/vlan.h> +#include <sys/zone.h> +#include <inet/iptun.h> +#include <sys/byteorder.h> +#include <limits.h> +#include <inet/ip.h> +#include <inet/ip6.h> +#include <net/trill.h> + +#include "at.h" +#include "snoop.h" + +static headerlen_fn_t ether_header_len, fddi_header_len, tr_header_len, + ib_header_len, ipnet_header_len, ipv4_header_len, ipv6_header_len; +static interpreter_fn_t interpret_ether, interpret_fddi, interpret_tr, + interpret_ib, interpret_ipnet, interpret_iptun; +static void addr_copy_swap(struct ether_addr *, struct ether_addr *); +static int tr_machdr_len(char *, int *, int *); + +interface_t *interface; +interface_t INTERFACES[] = { + + /* IEEE 802.3 CSMA/CD network */ + { DL_CSMACD, 1550, 12, 2, ETHERTYPE_IP, ETHERTYPE_IPV6, + ether_header_len, interpret_ether, B_TRUE }, + + /* Ethernet Bus */ + { DL_ETHER, 1550, 12, 2, ETHERTYPE_IP, ETHERTYPE_IPV6, + ether_header_len, interpret_ether, B_TRUE }, + + /* Fiber Distributed data interface */ + { DL_FDDI, 4500, 19, 2, ETHERTYPE_IP, ETHERTYPE_IPV6, + fddi_header_len, interpret_fddi, B_FALSE }, + + /* Token Ring interface */ + { DL_TPR, 17800, 0, 2, ETHERTYPE_IP, ETHERTYPE_IPV6, + tr_header_len, interpret_tr, B_FALSE }, + + /* Infiniband */ + { DL_IB, 4096, 0, 2, ETHERTYPE_IP, ETHERTYPE_IPV6, + ib_header_len, interpret_ib, B_TRUE }, + + /* ipnet */ + { DL_IPNET, INT_MAX, 1, 1, IPV4_VERSION, IPV6_VERSION, + ipnet_header_len, interpret_ipnet, B_TRUE }, + + /* IPv4 tunnel */ + { DL_IPV4, 0, 9, 1, IPPROTO_ENCAP, IPPROTO_IPV6, + ipv4_header_len, interpret_iptun, B_FALSE }, + + /* IPv6 tunnel */ + { DL_IPV6, 0, 40, 1, IPPROTO_ENCAP, IPPROTO_IPV6, + ipv6_header_len, interpret_iptun, B_FALSE }, + + /* 6to4 tunnel */ + { DL_6TO4, 0, 9, 1, IPPROTO_ENCAP, IPPROTO_IPV6, + ipv4_header_len, interpret_iptun, B_FALSE }, + + { (uint_t)-1, 0, 0, 0, 0, NULL, NULL, B_FALSE } +}; + +/* externals */ +extern char *dlc_header; +extern int pi_frame; +extern int pi_time_hour; +extern int pi_time_min; +extern int pi_time_sec; +extern int pi_time_usec; + +char *printether(); +char *print_ethertype(); +static char *print_etherinfo(); + +char *print_fc(); +char *print_smttype(); +char *print_smtclass(); + +struct ether_addr ether_broadcast = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +static char *data; /* current data buffer */ +static int datalen; /* current data buffer length */ +static const struct ether_addr all_isis_rbridges = ALL_ISIS_RBRIDGES; + +uint_t +interpret_ether(int flags, char *header, int elen, int origlen) +{ + struct ether_header *e = (struct ether_header *)header; + uchar_t *off, *ieeestart; + int len; + int ieee8023 = 0; + extern char *dst_name; + int ethertype; + struct ether_vlan_extinfo *evx = NULL; + int blen = MAX(origlen, ETHERMTU); + boolean_t trillpkt = B_FALSE; + uint16_t tci = 0; + + if (data != NULL && datalen != 0 && datalen < blen) { + free(data); + data = NULL; + datalen = 0; + } + if (!data) { + data = (char *)malloc(blen); + if (!data) + pr_err("Warning: malloc failure"); + datalen = blen; + } +inner_pkt: + if (origlen < 14) { + if (flags & F_SUM) { + (void) sprintf(get_sum_line(), + "RUNT (short packet - %d bytes)", + origlen); + } + if (flags & F_DTAIL) + show_header("RUNT: ", "Short packet", origlen); + return (elen); + } + if (elen < 14) + return (elen); + + if (memcmp(&e->ether_dhost, ðer_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, ðer_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, ðer_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, ðer_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, ¬, 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 *)×tamp)); + 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; + } + } +} |