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