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