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.14 (Berkeley) 10/02/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 /* Enforce Negotation rules below */ 265 if (tpcb->tp_tpdusize > param->p_tpdusize) 266 tpcb->tp_tpdusize = param->p_tpdusize; 267 tpcb->tp_class = param->p_class; 268 if (tpcb->tp_use_checksum || param->p_use_checksum) 269 tpcb->tp_use_checksum = 1; 270 if (!tpcb->tp_xpd_service || !param->p_xpd_service) 271 tpcb->tp_xpd_service = 0; 272 if (!tpcb->tp_xtd_format || !param->p_xtd_format) 273 tpcb->tp_xtd_format = 0; 274 } 275 tpcb->tp_l_tpdusize = 1 << tpcb->tp_tpdusize; 276 done: 277 278 IFTRACE(D_CONN) 279 tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd", 280 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd); 281 ENDTRACE 282 IFDEBUG(D_CONN) 283 printf( 284 "tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n", 285 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd); 286 ENDDEBUG 287 return error; 288 } 289 290 /* 291 * NAME: tp_ctloutput() 292 * 293 * CALLED FROM: 294 * [sg]etsockopt(), via so[sg]etopt(). 295 * 296 * FUNCTION and ARGUMENTS: 297 * Implements the socket options at transport level. 298 * (cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h). 299 * (so) is the socket. 300 * (level) is SOL_TRANSPORT (see ../sys/socket.h) 301 * (optname) is the particular command or option to be set. 302 * (**mp) is an mbuf structure. 303 * 304 * RETURN VALUE: 305 * ENOTSOCK if the socket hasn't got an associated tpcb 306 * EINVAL if 307 * trying to set window too big 308 * trying to set illegal max tpdu size 309 * trying to set illegal credit fraction 310 * trying to use unknown or unimplemented class of TP 311 * structure passed to set timer values is wrong size 312 * illegal combination of command/GET-SET option, 313 * e.g., GET w/ TPOPT_CDDATA_CLEAR: 314 * EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET 315 * or if the transport-specific command is not implemented 316 * EISCONN if trying a command that isn't allowed after a connection 317 * is established 318 * ENOTCONN if trying a command that is allowed only if a connection is 319 * established 320 * EMSGSIZE if trying to give too much data on connect/disconnect 321 * 322 * SIDE EFFECTS: 323 * 324 * NOTES: 325 */ 326 ProtoHook 327 tp_ctloutput(cmd, so, level, optname, mp) 328 int cmd, level, optname; 329 struct socket *so; 330 struct mbuf **mp; 331 { 332 struct tp_pcb *tpcb = sototpcb(so); 333 int s = splnet(); 334 caddr_t value; 335 unsigned val_len; 336 int error = 0; 337 338 IFTRACE(D_REQUEST) 339 tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp", 340 cmd, so, optname, mp); 341 ENDTRACE 342 IFDEBUG(D_REQUEST) 343 printf( 344 "tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n", 345 so, cmd, optname, mp, mp?*mp:0, tpcb); 346 ENDDEBUG 347 if( tpcb == (struct tp_pcb *)0 ) { 348 error = ENOTSOCK; goto done; 349 } 350 if(*mp == MNULL) { 351 register struct mbuf *m; 352 353 MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */ 354 if (m == NULL) { 355 splx(s); 356 return ENOBUFS; 357 } 358 m->m_len = 0; 359 m->m_act = 0; 360 *mp = m; 361 } 362 363 /* 364 * Hook so one can set network options via a tp socket. 365 */ 366 if ( level == SOL_NETWORK ) { 367 if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL)) 368 error = ENOTSOCK; 369 else if (tpcb->tp_nlproto->nlp_ctloutput == NULL) 370 error = EOPNOTSUPP; 371 else 372 return ((tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname, 373 tpcb->tp_npcb, *mp)); 374 goto done; 375 } else if ( level == SOL_SOCKET) { 376 if (optname == SO_RCVBUF && cmd == PRCO_SETOPT) { 377 tp_rsyset(tpcb); 378 } 379 goto done; 380 } else if ( level != SOL_TRANSPORT ) { 381 error = EOPNOTSUPP; goto done; 382 } 383 if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) { 384 error = EOPNOTSUPP; goto done; 385 } 386 if ( so->so_error ) { 387 error = so->so_error; goto done; 388 } 389 390 /* The only options allowed after connection is established 391 * are GET (anything) and SET DISC DATA and SET PERF MEAS 392 */ 393 if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED)) 394 && 395 (cmd == PRCO_SETOPT && 396 optname != TPOPT_DISC_DATA && 397 optname != TPOPT_CFRM_DATA && 398 optname != TPOPT_PERF_MEAS && 399 optname != TPOPT_CDDATA_CLEAR ) ) { 400 error = EISCONN; goto done; 401 } 402 /* The only options allowed after disconnection are GET DISC DATA, 403 * and TPOPT_PSTATISTICS 404 * and they're not allowed if the ref timer has gone off, because 405 * the tpcb is gone 406 */ 407 if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) == 0) { 408 if ( so->so_pcb == (caddr_t)0 ) { 409 error = ENOTCONN; goto done; 410 } 411 if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) && 412 (optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) { 413 error = ENOTCONN; goto done; 414 } 415 } 416 417 value = mtod(*mp, caddr_t); /* it's aligned, don't worry, 418 * but lint complains about it 419 */ 420 val_len = (*mp)->m_len; 421 422 switch (optname) { 423 424 case TPOPT_INTERCEPT: 425 #define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr) 426 #define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr) 427 428 if ((so->so_state & SS_PRIV) == 0) { 429 error = EPERM; 430 } else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED || 431 (tpcb->tp_flags & TPF_GENERAL_ADDR) || 432 tpcb->tp_next == 0) 433 error = EINVAL; 434 else { 435 register struct tp_pcb *t; 436 error = EADDRINUSE; 437 for (t = tp_listeners; t; t = t->tp_nextlisten) 438 if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 && 439 t->tp_domain == tpcb->tp_domain) 440 switch (tpcb->tp_domain) { 441 default: 442 goto done; 443 #ifdef INET 444 case AF_INET: 445 if (INA(t) == INA(tpcb)) 446 goto done; 447 continue; 448 #endif 449 #ifdef ISO 450 case AF_ISO: 451 if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr, 452 ISOA(t).isoa_len) == 0) 453 goto done; 454 continue; 455 #endif 456 } 457 tpcb->tp_lsuffixlen = 0; 458 tpcb->tp_state = TP_LISTENING; 459 error = 0; 460 remque(tpcb); 461 tpcb->tp_next = tpcb->tp_prev = tpcb; 462 tpcb->tp_nextlisten = tp_listeners; 463 tp_listeners = tpcb; 464 } 465 break; 466 467 case TPOPT_MY_TSEL: 468 if ( cmd == PRCO_GETOPT ) { 469 ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN ); 470 bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen); 471 (*mp)->m_len = tpcb->tp_lsuffixlen; 472 } else /* cmd == PRCO_SETOPT */ { 473 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) { 474 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp)); 475 error = EINVAL; 476 } else { 477 bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len); 478 tpcb->tp_lsuffixlen = val_len; 479 } 480 } 481 break; 482 483 case TPOPT_PEER_TSEL: 484 if ( cmd == PRCO_GETOPT ) { 485 ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN ); 486 bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen); 487 (*mp)->m_len = tpcb->tp_fsuffixlen; 488 } else /* cmd == PRCO_SETOPT */ { 489 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) { 490 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp)); 491 error = EINVAL; 492 } else { 493 bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len); 494 tpcb->tp_fsuffixlen = val_len; 495 } 496 } 497 break; 498 499 case TPOPT_FLAGS: 500 IFDEBUG(D_REQUEST) 501 printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n", 502 cmd==PRCO_GETOPT?"GET":"SET", 503 value, 504 *value, 505 tpcb->tp_flags); 506 ENDDEBUG 507 508 if ( cmd == PRCO_GETOPT ) { 509 *(int *)value = (int)tpcb->tp_flags; 510 (*mp)->m_len = sizeof(u_int); 511 } else /* cmd == PRCO_SETOPT */ { 512 error = EINVAL; goto done; 513 } 514 break; 515 516 case TPOPT_PARAMS: 517 /* This handles: 518 * timer values, 519 * class, use of transport expedited data, 520 * max tpdu size, checksum, xtd format and 521 * disconnect indications, and may get rid of connect/disc data 522 */ 523 IFDEBUG(D_SETPARAMS) 524 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value, 525 cmd==PRCO_GETOPT?"GET":"SET"); 526 ENDDEBUG 527 IFDEBUG(D_REQUEST) 528 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value, 529 cmd==PRCO_GETOPT?"GET":"SET"); 530 ENDDEBUG 531 532 if ( cmd == PRCO_GETOPT ) { 533 *(struct tp_conn_param *)value = tpcb->_tp_param; 534 (*mp)->m_len = sizeof(tpcb->_tp_param); 535 } else /* cmd == PRCO_SETOPT */ { 536 if( (error = 537 tp_consistency(tpcb, TP_STRICT | TP_FORCE, 538 (struct tp_conn_param *)value))==0) { 539 /* 540 * tp_consistency doesn't copy the whole set of params 541 */ 542 tpcb->_tp_param = *(struct tp_conn_param *)value; 543 (*mp)->m_len = sizeof(tpcb->_tp_param); 544 } 545 } 546 break; 547 548 case TPOPT_PSTATISTICS: 549 #ifdef TP_PERF_MEAS 550 if (cmd == PRCO_SETOPT) { 551 error = EINVAL; goto done; 552 } 553 IFPERF(tpcb) 554 if (*mp) { 555 struct mbuf * n; 556 do { 557 MFREE(*mp, n); 558 *mp = n; 559 } while (n); 560 } 561 *mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK); 562 ENDPERF 563 else { 564 error = EINVAL; goto done; 565 } 566 break; 567 #else 568 error = EOPNOTSUPP; 569 goto done; 570 #endif TP_PERF_MEAS 571 572 case TPOPT_CDDATA_CLEAR: 573 if (cmd == PRCO_GETOPT) { 574 error = EINVAL; 575 } else { 576 if (tpcb->tp_ucddata) { 577 m_freem(tpcb->tp_ucddata); 578 tpcb->tp_ucddata = 0; 579 } 580 } 581 break; 582 583 case TPOPT_CFRM_DATA: 584 case TPOPT_DISC_DATA: 585 case TPOPT_CONN_DATA: 586 if( tpcb->tp_class == TP_CLASS_0 ) { 587 error = EOPNOTSUPP; 588 break; 589 } 590 IFDEBUG(D_REQUEST) 591 printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data"); 592 printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n", 593 (*mp)->m_len, val_len, so->so_snd.sb_cc); 594 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd "); 595 ENDDEBUG 596 if (cmd == PRCO_SETOPT) { 597 int len = tpcb->tp_ucddata ? tpcb->tp_ucddata->m_len : 0; 598 /* can append connect data in several calls */ 599 if (len + val_len > 600 (optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) { 601 error = EMSGSIZE; goto done; 602 } 603 (*mp)->m_next = MNULL; 604 (*mp)->m_act = 0; 605 if (tpcb->tp_ucddata) 606 m_cat(tpcb->tp_ucddata, *mp); 607 else 608 tpcb->tp_ucddata = *mp; 609 IFDEBUG(D_REQUEST) 610 dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA"); 611 ENDDEBUG 612 IFTRACE(D_REQUEST) 613 tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len", 614 tpcb->tp_flags, so->so_snd.sb_cc,val_len,0); 615 ENDTRACE 616 *mp = MNULL; 617 if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING)) 618 (void) tp_confirm(tpcb); 619 } 620 break; 621 622 case TPOPT_PERF_MEAS: 623 #ifdef TP_PERF_MEAS 624 if (cmd == PRCO_GETOPT) { 625 *value = (u_int)tpcb->tp_perf_on; 626 (*mp)->m_len = sizeof(u_int); 627 } else if (cmd == PRCO_SETOPT) { 628 (*mp)->m_len = 0; 629 if ((*value) != 0 && (*value) != 1 ) 630 error = EINVAL; 631 else tpcb->tp_perf_on = (*value); 632 } 633 if( tpcb->tp_perf_on ) 634 error = tp_setup_perf(tpcb); 635 #else TP_PERF_MEAS 636 error = EOPNOTSUPP; 637 #endif TP_PERF_MEAS 638 break; 639 640 default: 641 error = EOPNOTSUPP; 642 } 643 644 done: 645 IFDEBUG(D_REQUEST) 646 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end"); 647 dump_mbuf(*mp, "tp_ctloutput *mp"); 648 ENDDEBUG 649 /* 650 * sigh: getsockopt looks only at m_len : all output data must 651 * reside in the first mbuf 652 */ 653 if (*mp) { 654 if (cmd == PRCO_SETOPT) 655 m_freem(*mp); 656 else { 657 ASSERT ( m_compress(*mp, mp) <= MLEN ); 658 if (error) 659 (*mp)->m_len = 0; 660 IFDEBUG(D_REQUEST) 661 dump_mbuf(*mp, "tp_ctloutput *mp after compress"); 662 ENDDEBUG 663 } 664 } 665 splx(s); 666 return error; 667 } 668