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