/*
* 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
* 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/sysmacros.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.
*/
/*
* 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 struct {
} dispatch_t;
static void interpret_params(const void *, int, char *, const dispatch_t *,
int, int);
/*
* Chunk parsers
*/
/*
* 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.
*/
/* code F_SUM desc F_DTAIL desc parser function */
NULL },
};
/*
* Parameter Parsers
*/
/*
* 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.
*/
/* code F_SUM desc F_DTAIL desc parser function */
{ 10, "", "Reserved for ECN",
NULL },
};
/*
* Errors have the same wire format at parameters.
*/
/* code F_SUM desc F_DTAIL desc parser function */
};
/*
* 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)...
*/
/* Summary line miscellany */
static int sumlen;
static char *sumline;
static const dispatch_t *
{
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.
*/
}
}
/*
* 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--) {
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
{
int index;
int end;
if (payload_len == 0) {
return;
}
}
if (payload_len > DUMPHEX_MAX) {
}
}
/*
* 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 *
{
return (": skip on unknown, return error");
return (": skip on unknown, no error");
return (": stop on unknown, return error");
}
/* Top two bits are clear */
return (": stop on unknown, no error");
}
/* ARGSUSED */
static void
{
" ==> Incomplete ASCONF Success Ind parameter");
return;
}
}
/* ARGSUSED */
static void
{
" ==> Incomplete ASCONF Error Ind parameter");
return;
}
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static void
{
" ==> Incomplete ASCONF Error Ind parameter");
return;
}
}
/* ARGSUSED */
static void
{
char *ap;
" ==> Incomplete IPv4 Addr parameter");
return;
}
ap = "<Bad Address>";
}
}
/* ARGSUSED */
static void
{
char *ap;
if (datalen < sizeof (in6_addr_t)) {
" ==> Incomplete IPv6 Addr parameter");
return;
}
ap = "<Bad Address>";
}
}
/* ARGSUSED */
static void
{
if (datalen < 4) {
" ==> Incomplete INT32 parameter");
return;
}
}
/* ARGSUSED */
static void
{
if (dlen < 2) {
"==> Incomplete Supported Addr parameter");
return;
}
while (dlen > 0) {
case PARM_ADDR4:
" IPv4");
break;
case PARM_ADDR6:
" IPv6");
break;
case PARM_ADDR_HOST_NAME:
" Host Name");
break;
default:
break;
}
type++;
}
}
/*ARGSUSED*/
static void
{
if (dlen < sizeof (sctp_parm_hdr_t)) {
"==> Incomplete Parameter");
return;
}
}
/* ARGSUSED */
static void
{
const char *actstr;
"==> Incomplete Unrecognized Chunk Error");
return;
}
/* Maybe snoop knows about this chunk? */
} else {
}
}
/*
* Same as parse_opaque_chunk except for the indentation.
*/
/* ARGSUSED */
static void
{
}
/*
* 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
{
const char *desc;
int pad;
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.
*/
/* LINTED pointer cast may result in improper alignment */
}
/* Need to compare against 0 1st, since sizeof is unsigned */
if (datalen > 0) {
"==> Extra data after last parameter");
}
return;
}
" ==> Incomplete %s", type);
return;
}
/* Get description and parser */
desc = "Unknown Parameter Type";
}
show_space();
actstr = "";
} else {
}
actstr);
}
/* LINTED pointer cast may result in improper alignment */
}
}
/* ARGSUSED */
static void
{
"==> Incomplete FORWARD-TSN chunk");
}
return;
}
return;
}
while (datalen >= sizeof (*ftsn_entry)) {
datalen -= sizeof (*ftsn_entry);
ftsn_entry++;
}
}
/* ARGSUSED */
static void
{
"==> Incomplete ASCONF chunk");
}
return;
}
return;
}
}
static void
{
"==> Incomplete INIT chunk");
}
return;
}
return;
}
cflags);
"Parameter", parm_dispatch_table,
}
}
static void
{
char *payload;
"==> Incomplete DATA chunk %d (%d)", datalen,
sizeof (*dcp));
}
return;
}
/* This is the actual data len, excluding the data chunk header. */
"flags = 0x%.2x", cflags);
"beginning", "(beginning unset)"));
"Payload Protocol ID = 0x%.8x", ppid);
"Data Length = %d", datalen);
show_space();
}
}
/*
* 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.
*/
return;
}
}
/*
* Reset the protocol prefix, since it may have been changed
* by a ULP interpreter.
*/
prot_prefix = "SCTP: ";
}
/* ARGSUSED */
static void
{
int i;
"==> Incomplete SACK chunk");
}
return;
}
"Advertised Receiver Window Credit = %u",
"Number of Fragments = %hu", numfrags);
"Number of Duplicates = %hu", numdups);
/* Display any gap reports */
" ==> Malformed gap report listing");
return;
}
for (i = 0; i < numfrags; i++) {
" Fragment #%d: Start = %hu, end = %hu", i,
frag += 1;
}
/* Display any duplicate reports */
" ==> Malformed duplicate report listing");
return;
}
/* LINTED pointer cast may result in improper alignment */
for (i = 0; i < numdups; i++) {
" Duplicate #%d: TSN = %x", i, *tsn);
tsn++;
}
}
}
}
/* ARGSUSED */
static void
{
"==> Incomplete Shutdown chunk");
}
return;
}
}
}
}
/* ARGSUSED */
static void
{
return;
}
}
static void
{
return;
}
cflags);
}
/* ARGSUSED2 */
static void
{
return;
}
cflags);
}
/* ARGSUSED */
static void
{
return;
}
if (datalen == 0) {
return;
}
}
/*
* 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
{
int signed_len;
int pad;
const char *desc;
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.
*/
/* LINTED pointer cast may result in improper alignment */
}
/* Need to compare against 0 1st, since sizeof is unsigned */
"==> Extra data after last chunk");
}
return (fraglen);
}
get_line_remain(), "==> Corrupted chunk");
}
return (fraglen);
}
if (signed_len < 0) {
"==> Incomplete or corrupted chunk");
}
return (0);
}
/* Get description and parser */
}
} else {
desc = "UNK";
desc = "Unknown Chunk Type";
}
}
}
show_space();
actstr = "";
} else {
}
"------- SCTP Chunk Type = %s (%u%s)", desc,
"Chunk length = %hu", clen);
}
}
/* LINTED pointer cast may result in improper alignment */
}
}
void
{
int len_from_iphdr;
char *pn;
/*
* 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.
*/
"==> SCTP header not aligned, dropping");
}
return;
}
if (fraglen < 0) {
"==> Incomplete sctp header");
}
return;
}
/* If fraglen is somehow longer than the IP payload, adjust it */
if (fraglen > len_from_iphdr) {
}
/* Keep track of the ports */
/* Set pointer to first chunk */
sumline = get_sum_line();
*sumline = '\0';
}
show_space();
pn = "";
} else {
}
pn = "";
} else {
}
}
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;
};
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
{
struct protoid_table *p;
/*
* Branch to a ULP interpreter here, or continue on to
* the default parser, which just tries to display
* printable characters from the payload.
*/
}
p->pid_short);
show_space();
get_line_remain(), "\"%s\"",
show_trailer();
}
return;
}
}
}