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.15 (Berkeley) 12/17/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("use_csum 0x%x\n", param->p_use_checksum ); 154 printf("xtd_format 0x%x\n", param->p_xtd_format ); 155 printf("xpd_service 0x%x\n", param->p_xpd_service ); 156 printf("tpdusize 0x%x\n", param->p_tpdusize ); 157 printf("tpcb->flags 0x%x\n", tpcb->tp_flags ); 158 ENDDEBUG 159 switch( class_to_use ) { 160 161 case 0: 162 /* do not use checksums, xtd format, or XPD */ 163 164 if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) { 165 if(cmd & TP_STRICT) { 166 error = EINVAL; 167 } else { 168 param->p_use_checksum = 0; 169 param->p_xtd_format = 0; 170 param->p_xpd_service = 0; 171 } 172 break; 173 } 174 175 if (param->p_tpdusize < TP_MIN_TPDUSIZE) { 176 if(cmd & TP_STRICT) { 177 error = EINVAL; 178 } else { 179 param->p_tpdusize = TP_MIN_TPDUSIZE; 180 } 181 break; 182 } 183 if (param->p_tpdusize > TP0_TPDUSIZE) { 184 if (cmd & TP_STRICT) { 185 error = EINVAL; 186 } else { 187 param->p_tpdusize = TP0_TPDUSIZE; 188 } 189 break; 190 } 191 192 /* connect/disc data not allowed for class 0 */ 193 if (tpcb->tp_ucddata) { 194 if(cmd & TP_STRICT) { 195 error = EINVAL; 196 } else if(cmd & TP_FORCE) { 197 m_freem(tpcb->tp_ucddata); 198 tpcb->tp_ucddata = 0; 199 } 200 } 201 break; 202 203 case 4: 204 IFDEBUG(D_SETPARAMS) 205 printf("dt_ticks 0x%x\n", param->p_dt_ticks ); 206 printf("x_ticks 0x%x\n", param->p_x_ticks ); 207 printf("dr_ticks 0x%x\n", param->p_dr_ticks ); 208 printf("keepalive 0x%x\n", param->p_keepalive_ticks ); 209 printf("sendack 0x%x\n", param->p_sendack_ticks ); 210 printf("inact 0x%x\n", param->p_inact_ticks ); 211 printf("ref 0x%x\n", param->p_ref_ticks ); 212 ENDDEBUG 213 if( (param->p_class & TP_CLASS_4 ) && ( 214 (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) || 215 (param->p_x_ticks < 1) || (param->p_keepalive_ticks < 1) || 216 (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) || 217 (param->p_inact_ticks < 1) ) ) { 218 error = EINVAL; 219 break; 220 } 221 IFDEBUG(D_SETPARAMS) 222 printf("rx_strat 0x%x\n", param->p_rx_strat ); 223 ENDDEBUG 224 if(param->p_rx_strat > 225 ( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) { 226 if(cmd & TP_STRICT) { 227 error = EINVAL; 228 } else { 229 param->p_rx_strat = TPRX_USE_CW; 230 } 231 break; 232 } 233 IFDEBUG(D_SETPARAMS) 234 printf("ack_strat 0x%x\n", param->p_ack_strat ); 235 ENDDEBUG 236 if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) { 237 if(cmd & TP_STRICT) { 238 error = EINVAL; 239 } else { 240 param->p_ack_strat = TPACK_WINDOW; 241 } 242 break; 243 } 244 if (param->p_tpdusize < TP_MIN_TPDUSIZE) { 245 if(cmd & TP_STRICT) { 246 error = EINVAL; 247 } else { 248 param->p_tpdusize = TP_MIN_TPDUSIZE; 249 } 250 break; 251 } 252 if (param->p_tpdusize > TP_TPDUSIZE) { 253 if(cmd & TP_STRICT) { 254 error = EINVAL; 255 } else { 256 param->p_tpdusize = TP_TPDUSIZE; 257 } 258 break; 259 } 260 break; 261 } 262 263 if ((error==0) && (cmd & TP_FORCE)) { 264 long dusize = ((long)param->p_ptpdusize) << 7; 265 /* Enforce Negotation rules below */ 266 tpcb->tp_class = param->p_class; 267 if (tpcb->tp_use_checksum || param->p_use_checksum) 268 tpcb->tp_use_checksum = 1; 269 if (!tpcb->tp_xpd_service || !param->p_xpd_service) 270 tpcb->tp_xpd_service = 0; 271 if (!tpcb->tp_xtd_format || !param->p_xtd_format) 272 tpcb->tp_xtd_format = 0; 273 if (dusize) { 274 if (tpcb->tp_l_tpdusize > dusize) 275 tpcb->tp_l_tpdusize = dusize; 276 if (tpcb->tp_ptpdusize == 0 || 277 tpcb->tp_ptpdusize > param->p_ptpdusize) 278 tpcb->tp_ptpdusize = param->p_ptpdusize; 279 } else { 280 if (param->p_tpdusize != 0 && 281 tpcb->tp_tpdusize > param->p_tpdusize) 282 tpcb->tp_tpdusize = param->p_tpdusize; 283 tpcb->tp_l_tpdusize = 1 << tpcb->tp_tpdusize; 284 } 285 } 286 done: 287 288 IFTRACE(D_CONN) 289 tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd", 290 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd); 291 ENDTRACE 292 IFDEBUG(D_CONN) 293 printf( 294 "tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n", 295 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd); 296 ENDDEBUG 297 return error; 298 } 299 300 /* 301 * NAME: tp_ctloutput() 302 * 303 * CALLED FROM: 304 * [sg]etsockopt(), via so[sg]etopt(). 305 * 306 * FUNCTION and ARGUMENTS: 307 * Implements the socket options at transport level. 308 * (cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h). 309 * (so) is the socket. 310 * (level) is SOL_TRANSPORT (see ../sys/socket.h) 311 * (optname) is the particular command or option to be set. 312 * (**mp) is an mbuf structure. 313 * 314 * RETURN VALUE: 315 * ENOTSOCK if the socket hasn't got an associated tpcb 316 * EINVAL if 317 * trying to set window too big 318 * trying to set illegal max tpdu size 319 * trying to set illegal credit fraction 320 * trying to use unknown or unimplemented class of TP 321 * structure passed to set timer values is wrong size 322 * illegal combination of command/GET-SET option, 323 * e.g., GET w/ TPOPT_CDDATA_CLEAR: 324 * EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET 325 * or if the transport-specific command is not implemented 326 * EISCONN if trying a command that isn't allowed after a connection 327 * is established 328 * ENOTCONN if trying a command that is allowed only if a connection is 329 * established 330 * EMSGSIZE if trying to give too much data on connect/disconnect 331 * 332 * SIDE EFFECTS: 333 * 334 * NOTES: 335 */ 336 ProtoHook 337 tp_ctloutput(cmd, so, level, optname, mp) 338 int cmd, level, optname; 339 struct socket *so; 340 struct mbuf **mp; 341 { 342 struct tp_pcb *tpcb = sototpcb(so); 343 int s = splnet(); 344 caddr_t value; 345 unsigned val_len; 346 int error = 0; 347 348 IFTRACE(D_REQUEST) 349 tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp", 350 cmd, so, optname, mp); 351 ENDTRACE 352 IFDEBUG(D_REQUEST) 353 printf( 354 "tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n", 355 so, cmd, optname, mp, mp?*mp:0, tpcb); 356 ENDDEBUG 357 if( tpcb == (struct tp_pcb *)0 ) { 358 error = ENOTSOCK; goto done; 359 } 360 if(*mp == MNULL) { 361 register struct mbuf *m; 362 363 MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */ 364 if (m == NULL) { 365 splx(s); 366 return ENOBUFS; 367 } 368 m->m_len = 0; 369 m->m_act = 0; 370 *mp = m; 371 } 372 373 /* 374 * Hook so one can set network options via a tp socket. 375 */ 376 if ( level == SOL_NETWORK ) { 377 if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL)) 378 error = ENOTSOCK; 379 else if (tpcb->tp_nlproto->nlp_ctloutput == NULL) 380 error = EOPNOTSUPP; 381 else 382 return ((tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname, 383 tpcb->tp_npcb, *mp)); 384 goto done; 385 } else if ( level == SOL_SOCKET) { 386 if (optname == SO_RCVBUF && cmd == PRCO_SETOPT) { 387 tp_rsyset(tpcb); 388 } 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_pcb == (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 #define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr) 436 #define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr) 437 438 if ((so->so_state & SS_PRIV) == 0) { 439 error = EPERM; 440 } else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED || 441 (tpcb->tp_flags & TPF_GENERAL_ADDR) || 442 tpcb->tp_next == 0) 443 error = EINVAL; 444 else { 445 register struct tp_pcb *t; 446 error = EADDRINUSE; 447 for (t = tp_listeners; t; t = t->tp_nextlisten) 448 if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 && 449 t->tp_domain == tpcb->tp_domain) 450 switch (tpcb->tp_domain) { 451 default: 452 goto done; 453 #ifdef INET 454 case AF_INET: 455 if (INA(t) == INA(tpcb)) 456 goto done; 457 continue; 458 #endif 459 #ifdef ISO 460 case AF_ISO: 461 if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr, 462 ISOA(t).isoa_len) == 0) 463 goto done; 464 continue; 465 #endif 466 } 467 tpcb->tp_lsuffixlen = 0; 468 tpcb->tp_state = TP_LISTENING; 469 error = 0; 470 remque(tpcb); 471 tpcb->tp_next = tpcb->tp_prev = tpcb; 472 tpcb->tp_nextlisten = tp_listeners; 473 tp_listeners = tpcb; 474 } 475 break; 476 477 case TPOPT_MY_TSEL: 478 if ( cmd == PRCO_GETOPT ) { 479 ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN ); 480 bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen); 481 (*mp)->m_len = tpcb->tp_lsuffixlen; 482 } else /* cmd == PRCO_SETOPT */ { 483 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) { 484 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp)); 485 error = EINVAL; 486 } else { 487 bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len); 488 tpcb->tp_lsuffixlen = val_len; 489 } 490 } 491 break; 492 493 case TPOPT_PEER_TSEL: 494 if ( cmd == PRCO_GETOPT ) { 495 ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN ); 496 bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen); 497 (*mp)->m_len = tpcb->tp_fsuffixlen; 498 } else /* cmd == PRCO_SETOPT */ { 499 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) { 500 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp)); 501 error = EINVAL; 502 } else { 503 bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len); 504 tpcb->tp_fsuffixlen = val_len; 505 } 506 } 507 break; 508 509 case TPOPT_FLAGS: 510 IFDEBUG(D_REQUEST) 511 printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n", 512 cmd==PRCO_GETOPT?"GET":"SET", 513 value, 514 *value, 515 tpcb->tp_flags); 516 ENDDEBUG 517 518 if ( cmd == PRCO_GETOPT ) { 519 *(int *)value = (int)tpcb->tp_flags; 520 (*mp)->m_len = sizeof(u_int); 521 } else /* cmd == PRCO_SETOPT */ { 522 error = EINVAL; goto done; 523 } 524 break; 525 526 case TPOPT_PARAMS: 527 /* This handles: 528 * timer values, 529 * class, use of transport expedited data, 530 * max tpdu size, checksum, xtd format and 531 * disconnect indications, and may get rid of connect/disc data 532 */ 533 IFDEBUG(D_SETPARAMS) 534 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value, 535 cmd==PRCO_GETOPT?"GET":"SET"); 536 ENDDEBUG 537 IFDEBUG(D_REQUEST) 538 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value, 539 cmd==PRCO_GETOPT?"GET":"SET"); 540 ENDDEBUG 541 542 if ( cmd == PRCO_GETOPT ) { 543 *(struct tp_conn_param *)value = tpcb->_tp_param; 544 (*mp)->m_len = sizeof(tpcb->_tp_param); 545 } else /* cmd == PRCO_SETOPT */ { 546 if( (error = 547 tp_consistency(tpcb, TP_STRICT | TP_FORCE, 548 (struct tp_conn_param *)value))==0) { 549 /* 550 * tp_consistency doesn't copy the whole set of params 551 */ 552 tpcb->_tp_param = *(struct tp_conn_param *)value; 553 (*mp)->m_len = sizeof(tpcb->_tp_param); 554 } 555 } 556 break; 557 558 case TPOPT_PSTATISTICS: 559 #ifdef TP_PERF_MEAS 560 if (cmd == PRCO_SETOPT) { 561 error = EINVAL; goto done; 562 } 563 IFPERF(tpcb) 564 if (*mp) { 565 struct mbuf * n; 566 do { 567 MFREE(*mp, n); 568 *mp = n; 569 } while (n); 570 } 571 *mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK); 572 ENDPERF 573 else { 574 error = EINVAL; goto done; 575 } 576 break; 577 #else 578 error = EOPNOTSUPP; 579 goto done; 580 #endif TP_PERF_MEAS 581 582 case TPOPT_CDDATA_CLEAR: 583 if (cmd == PRCO_GETOPT) { 584 error = EINVAL; 585 } else { 586 if (tpcb->tp_ucddata) { 587 m_freem(tpcb->tp_ucddata); 588 tpcb->tp_ucddata = 0; 589 } 590 } 591 break; 592 593 case TPOPT_CFRM_DATA: 594 case TPOPT_DISC_DATA: 595 case TPOPT_CONN_DATA: 596 if( tpcb->tp_class == TP_CLASS_0 ) { 597 error = EOPNOTSUPP; 598 break; 599 } 600 IFDEBUG(D_REQUEST) 601 printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data"); 602 printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n", 603 (*mp)->m_len, val_len, so->so_snd.sb_cc); 604 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd "); 605 ENDDEBUG 606 if (cmd == PRCO_SETOPT) { 607 int len = tpcb->tp_ucddata ? tpcb->tp_ucddata->m_len : 0; 608 /* can append connect data in several calls */ 609 if (len + val_len > 610 (optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) { 611 error = EMSGSIZE; goto done; 612 } 613 (*mp)->m_next = MNULL; 614 (*mp)->m_act = 0; 615 if (tpcb->tp_ucddata) 616 m_cat(tpcb->tp_ucddata, *mp); 617 else 618 tpcb->tp_ucddata = *mp; 619 IFDEBUG(D_REQUEST) 620 dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA"); 621 ENDDEBUG 622 IFTRACE(D_REQUEST) 623 tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len", 624 tpcb->tp_flags, so->so_snd.sb_cc,val_len,0); 625 ENDTRACE 626 *mp = MNULL; 627 if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING)) 628 (void) tp_confirm(tpcb); 629 } 630 break; 631 632 case TPOPT_PERF_MEAS: 633 #ifdef TP_PERF_MEAS 634 if (cmd == PRCO_GETOPT) { 635 *value = (u_int)tpcb->tp_perf_on; 636 (*mp)->m_len = sizeof(u_int); 637 } else if (cmd == PRCO_SETOPT) { 638 (*mp)->m_len = 0; 639 if ((*value) != 0 && (*value) != 1 ) 640 error = EINVAL; 641 else tpcb->tp_perf_on = (*value); 642 } 643 if( tpcb->tp_perf_on ) 644 error = tp_setup_perf(tpcb); 645 #else TP_PERF_MEAS 646 error = EOPNOTSUPP; 647 #endif TP_PERF_MEAS 648 break; 649 650 default: 651 error = EOPNOTSUPP; 652 } 653 654 done: 655 IFDEBUG(D_REQUEST) 656 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end"); 657 dump_mbuf(*mp, "tp_ctloutput *mp"); 658 ENDDEBUG 659 /* 660 * sigh: getsockopt looks only at m_len : all output data must 661 * reside in the first mbuf 662 */ 663 if (*mp) { 664 if (cmd == PRCO_SETOPT) 665 m_freem(*mp); 666 else { 667 ASSERT ( m_compress(*mp, mp) <= MLEN ); 668 if (error) 669 (*mp)->m_len = 0; 670 IFDEBUG(D_REQUEST) 671 dump_mbuf(*mp, "tp_ctloutput *mp after compress"); 672 ENDDEBUG 673 } 674 } 675 splx(s); 676 return error; 677 } 678