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