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