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.16 (Berkeley) 05/27/92 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 u_long old_credit = tpcb->tp_maxlcredit; 388 tp_rsyset(tpcb); 389 if (tpcb->tp_rhiwat != so->so_rcv.sb_hiwat && 390 tpcb->tp_state == TP_OPEN && 391 (old_credit < tpcb->tp_maxlcredit)) 392 tp_emit(AK_TPDU_type, tpcb, 393 tpcb->tp_rcvnxt, 0, MNULL); 394 tpcb->tp_rhiwat = so->so_rcv.sb_hiwat; 395 } 396 goto done; 397 } else if ( level != SOL_TRANSPORT ) { 398 error = EOPNOTSUPP; goto done; 399 } 400 if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) { 401 error = EOPNOTSUPP; goto done; 402 } 403 if ( so->so_error ) { 404 error = so->so_error; goto done; 405 } 406 407 /* The only options allowed after connection is established 408 * are GET (anything) and SET DISC DATA and SET PERF MEAS 409 */ 410 if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED)) 411 && 412 (cmd == PRCO_SETOPT && 413 optname != TPOPT_DISC_DATA && 414 optname != TPOPT_CFRM_DATA && 415 optname != TPOPT_PERF_MEAS && 416 optname != TPOPT_CDDATA_CLEAR ) ) { 417 error = EISCONN; goto done; 418 } 419 /* The only options allowed after disconnection are GET DISC DATA, 420 * and TPOPT_PSTATISTICS 421 * and they're not allowed if the ref timer has gone off, because 422 * the tpcb is gone 423 */ 424 if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) == 0) { 425 if ( so->so_pcb == (caddr_t)0 ) { 426 error = ENOTCONN; goto done; 427 } 428 if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) && 429 (optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) { 430 error = ENOTCONN; goto done; 431 } 432 } 433 434 value = mtod(*mp, caddr_t); /* it's aligned, don't worry, 435 * but lint complains about it 436 */ 437 val_len = (*mp)->m_len; 438 439 switch (optname) { 440 441 case TPOPT_INTERCEPT: 442 #define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr) 443 #define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr) 444 445 if ((so->so_state & SS_PRIV) == 0) { 446 error = EPERM; 447 } else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED || 448 (tpcb->tp_flags & TPF_GENERAL_ADDR) || 449 tpcb->tp_next == 0) 450 error = EINVAL; 451 else { 452 register struct tp_pcb *t; 453 error = EADDRINUSE; 454 for (t = tp_listeners; t; t = t->tp_nextlisten) 455 if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 && 456 t->tp_domain == tpcb->tp_domain) 457 switch (tpcb->tp_domain) { 458 default: 459 goto done; 460 #ifdef INET 461 case AF_INET: 462 if (INA(t) == INA(tpcb)) 463 goto done; 464 continue; 465 #endif 466 #ifdef ISO 467 case AF_ISO: 468 if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr, 469 ISOA(t).isoa_len) == 0) 470 goto done; 471 continue; 472 #endif 473 } 474 tpcb->tp_lsuffixlen = 0; 475 tpcb->tp_state = TP_LISTENING; 476 error = 0; 477 remque(tpcb); 478 tpcb->tp_next = tpcb->tp_prev = tpcb; 479 tpcb->tp_nextlisten = tp_listeners; 480 tp_listeners = tpcb; 481 } 482 break; 483 484 case TPOPT_MY_TSEL: 485 if ( cmd == PRCO_GETOPT ) { 486 ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN ); 487 bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen); 488 (*mp)->m_len = tpcb->tp_lsuffixlen; 489 } else /* cmd == PRCO_SETOPT */ { 490 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) { 491 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp)); 492 error = EINVAL; 493 } else { 494 bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len); 495 tpcb->tp_lsuffixlen = val_len; 496 } 497 } 498 break; 499 500 case TPOPT_PEER_TSEL: 501 if ( cmd == PRCO_GETOPT ) { 502 ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN ); 503 bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen); 504 (*mp)->m_len = tpcb->tp_fsuffixlen; 505 } else /* cmd == PRCO_SETOPT */ { 506 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) { 507 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp)); 508 error = EINVAL; 509 } else { 510 bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len); 511 tpcb->tp_fsuffixlen = val_len; 512 } 513 } 514 break; 515 516 case TPOPT_FLAGS: 517 IFDEBUG(D_REQUEST) 518 printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n", 519 cmd==PRCO_GETOPT?"GET":"SET", 520 value, 521 *value, 522 tpcb->tp_flags); 523 ENDDEBUG 524 525 if ( cmd == PRCO_GETOPT ) { 526 *(int *)value = (int)tpcb->tp_flags; 527 (*mp)->m_len = sizeof(u_int); 528 } else /* cmd == PRCO_SETOPT */ { 529 error = EINVAL; goto done; 530 } 531 break; 532 533 case TPOPT_PARAMS: 534 /* This handles: 535 * timer values, 536 * class, use of transport expedited data, 537 * max tpdu size, checksum, xtd format and 538 * disconnect indications, and may get rid of connect/disc data 539 */ 540 IFDEBUG(D_SETPARAMS) 541 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value, 542 cmd==PRCO_GETOPT?"GET":"SET"); 543 ENDDEBUG 544 IFDEBUG(D_REQUEST) 545 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value, 546 cmd==PRCO_GETOPT?"GET":"SET"); 547 ENDDEBUG 548 549 if ( cmd == PRCO_GETOPT ) { 550 *(struct tp_conn_param *)value = tpcb->_tp_param; 551 (*mp)->m_len = sizeof(tpcb->_tp_param); 552 } else /* cmd == PRCO_SETOPT */ { 553 if( (error = 554 tp_consistency(tpcb, TP_STRICT | TP_FORCE, 555 (struct tp_conn_param *)value))==0) { 556 /* 557 * tp_consistency doesn't copy the whole set of params 558 */ 559 tpcb->_tp_param = *(struct tp_conn_param *)value; 560 (*mp)->m_len = sizeof(tpcb->_tp_param); 561 } 562 } 563 break; 564 565 case TPOPT_PSTATISTICS: 566 #ifdef TP_PERF_MEAS 567 if (cmd == PRCO_SETOPT) { 568 error = EINVAL; goto done; 569 } 570 IFPERF(tpcb) 571 if (*mp) { 572 struct mbuf * n; 573 do { 574 MFREE(*mp, n); 575 *mp = n; 576 } while (n); 577 } 578 *mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK); 579 ENDPERF 580 else { 581 error = EINVAL; goto done; 582 } 583 break; 584 #else 585 error = EOPNOTSUPP; 586 goto done; 587 #endif TP_PERF_MEAS 588 589 case TPOPT_CDDATA_CLEAR: 590 if (cmd == PRCO_GETOPT) { 591 error = EINVAL; 592 } else { 593 if (tpcb->tp_ucddata) { 594 m_freem(tpcb->tp_ucddata); 595 tpcb->tp_ucddata = 0; 596 } 597 } 598 break; 599 600 case TPOPT_CFRM_DATA: 601 case TPOPT_DISC_DATA: 602 case TPOPT_CONN_DATA: 603 if( tpcb->tp_class == TP_CLASS_0 ) { 604 error = EOPNOTSUPP; 605 break; 606 } 607 IFDEBUG(D_REQUEST) 608 printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data"); 609 printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n", 610 (*mp)->m_len, val_len, so->so_snd.sb_cc); 611 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd "); 612 ENDDEBUG 613 if (cmd == PRCO_SETOPT) { 614 int len = tpcb->tp_ucddata ? tpcb->tp_ucddata->m_len : 0; 615 /* can append connect data in several calls */ 616 if (len + val_len > 617 (optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) { 618 error = EMSGSIZE; goto done; 619 } 620 (*mp)->m_next = MNULL; 621 (*mp)->m_act = 0; 622 if (tpcb->tp_ucddata) 623 m_cat(tpcb->tp_ucddata, *mp); 624 else 625 tpcb->tp_ucddata = *mp; 626 IFDEBUG(D_REQUEST) 627 dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA"); 628 ENDDEBUG 629 IFTRACE(D_REQUEST) 630 tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len", 631 tpcb->tp_flags, so->so_snd.sb_cc,val_len,0); 632 ENDTRACE 633 *mp = MNULL; 634 if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING)) 635 (void) tp_confirm(tpcb); 636 } 637 break; 638 639 case TPOPT_PERF_MEAS: 640 #ifdef TP_PERF_MEAS 641 if (cmd == PRCO_GETOPT) { 642 *value = (u_int)tpcb->tp_perf_on; 643 (*mp)->m_len = sizeof(u_int); 644 } else if (cmd == PRCO_SETOPT) { 645 (*mp)->m_len = 0; 646 if ((*value) != 0 && (*value) != 1 ) 647 error = EINVAL; 648 else tpcb->tp_perf_on = (*value); 649 } 650 if( tpcb->tp_perf_on ) 651 error = tp_setup_perf(tpcb); 652 #else TP_PERF_MEAS 653 error = EOPNOTSUPP; 654 #endif TP_PERF_MEAS 655 break; 656 657 default: 658 error = EOPNOTSUPP; 659 } 660 661 done: 662 IFDEBUG(D_REQUEST) 663 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end"); 664 dump_mbuf(*mp, "tp_ctloutput *mp"); 665 ENDDEBUG 666 /* 667 * sigh: getsockopt looks only at m_len : all output data must 668 * reside in the first mbuf 669 */ 670 if (*mp) { 671 if (cmd == PRCO_SETOPT) { 672 m_freem(*mp); 673 *mp = MNULL; 674 } else { 675 ASSERT ( m_compress(*mp, mp) <= MLEN ); 676 if (error) 677 (*mp)->m_len = 0; 678 IFDEBUG(D_REQUEST) 679 dump_mbuf(*mp, "tp_ctloutput *mp after compress"); 680 ENDDEBUG 681 } 682 } 683 splx(s); 684 return error; 685 } 686