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.8 (Berkeley) 03/12/91 * 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 /* Enforce Negotation rules below */ 276 if (tpcb->tp_tpdusize > param->p_tpdusize) 277 tpcb->tp_tpdusize = param->p_tpdusize; 278 tpcb->tp_class = param->p_class; 279 if (tpcb->tp_use_checksum || param->p_use_checksum) 280 tpcb->tp_use_checksum = 1; 281 if (!tpcb->tp_xpd_service || !param->p_xpd_service) 282 tpcb->tp_xpd_service = 0; 283 if (!tpcb->tp_xtd_format || !param->p_xtd_format) 284 tpcb->tp_xtd_format = 0; 285 } 286 287 done: 288 289 IFTRACE(D_CONN) 290 tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd", 291 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd); 292 ENDTRACE 293 IFDEBUG(D_CONN) 294 printf( 295 "tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n", 296 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd); 297 ENDDEBUG 298 return error; 299 } 300 301 /* 302 * NAME: tp_ctloutput() 303 * 304 * CALLED FROM: 305 * [sg]etsockopt(), via so[sg]etopt(). 306 * 307 * FUNCTION and ARGUMENTS: 308 * Implements the socket options at transport level. 309 * (cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h). 310 * (so) is the socket. 311 * (level) is SOL_TRANSPORT (see ../sys/socket.h) 312 * (optname) is the particular command or option to be set. 313 * (**mp) is an mbuf structure. 314 * 315 * RETURN VALUE: 316 * ENOTSOCK if the socket hasn't got an associated tpcb 317 * EINVAL if 318 * trying to set window too big 319 * trying to set illegal max tpdu size 320 * trying to set illegal credit fraction 321 * trying to use unknown or unimplemented class of TP 322 * structure passed to set timer values is wrong size 323 * illegal combination of command/GET-SET option, 324 * e.g., GET w/ TPOPT_CDDATA_CLEAR: 325 * EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET 326 * or if the transport-specific command is not implemented 327 * EISCONN if trying a command that isn't allowed after a connection 328 * is established 329 * ENOTCONN if trying a command that is allowed only if a connection is 330 * established 331 * EMSGSIZE if trying to give too much data on connect/disconnect 332 * 333 * SIDE EFFECTS: 334 * 335 * NOTES: 336 */ 337 ProtoHook 338 tp_ctloutput(cmd, so, level, optname, mp) 339 int cmd, level, optname; 340 struct socket *so; 341 struct mbuf **mp; 342 { 343 struct tp_pcb *tpcb = sototpcb(so); 344 int s = splnet(); 345 caddr_t value; 346 unsigned val_len; 347 int error = 0; 348 349 IFTRACE(D_REQUEST) 350 tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp", 351 cmd, so, optname, mp); 352 ENDTRACE 353 IFDEBUG(D_REQUEST) 354 printf( 355 "tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n", 356 so, cmd, optname, mp, mp?*mp:0, tpcb); 357 ENDDEBUG 358 if( tpcb == (struct tp_pcb *)0 ) { 359 error = ENOTSOCK; goto done; 360 } 361 if(*mp == MNULL) { 362 register struct mbuf *m; 363 364 MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */ 365 if (m == NULL) { 366 splx(s); 367 return ENOBUFS; 368 } 369 m->m_len = 0; 370 m->m_act = 0; 371 *mp = m; 372 } 373 374 /* 375 * Hook so one can set network options via a tp socket. 376 */ 377 if ( level == SOL_NETWORK ) { 378 if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL)) 379 error = ENOTSOCK; 380 else if (tpcb->tp_nlproto->nlp_ctloutput == NULL) 381 error = EOPNOTSUPP; 382 else 383 error = (tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname, 384 tpcb->tp_npcb, *mp); 385 goto done; 386 } else if ( level != SOL_TRANSPORT ) { 387 error = EOPNOTSUPP; goto done; 388 } 389 if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) { 390 error = EOPNOTSUPP; goto done; 391 } 392 if ( so->so_error ) { 393 error = so->so_error; goto done; 394 } 395 396 /* The only options allowed after connection is established 397 * are GET (anything) and SET DISC DATA and SET PERF MEAS 398 */ 399 if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED)) 400 && 401 (cmd == PRCO_SETOPT && 402 optname != TPOPT_DISC_DATA && 403 optname != TPOPT_CFRM_DATA && 404 optname != TPOPT_PERF_MEAS && 405 optname != TPOPT_CDDATA_CLEAR ) ) { 406 error = EISCONN; goto done; 407 } 408 /* The only options allowed after disconnection are GET DISC DATA, 409 * and TPOPT_PSTATISTICS 410 * and they're not allowed if the ref timer has gone off, because 411 * the tpcb is gone 412 */ 413 if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) == 0) { 414 if ( so->so_tpcb == (caddr_t)0 ) { 415 error = ENOTCONN; goto done; 416 } 417 if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) && 418 (optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) { 419 error = ENOTCONN; goto done; 420 } 421 } 422 423 value = mtod(*mp, caddr_t); /* it's aligned, don't worry, 424 * but lint complains about it 425 */ 426 val_len = (*mp)->m_len; 427 428 switch (optname) { 429 430 case TPOPT_INTERCEPT: 431 if (error = suser(u.u_cred, &u.u_acflag)) 432 break; 433 else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_LISTENING) 434 error = EINVAL; 435 else { 436 register struct tp_pcb *t = 0; 437 struct mbuf *m = m_getclr(M_WAIT, MT_SONAME); 438 struct sockaddr *sa = mtod(m, struct sockaddr *); 439 (*tpcb->tp_nlproto->nlp_getnetaddr)(tpcb->tp_npcb, m, TP_LOCAL); 440 switch (sa->sa_family) { 441 case AF_ISO: 442 if (((struct sockaddr_iso *)sa)->siso_nlen == 0) 443 default: error = EINVAL; 444 break; 445 case AF_INET: 446 if (((struct sockaddr_in *)sa)->sin_addr.s_addr == 0) 447 error = EINVAL; 448 break; 449 } 450 for (t = tp_intercepts; t; t = t->tp_nextlisten) { 451 if (t->tp_nlproto->nlp_afamily != tpcb->tp_nlproto->nlp_afamily) 452 continue; 453 if ((*t->tp_nlproto->nlp_cmpnetaddr)(t->tp_npcb, sa, TP_LOCAL)) 454 error = EADDRINUSE; 455 } 456 m_freem(m); 457 if (error) 458 break; 459 } 460 { 461 register struct tp_pcb **tt; 462 for (tt = &tp_listeners; *tt; tt = &((*tt)->tp_nextlisten)) 463 if (*tt == tpcb) 464 break; 465 if (*tt) 466 *tt = tpcb->tp_nextlisten; 467 else 468 {error = EHOSTUNREACH; goto done; } 469 } 470 tpcb->tp_nextlisten = tp_intercepts; 471 tp_intercepts = tpcb; 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