4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/sysmacros.h>
34 #include <inet/common.h>
35 #include <netinet/in.h>
36 #include <netinet/sctp.h>
37 #include <arpa/inet.h>
42 * Snoop interpreter for SCTP (rfc2960).
44 * To add support for an upper-layer protocol, modify either
45 * the port-dispatcher in snoop_rport.c, or the protocol ID
46 * dispatcher at the bottom of this file (or both).
49 static void interpret_protoid(int, uint32_t, char *, int);
50 extern char *prot_prefix
;
53 * This defines the length of internal, unbounded buffers. We set
54 * this to be MAXLINE (the maximum verbose display line length) -
55 * 64, which should be enough for all necessary descriptions. 64
56 * bytes seems like a reasonably conservative estimate of the
57 * maximum prefix length snoop may add to any text buffer it hands out.
59 #define BUFLEN MAXLINE - 64
62 * Common structure to hold descriptions and parsers for all
63 * chunks, parameters, and errors. Each parser should implement
66 * void parse(int flags, uint8_t cflags, void *data, int datalen);
68 * Where flags is the snoop flags, cflags are the chunk flags, data
69 * is the chunk or parameter data (not including the chunk or
70 * parameter header), and datalen is the length of the chunk or
71 * parameter data (again not including any headers).
73 typedef void parse_func_t(int, uint8_t, const void *, int);
77 const char *sdesc
; /* short description */
78 const char *vdesc
; /* verbose description */
79 parse_func_t
*parse
; /* parser function */
82 static void interpret_params(const void *, int, char *, const dispatch_t
*,
88 static parse_func_t parse_abort_chunk
, parse_data_chunk
, parse_error_chunk
,
89 parse_init_chunk
, parse_opaque_chunk
, parse_sack_chunk
,
90 parse_shutdone_chunk
, parse_shutdown_chunk
, parse_asconf_chunk
,
95 * Chunk parser dispatch table. There are few enough chunks defined
96 * in the core protocol, and they are sequential, so the chunk code
97 * can be used as the index into this array for the common case.
98 * It is still necessary to check that the code and index match,
99 * since optional extensions will not follow sequentially the
102 static const dispatch_t chunk_dispatch_table
[] = {
103 /* code F_SUM desc F_DTAIL desc parser function */
104 { CHUNK_DATA
, "Data", "Data Chunk",
106 { CHUNK_INIT
, "Init", "Init Chunk",
108 { CHUNK_INIT_ACK
, "Init ACK", "Init ACK Chunk",
110 { CHUNK_SACK
, "SACK", "SACK Chunk",
112 { CHUNK_HEARTBEAT
, "Heartbeat", "Heartbeat Chunk",
113 parse_opaque_chunk
},
114 { CHUNK_HEARTBEAT_ACK
, "Heartbeat ACK", "Heartbeat ACK Chunk",
115 parse_opaque_chunk
},
116 { CHUNK_ABORT
, "Abort", "Abort Chunk",
118 { CHUNK_SHUTDOWN
, "Shutdown", "Shutdown Chunk",
119 parse_shutdown_chunk
},
120 { CHUNK_SHUTDOWN_ACK
, "Shutdown ACK", "Shutdown ACK Chunk",
122 { CHUNK_ERROR
, "Err", "Error Chunk",
124 { CHUNK_COOKIE
, "Cookie", "Cookie Chunk",
125 parse_opaque_chunk
},
126 { CHUNK_COOKIE_ACK
, "Cookie ACK", "Cookie ACK Chunk",
127 parse_opaque_chunk
},
128 { CHUNK_ECNE
, "ECN Echo", "ECN Echo Chunk",
129 parse_opaque_chunk
},
130 { CHUNK_CWR
, "CWR", "CWR Chunk",
131 parse_opaque_chunk
},
132 { CHUNK_SHUTDOWN_COMPLETE
, "Shutdown Done", "Shutdown Done",
133 parse_shutdone_chunk
},
134 { CHUNK_FORWARD_TSN
, "FORWARD TSN", "Forward TSN Chunk",
136 { CHUNK_ASCONF_ACK
, "ASCONF ACK", "ASCONF ACK Chunk",
137 parse_asconf_chunk
},
138 { CHUNK_ASCONF
, "ASCONF", "ASCONF Chunk",
145 static parse_func_t parse_encap_param
, parse_int32_param
, parse_ip4_param
,
146 parse_ip6_param
, parse_opaque_param
, parse_suppaddr_param
,
147 parse_unrec_chunk
, parse_addip_param
, parse_asconferr_param
,
148 parse_asconfok_param
, parse_addiperr_param
;
151 * Parameter parser dispatch table. The summary description is not
152 * used here. Strictly speaking, parameter types are defined within
153 * the context of a chunk type. However, thus far the IETF WG has
154 * agreed to follow the convention that parameter types are globally
155 * unique (and why not, with a 16-bit namespace). However, if this
156 * ever changes, there will need to be different parameter dispatch
157 * tables for each chunk type.
159 static const dispatch_t parm_dispatch_table
[] = {
160 /* code F_SUM desc F_DTAIL desc parser function */
161 { PARM_UNKNOWN
, "", "Unknown Parameter",
162 parse_opaque_param
},
163 { PARM_HBINFO
, "", "Heartbeat Info",
164 parse_opaque_param
},
165 { PARM_ADDR4
, "", "IPv4 Address",
167 { PARM_ADDR6
, "", "IPv6 Address",
169 { PARM_COOKIE
, "", "Cookie",
170 parse_opaque_param
},
171 { PARM_UNRECOGNIZED
, "", "Unrecognized Param",
173 { PARM_COOKIE_PRESERVE
, "", "Cookie Preservative",
174 parse_opaque_param
},
175 { 10, "", "Reserved for ECN",
176 parse_opaque_param
},
177 { PARM_ADDR_HOST_NAME
, "", "Host Name Parameter",
178 parse_opaque_param
},
179 { PARM_SUPP_ADDRS
, "", "Supported Addresses",
180 parse_suppaddr_param
},
181 { PARM_ECN_CAPABLE
, "", "ECN Capable",
182 parse_opaque_param
},
183 { PARM_ADD_IP
, "", "Add IP",
185 { PARM_DEL_IP
, "", "Del IP",
187 { PARM_ASCONF_ERROR
, "", "ASCONF Error Ind",
188 parse_asconferr_param
},
189 { PARM_PRIMARY_ADDR
, "", "Set Primary Address",
191 { PARM_FORWARD_TSN
, "", "Forward TSN",
193 { PARM_ASCONF_SUCCESS
, "", "ASCONF Success Ind",
194 parse_asconfok_param
}
198 * Errors have the same wire format at parameters.
200 static const dispatch_t err_dispatch_table
[] = {
201 /* code F_SUM desc F_DTAIL desc parser function */
202 { SCTP_ERR_UNKNOWN
, "", "Unknown Error",
203 parse_opaque_param
},
204 { SCTP_ERR_BAD_SID
, "", "Invalid Stream ID",
205 parse_opaque_param
},
206 { SCTP_ERR_MISSING_PARM
, "", "Missing Parameter",
207 parse_opaque_param
},
208 { SCTP_ERR_STALE_COOKIE
, "", "Stale Cookie",
210 { SCTP_ERR_NO_RESOURCES
, "", "Out Of Resources",
211 parse_opaque_param
},
212 { SCTP_ERR_BAD_ADDR
, "", "Unresolvable Address",
213 parse_opaque_param
},
214 { SCTP_ERR_UNREC_CHUNK
, "", "Unrecognized Chunk",
216 { SCTP_ERR_BAD_MANDPARM
, "", "Bad Mandatory Parameter",
217 parse_opaque_param
},
218 { SCTP_ERR_UNREC_PARM
, "", "Unrecognized Parameter",
219 parse_opaque_param
},
220 { SCTP_ERR_NO_USR_DATA
, "", "No User Data",
222 { SCTP_ERR_COOKIE_SHUT
, "", "Cookie During Shutdown",
223 parse_opaque_param
},
224 { SCTP_ERR_DELETE_LASTADDR
, "", "Delete Last Remaining Address",
225 parse_addiperr_param
},
226 { SCTP_ERR_RESOURCE_SHORTAGE
, "", "Resource Shortage",
227 parse_addiperr_param
},
228 { SCTP_ERR_DELETE_SRCADDR
, "", "Delete Source IP Address",
229 parse_addiperr_param
},
230 { SCTP_ERR_AUTH_ERR
, "", "Not authorized",
231 parse_addiperr_param
}
235 * These are global because the data chunk parser needs them to dispatch
236 * to ULPs. The alternative is to add source and dest port arguments
237 * to every parser, which seems even messier (since *only* the data
238 * chunk parser needs it)...
240 static in_port_t sport
, dport
;
242 /* Summary line miscellany */
244 static char scratch
[MAXLINE
];
245 static char *sumline
;
247 #define SUMAPPEND(fmt) \
248 sumlen -= snprintf fmt; \
249 (void) strlcat(sumline, scratch, sumlen)
251 #define DUMPHEX_MAX 16
253 static const dispatch_t
*
254 lookup_dispatch(int id
, const dispatch_t
*tbl
, int tblsz
)
259 * Try fast lookup first. The common chunks defined in RFC2960
260 * will have indices aligned with their IDs, so this works for
263 if (id
< (tblsz
- 1)) {
264 if (id
== tbl
[id
].id
) {
270 * Nope - probably an extension. Search the whole table,
271 * starting from the end, since extensions are at the end.
273 for (i
= tblsz
- 1; i
>= 0; i
--) {
274 if (id
== tbl
[i
].id
) {
283 * Dumps no more than the first DUMPHEX_MAX bytes in hex. If
284 * the user wants more, they can use the -x option to snoop.
287 dumphex(const uchar_t
*payload
, int payload_len
, char *msg
)
293 if (payload_len
== 0) {
297 end
= payload_len
> DUMPHEX_MAX
? DUMPHEX_MAX
: payload_len
;
299 for (index
= 0; index
< end
; index
++) {
300 (void) snprintf(&buf
[index
* 3], 4, " %.2x", payload
[index
]);
303 if (payload_len
> DUMPHEX_MAX
) {
304 (void) strlcat(buf
, " ...", BUFLEN
);
307 (void) snprintf(get_line(0, 0), BUFLEN
, msg
, buf
);
311 * Present perscribed action for unknowns according to rfc2960. Works
312 * for chunks and parameters as well if the parameter type is
313 * shifted 8 bits right.
316 get_action_desc(uint8_t id
)
318 if ((id
& 0xc0) == 0xc0) {
319 return (": skip on unknown, return error");
320 } else if ((id
& 0x80) == 0x80) {
321 return (": skip on unknown, no error");
322 } else if ((id
& 0x40) == 0x40) {
323 return (": stop on unknown, return error");
326 /* Top two bits are clear */
327 return (": stop on unknown, no error");
332 parse_asconfok_param(int flags
, uint8_t notused
, const void *data
, int dlen
)
336 if (dlen
< sizeof (*cid
)) {
337 (void) snprintf(get_line(0, 0), get_line_remain(),
338 " ==> Incomplete ASCONF Success Ind parameter");
341 cid
= (uint32_t *)data
;
342 (void) snprintf(get_line(0, 0), get_line_remain(), " ASCONF CID = %u",
348 parse_asconferr_param(int flags
, uint8_t notused
, const void *data
, int dlen
)
352 if (dlen
< sizeof (*cid
)) {
353 (void) snprintf(get_line(0, 0), get_line_remain(),
354 " ==> Incomplete ASCONF Error Ind parameter");
357 cid
= (uint32_t *)data
;
358 (void) snprintf(get_line(0, 0), get_line_remain(), " ASCONF CID = %u",
361 interpret_params(cid
+ 1, dlen
- sizeof (*cid
), "Error",
362 err_dispatch_table
, A_CNT(err_dispatch_table
), flags
);
367 parse_addiperr_param(int flags
, uint8_t notused
, const void *data
, int dlen
)
370 interpret_params(data
, dlen
, "Parameter",
371 parm_dispatch_table
, A_CNT(parm_dispatch_table
), flags
);
376 parse_addip_param(int flags
, uint8_t notused
, const void *data
, int dlen
)
381 if (dlen
< sizeof (*cid
)) {
382 (void) snprintf(get_line(0, 0), get_line_remain(),
383 " ==> Incomplete ASCONF Error Ind parameter");
386 cid
= (uint32_t *)data
;
387 (void) snprintf(get_line(0, 0), get_line_remain(), " ASCONF CID = %u",
390 interpret_params(cid
+ 1, dlen
- sizeof (*cid
), "Parameter",
391 parm_dispatch_table
, A_CNT(parm_dispatch_table
), flags
);
396 parse_ip4_param(int flags
, uint8_t notused
, const void *data
, int datalen
)
398 char abuf
[INET_ADDRSTRLEN
];
401 if (datalen
< sizeof (in_addr_t
)) {
402 (void) snprintf(get_line(0, 0), get_line_remain(),
403 " ==> Incomplete IPv4 Addr parameter");
407 ap
= (char *)inet_ntop(AF_INET
, data
, abuf
, INET_ADDRSTRLEN
);
409 ap
= "<Bad Address>";
412 (void) snprintf(get_line(0, 0), get_line_remain(), " Addr = %s", ap
);
417 parse_ip6_param(int flags
, uint8_t notused
, const void *data
, int datalen
)
419 char abuf
[INET6_ADDRSTRLEN
];
422 if (datalen
< sizeof (in6_addr_t
)) {
423 (void) snprintf(get_line(0, 0), get_line_remain(),
424 " ==> Incomplete IPv6 Addr parameter");
428 ap
= (char *)inet_ntop(AF_INET6
, data
, abuf
, INET6_ADDRSTRLEN
);
430 ap
= "<Bad Address>";
433 (void) snprintf(get_line(0, 0), get_line_remain(), " Addr = %s", ap
);
438 parse_int32_param(int flags
, uint8_t notused
, const void *data
, int datalen
)
441 (void) snprintf(get_line(0, 0), get_line_remain(),
442 " ==> Incomplete INT32 parameter");
445 (void) snprintf(get_line(0, 0), get_line_remain(), " INT32 = %u",
446 ntohl(*(uint32_t *)data
));
451 parse_suppaddr_param(int flags
, uint8_t notused
, const void *data
, int dlen
)
453 const uint16_t *type
;
456 (void) snprintf(get_line(0, 0), get_line_remain(),
457 "==> Incomplete Supported Addr parameter");
463 switch (ntohs(*type
)) {
465 (void) snprintf(get_line(0, 0), get_line_remain(),
469 (void) snprintf(get_line(0, 0), get_line_remain(),
472 case PARM_ADDR_HOST_NAME
:
473 (void) snprintf(get_line(0, 0), get_line_remain(),
477 (void) snprintf(get_line(0, 0), get_line_remain(),
478 "Unknown Type (%hu)", ntohs(*type
));
481 dlen
-= sizeof (*type
);
488 parse_encap_param(int flags
, uint8_t notused
, const void *data
, int dlen
)
490 if (dlen
< sizeof (sctp_parm_hdr_t
)) {
491 (void) snprintf(get_line(0, 0), get_line_remain(),
492 "==> Incomplete Parameter");
496 interpret_params(data
, dlen
, "Parameter",
497 parm_dispatch_table
, A_CNT(parm_dispatch_table
), flags
);
502 parse_unrec_chunk(int flags
, uint8_t cflags
, const void *data
, int datalen
)
504 const sctp_chunk_hdr_t
*cp
= data
;
505 const dispatch_t
*dp
;
508 if (datalen
< sizeof (*cp
)) {
509 (void) snprintf(get_line(0, 0), get_line_remain(),
510 "==> Incomplete Unrecognized Chunk Error");
514 /* Maybe snoop knows about this chunk? */
515 dp
= lookup_dispatch(cp
->sch_id
, chunk_dispatch_table
,
516 A_CNT(chunk_dispatch_table
));
518 (void) snprintf(get_line(0, 0), get_line_remain(),
519 " Chunk Type = %u (%s)", cp
->sch_id
, dp
->vdesc
);
521 actstr
= get_action_desc(cp
->sch_id
);
522 (void) snprintf(get_line(0, 0), get_line_remain(),
523 " Chunk Type = %u%s", cp
->sch_id
, actstr
);
528 * Same as parse_opaque_chunk except for the indentation.
532 parse_opaque_param(int flags
, uint8_t cflags
, const void *data
, int datalen
)
534 dumphex(data
, datalen
, " Data = %s");
538 * Loops through all parameters (or errors) until it has read
539 * datalen bytes of information, finding a parser for each.
540 * The tbl argument allows the caller to specify which dispatch
541 * table to use, making this function useful for both parameters
542 * and errors. The type argument is used to denote whether this
543 * is an error or parameter in detailed mode.
546 interpret_params(const void *data
, int datalen
, char *type
,
547 const dispatch_t
*tbl
, int tbl_size
, int flags
)
549 const sctp_parm_hdr_t
*hdr
= data
;
555 const dispatch_t
*dp
;
560 * Adjust for padding: if the address isn't aligned, there
561 * should be some padding. So skip over the padding and
562 * adjust hdr accordingly. RFC2960 mandates that all
563 * parameters must be 32-bit aligned WRT the enclosing chunk,
564 * which ensures that this parameter header will
565 * be 32-bit aligned in memory. We must, of course, bounds
566 * check fraglen before actually trying to use hdr, in
567 * case the packet has been mangled or is the product
568 * of a buggy implementation.
570 if ((pad
= (uintptr_t)hdr
% SCTP_ALIGN
) != 0) {
571 pad
= SCTP_ALIGN
- pad
;
573 /* LINTED pointer cast may result in improper alignment */
574 hdr
= (sctp_parm_hdr_t
*)((char *)hdr
+ pad
);
577 /* Need to compare against 0 1st, since sizeof is unsigned */
578 if (datalen
< 0 || datalen
< sizeof (*hdr
)) {
580 (void) snprintf(get_line(0, 0),
582 "==> Extra data after last parameter");
586 plen
= ntohs(hdr
->sph_len
);
587 if (datalen
< plen
|| plen
< sizeof (*hdr
)) {
588 (void) snprintf(get_line(0, 0), get_line_remain(),
589 " ==> Incomplete %s", type
);
593 /* Get description and parser */
594 ptype
= ntohs(hdr
->sph_type
);
595 desc
= "Unknown Parameter Type";
596 parse
= parse_opaque_param
;
597 dp
= lookup_dispatch(ptype
, tbl
, tbl_size
);
607 actstr
= get_action_desc((uint8_t)(ptype
>> 8));
609 (void) snprintf(get_line(0, 0), get_line_remain(),
610 " ------- SCTP %s Type = %s (%u%s)", type
, desc
, ptype
,
612 (void) snprintf(get_line(0, 0), get_line_remain(),
613 " Data length = %hu", plen
- sizeof (*hdr
));
616 parse(flags
, 0, (char *)(hdr
+ 1),
617 plen
- sizeof (*hdr
));
620 /* LINTED pointer cast may result in improper alignment */
621 hdr
= (sctp_parm_hdr_t
*)((char *)hdr
+ plen
);
627 parse_ftsn_chunk(int flags
, uint8_t cflags
, const void *data
, int datalen
)
630 ftsn_entry_t
*ftsn_entry
;
632 if (datalen
< (sizeof (*ftsn
) + sizeof (*ftsn_entry
))) {
633 if (flags
& F_DTAIL
) {
634 (void) snprintf(get_line(0, 0), get_line_remain(),
635 "==> Incomplete FORWARD-TSN chunk");
640 ftsn
= (uint32_t *)data
;
642 SUMAPPEND((scratch
, MAXLINE
, "CTSN %x ", ntohl(*ftsn
)));
645 (void) snprintf(get_line(0, 0), get_line_remain(), "Cum TSN= %x",
648 datalen
-= sizeof (*ftsn
);
649 ftsn_entry
= (ftsn_entry_t
*)(ftsn
+ 1);
650 while (datalen
>= sizeof (*ftsn_entry
)) {
651 (void) snprintf(get_line(0, 0), get_line_remain(),
652 "SID = %u : SSN = %u", ntohs(ftsn_entry
->ftsn_sid
),
653 ntohs(ftsn_entry
->ftsn_ssn
));
654 datalen
-= sizeof (*ftsn_entry
);
661 parse_asconf_chunk(int flags
, uint8_t cflags
, const void *data
, int datalen
)
665 if (datalen
< sizeof (*sn
)) {
666 if (flags
& F_DTAIL
) {
667 (void) snprintf(get_line(0, 0), get_line_remain(),
668 "==> Incomplete ASCONF chunk");
673 sn
= (uint32_t *)data
;
675 SUMAPPEND((scratch
, MAXLINE
, "sn %x ", ntohl(*sn
)));
678 (void) snprintf(get_line(0, 0), get_line_remain(), "Serial Number= %x",
680 interpret_params(sn
+ 1, datalen
- sizeof (*sn
), "Parameter",
681 parm_dispatch_table
, A_CNT(parm_dispatch_table
), flags
);
685 parse_init_chunk(int flags
, uint8_t cflags
, const void *data
, int datalen
)
687 const sctp_init_chunk_t
*icp
= data
;
689 if (datalen
< sizeof (*icp
)) {
690 if (flags
& F_DTAIL
) {
691 (void) snprintf(get_line(0, 0), get_line_remain(),
692 "==> Incomplete INIT chunk");
698 SUMAPPEND((scratch
, MAXLINE
, "tsn %x str %hu/%hu win %u ",
699 ntohl(icp
->sic_inittsn
), ntohs(icp
->sic_outstr
),
700 ntohs(icp
->sic_instr
), ntohl(icp
->sic_a_rwnd
)));
704 (void) snprintf(get_line(0, 0), get_line_remain(), "Flags = 0x%.2x",
706 (void) snprintf(get_line(0, 0), get_line_remain(),
707 "Initiate tag = 0x%.8x", ntohl(icp
->sic_inittag
));
708 (void) snprintf(get_line(0, 0), get_line_remain(),
709 "Advertised receiver window credit = %u", ntohl(icp
->sic_a_rwnd
));
710 (void) snprintf(get_line(0, 0), get_line_remain(),
711 "Outbound streams = %hu", ntohs(icp
->sic_outstr
));
712 (void) snprintf(get_line(0, 0), get_line_remain(),
713 "Inbound streams = %hu", ntohs(icp
->sic_instr
));
714 (void) snprintf(get_line(0, 0), get_line_remain(),
715 "Initial TSN = 0x%.8x", ntohl(icp
->sic_inittsn
));
717 if (datalen
> sizeof (*icp
)) {
718 interpret_params(icp
+ 1, datalen
- sizeof (*icp
),
719 "Parameter", parm_dispatch_table
,
720 A_CNT(parm_dispatch_table
), flags
);
725 parse_data_chunk(int flags
, uint8_t cflags
, const void *data
, int datalen
)
727 const sctp_data_chunk_t
*dcp
= data
;
731 if (datalen
< sizeof (*dcp
)) {
732 if (flags
& F_DTAIL
) {
733 (void) snprintf(get_line(0, 0), get_line_remain(),
734 "==> Incomplete DATA chunk %d (%d)", datalen
,
740 ppid
= ntohl(dcp
->sdc_payload_id
);
741 /* This is the actual data len, excluding the data chunk header. */
742 datalen
-= sizeof (*dcp
);
744 if (flags
& F_DTAIL
) {
745 (void) snprintf(get_line(0, 0), get_line_remain(),
746 "flags = 0x%.2x", cflags
);
747 (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
748 getflag(cflags
, SCTP_DATA_UBIT
, "unordered", "ordered"));
749 (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
750 getflag(cflags
, SCTP_DATA_BBIT
,
751 "beginning", "(beginning unset)"));
752 (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
753 getflag(cflags
, SCTP_DATA_EBIT
, "end", "(end unset)"));
754 (void) snprintf(get_line(0, 0), get_line_remain(),
755 "TSN = 0x%.8x", ntohl(dcp
->sdc_tsn
));
756 (void) snprintf(get_line(0, 0), get_line_remain(),
757 "Stream ID = %hu", ntohs(dcp
->sdc_sid
));
758 (void) snprintf(get_line(0, 0), get_line_remain(),
759 "Stream Sequence Number = %hu", ntohs(dcp
->sdc_ssn
));
760 (void) snprintf(get_line(0, 0), get_line_remain(),
761 "Payload Protocol ID = 0x%.8x", ppid
);
762 (void) snprintf(get_line(0, 0), get_line_remain(),
763 "Data Length = %d", datalen
);
767 SUMAPPEND((scratch
, MAXLINE
, "len %d tsn %x str %hu/%hu "
768 "ppid %x ", datalen
, ntohl(dcp
->sdc_tsn
),
769 ntohs(dcp
->sdc_sid
), ntohs(dcp
->sdc_ssn
), ppid
));
773 * Go to the next protocol layer, but not if we are in
774 * summary mode only. In summary mode, each ULP parse would
775 * create a new line, and if there were several data chunks
776 * bundled together in the packet, this would confuse snoop's
777 * packet numbering and timestamping.
779 * SCTP carries two ways to determine an ULP: ports and the
780 * payload protocol identifier (ppid). Since ports are the
781 * better entrenched convention, we first try interpret_reserved().
782 * If that fails to find a parser, we try by the PPID.
784 if (!(flags
& F_ALLSUM
) && !(flags
& F_DTAIL
)) {
788 payload
= (char *)(dcp
+ 1);
789 if (!interpret_reserved(flags
, IPPROTO_SCTP
, sport
, dport
, payload
,
790 datalen
) && ppid
!= 0) {
792 interpret_protoid(flags
, ppid
, payload
, datalen
);
796 * Reset the protocol prefix, since it may have been changed
797 * by a ULP interpreter.
799 prot_prefix
= "SCTP: ";
804 parse_sack_chunk(int flags
, uint8_t cflags
, const void *data
, int datalen
)
806 const sctp_sack_chunk_t
*scp
= data
;
807 uint16_t numfrags
, numdups
;
808 sctp_sack_frag_t
*frag
;
812 if (datalen
< sizeof (*scp
)) {
813 if (flags
& F_DTAIL
) {
814 (void) snprintf(get_line(0, 0), get_line_remain(),
815 "==> Incomplete SACK chunk");
820 if (flags
& F_DTAIL
) {
821 (void) snprintf(get_line(0, 0), get_line_remain(),
822 "Cumulative TSN ACK = 0x%.8x", ntohl(scp
->ssc_cumtsn
));
823 (void) snprintf(get_line(0, 0), get_line_remain(),
824 "Advertised Receiver Window Credit = %u",
825 ntohl(scp
->ssc_a_rwnd
));
826 numfrags
= ntohs(scp
->ssc_numfrags
);
827 numdups
= ntohs(scp
->ssc_numdups
);
828 (void) snprintf(get_line(0, 0), get_line_remain(),
829 "Number of Fragments = %hu", numfrags
);
830 (void) snprintf(get_line(0, 0), get_line_remain(),
831 "Number of Duplicates = %hu", numdups
);
833 /* Display any gap reports */
834 datalen
-= sizeof (*scp
);
835 if (datalen
< (numfrags
* sizeof (*frag
))) {
836 (void) snprintf(get_line(0, 0), get_line_remain(),
837 " ==> Malformed gap report listing");
840 frag
= (sctp_sack_frag_t
*)(scp
+ 1);
841 for (i
= 0; i
< numfrags
; i
++) {
842 (void) snprintf(get_line(0, 0), get_line_remain(),
843 " Fragment #%d: Start = %hu, end = %hu", i
,
844 ntohs(frag
->ssf_start
), ntohs(frag
->ssf_end
));
848 /* Display any duplicate reports */
849 datalen
-= numfrags
* sizeof (*frag
);
850 if (datalen
< (numdups
* sizeof (*tsn
))) {
851 (void) snprintf(get_line(0, 0), get_line_remain(),
852 " ==> Malformed duplicate report listing");
855 /* LINTED pointer cast may result in improper alignment */
856 tsn
= (uint32_t *)frag
;
857 for (i
= 0; i
< numdups
; i
++) {
858 (void) snprintf(get_line(0, 0), get_line_remain(),
859 " Duplicate #%d: TSN = %x", i
, *tsn
);
864 SUMAPPEND((scratch
, MAXLINE
,
865 "tsn %x win %u gaps/dups %hu/%hu ", ntohl(scp
->ssc_cumtsn
),
866 ntohl(scp
->ssc_a_rwnd
), ntohs(scp
->ssc_numfrags
),
867 ntohs(scp
->ssc_numdups
)));
873 parse_shutdown_chunk(int flags
, uint8_t cflags
, const void *data
, int datalen
)
875 const uint32_t *cumtsn
= data
;
877 if (datalen
< sizeof (*cumtsn
)) {
878 if (flags
& F_DTAIL
) {
879 (void) snprintf(get_line(0, 0), get_line_remain(),
880 "==> Incomplete Shutdown chunk");
885 if (flags
& F_DTAIL
) {
886 (void) snprintf(get_line(0, 0), get_line_remain(),
887 "Cumulative TSN = 0x%.8x", ntohl(*cumtsn
));
890 SUMAPPEND((scratch
, MAXLINE
, "tsn %x", ntohl(*cumtsn
)));
896 parse_error_chunk(int flags
, uint8_t cflags
, const void *data
, int datalen
)
898 if (!(flags
& F_DTAIL
)) {
902 interpret_params(data
, datalen
, "Error", err_dispatch_table
,
903 A_CNT(err_dispatch_table
), flags
);
907 parse_abort_chunk(int flags
, uint8_t cflags
, const void *data
, int datalen
)
909 if (!(flags
& F_DTAIL
)) {
913 (void) snprintf(get_line(0, 0), get_line_remain(), "flags = 0x%.2x",
915 (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
916 getflag(cflags
, SCTP_TBIT
, "TCB not destroyed", "TCB destroyed"));
918 interpret_params(data
, datalen
, "Error", err_dispatch_table
,
919 A_CNT(err_dispatch_table
), flags
);
924 parse_shutdone_chunk(int flags
, uint8_t cflags
, const void *data
, int datalen
)
926 if (!(flags
& F_DTAIL
)) {
930 (void) snprintf(get_line(0, 0), get_line_remain(), "flags = 0x%.2x",
932 (void) snprintf(get_line(0, 0), get_line_remain(), " %s",
933 getflag(cflags
, SCTP_TBIT
, "TCB not destroyed", "TCB destroyed"));
938 parse_opaque_chunk(int flags
, uint8_t cflags
, const void *data
, int datalen
)
940 if (!(flags
& F_DTAIL
)) {
947 dumphex(data
, datalen
, "Data = %s");
951 * Loops through all chunks until it has read fraglen bytes of
952 * information, finding a parser for each. If any parameters are
953 * present, interpret_params() is then called. Returns the remaining
957 interpret_chunks(int flags
, sctp_chunk_hdr_t
*cp
, int fraglen
)
964 const dispatch_t
*dp
;
969 * Adjust for padding: if the address isn't aligned, there
970 * should be some padding. So skip over the padding and
971 * adjust hdr accordingly. RFC2960 mandates that all
972 * chunks must be 32-bit aligned WRT the SCTP common hdr,
973 * which ensures that this chunk header will
974 * be 32-bit aligned in memory. We must, of course, bounds
975 * check fraglen before actually trying to use hdr, in
976 * case the packet has been mangled or is the product
977 * of a buggy implementation.
979 if ((pad
= (uintptr_t)cp
% SCTP_ALIGN
) != 0) {
980 pad
= SCTP_ALIGN
- pad
;
982 /* LINTED pointer cast may result in improper alignment */
983 cp
= (sctp_chunk_hdr_t
*)((char *)cp
+ pad
);
986 /* Need to compare against 0 1st, since sizeof is unsigned */
987 if (fraglen
< 0 || fraglen
< sizeof (*cp
)) {
988 if (fraglen
> 0 && flags
& F_DTAIL
) {
989 (void) snprintf(get_line(0, 0),
991 "==> Extra data after last chunk");
996 clen
= ntohs(cp
->sch_len
);
997 if (fraglen
< clen
) {
998 if (flags
& F_DTAIL
) {
999 (void) snprintf(get_line(0, 0),
1000 get_line_remain(), "==> Corrupted chunk");
1005 signed_len
= clen
- sizeof (*cp
);
1006 if (signed_len
< 0) {
1007 if (flags
& F_DTAIL
) {
1008 (void) snprintf(get_line(0, 0),
1010 "==> Incomplete or corrupted chunk");
1015 /* Get description and parser */
1016 dp
= lookup_dispatch(cp
->sch_id
, chunk_dispatch_table
,
1017 A_CNT(chunk_dispatch_table
));
1019 if (flags
& F_SUM
) {
1021 } else if (flags
& F_DTAIL
) {
1026 if (flags
& F_SUM
) {
1028 } else if (flags
& F_DTAIL
) {
1029 desc
= "Unknown Chunk Type";
1031 parse
= parse_opaque_chunk
;
1034 if (flags
& F_SUM
) {
1035 SUMAPPEND((scratch
, MAXLINE
, "%s ", desc
));
1037 if (flags
& F_DTAIL
) {
1043 actstr
= get_action_desc(cp
->sch_id
);
1045 (void) snprintf(get_line(0, 0), get_line_remain(),
1046 "------- SCTP Chunk Type = %s (%u%s)", desc
,
1047 cp
->sch_id
, actstr
);
1049 (void) snprintf(get_line(0, 0), get_line_remain(),
1050 "Chunk length = %hu", clen
);
1053 if (parse
!= NULL
) {
1054 parse(flags
, cp
->sch_flags
, (char *)(cp
+ 1),
1060 /* LINTED pointer cast may result in improper alignment */
1061 cp
= (sctp_chunk_hdr_t
*)((char *)cp
+ clen
);
1066 interpret_sctp(int flags
, sctp_hdr_t
*sctp
, int iplen
, int fraglen
)
1069 sctp_chunk_hdr_t
*cp
;
1074 * Alignment check. If the header is 32-bit aligned, all other
1075 * protocol units will also be aligned, as mandated by rfc2960.
1076 * Buggy packets will be caught and flagged by chunk and
1077 * parameter bounds checking.
1078 * If the header is not aligned, however, we drop the packet.
1080 if (!IS_P2ALIGNED(sctp
, SCTP_ALIGN
)) {
1081 if (flags
& F_DTAIL
) {
1082 (void) snprintf(get_line(0, 0), get_line_remain(),
1083 "==> SCTP header not aligned, dropping");
1088 fraglen
-= sizeof (*sctp
);
1090 if (flags
& F_DTAIL
) {
1091 (void) snprintf(get_line(0, 0), get_line_remain(),
1092 "==> Incomplete sctp header");
1096 /* If fraglen is somehow longer than the IP payload, adjust it */
1097 len_from_iphdr
= iplen
- sizeof (*sctp
);
1098 if (fraglen
> len_from_iphdr
) {
1099 fraglen
= len_from_iphdr
;
1102 /* Keep track of the ports */
1103 sport
= ntohs(sctp
->sh_sport
);
1104 dport
= ntohs(sctp
->sh_dport
);
1106 /* Set pointer to first chunk */
1107 cp
= (sctp_chunk_hdr_t
*)(sctp
+ 1);
1109 if (flags
& F_SUM
) {
1110 sumline
= get_sum_line();
1114 SUMAPPEND((scratch
, MAXLINE
, "SCTP D=%d S=%d ", dport
, sport
));
1117 if (flags
& F_DTAIL
) {
1118 show_header("SCTP: ", "SCTP Header", fraglen
);
1121 pn
= getportname(IPPROTO_SCTP
, (ushort_t
)sport
);
1125 (void) snprintf(buff
, sizeof (buff
), "(%s)", pn
);
1128 (void) snprintf(get_line(0, 0), get_line_remain(),
1129 "Source port = %hu %s", sport
, pn
);
1131 pn
= getportname(IPPROTO_SCTP
, (ushort_t
)dport
);
1135 (void) snprintf(buff
, sizeof (buff
), "(%s)", pn
);
1138 (void) snprintf(get_line(0, 0), get_line_remain(),
1139 "Destination port = %hu %s", dport
, pn
);
1140 (void) snprintf(get_line(0, 0), get_line_remain(),
1141 "Verification tag = 0x%.8x", ntohl(sctp
->sh_verf
));
1142 (void) snprintf(get_line(0, 0), get_line_remain(),
1143 "CRC-32c = 0x%.8x", ntohl(sctp
->sh_chksum
));
1146 (void) interpret_chunks(flags
, cp
, fraglen
);
1148 if (flags
& F_DTAIL
) {
1154 * Payload protocol ID table. Add new ULP information and parsers
1158 struct protoid_table
{
1164 static struct protoid_table pid_sctp
[] = {
1165 1, "IUA", "ISDN Q.921 User Adaption Layer",
1166 2, "M2UA", "SS7 MTP2 User Adaption Layer",
1167 3, "M3UA", "SS7 MTP3 User Adaption Layer",
1168 4, "SUA", "SS7 SCCP User Adaption Layer",
1169 5, "M2PA", "SS7 MTP2-User Peer-to-Peer Adaption Layer",
1175 interpret_protoid(int flags
, uint32_t ppid
, char *data
, int dlen
)
1177 struct protoid_table
*p
;
1181 * Branch to a ULP interpreter here, or continue on to
1182 * the default parser, which just tries to display
1183 * printable characters from the payload.
1186 for (p
= pid_sctp
; p
->pid_num
; p
++) {
1187 if (ppid
== p
->pid_num
) {
1188 if (flags
& F_SUM
) {
1189 (void) snprintf(get_sum_line(), MAXLINE
,
1190 "D=%d S=%d %s %s", dport
, sport
,
1191 p
->pid_short
, show_string(data
, dlen
, 20));
1194 if (flags
& F_DTAIL
) {
1195 (void) snprintf(pbuf
, MAXLINE
, "%s: ",
1197 show_header(pbuf
, p
->pid_long
, dlen
);
1199 (void) snprintf(get_line(0, 0),
1200 get_line_remain(), "\"%s\"",
1201 show_string(data
, dlen
, 60));