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