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