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