1 /*********************************************************** 2 Copyright IBM Corporation 1987 3 4 All Rights Reserved 5 6 Permission to use, copy, modify, and distribute this software and its 7 documentation for any purpose and without fee is hereby granted, 8 provided that the above copyright notice appear in all copies and that 9 both that copyright notice and this permission notice appear in 10 supporting documentation, and that the name of IBM not be 11 used in advertising or publicity pertaining to distribution of the 12 software without specific, written prior permission. 13 14 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 16 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20 SOFTWARE. 21 22 ******************************************************************/ 23 24 /* 25 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison 26 */ 27 /* 28 * ARGO TP 29 * 30 * $Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $ 31 * $Source: /usr/argo/sys/netiso/RCS/tp_output.c,v $ 32 * 33 * In here is tp_ctloutput(), the guy called by [sg]etsockopt(), 34 */ 35 36 #ifndef lint 37 static char *rcsid = "$Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $"; 38 #endif lint 39 40 #include "param.h" 41 #include "systm.h" 42 #include "mbuf.h" 43 #include "protosw.h" 44 #include "socket.h" 45 #include "socketvar.h" 46 #include "errno.h" 47 #include "types.h" 48 #include "time.h" 49 #include "tp_param.h" 50 #include "tp_user.h" 51 #include "tp_stat.h" 52 #include "tp_ip.h" 53 #include "tp_timer.h" 54 #include "argo_debug.h" 55 #include "tp_pcb.h" 56 #include "tp_trace.h" 57 58 #define USERFLAGSMASK_G 0x0f00643b 59 #define USERFLAGSMASK_S 0x0f000432 60 #define TPDUSIZESHIFT 24 61 #define CLASSHIFT 16 62 63 /* 64 * NAME: tp_consistency() 65 * 66 * CALLED FROM: 67 * tp_ctloutput(), tp_input() 68 * 69 * FUNCTION and ARGUMENTS: 70 * Checks the consistency of options and tpdusize with class, 71 * using the parameters passed in via (param). 72 * (cmd) may be TP_STRICT or TP_FORCE or both. 73 * Force means it will set all the values in (tpcb) to those in 74 * the input arguements iff no errors were encountered. 75 * Strict means that no inconsistency will be tolerated. If it's 76 * not used, checksum and tpdusize inconsistencies will be tolerated. 77 * The reason for this is that in some cases, when we're negotiating down 78 * from class 4, these options should be changed but should not 79 * cause negotiation to fail. 80 * 81 * RETURNS 82 * E* or EOK 83 * E* if the various parms aren't ok for a given class 84 * EOK if they are ok for a given class 85 */ 86 87 int 88 tp_consistency( tpcb, cmd, param ) 89 u_int cmd; 90 struct tp_conn_param *param; 91 struct tp_pcb *tpcb; 92 { 93 register int error = EOK; 94 int class_to_use = tp_mask_to_num(param->p_class); 95 96 IFTRACE(D_SETPARAMS) 97 tptrace(TPPTmisc, 98 "tp_consist enter class_to_use dontchange param.class cmd", 99 class_to_use, param->p_dont_change_params, param->p_class, cmd); 100 ENDTRACE 101 IFDEBUG(D_SETPARAMS) 102 printf("tp_consistency %s %s\n", 103 cmd& TP_FORCE? "TP_FORCE": "", 104 cmd& TP_STRICT? "TP_STRICT":""); 105 ENDDEBUG 106 if ((cmd & TP_FORCE) && (param->p_dont_change_params)) { 107 cmd &= ~TP_FORCE; 108 } 109 /* can switch net services within a domain, but 110 * cannot switch domains 111 */ 112 switch( param->p_netservice) { 113 case ISO_CONS: 114 case ISO_CLNS: 115 case ISO_COSNS: 116 /* param->p_netservice in ISO DOMAIN */ 117 if(tpcb->tp_domain != AF_ISO ) { 118 error = EINVAL; goto done; 119 } 120 break; 121 case IN_CLNS: 122 /* param->p_netservice in INET DOMAIN */ 123 if( tpcb->tp_domain != AF_INET ) { 124 error = EINVAL; goto done; 125 } 126 break; 127 /* no others not possible-> netservice is a 2-bit field! */ 128 } 129 130 IFDEBUG(D_SETPARAMS) 131 printf("p_class 0x%x, class_to_use 0x%x\n", param->p_class, 132 class_to_use); 133 ENDDEBUG 134 if((param->p_netservice < 0) || (param->p_netservice > TP_MAX_NETSERVICES)){ 135 error = EINVAL; goto done; 136 } 137 if( (param->p_class & TP_CLASSES_IMPLEMENTED) == 0 ) { 138 error = EINVAL; goto done; 139 } 140 IFDEBUG(D_SETPARAMS) 141 printf("Nretrans 0x%x\n", param->p_Nretrans ); 142 ENDDEBUG 143 if( ( param->p_Nretrans < 1 ) || 144 (param->p_cr_ticks < 1) || (param->p_cc_ticks < 1) ) { 145 /* bad for any class because negot has to be done a la class 4 */ 146 error = EINVAL; goto done; 147 } 148 IFDEBUG(D_SETPARAMS) 149 printf("winsize 0x%x\n", param->p_winsize ); 150 ENDDEBUG 151 if( (param->p_winsize < 128 ) || 152 (param->p_winsize < param->p_tpdusize ) || 153 (param->p_winsize > ((1+SB_MAX)>>2 /* 1/4 of the max */)) ) { 154 error = EINVAL; goto done; 155 } else { 156 if( tpcb->tp_state == TP_CLOSED ) 157 soreserve(tpcb->tp_sock, (u_long)param->p_winsize, 158 (u_long)param->p_winsize); 159 } 160 IFDEBUG(D_SETPARAMS) 161 printf("use_csum 0x%x\n", param->p_use_checksum ); 162 printf("xtd_format 0x%x\n", param->p_xtd_format ); 163 printf("xpd_service 0x%x\n", param->p_xpd_service ); 164 printf("tpdusize 0x%x\n", param->p_tpdusize ); 165 printf("tpcb->flags 0x%x\n", tpcb->tp_flags ); 166 ENDDEBUG 167 switch( class_to_use ) { 168 169 case 0: 170 /* do not use checksums, xtd format, or XPD */ 171 172 if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) { 173 if(cmd & TP_STRICT) { 174 error = EINVAL; 175 } else { 176 param->p_use_checksum = 0; 177 param->p_xtd_format = 0; 178 param->p_xpd_service = 0; 179 } 180 break; 181 } 182 183 if (param->p_tpdusize < TP_MIN_TPDUSIZE) { 184 if(cmd & TP_STRICT) { 185 error = EINVAL; 186 } else { 187 param->p_tpdusize = TP_MIN_TPDUSIZE; 188 } 189 break; 190 } 191 if (param->p_tpdusize > TP0_TPDUSIZE) { 192 if (cmd & TP_STRICT) { 193 error = EINVAL; 194 } else { 195 param->p_tpdusize = TP0_TPDUSIZE; 196 } 197 break; 198 } 199 200 /* connect/disc data not allowed for class 0 */ 201 if (tpcb->tp_ucddata) { 202 if(cmd & TP_STRICT) { 203 error = EINVAL; 204 } else if(cmd & TP_FORCE) { 205 m_freem(tpcb->tp_ucddata); 206 tpcb->tp_ucddata = 0; 207 } 208 } 209 break; 210 211 case 4: 212 IFDEBUG(D_SETPARAMS) 213 printf("dt_ticks 0x%x\n", param->p_dt_ticks ); 214 printf("x_ticks 0x%x\n", param->p_x_ticks ); 215 printf("dr_ticks 0x%x\n", param->p_dr_ticks ); 216 printf("keepalive 0x%x\n", param->p_keepalive_ticks ); 217 printf("sendack 0x%x\n", param->p_sendack_ticks ); 218 printf("inact 0x%x\n", param->p_inact_ticks ); 219 printf("ref 0x%x\n", param->p_ref_ticks ); 220 ENDDEBUG 221 if( (param->p_class & TP_CLASS_4 ) && ( 222 (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) || 223 (param->p_x_ticks < 1) || (param->p_keepalive_ticks < 1) || 224 (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) || 225 (param->p_inact_ticks < 1) ) ) { 226 error = EINVAL; 227 break; 228 } 229 IFDEBUG(D_SETPARAMS) 230 printf("rx_strat 0x%x\n", param->p_rx_strat ); 231 ENDDEBUG 232 if(param->p_rx_strat > 233 ( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) { 234 if(cmd & TP_STRICT) { 235 error = EINVAL; 236 } else { 237 param->p_rx_strat = TPRX_USE_CW; 238 } 239 break; 240 } 241 IFDEBUG(D_SETPARAMS) 242 printf("ack_strat 0x%x\n", param->p_ack_strat ); 243 ENDDEBUG 244 if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) { 245 if(cmd & TP_STRICT) { 246 error = EINVAL; 247 } else { 248 param->p_ack_strat = TPACK_WINDOW; 249 } 250 break; 251 } 252 if (param->p_tpdusize < TP_MIN_TPDUSIZE) { 253 if(cmd & TP_STRICT) { 254 error = EINVAL; 255 } else { 256 param->p_tpdusize = TP_MIN_TPDUSIZE; 257 } 258 break; 259 } 260 if (param->p_tpdusize > TP_TPDUSIZE) { 261 if(cmd & TP_STRICT) { 262 error = EINVAL; 263 } else { 264 param->p_tpdusize = TP_TPDUSIZE; 265 } 266 break; 267 } 268 break; 269 } 270 271 if ((error==0) && (cmd & TP_FORCE)) { 272 tpcb->tp_tpdusize = param->p_tpdusize; 273 tpcb->tp_class = param->p_class; 274 tpcb->tp_use_checksum = param->p_use_checksum; 275 tpcb->tp_xpd_service = param->p_xpd_service; 276 tpcb->tp_xtd_format = param->p_xtd_format; 277 } 278 279 done: 280 281 IFTRACE(D_CONN) 282 tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd", 283 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd); 284 ENDTRACE 285 IFDEBUG(D_CONN) 286 printf( 287 "tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n", 288 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd); 289 ENDDEBUG 290 return error; 291 } 292 293 /* 294 * NAME: tp_ctloutput() 295 * 296 * CALLED FROM: 297 * [sg]etsockopt(), via so[sg]etopt(). 298 * 299 * FUNCTION and ARGUMENTS: 300 * Implements the socket options at transport level. 301 * (cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h). 302 * (so) is the socket. 303 * (level) is SOL_TRANSPORT (see ../sys/socket.h) 304 * (optname) is the particular command or option to be set. 305 * (**mp) is an mbuf structure. 306 * 307 * RETURN VALUE: 308 * ENOTSOCK if the socket hasn't got an associated tpcb 309 * EINVAL if 310 * trying to set window too big 311 * trying to set illegal max tpdu size 312 * trying to set illegal credit fraction 313 * trying to use unknown or unimplemented class of TP 314 * structure passed to set timer values is wrong size 315 * illegal combination of command/GET-SET option, 316 * e.g., GET w/ TPOPT_CDDATA_CLEAR: 317 * EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET 318 * or if the transport-specific command is not implemented 319 * EISCONN if trying a command that isn't allowed after a connection 320 * is established 321 * ENOTCONN if trying a command that is allowed only if a connection is 322 * established 323 * EMSGSIZE if trying to give too much data on connect/disconnect 324 * 325 * SIDE EFFECTS: 326 * 327 * NOTES: 328 */ 329 ProtoHook 330 tp_ctloutput(cmd, so, level, optname, mp) 331 int cmd, level, optname; 332 struct socket *so; 333 struct mbuf **mp; 334 { 335 struct tp_pcb *tpcb = sototpcb(so); 336 int s = splnet(); 337 caddr_t value; 338 unsigned val_len; 339 int error = 0; 340 341 IFTRACE(D_REQUEST) 342 tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp", 343 cmd, so, optname, mp); 344 ENDTRACE 345 IFDEBUG(D_REQUEST) 346 printf( 347 "tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n", 348 so, cmd, optname, mp, mp?*mp:0, tpcb); 349 ENDDEBUG 350 if( tpcb == (struct tp_pcb *)0 ) { 351 error = ENOTSOCK; goto done; 352 } 353 if(*mp == MNULL) { 354 register struct mbuf *m; 355 356 MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */ 357 if (m == NULL) { 358 splx(s); 359 return ENOBUFS; 360 } 361 m->m_len = 0; 362 m->m_act = 0; 363 *mp = m; 364 } 365 366 /* 367 * Hook so one can set network options via a tp socket. 368 */ 369 if ( level == SOL_NETWORK ) { 370 if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL)) 371 error = ENOTSOCK; 372 else if (tpcb->tp_nlproto->nlp_ctloutput == NULL) 373 error = EOPNOTSUPP; 374 else 375 error = (tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname, 376 tpcb->tp_npcb, *mp); 377 goto done; 378 } else if ( level != SOL_TRANSPORT ) { 379 error = EOPNOTSUPP; goto done; 380 } 381 if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) { 382 error = EOPNOTSUPP; goto done; 383 } 384 if ( so->so_error ) { 385 error = so->so_error; goto done; 386 } 387 388 /* The only options allowed after connection is established 389 * are GET (anything) and SET DISC DATA and SET PERF MEAS 390 */ 391 if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED)) 392 && 393 (cmd == PRCO_SETOPT && 394 optname != TPOPT_DISC_DATA && 395 optname != TPOPT_PERF_MEAS && 396 optname != TPOPT_CDDATA_CLEAR ) ) { 397 error = EISCONN; goto done; 398 } 399 /* The only options allowed after disconnection are GET DISC DATA, 400 * and TPOPT_PSTATISTICS 401 * and they're not allowed if the ref timer has gone off, because 402 * the tpcb is gone 403 */ 404 if ((so->so_state & SS_ISCONNECTED) == 0) { 405 if ( so->so_tpcb == (caddr_t)0 ) { 406 error = ENOTCONN; goto done; 407 } 408 if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) && 409 (optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) { 410 error = ENOTCONN; goto done; 411 } 412 } 413 414 value = mtod(*mp, caddr_t); /* it's aligned, don't worry, 415 * but lint complains about it 416 */ 417 val_len = (*mp)->m_len; 418 419 switch (optname) { 420 421 case TPOPT_MY_TSEL: 422 if ( cmd == PRCO_GETOPT ) { 423 ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN ); 424 bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen); 425 (*mp)->m_len = tpcb->tp_lsuffixlen; 426 } else /* cmd == PRCO_SETOPT */ { 427 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) { 428 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp)); 429 error = EINVAL; 430 } else { 431 bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len); 432 tpcb->tp_lsuffixlen = val_len; 433 } 434 } 435 break; 436 437 case TPOPT_PEER_TSEL: 438 if ( cmd == PRCO_GETOPT ) { 439 ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN ); 440 bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen); 441 (*mp)->m_len = tpcb->tp_fsuffixlen; 442 } else /* cmd == PRCO_SETOPT */ { 443 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) { 444 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp)); 445 error = EINVAL; 446 } else { 447 bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len); 448 tpcb->tp_fsuffixlen = val_len; 449 } 450 } 451 break; 452 453 case TPOPT_FLAGS: 454 IFDEBUG(D_REQUEST) 455 printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n", 456 cmd==PRCO_GETOPT?"GET":"SET", 457 value, 458 *value, 459 tpcb->tp_flags); 460 ENDDEBUG 461 462 if ( cmd == PRCO_GETOPT ) { 463 *(int *)value = (int)tpcb->tp_flags; 464 (*mp)->m_len = sizeof(u_int); 465 } else /* cmd == PRCO_SETOPT */ { 466 error = EINVAL; goto done; 467 } 468 break; 469 470 case TPOPT_PARAMS: 471 /* This handles: 472 * timer values, 473 * class, use of transport expedited data, 474 * max tpdu size, checksum, xtd format and 475 * disconnect indications, and may get rid of connect/disc data 476 */ 477 IFDEBUG(D_SETPARAMS) 478 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value, 479 cmd==PRCO_GETOPT?"GET":"SET"); 480 ENDDEBUG 481 IFDEBUG(D_REQUEST) 482 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value, 483 cmd==PRCO_GETOPT?"GET":"SET"); 484 ENDDEBUG 485 486 if ( cmd == PRCO_GETOPT ) { 487 *(struct tp_conn_param *)value = tpcb->_tp_param; 488 (*mp)->m_len = sizeof(tpcb->_tp_param); 489 } else /* cmd == PRCO_SETOPT */ { 490 if( (error = 491 tp_consistency(tpcb, TP_STRICT | TP_FORCE, 492 (struct tp_conn_param *)value))==0) { 493 /* 494 * tp_consistency doesn't copy the whole set of params 495 */ 496 tpcb->_tp_param = *(struct tp_conn_param *)value; 497 (*mp)->m_len = sizeof(tpcb->_tp_param); 498 } 499 } 500 break; 501 502 case TPOPT_PSTATISTICS: 503 #ifdef TP_PERF_MEAS 504 if (cmd == PRCO_SETOPT) { 505 error = EINVAL; goto done; 506 } 507 IFPERF(tpcb) 508 if (*mp) { 509 struct mbuf * n; 510 do { 511 MFREE(*mp, n); 512 *mp = n; 513 } while (n); 514 } 515 *mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK); 516 ENDPERF 517 else { 518 error = EINVAL; goto done; 519 } 520 break; 521 #else 522 error = EOPNOTSUPP; 523 goto done; 524 #endif TP_PERF_MEAS 525 526 case TPOPT_CDDATA_CLEAR: 527 if (cmd == PRCO_GETOPT) { 528 error = EINVAL; 529 } else { 530 if (tpcb->tp_ucddata) { 531 m_freem(tpcb->tp_ucddata); 532 tpcb->tp_ucddata = 0; 533 } 534 } 535 break; 536 537 case TPOPT_DISC_DATA: 538 /* drop through */ 539 /* sending is for debugging purposes only -- we don't pretend 540 * to support * data on connect or disconnect fully. It's a 541 * kludge at best. 542 * This data-on-connect is only for the active side. It's sort of 543 * meaningless on the passive side (because 544 * you can't reject a connect request based on the data 545 * arriving w/ the CR, this, and because you'd have to 546 * do this setsockopt system call for each accept). 547 * but you can use it if you want. 548 */ 549 case TPOPT_CONN_DATA: 550 if( tpcb->tp_class == TP_CLASS_0 ) { 551 error = EOPNOTSUPP; 552 break; 553 } 554 IFDEBUG(D_REQUEST) 555 printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data"); 556 printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n", 557 (*mp)->m_len, val_len, so->so_snd.sb_cc); 558 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd "); 559 ENDDEBUG 560 if (cmd == PRCO_SETOPT) { 561 int len = tpcb->tp_ucddata ? tpcb->tp_ucddata->m_len : 0; 562 /* can append connect data in several calls */ 563 if (len + val_len > 564 (optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) { 565 error = EMSGSIZE; goto done; 566 } 567 (*mp)->m_next = MNULL; 568 (*mp)->m_act = 0; 569 if (tpcb->tp_ucddata) 570 m_cat(tpcb->tp_ucddata, *mp); 571 else 572 tpcb->tp_ucddata = *mp; 573 IFDEBUG(D_REQUEST) 574 dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA"); 575 ENDDEBUG 576 IFTRACE(D_REQUEST) 577 tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len", 578 tpcb->tp_flags, so->so_snd.sb_cc,val_len,0); 579 ENDTRACE 580 *mp = MNULL; /* prevent sosetopt from freeing it! */ 581 } 582 break; 583 584 case TPOPT_PERF_MEAS: 585 #ifdef TP_PERF_MEAS 586 if (cmd == PRCO_GETOPT) { 587 *value = (u_int)tpcb->tp_perf_on; 588 (*mp)->m_len = sizeof(u_int); 589 } else if (cmd == PRCO_SETOPT) { 590 (*mp)->m_len = 0; 591 if ((*value) != 0 && (*value) != 1 ) 592 error = EINVAL; 593 else tpcb->tp_perf_on = (*value); 594 } 595 if( tpcb->tp_perf_on ) 596 error = tp_setup_perf(tpcb); 597 #else TP_PERF_MEAS 598 error = EOPNOTSUPP; 599 #endif TP_PERF_MEAS 600 break; 601 602 default: 603 error = EOPNOTSUPP; 604 } 605 606 done: 607 IFDEBUG(D_REQUEST) 608 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end"); 609 dump_mbuf(*mp, "tp_ctloutput *mp"); 610 ENDDEBUG 611 /* 612 * sigh: getsockopt looks only at m_len : all output data must 613 * reside in the first mbuf 614 */ 615 if ( error && (*mp) != MNULL ) 616 (*mp)->m_len = 0; 617 if( (*mp) != MNULL ) { 618 ASSERT ( m_compress(*mp, mp) <= MLEN ); 619 IFDEBUG(D_REQUEST) 620 dump_mbuf(*mp, "tp_ctloutput *mp after compress"); 621 ENDDEBUG 622 } 623 624 splx(s); 625 return error; 626 } 627