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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <stdlib.h> 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> 38 #include <string.h> 39 #include "snoop.h" 40 41 /* 42 * Snoop interpreter for SCTP (rfc2960). 43 * 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). 47 */ 48 49 static void interpret_protoid(int, uint32_t, char *, int); 50 extern char *prot_prefix; 51 52 /* 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. 58 */ 59 #define BUFLEN MAXLINE - 64 60 61 /* 62 * Common structure to hold descriptions and parsers for all 63 * chunks, parameters, and errors. Each parser should implement 64 * this interface: 65 * 66 * void parse(int flags, uint8_t cflags, void *data, int datalen); 67 * 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). 72 */ 73 typedef void parse_func_t(int, uint8_t, const void *, int); 74 75 typedef struct { 76 uint16_t id; 77 const char *sdesc; /* short description */ 78 const char *vdesc; /* verbose description */ 79 parse_func_t *parse; /* parser function */ 80 } dispatch_t; 81 82 static void interpret_params(const void *, int, char *, const dispatch_t *, 83 int, int); 84 85 /* 86 * Chunk parsers 87 */ 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, 91 parse_ftsn_chunk; 92 93 94 /* 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 100 * core chunks. 101 */ 102 static const dispatch_t chunk_dispatch_table[] = { 103 /* code F_SUM desc F_DTAIL desc parser function */ 104 { CHUNK_DATA, "Data", "Data Chunk", 105 parse_data_chunk }, 106 { CHUNK_INIT, "Init", "Init Chunk", 107 parse_init_chunk }, 108 { CHUNK_INIT_ACK, "Init ACK", "Init ACK Chunk", 109 parse_init_chunk }, 110 { CHUNK_SACK, "SACK", "SACK Chunk", 111 parse_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", 117 parse_abort_chunk }, 118 { CHUNK_SHUTDOWN, "Shutdown", "Shutdown Chunk", 119 parse_shutdown_chunk }, 120 { CHUNK_SHUTDOWN_ACK, "Shutdown ACK", "Shutdown ACK Chunk", 121 NULL }, 122 { CHUNK_ERROR, "Err", "Error Chunk", 123 parse_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", 135 parse_ftsn_chunk }, 136 { CHUNK_ASCONF_ACK, "ASCONF ACK", "ASCONF ACK Chunk", 137 parse_asconf_chunk }, 138 { CHUNK_ASCONF, "ASCONF", "ASCONF Chunk", 139 parse_asconf_chunk } 140 }; 141 142 /* 143 * Parameter Parsers 144 */ 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; 149 150 /* 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. 158 */ 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", 166 parse_ip4_param }, 167 { PARM_ADDR6, "", "IPv6 Address", 168 parse_ip6_param }, 169 { PARM_COOKIE, "", "Cookie", 170 parse_opaque_param }, 171 { PARM_UNRECOGNIZED, "", "Unrecognized Param", 172 parse_encap_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", 184 parse_addip_param }, 185 { PARM_DEL_IP, "", "Del IP", 186 parse_addip_param }, 187 { PARM_ASCONF_ERROR, "", "ASCONF Error Ind", 188 parse_asconferr_param }, 189 { PARM_PRIMARY_ADDR, "", "Set Primary Address", 190 parse_addip_param }, 191 { PARM_FORWARD_TSN, "", "Forward TSN", 192 NULL }, 193 { PARM_ASCONF_SUCCESS, "", "ASCONF Success Ind", 194 parse_asconfok_param } 195 }; 196 197 /* 198 * Errors have the same wire format at parameters. 199 */ 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", 209 parse_int32_param }, 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", 215 parse_unrec_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", 221 parse_int32_param }, 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 } 232 }; 233 234 /* 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)... 239 */ 240 static in_port_t sport, dport; 241 242 /* Summary line miscellany */ 243 static int sumlen; 244 static char scratch[MAXLINE]; 245 static char *sumline; 246 247 #define SUMAPPEND(fmt) \ 248 sumlen -= snprintf fmt; \ 249 (void) strlcat(sumline, scratch, sumlen) 250 251 #define DUMPHEX_MAX 16 252 253 static const dispatch_t * 254 lookup_dispatch(int id, const dispatch_t *tbl, int tblsz) 255 { 256 int i; 257 258 /* 259 * Try fast lookup first. The common chunks defined in RFC2960 260 * will have indices aligned with their IDs, so this works for 261 * the common case. 262 */ 263 if (id < (tblsz - 1)) { 264 if (id == tbl[id].id) { 265 return (tbl + id); 266 } 267 } 268 269 /* 270 * Nope - probably an extension. Search the whole table, 271 * starting from the end, since extensions are at the end. 272 */ 273 for (i = tblsz - 1; i >= 0; i--) { 274 if (id == tbl[i].id) { 275 return (tbl + i); 276 } 277 } 278 279 return (NULL); 280 } 281 282 /* 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. 285 */ 286 static void 287 dumphex(const uchar_t *payload, int payload_len, char *msg) 288 { 289 int index; 290 int end; 291 char buf[BUFLEN]; 292 293 if (payload_len == 0) { 294 return; 295 } 296 297 end = payload_len > DUMPHEX_MAX ? DUMPHEX_MAX : payload_len; 298 299 for (index = 0; index < end; index++) { 300 (void) snprintf(&buf[index * 3], 4, " %.2x", payload[index]); 301 } 302 303 if (payload_len > DUMPHEX_MAX) { 304 (void) strlcat(buf, " ...", BUFLEN); 305 } 306 307 (void) snprintf(get_line(0, 0), BUFLEN, msg, buf); 308 } 309 310 /* 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. 314 */ 315 static const char * 316 get_action_desc(uint8_t id) 317 { 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"); 324 } 325 326 /* Top two bits are clear */ 327 return (": stop on unknown, no error"); 328 } 329 330 /* ARGSUSED */ 331 static void 332 parse_asconfok_param(int flags, uint8_t notused, const void *data, int dlen) 333 { 334 uint32_t *cid; 335 336 if (dlen < sizeof (*cid)) { 337 (void) snprintf(get_line(0, 0), get_line_remain(), 338 " ==> Incomplete ASCONF Success Ind parameter"); 339 return; 340 } 341 cid = (uint32_t *)data; 342 (void) snprintf(get_line(0, 0), get_line_remain(), " ASCONF CID = %u", 343 ntohl(*cid)); 344 } 345 346 /* ARGSUSED */ 347 static void 348 parse_asconferr_param(int flags, uint8_t notused, const void *data, int dlen) 349 { 350 uint32_t *cid; 351 352 if (dlen < sizeof (*cid)) { 353 (void) snprintf(get_line(0, 0), get_line_remain(), 354 " ==> Incomplete ASCONF Error Ind parameter"); 355 return; 356 } 357 cid = (uint32_t *)data; 358 (void) snprintf(get_line(0, 0), get_line_remain(), " ASCONF CID = %u", 359 ntohl(*cid)); 360 361 interpret_params(cid + 1, dlen - sizeof (*cid), "Error", 362 err_dispatch_table, A_CNT(err_dispatch_table), flags); 363 } 364 365 /* ARGSUSED */ 366 static void 367 parse_addiperr_param(int flags, uint8_t notused, const void *data, int dlen) 368 { 369 370 interpret_params(data, dlen, "Parameter", 371 parm_dispatch_table, A_CNT(parm_dispatch_table), flags); 372 } 373 374 /* ARGSUSED */ 375 static void 376 parse_addip_param(int flags, uint8_t notused, const void *data, int dlen) 377 { 378 379 uint32_t *cid; 380 381 if (dlen < sizeof (*cid)) { 382 (void) snprintf(get_line(0, 0), get_line_remain(), 383 " ==> Incomplete ASCONF Error Ind parameter"); 384 return; 385 } 386 cid = (uint32_t *)data; 387 (void) snprintf(get_line(0, 0), get_line_remain(), " ASCONF CID = %u", 388 ntohl(*cid)); 389 390 interpret_params(cid + 1, dlen - sizeof (*cid), "Parameter", 391 parm_dispatch_table, A_CNT(parm_dispatch_table), flags); 392 } 393 394 /* ARGSUSED */ 395 static void 396 parse_ip4_param(int flags, uint8_t notused, const void *data, int datalen) 397 { 398 char abuf[INET_ADDRSTRLEN]; 399 char *ap; 400 401 if (datalen < sizeof (in_addr_t)) { 402 (void) snprintf(get_line(0, 0), get_line_remain(), 403 " ==> Incomplete IPv4 Addr parameter"); 404 return; 405 } 406 407 ap = (char *)inet_ntop(AF_INET, data, abuf, INET_ADDRSTRLEN); 408 if (ap == NULL) { 409 ap = "<Bad Address>"; 410 } 411 412 (void) snprintf(get_line(0, 0), get_line_remain(), " Addr = %s", ap); 413 } 414 415 /* ARGSUSED */ 416 static void 417 parse_ip6_param(int flags, uint8_t notused, const void *data, int datalen) 418 { 419 char abuf[INET6_ADDRSTRLEN]; 420 char *ap; 421 422 if (datalen < sizeof (in6_addr_t)) { 423 (void) snprintf(get_line(0, 0), get_line_remain(), 424 " ==> Incomplete IPv6 Addr parameter"); 425 return; 426 } 427 428 ap = (char *)inet_ntop(AF_INET6, data, abuf, INET6_ADDRSTRLEN); 429 if (ap == NULL) { 430 ap = "<Bad Address>"; 431 } 432 433 (void) snprintf(get_line(0, 0), get_line_remain(), " Addr = %s", ap); 434 } 435 436 /* ARGSUSED */ 437 static void 438 parse_int32_param(int flags, uint8_t notused, const void *data, int datalen) 439 { 440 if (datalen < 4) { 441 (void) snprintf(get_line(0, 0), get_line_remain(), 442 " ==> Incomplete INT32 parameter"); 443 return; 444 } 445 (void) snprintf(get_line(0, 0), get_line_remain(), " INT32 = %u", 446 ntohl(*(uint32_t *)data)); 447 } 448 449 /* ARGSUSED */ 450 static void 451 parse_suppaddr_param(int flags, uint8_t notused, const void *data, int dlen) 452 { 453 const uint16_t *type; 454 455 if (dlen < 2) { 456 (void) snprintf(get_line(0, 0), get_line_remain(), 457 "==> Incomplete Supported Addr parameter"); 458 return; 459 } 460 461 type = data; 462 while (dlen > 0) { 463 switch (ntohs(*type)) { 464 case PARM_ADDR4: 465 (void) snprintf(get_line(0, 0), get_line_remain(), 466 " IPv4"); 467 break; 468 case PARM_ADDR6: 469 (void) snprintf(get_line(0, 0), get_line_remain(), 470 " IPv6"); 471 break; 472 case PARM_ADDR_HOST_NAME: 473 (void) snprintf(get_line(0, 0), get_line_remain(), 474 " Host Name"); 475 break; 476 default: 477 (void) snprintf(get_line(0, 0), get_line_remain(), 478 "Unknown Type (%hu)", ntohs(*type)); 479 break; 480 } 481 dlen -= sizeof (*type); 482 type++; 483 } 484 } 485 486 /*ARGSUSED*/ 487 static void 488 parse_encap_param(int flags, uint8_t notused, const void *data, int dlen) 489 { 490 if (dlen < sizeof (sctp_parm_hdr_t)) { 491 (void) snprintf(get_line(0, 0), get_line_remain(), 492 "==> Incomplete Parameter"); 493 return; 494 } 495 496 interpret_params(data, dlen, "Parameter", 497 parm_dispatch_table, A_CNT(parm_dispatch_table), flags); 498 } 499 500 /* ARGSUSED */ 501 static void 502 parse_unrec_chunk(int flags, uint8_t cflags, const void *data, int datalen) 503 { 504 const sctp_chunk_hdr_t *cp = data; 505 const dispatch_t *dp; 506 const char *actstr; 507 508 if (datalen < sizeof (*cp)) { 509 (void) snprintf(get_line(0, 0), get_line_remain(), 510 "==> Incomplete Unrecognized Chunk Error"); 511 return; 512 } 513 514 /* Maybe snoop knows about this chunk? */ 515 dp = lookup_dispatch(cp->sch_id, chunk_dispatch_table, 516 A_CNT(chunk_dispatch_table)); 517 if (dp != NULL) { 518 (void) snprintf(get_line(0, 0), get_line_remain(), 519 " Chunk Type = %u (%s)", cp->sch_id, dp->vdesc); 520 } else { 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); 524 } 525 } 526 527 /* 528 * Same as parse_opaque_chunk except for the indentation. 529 */ 530 /* ARGSUSED */ 531 static void 532 parse_opaque_param(int flags, uint8_t cflags, const void *data, int datalen) 533 { 534 dumphex(data, datalen, " Data = %s"); 535 } 536 537 /* 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. 544 */ 545 static void 546 interpret_params(const void *data, int datalen, char *type, 547 const dispatch_t *tbl, int tbl_size, int flags) 548 { 549 const sctp_parm_hdr_t *hdr = data; 550 uint16_t plen; 551 uint16_t ptype; 552 const char *desc; 553 parse_func_t *parse; 554 int pad; 555 const dispatch_t *dp; 556 const char *actstr; 557 558 for (;;) { 559 /* 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. 569 */ 570 if ((pad = (uintptr_t)hdr % SCTP_ALIGN) != 0) { 571 pad = SCTP_ALIGN - pad; 572 datalen -= pad; 573 /* LINTED pointer cast may result in improper alignment */ 574 hdr = (sctp_parm_hdr_t *)((char *)hdr + pad); 575 } 576 577 /* Need to compare against 0 1st, since sizeof is unsigned */ 578 if (datalen < 0 || datalen < sizeof (*hdr)) { 579 if (datalen > 0) { 580 (void) snprintf(get_line(0, 0), 581 get_line_remain(), 582 "==> Extra data after last parameter"); 583 } 584 return; 585 } 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); 590 return; 591 } 592 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); 598 if (dp != NULL) { 599 desc = dp->vdesc; 600 parse = dp->parse; 601 } 602 603 show_space(); 604 if (dp != NULL) { 605 actstr = ""; 606 } else { 607 actstr = get_action_desc((uint8_t)(ptype >> 8)); 608 } 609 (void) snprintf(get_line(0, 0), get_line_remain(), 610 " ------- SCTP %s Type = %s (%u%s)", type, desc, ptype, 611 actstr); 612 (void) snprintf(get_line(0, 0), get_line_remain(), 613 " Data length = %hu", plen - sizeof (*hdr)); 614 615 if (parse != NULL) { 616 parse(flags, 0, (char *)(hdr + 1), 617 plen - sizeof (*hdr)); 618 } 619 datalen -= plen; 620 /* LINTED pointer cast may result in improper alignment */ 621 hdr = (sctp_parm_hdr_t *)((char *)hdr + plen); 622 } 623 } 624 625 /* ARGSUSED */ 626 static void 627 parse_ftsn_chunk(int flags, uint8_t cflags, const void *data, int datalen) 628 { 629 uint32_t *ftsn; 630 ftsn_entry_t *ftsn_entry; 631 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"); 636 } 637 return; 638 } 639 640 ftsn = (uint32_t *)data; 641 if (flags & F_SUM) { 642 SUMAPPEND((scratch, MAXLINE, "CTSN %x ", ntohl(*ftsn))); 643 return; 644 } 645 (void) snprintf(get_line(0, 0), get_line_remain(), "Cum TSN= %x", 646 ntohl(*ftsn)); 647 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); 655 ftsn_entry++; 656 } 657 } 658 659 /* ARGSUSED */ 660 static void 661 parse_asconf_chunk(int flags, uint8_t cflags, const void *data, int datalen) 662 { 663 uint32_t *sn; 664 665 if (datalen < sizeof (*sn)) { 666 if (flags & F_DTAIL) { 667 (void) snprintf(get_line(0, 0), get_line_remain(), 668 "==> Incomplete ASCONF chunk"); 669 } 670 return; 671 } 672 673 sn = (uint32_t *)data; 674 if (flags & F_SUM) { 675 SUMAPPEND((scratch, MAXLINE, "sn %x ", ntohl(*sn))); 676 return; 677 } 678 (void) snprintf(get_line(0, 0), get_line_remain(), "Serial Number= %x", 679 ntohl(*sn)); 680 interpret_params(sn + 1, datalen - sizeof (*sn), "Parameter", 681 parm_dispatch_table, A_CNT(parm_dispatch_table), flags); 682 } 683 684 static void 685 parse_init_chunk(int flags, uint8_t cflags, const void *data, int datalen) 686 { 687 const sctp_init_chunk_t *icp = data; 688 689 if (datalen < sizeof (*icp)) { 690 if (flags & F_DTAIL) { 691 (void) snprintf(get_line(0, 0), get_line_remain(), 692 "==> Incomplete INIT chunk"); 693 } 694 return; 695 } 696 697 if (flags & F_SUM) { 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))); 701 return; 702 } 703 704 (void) snprintf(get_line(0, 0), get_line_remain(), "Flags = 0x%.2x", 705 cflags); 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)); 716 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); 721 } 722 } 723 724 static void 725 parse_data_chunk(int flags, uint8_t cflags, const void *data, int datalen) 726 { 727 const sctp_data_chunk_t *dcp = data; 728 char *payload; 729 uint32_t ppid; 730 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, 735 sizeof (*dcp)); 736 } 737 return; 738 } 739 740 ppid = ntohl(dcp->sdc_payload_id); 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 - sizeof (*dcp)); 762 show_space(); 763 } 764 if (flags & F_SUM) { 765 SUMAPPEND((scratch, MAXLINE, "tsn %x str %hu/%hu ppid %x ", 766 ntohl(dcp->sdc_tsn), ntohs(dcp->sdc_sid), 767 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 - sizeof (*dcp)) && ppid != 0) { 789 790 interpret_protoid(flags, ppid, payload, 791 datalen - sizeof (*dcp)); 792 } 793 794 /* 795 * Reset the protocol prefix, since it may have been changed 796 * by a ULP interpreter. 797 */ 798 prot_prefix = "SCTP: "; 799 } 800 801 /* ARGSUSED */ 802 static void 803 parse_sack_chunk(int flags, uint8_t cflags, const void *data, int datalen) 804 { 805 const sctp_sack_chunk_t *scp = data; 806 uint16_t numfrags, numdups; 807 sctp_sack_frag_t *frag; 808 int i; 809 uint32_t *tsn; 810 811 if (datalen < sizeof (*scp)) { 812 if (flags & F_DTAIL) { 813 (void) snprintf(get_line(0, 0), get_line_remain(), 814 "==> Incomplete SACK chunk"); 815 } 816 return; 817 } 818 819 if (flags & F_DTAIL) { 820 (void) snprintf(get_line(0, 0), get_line_remain(), 821 "Cumulative TSN ACK = 0x%.8x", ntohl(scp->ssc_cumtsn)); 822 (void) snprintf(get_line(0, 0), get_line_remain(), 823 "Advertised Receiver Window Credit = %u", 824 ntohl(scp->ssc_a_rwnd)); 825 numfrags = ntohs(scp->ssc_numfrags); 826 numdups = ntohs(scp->ssc_numdups); 827 (void) snprintf(get_line(0, 0), get_line_remain(), 828 "Number of Fragments = %hu", numfrags); 829 (void) snprintf(get_line(0, 0), get_line_remain(), 830 "Number of Duplicates = %hu", numdups); 831 832 /* Display any gap reports */ 833 datalen -= sizeof (*scp); 834 if (datalen < (numfrags * sizeof (*frag))) { 835 (void) snprintf(get_line(0, 0), get_line_remain(), 836 " ==> Malformed gap report listing"); 837 return; 838 } 839 frag = (sctp_sack_frag_t *)(scp + 1); 840 for (i = 0; i < numfrags; i++) { 841 (void) snprintf(get_line(0, 0), get_line_remain(), 842 " Fragment #%d: Start = %hu, end = %hu", i, 843 ntohs(frag->ssf_start), ntohs(frag->ssf_end)); 844 frag += 1; 845 } 846 847 /* Display any duplicate reports */ 848 datalen -= numfrags * sizeof (*frag); 849 if (datalen < (numdups * sizeof (*tsn))) { 850 (void) snprintf(get_line(0, 0), get_line_remain(), 851 " ==> Malformed duplicate report listing"); 852 return; 853 } 854 /* LINTED pointer cast may result in improper alignment */ 855 tsn = (uint32_t *)frag; 856 for (i = 0; i < numdups; i++) { 857 (void) snprintf(get_line(0, 0), get_line_remain(), 858 " Duplicate #%d: TSN = %x", i, *tsn); 859 tsn++; 860 } 861 } 862 if (flags & F_SUM) { 863 SUMAPPEND((scratch, MAXLINE, 864 "tsn %x win %u gaps/dups %hu/%hu ", ntohl(scp->ssc_cumtsn), 865 ntohl(scp->ssc_a_rwnd), ntohs(scp->ssc_numfrags), 866 ntohs(scp->ssc_numdups))); 867 } 868 } 869 870 /* ARGSUSED */ 871 static void 872 parse_shutdown_chunk(int flags, uint8_t cflags, const void *data, int datalen) 873 { 874 const uint32_t *cumtsn = data; 875 876 if (datalen < sizeof (*cumtsn)) { 877 if (flags & F_DTAIL) { 878 (void) snprintf(get_line(0, 0), get_line_remain(), 879 "==> Incomplete Shutdown chunk"); 880 } 881 return; 882 } 883 884 if (flags & F_DTAIL) { 885 (void) snprintf(get_line(0, 0), get_line_remain(), 886 "Cumulative TSN = 0x%.8x", ntohl(*cumtsn)); 887 } 888 if (flags & F_SUM) { 889 SUMAPPEND((scratch, MAXLINE, "tsn %x", ntohl(*cumtsn))); 890 } 891 } 892 893 /* ARGSUSED */ 894 static void 895 parse_error_chunk(int flags, uint8_t cflags, const void *data, int datalen) 896 { 897 if (!(flags & F_DTAIL)) { 898 return; 899 } 900 901 interpret_params(data, datalen, "Error", err_dispatch_table, 902 A_CNT(err_dispatch_table), flags); 903 } 904 905 static void 906 parse_abort_chunk(int flags, uint8_t cflags, const void *data, int datalen) 907 { 908 if (!(flags & F_DTAIL)) { 909 return; 910 } 911 912 (void) snprintf(get_line(0, 0), get_line_remain(), "flags = 0x%.2x", 913 cflags); 914 (void) snprintf(get_line(0, 0), get_line_remain(), " %s", 915 getflag(cflags, SCTP_TBIT, "TCB not destroyed", "TCB destroyed")); 916 917 interpret_params(data, datalen, "Error", err_dispatch_table, 918 A_CNT(err_dispatch_table), flags); 919 } 920 921 /* ARGSUSED2 */ 922 static void 923 parse_shutdone_chunk(int flags, uint8_t cflags, const void *data, int datalen) 924 { 925 if (!(flags & F_DTAIL)) { 926 return; 927 } 928 929 (void) snprintf(get_line(0, 0), get_line_remain(), "flags = 0x%.2x", 930 cflags); 931 (void) snprintf(get_line(0, 0), get_line_remain(), " %s", 932 getflag(cflags, SCTP_TBIT, "TCB not destroyed", "TCB destroyed")); 933 } 934 935 /* ARGSUSED */ 936 static void 937 parse_opaque_chunk(int flags, uint8_t cflags, const void *data, int datalen) 938 { 939 if (!(flags & F_DTAIL)) { 940 return; 941 } 942 if (datalen == 0) { 943 return; 944 } 945 946 dumphex(data, datalen, "Data = %s"); 947 } 948 949 /* 950 * Loops through all chunks until it has read fraglen bytes of 951 * information, finding a parser for each. If any parameters are 952 * present, interpret_params() is then called. Returns the remaining 953 * fraglen. 954 */ 955 static int 956 interpret_chunks(int flags, sctp_chunk_hdr_t *cp, int fraglen) 957 { 958 uint16_t clen; 959 int signed_len; 960 int pad; 961 const char *desc; 962 parse_func_t *parse; 963 const dispatch_t *dp; 964 const char *actstr; 965 966 for (;;) { 967 /* 968 * Adjust for padding: if the address isn't aligned, there 969 * should be some padding. So skip over the padding and 970 * adjust hdr accordingly. RFC2960 mandates that all 971 * chunks must be 32-bit aligned WRT the SCTP common hdr, 972 * which ensures that this chunk header will 973 * be 32-bit aligned in memory. We must, of course, bounds 974 * check fraglen before actually trying to use hdr, in 975 * case the packet has been mangled or is the product 976 * of a buggy implementation. 977 */ 978 if ((pad = (uintptr_t)cp % SCTP_ALIGN) != 0) { 979 pad = SCTP_ALIGN - pad; 980 fraglen -= pad; 981 /* LINTED pointer cast may result in improper alignment */ 982 cp = (sctp_chunk_hdr_t *)((char *)cp + pad); 983 } 984 985 /* Need to compare against 0 1st, since sizeof is unsigned */ 986 if (fraglen < 0 || fraglen < sizeof (*cp)) { 987 if (fraglen > 0 && flags & F_DTAIL) { 988 (void) snprintf(get_line(0, 0), 989 get_line_remain(), 990 "==> Extra data after last chunk"); 991 } 992 return (fraglen); 993 } 994 995 clen = ntohs(cp->sch_len); 996 if (fraglen < clen) { 997 if (flags & F_DTAIL) { 998 (void) snprintf(get_line(0, 0), 999 get_line_remain(), "==> Corrupted chunk"); 1000 } 1001 return (fraglen); 1002 } 1003 1004 signed_len = clen - sizeof (*cp); 1005 if (signed_len < 0) { 1006 if (flags & F_DTAIL) { 1007 (void) snprintf(get_line(0, 0), 1008 get_line_remain(), 1009 "==> Incomplete or corrupted chunk"); 1010 } 1011 return (0); 1012 } 1013 1014 /* Get description and parser */ 1015 dp = lookup_dispatch(cp->sch_id, chunk_dispatch_table, 1016 A_CNT(chunk_dispatch_table)); 1017 if (dp != NULL) { 1018 if (flags & F_SUM) { 1019 desc = dp->sdesc; 1020 } else if (flags & F_DTAIL) { 1021 desc = dp->vdesc; 1022 } 1023 parse = dp->parse; 1024 } else { 1025 if (flags & F_SUM) { 1026 desc = "UNK"; 1027 } else if (flags & F_DTAIL) { 1028 desc = "Unknown Chunk Type"; 1029 } 1030 parse = parse_opaque_chunk; 1031 } 1032 1033 if (flags & F_SUM) { 1034 SUMAPPEND((scratch, MAXLINE, "%s ", desc)); 1035 } 1036 if (flags & F_DTAIL) { 1037 show_space(); 1038 1039 if (dp != NULL) { 1040 actstr = ""; 1041 } else { 1042 actstr = get_action_desc(cp->sch_id); 1043 } 1044 (void) snprintf(get_line(0, 0), get_line_remain(), 1045 "------- SCTP Chunk Type = %s (%u%s)", desc, 1046 cp->sch_id, actstr); 1047 1048 (void) snprintf(get_line(0, 0), get_line_remain(), 1049 "Chunk length = %hu", clen); 1050 } 1051 1052 if (parse != NULL) { 1053 parse(flags, cp->sch_flags, (char *)(cp + 1), 1054 signed_len); 1055 } 1056 1057 fraglen -= clen; 1058 1059 /* LINTED pointer cast may result in improper alignment */ 1060 cp = (sctp_chunk_hdr_t *)((char *)cp + clen); 1061 } 1062 } 1063 1064 void 1065 interpret_sctp(int flags, sctp_hdr_t *sctp, int iplen, int fraglen) 1066 { 1067 int len_from_iphdr; 1068 sctp_chunk_hdr_t *cp; 1069 char *pn; 1070 char buff[32]; 1071 1072 /* 1073 * Alignment check. If the header is 32-bit aligned, all other 1074 * protocol units will also be aligned, as mandated by rfc2960. 1075 * Buggy packets will be caught and flagged by chunk and 1076 * parameter bounds checking. 1077 * If the header is not aligned, however, we drop the packet. 1078 */ 1079 if (!IS_P2ALIGNED(sctp, SCTP_ALIGN)) { 1080 if (flags & F_DTAIL) { 1081 (void) snprintf(get_line(0, 0), get_line_remain(), 1082 "==> SCTP header not aligned, dropping"); 1083 } 1084 return; 1085 } 1086 1087 fraglen -= sizeof (*sctp); 1088 if (fraglen < 0) { 1089 if (flags & F_DTAIL) { 1090 (void) snprintf(get_line(0, 0), get_line_remain(), 1091 "==> Incomplete sctp header"); 1092 } 1093 return; 1094 } 1095 /* If fraglen is somehow longer than the IP payload, adjust it */ 1096 len_from_iphdr = iplen - sizeof (*sctp); 1097 if (fraglen > len_from_iphdr) { 1098 fraglen = len_from_iphdr; 1099 } 1100 1101 /* Keep track of the ports */ 1102 sport = ntohs(sctp->sh_sport); 1103 dport = ntohs(sctp->sh_dport); 1104 1105 /* Set pointer to first chunk */ 1106 cp = (sctp_chunk_hdr_t *)(sctp + 1); 1107 1108 if (flags & F_SUM) { 1109 sumline = get_sum_line(); 1110 *sumline = '\0'; 1111 sumlen = MAXLINE; 1112 1113 SUMAPPEND((scratch, MAXLINE, "SCTP D=%d S=%d ", dport, sport)); 1114 } 1115 1116 if (flags & F_DTAIL) { 1117 show_header("SCTP: ", "SCTP Header", fraglen); 1118 show_space(); 1119 1120 pn = getportname(IPPROTO_SCTP, (ushort_t)sport); 1121 if (pn == NULL) { 1122 pn = ""; 1123 } else { 1124 (void) snprintf(buff, sizeof (buff), "(%s)", pn); 1125 pn = buff; 1126 } 1127 (void) snprintf(get_line(0, 0), get_line_remain(), 1128 "Source port = %hu %s", sport, pn); 1129 1130 pn = getportname(IPPROTO_SCTP, (ushort_t)dport); 1131 if (pn == NULL) { 1132 pn = ""; 1133 } else { 1134 (void) snprintf(buff, sizeof (buff), "(%s)", pn); 1135 pn = buff; 1136 } 1137 (void) snprintf(get_line(0, 0), get_line_remain(), 1138 "Destination port = %hu %s", dport, pn); 1139 (void) snprintf(get_line(0, 0), get_line_remain(), 1140 "Verification tag = 0x%.8x", ntohl(sctp->sh_verf)); 1141 (void) snprintf(get_line(0, 0), get_line_remain(), 1142 "CRC-32c = 0x%.8x", ntohl(sctp->sh_chksum)); 1143 } 1144 1145 (void) interpret_chunks(flags, cp, fraglen); 1146 1147 if (flags & F_DTAIL) { 1148 show_space(); 1149 } 1150 } 1151 1152 /* 1153 * Payload protocol ID table. Add new ULP information and parsers 1154 * here. 1155 */ 1156 1157 struct protoid_table { 1158 int pid_num; 1159 char *pid_short; 1160 char *pid_long; 1161 }; 1162 1163 static struct protoid_table pid_sctp[] = { 1164 1, "IUA", "ISDN Q.921 User Adaption Layer", 1165 2, "M2UA", "SS7 MTP2 User Adaption Layer", 1166 3, "M3UA", "SS7 MTP3 User Adaption Layer", 1167 4, "SUA", "SS7 SCCP User Adaption Layer", 1168 5, "M2PA", "SS7 MTP2-User Peer-to-Peer Adaption Layer", 1169 6, "V5UA", "V5UA", 1170 0, NULL, "", 1171 }; 1172 1173 static void 1174 interpret_protoid(int flags, uint32_t ppid, char *data, int dlen) 1175 { 1176 struct protoid_table *p; 1177 char pbuf[16]; 1178 1179 /* 1180 * Branch to a ULP interpreter here, or continue on to 1181 * the default parser, which just tries to display 1182 * printable characters from the payload. 1183 */ 1184 1185 for (p = pid_sctp; p->pid_num; p++) { 1186 if (ppid == p->pid_num) { 1187 if (flags & F_SUM) { 1188 (void) snprintf(get_sum_line(), MAXLINE, 1189 "D=%d S=%d %s %s", dport, sport, 1190 p->pid_short, show_string(data, dlen, 20)); 1191 } 1192 1193 if (flags & F_DTAIL) { 1194 (void) snprintf(pbuf, MAXLINE, "%s: ", 1195 p->pid_short); 1196 show_header(pbuf, p->pid_long, dlen); 1197 show_space(); 1198 (void) snprintf(get_line(0, 0), 1199 get_line_remain(), "\"%s\"", 1200 show_string(data, dlen, 60)); 1201 show_trailer(); 1202 } 1203 1204 return; 1205 } 1206 } 1207 } 1208