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