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 "../netiso/tp_param.h" 50 #include "../netiso/tp_user.h" 51 #include "../netiso/tp_stat.h" 52 #include "../netiso/tp_ip.h" 53 #include "../netiso/tp_timer.h" 54 #include "../netiso/argo_debug.h" 55 #include "../netiso/tp_pcb.h" 56 #include "../netiso/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, param->p_winsize, param->p_winsize); 158 } 159 IFDEBUG(D_SETPARAMS) 160 printf("use_csum 0x%x\n", param->p_use_checksum ); 161 printf("xtd_format 0x%x\n", param->p_xtd_format ); 162 printf("xpd_service 0x%x\n", param->p_xpd_service ); 163 printf("tpdusize 0x%x\n", param->p_tpdusize ); 164 printf("tpcb->flags 0x%x\n", tpcb->tp_flags ); 165 ENDDEBUG 166 switch( class_to_use ) { 167 168 case 0: 169 /* do not use checksums, xtd format, or XPD */ 170 171 if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) { 172 if(cmd & TP_STRICT) { 173 error = EINVAL; 174 } else { 175 param->p_use_checksum = 0; 176 param->p_xtd_format = 0; 177 param->p_xpd_service = 0; 178 } 179 break; 180 } 181 182 if (param->p_tpdusize < TP_MIN_TPDUSIZE) { 183 if(cmd & TP_STRICT) { 184 error = EINVAL; 185 } else { 186 param->p_tpdusize = TP_MIN_TPDUSIZE; 187 } 188 break; 189 } 190 if (param->p_tpdusize > TP0_TPDUSIZE) { 191 if (cmd & TP_STRICT) { 192 error = EINVAL; 193 } else { 194 param->p_tpdusize = TP0_TPDUSIZE; 195 } 196 break; 197 } 198 199 /* connect/disc data not allowed for class 0 */ 200 if ( tpcb->tp_flags & 201 (TPF_CONN_DATA_OUT | TPF_DISC_DATA_OUT) ) { 202 if(cmd & TP_STRICT) { 203 error = EINVAL; 204 } else if(cmd & TP_FORCE) { 205 sbdrop(&tpcb->tp_sock->so_snd, tpcb->tp_sock->so_snd.sb_cc); 206 tpcb->tp_flags &= 207 ~(TPF_CONN_DATA_OUT | TPF_DISC_DATA_OUT); 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 ../h/protosw.h). 303 * (so) is the socket. 304 * (level) is SOL_TRANSPORT (see ../h/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 u_char *value; 339 int 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_PERF_MEAS && 397 optname != TPOPT_CDDATA_CLEAR ) ) { 398 error = EISCONN; goto done; 399 } 400 /* The only options allowed after disconnection are GET DISC DATA, 401 * and TPOPT_PSTATISTICS 402 * and they're not allowed if the ref timer has gone off, because 403 * the tpcb is gone 404 */ 405 if ((so->so_state & SS_ISCONNECTED) == 0) { 406 if ( so->so_tpcb == (caddr_t)0 ) { 407 error = ENOTCONN; goto done; 408 } 409 if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) && 410 (optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) { 411 error = ENOTCONN; goto done; 412 } 413 } 414 415 value = (u_char *) mtod(*mp, u_char *); /* it's aligned, don't worry, 416 * but lint complains about it 417 */ 418 val_len = (*mp)->m_len; 419 420 switch (optname) { 421 422 case TPOPT_MY_TSEL: 423 if ( cmd == PRCO_GETOPT ) { 424 ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN ); 425 bcopy( tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen); 426 (*mp)->m_len = tpcb->tp_lsuffixlen; 427 } else /* cmd == PRCO_SETOPT */ { 428 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) { 429 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp)); 430 error = EINVAL; 431 } else { 432 bcopy( value, tpcb->tp_lsuffix, val_len ); 433 tpcb->tp_lsuffixlen = val_len; 434 } 435 } 436 break; 437 438 case TPOPT_PEER_TSEL: 439 if ( cmd == PRCO_GETOPT ) { 440 ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN ); 441 bcopy( tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen); 442 (*mp)->m_len = tpcb->tp_fsuffixlen; 443 } else /* cmd == PRCO_SETOPT */ { 444 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) { 445 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp)); 446 error = EINVAL; 447 } else { 448 bcopy( value, tpcb->tp_fsuffix, val_len ); 449 tpcb->tp_fsuffixlen = val_len; 450 } 451 } 452 break; 453 454 case TPOPT_FLAGS: 455 IFDEBUG(D_REQUEST) 456 printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n", 457 cmd==PRCO_GETOPT?"GET":"SET", 458 value, 459 *value, 460 tpcb->tp_flags); 461 ENDDEBUG 462 463 if ( cmd == PRCO_GETOPT ) { 464 *(int *)value = (int)tpcb->tp_flags; 465 (*mp)->m_len = sizeof(u_int); 466 } else /* cmd == PRCO_SETOPT */ { 467 error = EINVAL; goto done; 468 } 469 break; 470 471 case TPOPT_PARAMS: 472 /* This handles: 473 * timer values, 474 * class, use of transport expedited data, 475 * max tpdu size, checksum, xtd format and 476 * disconnect indications, and may get rid of connect/disc data 477 */ 478 IFDEBUG(D_SETPARAMS) 479 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value, 480 cmd==PRCO_GETOPT?"GET":"SET"); 481 ENDDEBUG 482 IFDEBUG(D_REQUEST) 483 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value, 484 cmd==PRCO_GETOPT?"GET":"SET"); 485 ENDDEBUG 486 487 if ( cmd == PRCO_GETOPT ) { 488 *(struct tp_conn_param *)value = tpcb->_tp_param; 489 (*mp)->m_len = sizeof(tpcb->_tp_param); 490 } else /* cmd == PRCO_SETOPT */ { 491 if( (error = 492 tp_consistency(tpcb, TP_STRICT | TP_FORCE, 493 (struct tp_conn_param *)value))==0) { 494 /* 495 * tp_consistency doesn't copy the whole set of params 496 */ 497 tpcb->_tp_param = *(struct tp_conn_param *)value; 498 (*mp)->m_len = sizeof(tpcb->_tp_param); 499 } 500 } 501 break; 502 503 case TPOPT_PSTATISTICS: 504 #ifdef TP_PERF_MEAS 505 if (cmd == PRCO_SETOPT) { 506 error = EINVAL; goto done; 507 } 508 IFPERF(tpcb) 509 /* tp_p_meas is a cluster : "copy" it */ 510 mclrefcnt[mtocl( (tpcb->tp_p_meas) )]++; 511 (*mp)->m_off = (u_long)((int)tpcb->tp_p_meas - (int)(*mp)); 512 (*mp)->m_len = sizeof(struct tp_pmeas); 513 ENDPERF 514 else { 515 error = EINVAL; goto done; 516 } 517 break; 518 #else 519 error = EOPNOTSUPP; 520 goto done; 521 #endif TP_PERF_MEAS 522 523 case TPOPT_CDDATA_CLEAR: 524 if (cmd == PRCO_GETOPT) { 525 error = EINVAL; 526 } else { 527 if ( tpcb->tp_flags & (TPF_CONN_DATA_OUT | TPF_DISC_DATA_OUT) ) { 528 sbdrop(&so->so_snd, so->so_snd.sb_cc); 529 tpcb->tp_flags &= ~(TPF_CONN_DATA_OUT | TPF_DISC_DATA_OUT); 530 } 531 } 532 break; 533 534 case TPOPT_DISC_DATA: 535 /* drop through */ 536 /* sending is for debugging purposes only -- we don't pretend 537 * to support * data on connect or disconnect fully. It's a 538 * kludge at best. 539 * This data-on-connect is only for the active side. It's sort of 540 * meaningless on the passive side (because 541 * you can't reject a connect request based on the data 542 * arriving w/ the CR, this, and because you'd have to 543 * do this setsockopt system call for each accept). 544 * but you can use it if you want. 545 */ 546 case TPOPT_CONN_DATA: 547 if( tpcb->tp_class == TP_CLASS_0 ) { 548 error = EOPNOTSUPP; 549 break; 550 } 551 IFDEBUG(D_REQUEST) 552 printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data"); 553 printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n", 554 (*mp)->m_len, val_len, so->so_snd.sb_cc); 555 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd "); 556 dump_mbuf(tpcb->tp_Xrcv.sb_mb, "tp_ctlout: tpXrcv "); 557 ENDDEBUG 558 if (cmd == PRCO_SETOPT) { 559 /* can append connect data in several calls */ 560 if (so->so_snd.sb_cc + val_len > 561 (optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) { 562 error = EMSGSIZE; goto done; 563 } 564 tpcb->tp_flags |= 565 ((optname==TPOPT_CONN_DATA)?TPF_CONN_DATA_OUT:TPF_DISC_DATA_OUT); 566 (*mp)->m_next = MNULL; 567 (*mp)->m_act = 0; 568 sbappendrecord( &so->so_snd, *mp); 569 IFDEBUG(D_REQUEST) 570 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput after sbappendrecord"); 571 ENDDEBUG 572 IFTRACE(D_REQUEST) 573 tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len", 574 tpcb->tp_flags, so->so_snd.sb_cc,val_len,0); 575 ENDTRACE 576 *mp = MNULL; /* prevent sosetopt from freeing it! */ 577 } else /* cmd == PRCO_GETOPT */ { 578 register int len = tpcb->tp_Xrcv.sb_cc; 579 580 /* getsockopt() allocated an mbuf but it's a whole lot easier 581 * to do an m_copy than to explicitly copy from the socket buf 582 * into the buffer provided by getsockopt() 583 */ 584 IFDEBUG(D_REQUEST) 585 dump_mbuf(tpcb->tp_Xrcv.sb_mb, 586 "tp_ctlout: tpXrcv before sbdrop"); 587 ENDDEBUG 588 if(len) { 589 (void) m_freem(*mp); 590 *mp = m_copy( tpcb->tp_Xrcv.sb_mb, 0, len); 591 if( *mp != MNULL ) { 592 (*mp)->m_act = 0; 593 sbdrop( &tpcb->tp_Xrcv, len); 594 } else { 595 error = ENOBUFS; 596 } 597 } else { 598 (*mp)->m_len = 0; 599 } 600 IFDEBUG(D_REQUEST) 601 dump_mbuf(tpcb->tp_Xrcv.sb_mb, 602 "tp_ctlout: tpXrcv after sbdrop"); 603 ENDDEBUG 604 /* a potential problem here is that REAL expedited may have arrived 605 * after the data-on-connect 606 * however, this presently works because incoming XPD_TPDUs are 607 * dropped if tp_Xrcv.sb_cc != 0 608 */ 609 610 if( tpcb->tp_Xrcv.sb_cc == 0) 611 tpcb->tp_flags &= 612 optname == TPOPT_CONN_DATA?~TPF_CONN_DATA_IN:~TPF_DISC_DATA_IN; 613 } 614 break; 615 616 case TPOPT_PERF_MEAS: 617 #ifdef TP_PERF_MEAS 618 if (cmd == PRCO_GETOPT) { 619 *value = (u_int)tpcb->tp_perf_on; 620 (*mp)->m_len = sizeof(u_int); 621 } else if (cmd == PRCO_SETOPT) { 622 (*mp)->m_len = 0; 623 if ((*value) != 0 && (*value) != 1 ) 624 error = EINVAL; 625 else tpcb->tp_perf_on = (*value); 626 } 627 if( tpcb->tp_perf_on ) 628 error = tp_setup_perf(tpcb); 629 #else TP_PERF_MEAS 630 error = EOPNOTSUPP; 631 #endif TP_PERF_MEAS 632 break; 633 634 default: 635 error = EOPNOTSUPP; 636 } 637 638 done: 639 IFDEBUG(D_REQUEST) 640 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end"); 641 dump_mbuf(*mp, "tp_ctloutput *mp"); 642 ENDDEBUG 643 /* 644 * sigh: getsockopt looks only at m_len : all output data must 645 * reside in the first mbuf 646 */ 647 if ( error && (*mp) != MNULL ) 648 (*mp)->m_len = 0; 649 if( (*mp) != MNULL ) { 650 ASSERT ( m_compress(*mp, mp) <= MLEN ); 651 IFDEBUG(D_REQUEST) 652 dump_mbuf(*mp, "tp_ctloutput *mp after compress"); 653 ENDDEBUG 654 } 655 656 splx(s); 657 return error; 658 } 659