Home | History | Annotate | Download | only in snoop
      1     0  stevel /*
      2     0  stevel  * CDDL HEADER START
      3     0  stevel  *
      4     0  stevel  * The contents of this file are subject to the terms of the
      5  1766  kcpoon  * Common Development and Distribution License (the "License").
      6  1766  kcpoon  * You may not use this file except in compliance with the License.
      7     0  stevel  *
      8     0  stevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9     0  stevel  * or http://www.opensolaris.org/os/licensing.
     10     0  stevel  * See the License for the specific language governing permissions
     11     0  stevel  * and limitations under the License.
     12     0  stevel  *
     13     0  stevel  * When distributing Covered Code, include this CDDL HEADER in each
     14     0  stevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15     0  stevel  * If applicable, add the following below this CDDL HEADER, with the
     16     0  stevel  * fields enclosed by brackets "[]" replaced with your own identifying
     17     0  stevel  * information: Portions Copyright [yyyy] [name of copyright owner]
     18     0  stevel  *
     19     0  stevel  * CDDL HEADER END
     20     0  stevel  */
     21  1766  kcpoon 
     22     0  stevel /*
     23  1766  kcpoon  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
     24     0  stevel  * Use is subject to license terms.
     25     0  stevel  */
     26     0  stevel 
     27     0  stevel #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28     0  stevel 
     29     0  stevel #include <stdio.h>
     30     0  stevel #include <stdlib.h>
     31     0  stevel #include <sys/types.h>
     32     0  stevel #include <sys/socket.h>
     33     0  stevel #include <sys/sysmacros.h>
     34     0  stevel #include <inet/common.h>
     35     0  stevel #include <netinet/in.h>
     36     0  stevel #include <netinet/sctp.h>
     37     0  stevel #include <arpa/inet.h>
     38     0  stevel #include <string.h>
     39     0  stevel #include "snoop.h"
     40     0  stevel 
     41     0  stevel /*
     42     0  stevel  * Snoop interpreter for SCTP (rfc2960).
     43     0  stevel  *
     44     0  stevel  * To add support for an upper-layer protocol, modify either
     45     0  stevel  * the port-dispatcher in snoop_rport.c, or the protocol ID
     46     0  stevel  * dispatcher at the bottom of this file (or both).
     47     0  stevel  */
     48     0  stevel 
     49     0  stevel static void interpret_protoid(int, uint32_t, char *, int);
     50     0  stevel extern char *prot_prefix;
     51     0  stevel 
     52     0  stevel /*
     53     0  stevel  * This defines the length of internal, unbounded buffers. We set
     54     0  stevel  * this to be MAXLINE (the maximum verbose display line length) -
     55     0  stevel  * 64, which should be enough for all necessary descriptions. 64
     56     0  stevel  * bytes seems like a reasonably conservative estimate of the
     57     0  stevel  * maximum prefix length snoop may add to any text buffer it hands out.
     58     0  stevel  */
     59     0  stevel #define	BUFLEN	MAXLINE - 64
     60     0  stevel 
     61     0  stevel /*
     62     0  stevel  * Common structure to hold descriptions and parsers for all
     63     0  stevel  * chunks, parameters, and errors. Each parser should implement
     64     0  stevel  * this interface:
     65     0  stevel  *
     66     0  stevel  * void parse(int flags, uint8_t cflags, void *data, int datalen);
     67     0  stevel  *
     68     0  stevel  * Where flags is the snoop flags, cflags are the chunk flags, data
     69     0  stevel  * is the chunk or parameter data (not including the chunk or
     70     0  stevel  * parameter header), and datalen is the length of the chunk or
     71     0  stevel  * parameter data (again not including any headers).
     72     0  stevel  */
     73     0  stevel typedef void parse_func_t(int, uint8_t, const void *, int);
     74     0  stevel 
     75     0  stevel typedef struct {
     76     0  stevel 	uint16_t id;
     77     0  stevel 	const char *sdesc;	/* short description */
     78     0  stevel 	const char *vdesc;	/* verbose description */
     79     0  stevel 	parse_func_t *parse;	/* parser function */
     80     0  stevel } dispatch_t;
     81     0  stevel 
     82     0  stevel static void interpret_params(const void *, int, char *, const dispatch_t *,
     83     0  stevel     int, int);
     84     0  stevel 
     85     0  stevel /*
     86     0  stevel  * Chunk parsers
     87     0  stevel  */
     88     0  stevel static parse_func_t parse_abort_chunk, parse_data_chunk, parse_error_chunk,
     89     0  stevel     parse_init_chunk, parse_opaque_chunk, parse_sack_chunk,
     90     0  stevel     parse_shutdone_chunk, parse_shutdown_chunk, parse_asconf_chunk,
     91     0  stevel     parse_ftsn_chunk;
     92     0  stevel 
     93     0  stevel 
     94     0  stevel /*
     95     0  stevel  * Chunk parser dispatch table. There are few enough chunks defined
     96     0  stevel  * in the core protocol, and they are sequential, so the chunk code
     97     0  stevel  * can be used as the index into this array for the common case.
     98     0  stevel  * It is still necessary to check that the code and index match,
     99     0  stevel  * since optional extensions will not follow sequentially the
    100     0  stevel  * core chunks.
    101     0  stevel  */
    102     0  stevel static const dispatch_t chunk_dispatch_table[] = {
    103     0  stevel /*	code	F_SUM desc	F_DTAIL desc		parser function */
    104     0  stevel 	{ CHUNK_DATA,			"Data",		"Data Chunk",
    105     0  stevel 	    parse_data_chunk },
    106     0  stevel 	{ CHUNK_INIT,			"Init",		"Init Chunk",
    107     0  stevel 	    parse_init_chunk },
    108     0  stevel 	{ CHUNK_INIT_ACK,		"Init ACK",	"Init ACK Chunk",
    109     0  stevel 	    parse_init_chunk },
    110     0  stevel 	{ CHUNK_SACK,			"SACK",		"SACK Chunk",
    111     0  stevel 	    parse_sack_chunk },
    112     0  stevel 	{ CHUNK_HEARTBEAT,		"Heartbeat",	"Heartbeat Chunk",
    113     0  stevel 	    parse_opaque_chunk },
    114     0  stevel 	{ CHUNK_HEARTBEAT_ACK,		"Heartbeat ACK", "Heartbeat ACK Chunk",
    115     0  stevel 	    parse_opaque_chunk },
    116     0  stevel 	{ CHUNK_ABORT,			"Abort",	"Abort Chunk",
    117     0  stevel 	    parse_abort_chunk },
    118     0  stevel 	{ CHUNK_SHUTDOWN,		"Shutdown",	"Shutdown Chunk",
    119     0  stevel 	    parse_shutdown_chunk },
    120     0  stevel 	{ CHUNK_SHUTDOWN_ACK,		"Shutdown ACK",	"Shutdown ACK Chunk",
    121     0  stevel 	    NULL },
    122     0  stevel 	{ CHUNK_ERROR,			"Err",		"Error Chunk",
    123     0  stevel 	    parse_error_chunk },
    124     0  stevel 	{ CHUNK_COOKIE,			"Cookie",	"Cookie Chunk",
    125     0  stevel 	    parse_opaque_chunk },
    126     0  stevel 	{ CHUNK_COOKIE_ACK,		"Cookie ACK",	"Cookie ACK Chunk",
    127     0  stevel 	    parse_opaque_chunk },
    128     0  stevel 	{ CHUNK_ECNE,			"ECN Echo",	"ECN Echo Chunk",
    129     0  stevel 	    parse_opaque_chunk },
    130     0  stevel 	{ CHUNK_CWR,			"CWR",		"CWR Chunk",
    131     0  stevel 	    parse_opaque_chunk },
    132     0  stevel 	{ CHUNK_SHUTDOWN_COMPLETE,	"Shutdown Done", "Shutdown Done",
    133     0  stevel 	    parse_shutdone_chunk },
    134     0  stevel 	{ CHUNK_FORWARD_TSN,		"FORWARD TSN", 	"Forward TSN Chunk",
    135     0  stevel 	    parse_ftsn_chunk },
    136     0  stevel 	{ CHUNK_ASCONF_ACK,		"ASCONF ACK", 	"ASCONF ACK Chunk",
    137     0  stevel 	    parse_asconf_chunk },
    138     0  stevel 	{ CHUNK_ASCONF,			"ASCONF", 	"ASCONF Chunk",
    139     0  stevel 	    parse_asconf_chunk }
    140     0  stevel };
    141     0  stevel 
    142     0  stevel /*
    143     0  stevel  * Parameter Parsers
    144     0  stevel  */
    145     0  stevel static parse_func_t parse_encap_param, parse_int32_param, parse_ip4_param,
    146     0  stevel     parse_ip6_param, parse_opaque_param, parse_suppaddr_param,
    147     0  stevel     parse_unrec_chunk, parse_addip_param, parse_asconferr_param,
    148     0  stevel     parse_asconfok_param, parse_addiperr_param;
    149     0  stevel 
    150     0  stevel /*
    151     0  stevel  * Parameter parser dispatch table. The summary description is not
    152     0  stevel  * used here. Strictly speaking, parameter types are defined within
    153     0  stevel  * the context of a chunk type. However, thus far the IETF WG has
    154     0  stevel  * agreed to follow the convention that parameter types are globally
    155     0  stevel  * unique (and why not, with a 16-bit namespace). However, if this
    156     0  stevel  * ever changes, there will need to be different parameter dispatch
    157     0  stevel  * tables for each chunk type.
    158     0  stevel  */
    159     0  stevel static const dispatch_t parm_dispatch_table[] = {
    160     0  stevel /*	code	F_SUM desc	F_DTAIL desc		parser function */
    161     0  stevel 	{ PARM_UNKNOWN,	"",		"Unknown Parameter",
    162     0  stevel 	    parse_opaque_param },
    163     0  stevel 	{ PARM_HBINFO,	"",		"Heartbeat Info",
    164     0  stevel 	    parse_opaque_param },
    165     0  stevel 	{ PARM_ADDR4,	"",		"IPv4 Address",
    166     0  stevel 	    parse_ip4_param },
    167     0  stevel 	{ PARM_ADDR6,	"",		"IPv6 Address",
    168     0  stevel 	    parse_ip6_param },
    169     0  stevel 	{ PARM_COOKIE,	"",		"Cookie",
    170     0  stevel 	    parse_opaque_param },
    171     0  stevel 	{ PARM_UNRECOGNIZED,	"",	"Unrecognized Param",
    172     0  stevel 	    parse_encap_param },
    173     0  stevel 	{ PARM_COOKIE_PRESERVE,	"",	"Cookie Preservative",
    174     0  stevel 	    parse_opaque_param },
    175     0  stevel 	{ 10,	"",			"Reserved for ECN",
    176     0  stevel 	    parse_opaque_param },
    177     0  stevel 	{ PARM_ADDR_HOST_NAME,	"",	"Host Name Parameter",
    178     0  stevel 	    parse_opaque_param },
    179     0  stevel 	{ PARM_SUPP_ADDRS,	"",	"Supported Addresses",
    180     0  stevel 	    parse_suppaddr_param },
    181     0  stevel 	{ PARM_ECN_CAPABLE,	"",	"ECN Capable",
    182     0  stevel 	    parse_opaque_param },
    183     0  stevel 	{ PARM_ADD_IP,	"",		"Add IP",
    184     0  stevel 	    parse_addip_param },
    185     0  stevel 	{ PARM_DEL_IP,	"",		"Del IP",
    186     0  stevel 	    parse_addip_param },
    187     0  stevel 	{ PARM_ASCONF_ERROR,	"",	"ASCONF Error Ind",
    188     0  stevel 	    parse_asconferr_param },
    189     0  stevel 	{ PARM_PRIMARY_ADDR,	"",	"Set Primary Address",
    190     0  stevel 	    parse_addip_param },
    191     0  stevel 	{ PARM_FORWARD_TSN,	"",	"Forward TSN",
    192     0  stevel 	    NULL },
    193     0  stevel 	{ PARM_ASCONF_SUCCESS,	"",	"ASCONF Success Ind",
    194     0  stevel 	    parse_asconfok_param }
    195     0  stevel };
    196     0  stevel 
    197     0  stevel /*
    198     0  stevel  * Errors have the same wire format at parameters.
    199     0  stevel  */
    200     0  stevel static const dispatch_t err_dispatch_table[] = {
    201     0  stevel /*	code	F_SUM desc	F_DTAIL desc		parser function */
    202     0  stevel 	{ SCTP_ERR_UNKNOWN,	"",		"Unknown Error",
    203     0  stevel 	    parse_opaque_param },
    204     0  stevel 	{ SCTP_ERR_BAD_SID,	"",		"Invalid Stream ID",
    205     0  stevel 	    parse_opaque_param },
    206     0  stevel 	{ SCTP_ERR_MISSING_PARM,	"",	"Missing Parameter",
    207     0  stevel 	    parse_opaque_param },
    208     0  stevel 	{ SCTP_ERR_STALE_COOKIE,	"",	"Stale Cookie",
    209     0  stevel 	    parse_int32_param },
    210     0  stevel 	{ SCTP_ERR_NO_RESOURCES,	"",	"Out Of Resources",
    211     0  stevel 	    parse_opaque_param },
    212     0  stevel 	{ SCTP_ERR_BAD_ADDR,	"",		"Unresolvable Address",
    213     0  stevel 	    parse_opaque_param },
    214     0  stevel 	{ SCTP_ERR_UNREC_CHUNK,	"",		"Unrecognized Chunk",
    215     0  stevel 	    parse_unrec_chunk },
    216     0  stevel 	{ SCTP_ERR_BAD_MANDPARM,	"",	"Bad Mandatory Parameter",
    217     0  stevel 	    parse_opaque_param },
    218     0  stevel 	{ SCTP_ERR_UNREC_PARM,	"",		"Unrecognized Parameter",
    219     0  stevel 	    parse_opaque_param },
    220     0  stevel 	{ SCTP_ERR_NO_USR_DATA,	"",		"No User Data",
    221     0  stevel 	    parse_int32_param },
    222     0  stevel 	{ SCTP_ERR_COOKIE_SHUT,	"",		"Cookie During Shutdown",
    223     0  stevel 	    parse_opaque_param },
    224     0  stevel 	{ SCTP_ERR_DELETE_LASTADDR,	"",	"Delete Last Remaining Address",
    225     0  stevel 	    parse_addiperr_param },
    226     0  stevel 	{ SCTP_ERR_RESOURCE_SHORTAGE,	"",	"Resource Shortage",
    227     0  stevel 	    parse_addiperr_param },
    228     0  stevel 	{ SCTP_ERR_DELETE_SRCADDR,	"",	"Delete Source IP Address",
    229     0  stevel 	    parse_addiperr_param },
    230     0  stevel 	{ SCTP_ERR_AUTH_ERR,	"",		"Not authorized",
    231     0  stevel 	    parse_addiperr_param }
    232     0  stevel };
    233     0  stevel 
    234     0  stevel /*
    235     0  stevel  * These are global because the data chunk parser needs them to dispatch
    236     0  stevel  * to ULPs. The alternative is to add source and dest port arguments
    237     0  stevel  * to every parser, which seems even messier (since *only* the data
    238     0  stevel  * chunk parser needs it)...
    239     0  stevel  */
    240     0  stevel static in_port_t sport, dport;
    241     0  stevel 
    242     0  stevel /* Summary line miscellany */
    243     0  stevel static int sumlen;
    244     0  stevel static char scratch[MAXLINE];
    245     0  stevel static char *sumline;
    246     0  stevel 
    247     0  stevel #define	SUMAPPEND(fmt) \
    248     0  stevel 	sumlen -= snprintf fmt; \
    249     0  stevel 	(void) strlcat(sumline, scratch, sumlen)
    250     0  stevel 
    251     0  stevel #define	DUMPHEX_MAX	16
    252     0  stevel 
    253     0  stevel static const dispatch_t *
    254     0  stevel lookup_dispatch(int id, const dispatch_t *tbl, int tblsz)
    255     0  stevel {
    256     0  stevel 	int i;
    257     0  stevel 
    258     0  stevel 	/*
    259     0  stevel 	 * Try fast lookup first. The common chunks defined in RFC2960
    260     0  stevel 	 * will have indices aligned with their IDs, so this works for
    261     0  stevel 	 * the common case.
    262     0  stevel 	 */
    263     0  stevel 	if (id < (tblsz - 1)) {
    264     0  stevel 		if (id == tbl[id].id) {
    265     0  stevel 			return (tbl + id);
    266     0  stevel 		}
    267     0  stevel 	}
    268     0  stevel 
    269     0  stevel 	/*
    270     0  stevel 	 * Nope - probably an extension. Search the whole table,
    271     0  stevel 	 * starting from the end, since extensions are at the end.
    272     0  stevel 	 */
    273     0  stevel 	for (i = tblsz - 1; i >= 0; i--) {
    274     0  stevel 		if (id == tbl[i].id) {
    275     0  stevel 			return (tbl + i);
    276     0  stevel 		}
    277     0  stevel 	}
    278     0  stevel 
    279     0  stevel 	return (NULL);
    280     0  stevel }
    281     0  stevel 
    282     0  stevel /*
    283     0  stevel  * Dumps no more than the first DUMPHEX_MAX bytes in hex. If
    284     0  stevel  * the user wants more, they can use the -x option to snoop.
    285     0  stevel  */
    286     0  stevel static void
    287     0  stevel dumphex(const uchar_t *payload, int payload_len, char *msg)
    288     0  stevel {
    289     0  stevel 	int index;
    290     0  stevel 	int end;
    291     0  stevel 	char buf[BUFLEN];
    292     0  stevel 
    293     0  stevel 	if (payload_len == 0) {
    294     0  stevel 		return;
    295     0  stevel 	}
    296     0  stevel 
    297     0  stevel 	end = payload_len > DUMPHEX_MAX ? DUMPHEX_MAX : payload_len;
    298     0  stevel 
    299     0  stevel 	for (index = 0; index < end; index++) {
    300     0  stevel 		(void) snprintf(&buf[index * 3], 4, " %.2x", payload[index]);
    301     0  stevel 	}
    302     0  stevel 
    303     0  stevel 	if (payload_len > DUMPHEX_MAX) {
    304     0  stevel 		(void) strlcat(buf, " ...", BUFLEN);
    305     0  stevel 	}
    306     0  stevel 
    307     0  stevel 	(void) snprintf(get_line(0, 0), BUFLEN, msg, buf);
    308     0  stevel }
    309     0  stevel 
    310     0  stevel /*
    311     0  stevel  * Present perscribed action for unknowns according to rfc2960. Works
    312     0  stevel  * for chunks and parameters as well if the parameter type is
    313     0  stevel  * shifted 8 bits right.
    314     0  stevel  */
    315     0  stevel static const char *
    316     0  stevel get_action_desc(uint8_t id)
    317     0  stevel {
    318     0  stevel 	if ((id & 0xc0) == 0xc0) {
    319     0  stevel 		return (": skip on unknown, return error");
    320     0  stevel 	} else if ((id & 0x80) == 0x80) {
    321     0  stevel 		return (": skip on unknown, no error");
    322     0  stevel 	} else if ((id & 0x40) == 0x40) {
    323     0  stevel 		return (": stop on unknown, return error");
    324     0  stevel 	}
    325     0  stevel 
    326     0  stevel 	/* Top two bits are clear */
    327     0  stevel 	return (": stop on unknown, no error");
    328     0  stevel }
    329     0  stevel 
    330     0  stevel /* ARGSUSED */
    331     0  stevel static void
    332     0  stevel parse_asconfok_param(int flags, uint8_t notused, const void *data, int dlen)
    333     0  stevel {
    334     0  stevel 	uint32_t	*cid;
    335     0  stevel 
    336     0  stevel 	if (dlen < sizeof (*cid)) {
    337     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    338     0  stevel 		    "  ==> Incomplete ASCONF Success Ind parameter");
    339     0  stevel 		return;
    340     0  stevel 	}
    341     0  stevel 	cid = (uint32_t *)data;
    342     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(), "  ASCONF CID = %u",
    343     0  stevel 	    ntohl(*cid));
    344     0  stevel }
    345     0  stevel 
    346     0  stevel /* ARGSUSED */
    347     0  stevel static void
    348     0  stevel parse_asconferr_param(int flags, uint8_t notused, const void *data, int dlen)
    349     0  stevel {
    350     0  stevel 	uint32_t	*cid;
    351     0  stevel 
    352     0  stevel 	if (dlen < sizeof (*cid)) {
    353     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    354     0  stevel 		    "  ==> Incomplete ASCONF Error Ind parameter");
    355     0  stevel 		return;
    356     0  stevel 	}
    357     0  stevel 	cid = (uint32_t *)data;
    358     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(), "  ASCONF CID = %u",
    359     0  stevel 	    ntohl(*cid));
    360     0  stevel 
    361     0  stevel 	interpret_params(cid + 1, dlen - sizeof (*cid), "Error",
    362     0  stevel 	    err_dispatch_table, A_CNT(err_dispatch_table), flags);
    363     0  stevel }
    364     0  stevel 
    365     0  stevel /* ARGSUSED */
    366     0  stevel static void
    367     0  stevel parse_addiperr_param(int flags, uint8_t notused, const void *data, int dlen)
    368     0  stevel {
    369     0  stevel 
    370     0  stevel 	interpret_params(data, dlen, "Parameter",
    371     0  stevel 	    parm_dispatch_table, A_CNT(parm_dispatch_table), flags);
    372     0  stevel }
    373     0  stevel 
    374     0  stevel /* ARGSUSED */
    375     0  stevel static void
    376     0  stevel parse_addip_param(int flags, uint8_t notused, const void *data, int dlen)
    377     0  stevel {
    378     0  stevel 
    379     0  stevel 	uint32_t	*cid;
    380     0  stevel 
    381     0  stevel 	if (dlen < sizeof (*cid)) {
    382     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    383     0  stevel 		    "  ==> Incomplete ASCONF Error Ind parameter");
    384     0  stevel 		return;
    385     0  stevel 	}
    386     0  stevel 	cid = (uint32_t *)data;
    387     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(), "  ASCONF CID = %u",
    388     0  stevel 	    ntohl(*cid));
    389     0  stevel 
    390     0  stevel 	interpret_params(cid + 1, dlen - sizeof (*cid), "Parameter",
    391     0  stevel 	    parm_dispatch_table, A_CNT(parm_dispatch_table), flags);
    392     0  stevel }
    393     0  stevel 
    394     0  stevel /* ARGSUSED */
    395     0  stevel static void
    396     0  stevel parse_ip4_param(int flags, uint8_t notused, const void *data, int datalen)
    397     0  stevel {
    398     0  stevel 	char abuf[INET_ADDRSTRLEN];
    399     0  stevel 	char *ap;
    400     0  stevel 
    401     0  stevel 	if (datalen < sizeof (in_addr_t)) {
    402     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    403     0  stevel 		    "  ==> Incomplete IPv4 Addr parameter");
    404     0  stevel 		return;
    405     0  stevel 	}
    406     0  stevel 
    407     0  stevel 	ap = (char *)inet_ntop(AF_INET, data, abuf, INET_ADDRSTRLEN);
    408     0  stevel 	if (ap == NULL) {
    409     0  stevel 		ap = "<Bad Address>";
    410     0  stevel 	}
    411     0  stevel 
    412     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(), "  Addr = %s", ap);
    413     0  stevel }
    414     0  stevel 
    415     0  stevel /* ARGSUSED */
    416     0  stevel static void
    417     0  stevel parse_ip6_param(int flags, uint8_t notused, const void *data, int datalen)
    418     0  stevel {
    419     0  stevel 	char abuf[INET6_ADDRSTRLEN];
    420     0  stevel 	char *ap;
    421     0  stevel 
    422     0  stevel 	if (datalen < sizeof (in6_addr_t)) {
    423     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    424     0  stevel 		    "  ==> Incomplete IPv6 Addr parameter");
    425     0  stevel 		return;
    426     0  stevel 	}
    427     0  stevel 
    428     0  stevel 	ap = (char *)inet_ntop(AF_INET6, data, abuf, INET6_ADDRSTRLEN);
    429     0  stevel 	if (ap == NULL) {
    430     0  stevel 		ap = "<Bad Address>";
    431     0  stevel 	}
    432     0  stevel 
    433     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(), "  Addr = %s", ap);
    434     0  stevel }
    435     0  stevel 
    436     0  stevel /* ARGSUSED */
    437     0  stevel static void
    438     0  stevel parse_int32_param(int flags, uint8_t notused, const void *data, int datalen)
    439     0  stevel {
    440     0  stevel 	if (datalen < 4) {
    441     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    442     0  stevel 		    "  ==> Incomplete INT32 parameter");
    443     0  stevel 		return;
    444     0  stevel 	}
    445     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(), "  INT32 = %u",
    446     0  stevel 	    ntohl(*(uint32_t *)data));
    447     0  stevel }
    448     0  stevel 
    449     0  stevel /* ARGSUSED */
    450     0  stevel static void
    451     0  stevel parse_suppaddr_param(int flags, uint8_t notused, const void *data, int dlen)
    452     0  stevel {
    453     0  stevel 	const uint16_t *type;
    454     0  stevel 
    455     0  stevel 	if (dlen < 2) {
    456     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    457     0  stevel 		    "==> Incomplete Supported Addr parameter");
    458     0  stevel 		return;
    459     0  stevel 	}
    460     0  stevel 
    461     0  stevel 	type = data;
    462     0  stevel 	while (dlen > 0) {
    463     0  stevel 		switch (ntohs(*type)) {
    464     0  stevel 		case PARM_ADDR4:
    465     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    466     0  stevel 			    "  IPv4");
    467     0  stevel 			break;
    468     0  stevel 		case PARM_ADDR6:
    469     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    470     0  stevel 			    "  IPv6");
    471     0  stevel 			break;
    472     0  stevel 		case PARM_ADDR_HOST_NAME:
    473     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    474     0  stevel 			    "  Host Name");
    475     0  stevel 			break;
    476     0  stevel 		default:
    477     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    478     0  stevel 			    "Unknown Type (%hu)", ntohs(*type));
    479     0  stevel 			break;
    480     0  stevel 		}
    481     0  stevel 		dlen -= sizeof (*type);
    482     0  stevel 		type++;
    483     0  stevel 	}
    484     0  stevel }
    485     0  stevel 
    486     0  stevel /*ARGSUSED*/
    487     0  stevel static void
    488     0  stevel parse_encap_param(int flags, uint8_t notused, const void *data, int dlen)
    489     0  stevel {
    490     0  stevel 	if (dlen < sizeof (sctp_parm_hdr_t)) {
    491     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    492     0  stevel 		    "==> Incomplete Parameter");
    493     0  stevel 		return;
    494     0  stevel 	}
    495     0  stevel 
    496     0  stevel 	interpret_params(data, dlen, "Parameter",
    497     0  stevel 	    parm_dispatch_table, A_CNT(parm_dispatch_table), flags);
    498     0  stevel }
    499     0  stevel 
    500     0  stevel /* ARGSUSED */
    501     0  stevel static void
    502     0  stevel parse_unrec_chunk(int flags, uint8_t cflags, const void *data, int datalen)
    503     0  stevel {
    504     0  stevel 	const sctp_chunk_hdr_t *cp = data;
    505     0  stevel 	const dispatch_t *dp;
    506     0  stevel 	const char *actstr;
    507     0  stevel 
    508     0  stevel 	if (datalen < sizeof (*cp)) {
    509     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    510     0  stevel 		    "==> Incomplete Unrecognized Chunk Error");
    511     0  stevel 		return;
    512     0  stevel 	}
    513     0  stevel 
    514     0  stevel 	/* Maybe snoop knows about this chunk? */
    515     0  stevel 	dp = lookup_dispatch(cp->sch_id, chunk_dispatch_table,
    516     0  stevel 	    A_CNT(chunk_dispatch_table));
    517     0  stevel 	if (dp != NULL) {
    518     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    519     0  stevel 		    "  Chunk Type = %u (%s)", cp->sch_id, dp->vdesc);
    520     0  stevel 	} else {
    521     0  stevel 		actstr = get_action_desc(cp->sch_id);
    522     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    523     0  stevel 		    "  Chunk Type = %u%s", cp->sch_id, actstr);
    524     0  stevel 	}
    525     0  stevel }
    526     0  stevel 
    527     0  stevel /*
    528     0  stevel  * Same as parse_opaque_chunk except for the indentation.
    529     0  stevel  */
    530     0  stevel /* ARGSUSED */
    531     0  stevel static void
    532     0  stevel parse_opaque_param(int flags, uint8_t cflags, const void *data, int datalen)
    533     0  stevel {
    534     0  stevel 	dumphex(data, datalen, " Data = %s");
    535     0  stevel }
    536     0  stevel 
    537     0  stevel /*
    538     0  stevel  * Loops through all parameters (or errors) until it has read
    539     0  stevel  * datalen bytes of information, finding a parser for each.
    540     0  stevel  * The tbl argument allows the caller to specify which dispatch
    541     0  stevel  * table to use, making this function useful for both parameters
    542     0  stevel  * and errors. The type argument is used to denote whether this
    543     0  stevel  * is an error or parameter in detailed mode.
    544     0  stevel  */
    545     0  stevel static void
    546     0  stevel interpret_params(const void *data, int datalen, char *type,
    547     0  stevel     const dispatch_t *tbl, int tbl_size, int flags)
    548     0  stevel {
    549     0  stevel 	const sctp_parm_hdr_t *hdr = data;
    550     0  stevel 	uint16_t plen;
    551     0  stevel 	uint16_t ptype;
    552     0  stevel 	const char *desc;
    553     0  stevel 	parse_func_t *parse;
    554     0  stevel 	int pad;
    555     0  stevel 	const dispatch_t *dp;
    556     0  stevel 	const char *actstr;
    557     0  stevel 
    558     0  stevel 	for (;;) {
    559     0  stevel 		/*
    560     0  stevel 		 * Adjust for padding: if the address isn't aligned, there
    561     0  stevel 		 * should be some padding. So skip over the padding and
    562     0  stevel 		 * adjust hdr accordingly. RFC2960 mandates that all
    563     0  stevel 		 * parameters must be 32-bit aligned WRT the enclosing chunk,
    564     0  stevel 		 * which ensures that this parameter header will
    565     0  stevel 		 * be 32-bit aligned in memory. We must, of course, bounds
    566     0  stevel 		 * check fraglen before actually trying to use hdr, in
    567     0  stevel 		 * case the packet has been mangled or is the product
    568     0  stevel 		 * of a buggy implementation.
    569     0  stevel 		 */
    570     0  stevel 		if ((pad = (uintptr_t)hdr % SCTP_ALIGN) != 0) {
    571     0  stevel 			pad = SCTP_ALIGN - pad;
    572     0  stevel 			datalen -= pad;
    573     0  stevel 		/* LINTED pointer cast may result in improper alignment */
    574     0  stevel 			hdr = (sctp_parm_hdr_t *)((char *)hdr + pad);
    575     0  stevel 		}
    576     0  stevel 
    577     0  stevel 		/* Need to compare against 0 1st, since sizeof is unsigned */
    578     0  stevel 		if (datalen < 0 || datalen < sizeof (*hdr)) {
    579     0  stevel 			if (datalen > 0) {
    580     0  stevel 				(void) snprintf(get_line(0, 0),
    581     0  stevel 				    get_line_remain(),
    582     0  stevel 				    "==> Extra data after last parameter");
    583     0  stevel 			}
    584     0  stevel 			return;
    585     0  stevel 		}
    586     0  stevel 		plen = ntohs(hdr->sph_len);
    587     0  stevel 		if (datalen < plen || plen < sizeof (*hdr)) {
    588     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    589     0  stevel 			    "  ==> Incomplete %s", type);
    590     0  stevel 			return;
    591     0  stevel 		}
    592     0  stevel 
    593     0  stevel 		/* Get description and parser */
    594     0  stevel 		ptype = ntohs(hdr->sph_type);
    595     0  stevel 		desc = "Unknown Parameter Type";
    596     0  stevel 		parse = parse_opaque_param;
    597     0  stevel 		dp = lookup_dispatch(ptype, tbl, tbl_size);
    598     0  stevel 		if (dp != NULL) {
    599     0  stevel 			desc = dp->vdesc;
    600     0  stevel 			parse = dp->parse;
    601     0  stevel 		}
    602     0  stevel 
    603     0  stevel 		show_space();
    604     0  stevel 		if (dp != NULL) {
    605     0  stevel 			actstr = "";
    606     0  stevel 		} else {
    607     0  stevel 			actstr = get_action_desc((uint8_t)(ptype >> 8));
    608     0  stevel 		}
    609     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    610     0  stevel 		    "  ------- SCTP %s Type = %s (%u%s)", type, desc, ptype,
    611     0  stevel 		    actstr);
    612     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    613     0  stevel 		    "  Data length = %hu", plen - sizeof (*hdr));
    614     0  stevel 
    615     0  stevel 		if (parse != NULL) {
    616     0  stevel 			parse(flags, 0, (char *)(hdr + 1),
    617     0  stevel 			    plen - sizeof (*hdr));
    618     0  stevel 		}
    619     0  stevel 		datalen -= plen;
    620     0  stevel 		/* LINTED pointer cast may result in improper alignment */
    621     0  stevel 		hdr = (sctp_parm_hdr_t *)((char *)hdr + plen);
    622     0  stevel 	}
    623     0  stevel }
    624     0  stevel 
    625     0  stevel /* ARGSUSED */
    626     0  stevel static void
    627     0  stevel parse_ftsn_chunk(int flags, uint8_t cflags, const void *data, int datalen)
    628     0  stevel {
    629     0  stevel 	uint32_t	*ftsn;
    630     0  stevel 	ftsn_entry_t	*ftsn_entry;
    631     0  stevel 
    632     0  stevel 	if (datalen < (sizeof (*ftsn) + sizeof (*ftsn_entry))) {
    633     0  stevel 		if (flags & F_DTAIL) {
    634     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    635     0  stevel 			    "==> Incomplete FORWARD-TSN chunk");
    636     0  stevel 		}
    637     0  stevel 		return;
    638     0  stevel 	}
    639     0  stevel 
    640     0  stevel 	ftsn = (uint32_t *)data;
    641     0  stevel 	if (flags & F_SUM) {
    642     0  stevel 		SUMAPPEND((scratch, MAXLINE, "CTSN %x ", ntohl(*ftsn)));
    643     0  stevel 		return;
    644     0  stevel 	}
    645     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(), "Cum TSN=  %x",
    646     0  stevel 	    ntohl(*ftsn));
    647     0  stevel 
    648     0  stevel 	datalen -= sizeof (*ftsn);
    649     0  stevel 	ftsn_entry = (ftsn_entry_t *)(ftsn + 1);
    650     0  stevel 	while (datalen >= sizeof (*ftsn_entry)) {
    651     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    652     0  stevel 		    "SID =  %u : SSN = %u", ntohs(ftsn_entry->ftsn_sid),
    653     0  stevel 		    ntohs(ftsn_entry->ftsn_ssn));
    654     0  stevel 		datalen -= sizeof (*ftsn_entry);
    655     0  stevel 		ftsn_entry++;
    656     0  stevel 	}
    657     0  stevel }
    658     0  stevel 
    659     0  stevel /* ARGSUSED */
    660     0  stevel static void
    661     0  stevel parse_asconf_chunk(int flags, uint8_t cflags, const void *data, int datalen)
    662     0  stevel {
    663     0  stevel 	uint32_t	*sn;
    664     0  stevel 
    665     0  stevel 	if (datalen < sizeof (*sn)) {
    666     0  stevel 		if (flags & F_DTAIL) {
    667     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    668     0  stevel 			    "==> Incomplete ASCONF chunk");
    669     0  stevel 		}
    670     0  stevel 		return;
    671     0  stevel 	}
    672     0  stevel 
    673     0  stevel 	sn = (uint32_t *)data;
    674     0  stevel 	if (flags & F_SUM) {
    675     0  stevel 		SUMAPPEND((scratch, MAXLINE, "sn %x ", ntohl(*sn)));
    676     0  stevel 		return;
    677     0  stevel 	}
    678     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(), "Serial Number=  %x",
    679     0  stevel 	    ntohl(*sn));
    680     0  stevel 	interpret_params(sn + 1, datalen - sizeof (*sn), "Parameter",
    681     0  stevel 	    parm_dispatch_table, A_CNT(parm_dispatch_table), flags);
    682     0  stevel }
    683     0  stevel 
    684     0  stevel static void
    685     0  stevel parse_init_chunk(int flags, uint8_t cflags, const void *data, int datalen)
    686     0  stevel {
    687     0  stevel 	const sctp_init_chunk_t *icp = data;
    688     0  stevel 
    689     0  stevel 	if (datalen < sizeof (*icp)) {
    690     0  stevel 		if (flags & F_DTAIL) {
    691     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    692     0  stevel 			    "==> Incomplete INIT chunk");
    693     0  stevel 		}
    694     0  stevel 		return;
    695     0  stevel 	}
    696     0  stevel 
    697     0  stevel 	if (flags & F_SUM) {
    698     0  stevel 		SUMAPPEND((scratch, MAXLINE, "tsn %x str %hu/%hu win %u ",
    699     0  stevel 		    ntohl(icp->sic_inittsn), ntohs(icp->sic_outstr),
    700     0  stevel 		    ntohs(icp->sic_instr), ntohl(icp->sic_a_rwnd)));
    701     0  stevel 		return;
    702     0  stevel 	}
    703     0  stevel 
    704     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(), "Flags = 0x%.2x",
    705     0  stevel 	    cflags);
    706     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(),
    707     0  stevel 	    "Initiate tag = 0x%.8x", ntohl(icp->sic_inittag));
    708     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(),
    709     0  stevel 	    "Advertised receiver window credit = %u", ntohl(icp->sic_a_rwnd));
    710     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(),
    711     0  stevel 	    "Outbound streams = %hu", ntohs(icp->sic_outstr));
    712     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(),
    713     0  stevel 	    "Inbound streams = %hu", ntohs(icp->sic_instr));
    714     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(),
    715     0  stevel 	    "Initial TSN = 0x%.8x", ntohl(icp->sic_inittsn));
    716     0  stevel 
    717     0  stevel 	if (datalen > sizeof (*icp)) {
    718     0  stevel 		interpret_params(icp + 1, datalen - sizeof (*icp),
    719     0  stevel 		    "Parameter", parm_dispatch_table,
    720     0  stevel 		    A_CNT(parm_dispatch_table), flags);
    721     0  stevel 	}
    722     0  stevel }
    723     0  stevel 
    724     0  stevel static void
    725     0  stevel parse_data_chunk(int flags, uint8_t cflags, const void *data, int datalen)
    726     0  stevel {
    727     0  stevel 	const sctp_data_chunk_t	*dcp = data;
    728     0  stevel 	char			*payload;
    729     0  stevel 	uint32_t		ppid;
    730     0  stevel 
    731     0  stevel 	if (datalen < sizeof (*dcp)) {
    732     0  stevel 		if (flags & F_DTAIL) {
    733     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    734     0  stevel 			    "==> Incomplete DATA chunk %d (%d)", datalen,
    735     0  stevel 			    sizeof (*dcp));
    736     0  stevel 		}
    737     0  stevel 		return;
    738     0  stevel 	}
    739     0  stevel 
    740     0  stevel 	ppid = ntohl(dcp->sdc_payload_id);
    741  1766  kcpoon 	/* This is the actual data len, excluding the data chunk header. */
    742  1766  kcpoon 	datalen -= sizeof (*dcp);
    743     0  stevel 
    744     0  stevel 	if (flags & F_DTAIL) {
    745     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    746     0  stevel 		    "flags = 0x%.2x", cflags);
    747     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
    748     0  stevel 		    getflag(cflags, SCTP_DATA_UBIT, "unordered", "ordered"));
    749     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
    750     0  stevel 		    getflag(cflags, SCTP_DATA_BBIT,
    751     0  stevel 		    "beginning", "(beginning unset)"));
    752     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
    753     0  stevel 		    getflag(cflags, SCTP_DATA_EBIT, "end", "(end unset)"));
    754     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    755     0  stevel 		    "TSN = 0x%.8x", ntohl(dcp->sdc_tsn));
    756     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    757     0  stevel 		    "Stream ID = %hu", ntohs(dcp->sdc_sid));
    758     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    759     0  stevel 		    "Stream Sequence Number = %hu", ntohs(dcp->sdc_ssn));
    760     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    761     0  stevel 		    "Payload Protocol ID = 0x%.8x", ppid);
    762     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    763  1766  kcpoon 		    "Data Length = %d", datalen);
    764     0  stevel 		show_space();
    765     0  stevel 	}
    766     0  stevel 	if (flags & F_SUM) {
    767  1766  kcpoon 		SUMAPPEND((scratch, MAXLINE, "len %d tsn %x str %hu/%hu "
    768  1766  kcpoon 		    "ppid %x ", datalen, ntohl(dcp->sdc_tsn),
    769  1766  kcpoon 		    ntohs(dcp->sdc_sid), ntohs(dcp->sdc_ssn), ppid));
    770     0  stevel 	}
    771     0  stevel 
    772     0  stevel 	/*
    773     0  stevel 	 * Go to the next protocol layer, but not if we are in
    774     0  stevel 	 * summary mode only. In summary mode, each ULP parse would
    775     0  stevel 	 * create a new line, and if there were several data chunks
    776     0  stevel 	 * bundled together in the packet, this would confuse snoop's
    777     0  stevel 	 * packet numbering and timestamping.
    778     0  stevel 	 *
    779     0  stevel 	 * SCTP carries two ways to determine an ULP: ports and the
    780     0  stevel 	 * payload protocol identifier (ppid). Since ports are the
    781     0  stevel 	 * better entrenched convention, we first try interpret_reserved().
    782     0  stevel 	 * If that fails to find a parser, we try by the PPID.
    783     0  stevel 	 */
    784     0  stevel 	if (!(flags & F_ALLSUM) && !(flags & F_DTAIL)) {
    785     0  stevel 		return;
    786     0  stevel 	}
    787     0  stevel 
    788     0  stevel 	payload = (char *)(dcp + 1);
    789     0  stevel 	if (!interpret_reserved(flags, IPPROTO_SCTP, sport, dport, payload,
    790  1766  kcpoon 	    datalen) && ppid != 0) {
    791     0  stevel 
    792  1766  kcpoon 		interpret_protoid(flags, ppid, payload, datalen);
    793     0  stevel 	}
    794     0  stevel 
    795     0  stevel 	/*
    796     0  stevel 	 * Reset the protocol prefix, since it may have been changed
    797     0  stevel 	 * by a ULP interpreter.
    798     0  stevel 	 */
    799     0  stevel 	prot_prefix = "SCTP:  ";
    800     0  stevel }
    801     0  stevel 
    802     0  stevel /* ARGSUSED */
    803     0  stevel static void
    804     0  stevel parse_sack_chunk(int flags, uint8_t cflags, const void *data, int datalen)
    805     0  stevel {
    806     0  stevel 	const sctp_sack_chunk_t *scp = data;
    807     0  stevel 	uint16_t numfrags, numdups;
    808     0  stevel 	sctp_sack_frag_t *frag;
    809     0  stevel 	int i;
    810     0  stevel 	uint32_t *tsn;
    811     0  stevel 
    812     0  stevel 	if (datalen < sizeof (*scp)) {
    813     0  stevel 		if (flags & F_DTAIL) {
    814     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    815     0  stevel 			    "==> Incomplete SACK chunk");
    816     0  stevel 		}
    817     0  stevel 		return;
    818     0  stevel 	}
    819     0  stevel 
    820     0  stevel 	if (flags & F_DTAIL) {
    821     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    822     0  stevel 		    "Cumulative TSN ACK = 0x%.8x", ntohl(scp->ssc_cumtsn));
    823     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    824     0  stevel 		    "Advertised Receiver Window Credit = %u",
    825     0  stevel 		    ntohl(scp->ssc_a_rwnd));
    826     0  stevel 		numfrags = ntohs(scp->ssc_numfrags);
    827     0  stevel 		numdups = ntohs(scp->ssc_numdups);
    828     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    829     0  stevel 		    "Number of Fragments = %hu", numfrags);
    830     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    831     0  stevel 		    "Number of Duplicates = %hu", numdups);
    832     0  stevel 
    833     0  stevel 		/* Display any gap reports */
    834     0  stevel 		datalen -= sizeof (*scp);
    835     0  stevel 		if (datalen < (numfrags * sizeof (*frag))) {
    836     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    837     0  stevel 			    "  ==> Malformed gap report listing");
    838     0  stevel 			return;
    839     0  stevel 		}
    840     0  stevel 		frag = (sctp_sack_frag_t *)(scp + 1);
    841     0  stevel 		for (i = 0; i < numfrags; i++) {
    842     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    843     0  stevel 			    "  Fragment #%d: Start = %hu, end = %hu", i,
    844     0  stevel 			    ntohs(frag->ssf_start), ntohs(frag->ssf_end));
    845     0  stevel 			frag += 1;
    846     0  stevel 		}
    847     0  stevel 
    848     0  stevel 		/* Display any duplicate reports */
    849     0  stevel 		datalen -= numfrags * sizeof (*frag);
    850     0  stevel 		if (datalen < (numdups * sizeof (*tsn))) {
    851     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    852     0  stevel 			    "  ==> Malformed duplicate report listing");
    853     0  stevel 			return;
    854     0  stevel 		}
    855     0  stevel 		/* LINTED pointer cast may result in improper alignment */
    856     0  stevel 		tsn = (uint32_t *)frag;
    857     0  stevel 		for (i = 0; i < numdups; i++) {
    858     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    859     0  stevel 			    "  Duplicate #%d: TSN = %x", i, *tsn);
    860     0  stevel 			tsn++;
    861     0  stevel 		}
    862     0  stevel 	}
    863     0  stevel 	if (flags & F_SUM) {
    864     0  stevel 		SUMAPPEND((scratch, MAXLINE,
    865     0  stevel 		    "tsn %x win %u gaps/dups %hu/%hu ", ntohl(scp->ssc_cumtsn),
    866     0  stevel 		    ntohl(scp->ssc_a_rwnd), ntohs(scp->ssc_numfrags),
    867     0  stevel 		    ntohs(scp->ssc_numdups)));
    868     0  stevel 	}
    869     0  stevel }
    870     0  stevel 
    871     0  stevel /* ARGSUSED */
    872     0  stevel static void
    873     0  stevel parse_shutdown_chunk(int flags, uint8_t cflags, const void *data, int datalen)
    874     0  stevel {
    875     0  stevel 	const uint32_t *cumtsn = data;
    876     0  stevel 
    877     0  stevel 	if (datalen < sizeof (*cumtsn)) {
    878     0  stevel 		if (flags & F_DTAIL) {
    879     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
    880     0  stevel 			    "==> Incomplete Shutdown chunk");
    881     0  stevel 		}
    882     0  stevel 		return;
    883     0  stevel 	}
    884     0  stevel 
    885     0  stevel 	if (flags & F_DTAIL) {
    886     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
    887     0  stevel 		    "Cumulative TSN = 0x%.8x", ntohl(*cumtsn));
    888     0  stevel 	}
    889     0  stevel 	if (flags & F_SUM) {
    890     0  stevel 		SUMAPPEND((scratch, MAXLINE, "tsn %x", ntohl(*cumtsn)));
    891     0  stevel 	}
    892     0  stevel }
    893     0  stevel 
    894     0  stevel /* ARGSUSED */
    895     0  stevel static void
    896     0  stevel parse_error_chunk(int flags, uint8_t cflags, const void *data, int datalen)
    897     0  stevel {
    898     0  stevel 	if (!(flags & F_DTAIL)) {
    899     0  stevel 		return;
    900     0  stevel 	}
    901     0  stevel 
    902     0  stevel 	interpret_params(data, datalen, "Error", err_dispatch_table,
    903     0  stevel 	    A_CNT(err_dispatch_table), flags);
    904     0  stevel }
    905     0  stevel 
    906     0  stevel static void
    907     0  stevel parse_abort_chunk(int flags, uint8_t cflags, const void *data, int datalen)
    908     0  stevel {
    909     0  stevel 	if (!(flags & F_DTAIL)) {
    910     0  stevel 		return;
    911     0  stevel 	}
    912     0  stevel 
    913     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(), "flags = 0x%.2x",
    914     0  stevel 	    cflags);
    915     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
    916     0  stevel 	    getflag(cflags, SCTP_TBIT, "TCB not destroyed", "TCB destroyed"));
    917     0  stevel 
    918     0  stevel 	interpret_params(data, datalen, "Error", err_dispatch_table,
    919     0  stevel 	    A_CNT(err_dispatch_table), flags);
    920     0  stevel }
    921     0  stevel 
    922     0  stevel /* ARGSUSED2 */
    923     0  stevel static void
    924     0  stevel parse_shutdone_chunk(int flags, uint8_t cflags, const void *data, int datalen)
    925     0  stevel {
    926     0  stevel 	if (!(flags & F_DTAIL)) {
    927     0  stevel 		return;
    928     0  stevel 	}
    929     0  stevel 
    930     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(), "flags = 0x%.2x",
    931     0  stevel 	    cflags);
    932     0  stevel 	(void) snprintf(get_line(0, 0), get_line_remain(), "      %s",
    933     0  stevel 	    getflag(cflags, SCTP_TBIT, "TCB not destroyed", "TCB destroyed"));
    934     0  stevel }
    935     0  stevel 
    936     0  stevel /* ARGSUSED */
    937     0  stevel static void
    938     0  stevel parse_opaque_chunk(int flags, uint8_t cflags, const void *data, int datalen)
    939     0  stevel {
    940     0  stevel 	if (!(flags & F_DTAIL)) {
    941     0  stevel 		return;
    942     0  stevel 	}
    943     0  stevel 	if (datalen == 0) {
    944     0  stevel 		return;
    945     0  stevel 	}
    946     0  stevel 
    947     0  stevel 	dumphex(data, datalen, "Data = %s");
    948     0  stevel }
    949     0  stevel 
    950     0  stevel /*
    951     0  stevel  * Loops through all chunks until it has read fraglen bytes of
    952     0  stevel  * information, finding a parser for each. If any parameters are
    953     0  stevel  * present, interpret_params() is then called. Returns the remaining
    954     0  stevel  * fraglen.
    955     0  stevel  */
    956     0  stevel static int
    957     0  stevel interpret_chunks(int flags, sctp_chunk_hdr_t *cp, int fraglen)
    958     0  stevel {
    959     0  stevel 	uint16_t clen;
    960     0  stevel 	int signed_len;
    961     0  stevel 	int pad;
    962     0  stevel 	const char *desc;
    963     0  stevel 	parse_func_t *parse;
    964     0  stevel 	const dispatch_t *dp;
    965     0  stevel 	const char *actstr;
    966     0  stevel 
    967     0  stevel 	for (;;) {
    968     0  stevel 		/*
    969     0  stevel 		 * Adjust for padding: if the address isn't aligned, there
    970     0  stevel 		 * should be some padding. So skip over the padding and
    971     0  stevel 		 * adjust hdr accordingly. RFC2960 mandates that all
    972     0  stevel 		 * chunks must be 32-bit aligned WRT the SCTP common hdr,
    973     0  stevel 		 * which ensures that this chunk header will
    974     0  stevel 		 * be 32-bit aligned in memory. We must, of course, bounds
    975     0  stevel 		 * check fraglen before actually trying to use hdr, in
    976     0  stevel 		 * case the packet has been mangled or is the product
    977     0  stevel 		 * of a buggy implementation.
    978     0  stevel 		 */
    979     0  stevel 		if ((pad = (uintptr_t)cp % SCTP_ALIGN) != 0) {
    980     0  stevel 			pad = SCTP_ALIGN - pad;
    981     0  stevel 			fraglen -= pad;
    982     0  stevel 		/* LINTED pointer cast may result in improper alignment */
    983     0  stevel 			cp = (sctp_chunk_hdr_t *)((char *)cp + pad);
    984     0  stevel 		}
    985     0  stevel 
    986     0  stevel 		/* Need to compare against 0 1st, since sizeof is unsigned */
    987     0  stevel 		if (fraglen < 0 || fraglen < sizeof (*cp)) {
    988     0  stevel 			if (fraglen > 0 && flags & F_DTAIL) {
    989     0  stevel 				(void) snprintf(get_line(0, 0),
    990     0  stevel 				    get_line_remain(),
    991     0  stevel 				    "==> Extra data after last chunk");
    992     0  stevel 			}
    993     0  stevel 			return (fraglen);
    994     0  stevel 		}
    995     0  stevel 
    996     0  stevel 		clen = ntohs(cp->sch_len);
    997     0  stevel 		if (fraglen < clen) {
    998     0  stevel 			if (flags & F_DTAIL) {
    999     0  stevel 				(void) snprintf(get_line(0, 0),
   1000     0  stevel 				    get_line_remain(), "==> Corrupted chunk");
   1001     0  stevel 			}
   1002     0  stevel 			return (fraglen);
   1003     0  stevel 		}
   1004     0  stevel 
   1005     0  stevel 		signed_len = clen - sizeof (*cp);
   1006     0  stevel 		if (signed_len < 0) {
   1007     0  stevel 			if (flags & F_DTAIL) {
   1008     0  stevel 				(void) snprintf(get_line(0, 0),
   1009     0  stevel 				    get_line_remain(),
   1010     0  stevel 				    "==> Incomplete or corrupted chunk");
   1011     0  stevel 			}
   1012     0  stevel 			return (0);
   1013     0  stevel 		}
   1014     0  stevel 
   1015     0  stevel 		/* Get description and parser */
   1016     0  stevel 		dp = lookup_dispatch(cp->sch_id, chunk_dispatch_table,
   1017     0  stevel 		    A_CNT(chunk_dispatch_table));
   1018     0  stevel 		if (dp != NULL) {
   1019     0  stevel 			if (flags & F_SUM) {
   1020     0  stevel 				desc = dp->sdesc;
   1021     0  stevel 			} else if (flags & F_DTAIL) {
   1022     0  stevel 				desc = dp->vdesc;
   1023     0  stevel 			}
   1024     0  stevel 			parse = dp->parse;
   1025     0  stevel 		} else {
   1026     0  stevel 			if (flags & F_SUM) {
   1027     0  stevel 				desc = "UNK";
   1028     0  stevel 			} else if (flags & F_DTAIL) {
   1029     0  stevel 				desc = "Unknown Chunk Type";
   1030     0  stevel 			}
   1031     0  stevel 			parse = parse_opaque_chunk;
   1032     0  stevel 		}
   1033     0  stevel 
   1034     0  stevel 		if (flags & F_SUM) {
   1035     0  stevel 			SUMAPPEND((scratch, MAXLINE, "%s ", desc));
   1036     0  stevel 		}
   1037     0  stevel 		if (flags & F_DTAIL) {
   1038     0  stevel 			show_space();
   1039     0  stevel 
   1040     0  stevel 			if (dp != NULL) {
   1041     0  stevel 				actstr = "";
   1042     0  stevel 			} else {
   1043     0  stevel 				actstr = get_action_desc(cp->sch_id);
   1044     0  stevel 			}
   1045     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
   1046     0  stevel 			    "------- SCTP Chunk Type = %s (%u%s)", desc,
   1047     0  stevel 			    cp->sch_id, actstr);
   1048     0  stevel 
   1049     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
   1050     0  stevel 			    "Chunk length = %hu", clen);
   1051     0  stevel 		}
   1052     0  stevel 
   1053     0  stevel 		if (parse != NULL) {
   1054     0  stevel 			parse(flags, cp->sch_flags, (char *)(cp + 1),
   1055     0  stevel 			    signed_len);
   1056     0  stevel 		}
   1057     0  stevel 
   1058     0  stevel 		fraglen -= clen;
   1059     0  stevel 
   1060     0  stevel 		/* LINTED pointer cast may result in improper alignment */
   1061     0  stevel 		cp = (sctp_chunk_hdr_t *)((char *)cp + clen);
   1062     0  stevel 	}
   1063     0  stevel }
   1064     0  stevel 
   1065     0  stevel void
   1066     0  stevel interpret_sctp(int flags, sctp_hdr_t *sctp, int iplen, int fraglen)
   1067     0  stevel {
   1068     0  stevel 	int len_from_iphdr;
   1069     0  stevel 	sctp_chunk_hdr_t *cp;
   1070     0  stevel 	char *pn;
   1071     0  stevel 	char buff[32];
   1072     0  stevel 
   1073     0  stevel 	/*
   1074     0  stevel 	 * Alignment check. If the header is 32-bit aligned, all other
   1075     0  stevel 	 * protocol units will also be aligned, as mandated by rfc2960.
   1076     0  stevel 	 * Buggy packets will be caught and flagged by chunk and
   1077     0  stevel 	 * parameter bounds checking.
   1078     0  stevel 	 * If the header is not aligned, however, we drop the packet.
   1079     0  stevel 	 */
   1080     0  stevel 	if (!IS_P2ALIGNED(sctp, SCTP_ALIGN)) {
   1081     0  stevel 		if (flags & F_DTAIL) {
   1082     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
   1083     0  stevel 			    "==> SCTP header not aligned, dropping");
   1084     0  stevel 		}
   1085     0  stevel 		return;
   1086     0  stevel 	}
   1087     0  stevel 
   1088     0  stevel 	fraglen -= sizeof (*sctp);
   1089     0  stevel 	if (fraglen < 0) {
   1090     0  stevel 		if (flags & F_DTAIL) {
   1091     0  stevel 			(void) snprintf(get_line(0, 0), get_line_remain(),
   1092     0  stevel 			    "==> Incomplete sctp header");
   1093     0  stevel 		}
   1094     0  stevel 		return;
   1095     0  stevel 	}
   1096     0  stevel 	/* If fraglen is somehow longer than the IP payload, adjust it */
   1097     0  stevel 	len_from_iphdr = iplen - sizeof (*sctp);
   1098     0  stevel 	if (fraglen > len_from_iphdr) {
   1099     0  stevel 		fraglen = len_from_iphdr;
   1100     0  stevel 	}
   1101     0  stevel 
   1102     0  stevel 	/* Keep track of the ports */
   1103     0  stevel 	sport = ntohs(sctp->sh_sport);
   1104     0  stevel 	dport = ntohs(sctp->sh_dport);
   1105     0  stevel 
   1106     0  stevel 	/* Set pointer to first chunk */
   1107     0  stevel 	cp = (sctp_chunk_hdr_t *)(sctp + 1);
   1108     0  stevel 
   1109     0  stevel 	if (flags & F_SUM) {
   1110     0  stevel 		sumline = get_sum_line();
   1111     0  stevel 		*sumline = '\0';
   1112     0  stevel 		sumlen = MAXLINE;
   1113     0  stevel 
   1114     0  stevel 		SUMAPPEND((scratch, MAXLINE, "SCTP D=%d S=%d ", dport, sport));
   1115     0  stevel 	}
   1116     0  stevel 
   1117     0  stevel 	if (flags & F_DTAIL) {
   1118     0  stevel 		show_header("SCTP:  ", "SCTP Header", fraglen);
   1119     0  stevel 		show_space();
   1120     0  stevel 
   1121     0  stevel 		pn = getportname(IPPROTO_SCTP, (ushort_t)sport);
   1122     0  stevel 		if (pn == NULL) {
   1123     0  stevel 			pn = "";
   1124     0  stevel 		} else {
   1125     0  stevel 			(void) snprintf(buff, sizeof (buff), "(%s)", pn);
   1126     0  stevel 			pn = buff;
   1127     0  stevel 		}
   1128     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
   1129     0  stevel 		    "Source port = %hu %s", sport, pn);
   1130     0  stevel 
   1131     0  stevel 		pn = getportname(IPPROTO_SCTP, (ushort_t)dport);
   1132     0  stevel 		if (pn == NULL) {
   1133     0  stevel 			pn = "";
   1134     0  stevel 		} else {
   1135     0  stevel 			(void) snprintf(buff, sizeof (buff), "(%s)", pn);
   1136     0  stevel 			pn = buff;
   1137     0  stevel 		}
   1138     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
   1139     0  stevel 		    "Destination port = %hu %s", dport, pn);
   1140     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
   1141     0  stevel 		    "Verification tag = 0x%.8x", ntohl(sctp->sh_verf));
   1142     0  stevel 		(void) snprintf(get_line(0, 0), get_line_remain(),
   1143     0  stevel 		    "CRC-32c = 0x%.8x", ntohl(sctp->sh_chksum));
   1144     0  stevel 	}
   1145     0  stevel 
   1146     0  stevel 	(void) interpret_chunks(flags, cp, fraglen);
   1147     0  stevel 
   1148     0  stevel 	if (flags & F_DTAIL) {
   1149     0  stevel 		show_space();
   1150     0  stevel 	}
   1151     0  stevel }
   1152     0  stevel 
   1153     0  stevel /*
   1154     0  stevel  * Payload protocol ID table. Add new ULP information and parsers
   1155     0  stevel  * here.
   1156     0  stevel  */
   1157     0  stevel 
   1158     0  stevel struct protoid_table {
   1159     0  stevel 	int	pid_num;
   1160     0  stevel 	char	*pid_short;
   1161     0  stevel 	char	*pid_long;
   1162     0  stevel };
   1163     0  stevel 
   1164     0  stevel static struct protoid_table pid_sctp[] = {
   1165     0  stevel 	1,	"IUA",		"ISDN Q.921 User Adaption Layer",
   1166     0  stevel 	2,	"M2UA",		"SS7 MTP2 User Adaption Layer",
   1167     0  stevel 	3,	"M3UA",		"SS7 MTP3 User Adaption Layer",
   1168     0  stevel 	4,	"SUA",		"SS7 SCCP User Adaption Layer",
   1169     0  stevel 	5,	"M2PA",		"SS7 MTP2-User Peer-to-Peer Adaption Layer",
   1170     0  stevel 	6,	"V5UA",		"V5UA",
   1171     0  stevel 	0,	NULL,		"",
   1172     0  stevel };
   1173     0  stevel 
   1174     0  stevel static void
   1175     0  stevel interpret_protoid(int flags, uint32_t ppid, char *data, int dlen)
   1176     0  stevel {
   1177     0  stevel 	struct protoid_table *p;
   1178     0  stevel 	char pbuf[16];
   1179     0  stevel 
   1180     0  stevel 	/*
   1181     0  stevel 	 * Branch to a ULP interpreter here, or continue on to
   1182     0  stevel 	 * the default parser, which just tries to display
   1183     0  stevel 	 * printable characters from the payload.
   1184     0  stevel 	 */
   1185     0  stevel 
   1186     0  stevel 	for (p = pid_sctp; p->pid_num; p++) {
   1187     0  stevel 		if (ppid == p->pid_num) {
   1188     0  stevel 			if (flags & F_SUM) {
   1189     0  stevel 				(void) snprintf(get_sum_line(), MAXLINE,
   1190     0  stevel 				    "D=%d S=%d %s %s", dport, sport,
   1191     0  stevel 				    p->pid_short, show_string(data, dlen, 20));
   1192     0  stevel 			}
   1193     0  stevel 
   1194     0  stevel 			if (flags & F_DTAIL) {
   1195     0  stevel 				(void) snprintf(pbuf, MAXLINE, "%s:  ",
   1196     0  stevel 				    p->pid_short);
   1197     0  stevel 				show_header(pbuf, p->pid_long, dlen);
   1198     0  stevel 				show_space();
   1199     0  stevel 				(void) snprintf(get_line(0, 0),
   1200     0  stevel 				    get_line_remain(), "\"%s\"",
   1201     0  stevel 				    show_string(data, dlen, 60));
   1202     0  stevel 				show_trailer();
   1203     0  stevel 			}
   1204     0  stevel 
   1205     0  stevel 			return;
   1206     0  stevel 		}
   1207     0  stevel 	}
   1208     0  stevel }
   1209