| 1 | /*********************************************************** |
| 2 | Copyright IBM Corporation 1987 |
| 3 | |
| 4 | All Rights Reserved |
| 5 | |
| 6 | Permission to use, copy, modify, and distribute this software and its |
| 7 | documentation for any purpose and without fee is hereby granted, |
| 8 | provided that the above copyright notice appear in all copies and that |
| 9 | both that copyright notice and this permission notice appear in |
| 10 | supporting documentation, and that the name of IBM not be |
| 11 | used in advertising or publicity pertaining to distribution of the |
| 12 | software without specific, written prior permission. |
| 13 | |
| 14 | IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING |
| 15 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL |
| 16 | IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR |
| 17 | ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
| 18 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
| 19 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
| 20 | SOFTWARE. |
| 21 | |
| 22 | ******************************************************************/ |
| 23 | |
| 24 | /* |
| 25 | * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison |
| 26 | */ |
| 27 | /* |
| 28 | * ARGO TP |
| 29 | * |
| 30 | * $Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $ |
| 31 | * $Source: /usr/argo/sys/netiso/RCS/tp_output.c,v $ |
| 32 | * @(#)tp_output.c 7.4 (Berkeley) %G% * |
| 33 | * |
| 34 | * In here is tp_ctloutput(), the guy called by [sg]etsockopt(), |
| 35 | */ |
| 36 | |
| 37 | #ifndef lint |
| 38 | static char *rcsid = "$Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $"; |
| 39 | #endif lint |
| 40 | |
| 41 | #include "param.h" |
| 42 | #include "systm.h" |
| 43 | #include "mbuf.h" |
| 44 | #include "protosw.h" |
| 45 | #include "socket.h" |
| 46 | #include "socketvar.h" |
| 47 | #include "errno.h" |
| 48 | #include "types.h" |
| 49 | #include "time.h" |
| 50 | #include "tp_param.h" |
| 51 | #include "tp_user.h" |
| 52 | #include "tp_stat.h" |
| 53 | #include "tp_ip.h" |
| 54 | #include "tp_timer.h" |
| 55 | #include "argo_debug.h" |
| 56 | #include "tp_pcb.h" |
| 57 | #include "tp_trace.h" |
| 58 | |
| 59 | #define USERFLAGSMASK_G 0x0f00643b |
| 60 | #define USERFLAGSMASK_S 0x0f000432 |
| 61 | #define TPDUSIZESHIFT 24 |
| 62 | #define CLASSHIFT 16 |
| 63 | |
| 64 | /* |
| 65 | * NAME: tp_consistency() |
| 66 | * |
| 67 | * CALLED FROM: |
| 68 | * tp_ctloutput(), tp_input() |
| 69 | * |
| 70 | * FUNCTION and ARGUMENTS: |
| 71 | * Checks the consistency of options and tpdusize with class, |
| 72 | * using the parameters passed in via (param). |
| 73 | * (cmd) may be TP_STRICT or TP_FORCE or both. |
| 74 | * Force means it will set all the values in (tpcb) to those in |
| 75 | * the input arguements iff no errors were encountered. |
| 76 | * Strict means that no inconsistency will be tolerated. If it's |
| 77 | * not used, checksum and tpdusize inconsistencies will be tolerated. |
| 78 | * The reason for this is that in some cases, when we're negotiating down |
| 79 | * from class 4, these options should be changed but should not |
| 80 | * cause negotiation to fail. |
| 81 | * |
| 82 | * RETURNS |
| 83 | * E* or EOK |
| 84 | * E* if the various parms aren't ok for a given class |
| 85 | * EOK if they are ok for a given class |
| 86 | */ |
| 87 | |
| 88 | int |
| 89 | tp_consistency( tpcb, cmd, param ) |
| 90 | u_int cmd; |
| 91 | struct tp_conn_param *param; |
| 92 | struct tp_pcb *tpcb; |
| 93 | { |
| 94 | register int error = EOK; |
| 95 | int class_to_use = tp_mask_to_num(param->p_class); |
| 96 | |
| 97 | IFTRACE(D_SETPARAMS) |
| 98 | tptrace(TPPTmisc, |
| 99 | "tp_consist enter class_to_use dontchange param.class cmd", |
| 100 | class_to_use, param->p_dont_change_params, param->p_class, cmd); |
| 101 | ENDTRACE |
| 102 | IFDEBUG(D_SETPARAMS) |
| 103 | printf("tp_consistency %s %s\n", |
| 104 | cmd& TP_FORCE? "TP_FORCE": "", |
| 105 | cmd& TP_STRICT? "TP_STRICT":""); |
| 106 | ENDDEBUG |
| 107 | if ((cmd & TP_FORCE) && (param->p_dont_change_params)) { |
| 108 | cmd &= ~TP_FORCE; |
| 109 | } |
| 110 | /* can switch net services within a domain, but |
| 111 | * cannot switch domains |
| 112 | */ |
| 113 | switch( param->p_netservice) { |
| 114 | case ISO_CONS: |
| 115 | case ISO_CLNS: |
| 116 | case ISO_COSNS: |
| 117 | /* param->p_netservice in ISO DOMAIN */ |
| 118 | if(tpcb->tp_domain != AF_ISO ) { |
| 119 | error = EINVAL; goto done; |
| 120 | } |
| 121 | break; |
| 122 | case IN_CLNS: |
| 123 | /* param->p_netservice in INET DOMAIN */ |
| 124 | if( tpcb->tp_domain != AF_INET ) { |
| 125 | error = EINVAL; goto done; |
| 126 | } |
| 127 | break; |
| 128 | /* no others not possible-> netservice is a 2-bit field! */ |
| 129 | } |
| 130 | |
| 131 | IFDEBUG(D_SETPARAMS) |
| 132 | printf("p_class 0x%x, class_to_use 0x%x\n", param->p_class, |
| 133 | class_to_use); |
| 134 | ENDDEBUG |
| 135 | if((param->p_netservice < 0) || (param->p_netservice > TP_MAX_NETSERVICES)){ |
| 136 | error = EINVAL; goto done; |
| 137 | } |
| 138 | if( (param->p_class & TP_CLASSES_IMPLEMENTED) == 0 ) { |
| 139 | error = EINVAL; goto done; |
| 140 | } |
| 141 | IFDEBUG(D_SETPARAMS) |
| 142 | printf("Nretrans 0x%x\n", param->p_Nretrans ); |
| 143 | ENDDEBUG |
| 144 | if( ( param->p_Nretrans < 1 ) || |
| 145 | (param->p_cr_ticks < 1) || (param->p_cc_ticks < 1) ) { |
| 146 | /* bad for any class because negot has to be done a la class 4 */ |
| 147 | error = EINVAL; goto done; |
| 148 | } |
| 149 | IFDEBUG(D_SETPARAMS) |
| 150 | printf("winsize 0x%x\n", param->p_winsize ); |
| 151 | ENDDEBUG |
| 152 | if( (param->p_winsize < 128 ) || |
| 153 | (param->p_winsize < param->p_tpdusize ) || |
| 154 | (param->p_winsize > ((1+SB_MAX)>>2 /* 1/4 of the max */)) ) { |
| 155 | error = EINVAL; goto done; |
| 156 | } else { |
| 157 | if( tpcb->tp_state == TP_CLOSED ) |
| 158 | soreserve(tpcb->tp_sock, (u_long)param->p_winsize, |
| 159 | (u_long)param->p_winsize); |
| 160 | } |
| 161 | IFDEBUG(D_SETPARAMS) |
| 162 | printf("use_csum 0x%x\n", param->p_use_checksum ); |
| 163 | printf("xtd_format 0x%x\n", param->p_xtd_format ); |
| 164 | printf("xpd_service 0x%x\n", param->p_xpd_service ); |
| 165 | printf("tpdusize 0x%x\n", param->p_tpdusize ); |
| 166 | printf("tpcb->flags 0x%x\n", tpcb->tp_flags ); |
| 167 | ENDDEBUG |
| 168 | switch( class_to_use ) { |
| 169 | |
| 170 | case 0: |
| 171 | /* do not use checksums, xtd format, or XPD */ |
| 172 | |
| 173 | if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) { |
| 174 | if(cmd & TP_STRICT) { |
| 175 | error = EINVAL; |
| 176 | } else { |
| 177 | param->p_use_checksum = 0; |
| 178 | param->p_xtd_format = 0; |
| 179 | param->p_xpd_service = 0; |
| 180 | } |
| 181 | break; |
| 182 | } |
| 183 | |
| 184 | if (param->p_tpdusize < TP_MIN_TPDUSIZE) { |
| 185 | if(cmd & TP_STRICT) { |
| 186 | error = EINVAL; |
| 187 | } else { |
| 188 | param->p_tpdusize = TP_MIN_TPDUSIZE; |
| 189 | } |
| 190 | break; |
| 191 | } |
| 192 | if (param->p_tpdusize > TP0_TPDUSIZE) { |
| 193 | if (cmd & TP_STRICT) { |
| 194 | error = EINVAL; |
| 195 | } else { |
| 196 | param->p_tpdusize = TP0_TPDUSIZE; |
| 197 | } |
| 198 | break; |
| 199 | } |
| 200 | |
| 201 | /* connect/disc data not allowed for class 0 */ |
| 202 | if (tpcb->tp_ucddata) { |
| 203 | if(cmd & TP_STRICT) { |
| 204 | error = EINVAL; |
| 205 | } else if(cmd & TP_FORCE) { |
| 206 | m_freem(tpcb->tp_ucddata); |
| 207 | tpcb->tp_ucddata = 0; |
| 208 | } |
| 209 | } |
| 210 | break; |
| 211 | |
| 212 | case 4: |
| 213 | IFDEBUG(D_SETPARAMS) |
| 214 | printf("dt_ticks 0x%x\n", param->p_dt_ticks ); |
| 215 | printf("x_ticks 0x%x\n", param->p_x_ticks ); |
| 216 | printf("dr_ticks 0x%x\n", param->p_dr_ticks ); |
| 217 | printf("keepalive 0x%x\n", param->p_keepalive_ticks ); |
| 218 | printf("sendack 0x%x\n", param->p_sendack_ticks ); |
| 219 | printf("inact 0x%x\n", param->p_inact_ticks ); |
| 220 | printf("ref 0x%x\n", param->p_ref_ticks ); |
| 221 | ENDDEBUG |
| 222 | if( (param->p_class & TP_CLASS_4 ) && ( |
| 223 | (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) || |
| 224 | (param->p_x_ticks < 1) || (param->p_keepalive_ticks < 1) || |
| 225 | (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) || |
| 226 | (param->p_inact_ticks < 1) ) ) { |
| 227 | error = EINVAL; |
| 228 | break; |
| 229 | } |
| 230 | IFDEBUG(D_SETPARAMS) |
| 231 | printf("rx_strat 0x%x\n", param->p_rx_strat ); |
| 232 | ENDDEBUG |
| 233 | if(param->p_rx_strat > |
| 234 | ( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) { |
| 235 | if(cmd & TP_STRICT) { |
| 236 | error = EINVAL; |
| 237 | } else { |
| 238 | param->p_rx_strat = TPRX_USE_CW; |
| 239 | } |
| 240 | break; |
| 241 | } |
| 242 | IFDEBUG(D_SETPARAMS) |
| 243 | printf("ack_strat 0x%x\n", param->p_ack_strat ); |
| 244 | ENDDEBUG |
| 245 | if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) { |
| 246 | if(cmd & TP_STRICT) { |
| 247 | error = EINVAL; |
| 248 | } else { |
| 249 | param->p_ack_strat = TPACK_WINDOW; |
| 250 | } |
| 251 | break; |
| 252 | } |
| 253 | if (param->p_tpdusize < TP_MIN_TPDUSIZE) { |
| 254 | if(cmd & TP_STRICT) { |
| 255 | error = EINVAL; |
| 256 | } else { |
| 257 | param->p_tpdusize = TP_MIN_TPDUSIZE; |
| 258 | } |
| 259 | break; |
| 260 | } |
| 261 | if (param->p_tpdusize > TP_TPDUSIZE) { |
| 262 | if(cmd & TP_STRICT) { |
| 263 | error = EINVAL; |
| 264 | } else { |
| 265 | param->p_tpdusize = TP_TPDUSIZE; |
| 266 | } |
| 267 | break; |
| 268 | } |
| 269 | break; |
| 270 | } |
| 271 | |
| 272 | if ((error==0) && (cmd & TP_FORCE)) { |
| 273 | tpcb->tp_tpdusize = param->p_tpdusize; |
| 274 | tpcb->tp_class = param->p_class; |
| 275 | tpcb->tp_use_checksum = param->p_use_checksum; |
| 276 | tpcb->tp_xpd_service = param->p_xpd_service; |
| 277 | tpcb->tp_xtd_format = param->p_xtd_format; |
| 278 | } |
| 279 | |
| 280 | done: |
| 281 | |
| 282 | IFTRACE(D_CONN) |
| 283 | tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd", |
| 284 | error, tpcb->tp_class, tpcb->tp_xtd_format, cmd); |
| 285 | ENDTRACE |
| 286 | IFDEBUG(D_CONN) |
| 287 | printf( |
| 288 | "tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n", |
| 289 | error, tpcb->tp_class, tpcb->tp_xtd_format, cmd); |
| 290 | ENDDEBUG |
| 291 | return error; |
| 292 | } |
| 293 | |
| 294 | /* |
| 295 | * NAME: tp_ctloutput() |
| 296 | * |
| 297 | * CALLED FROM: |
| 298 | * [sg]etsockopt(), via so[sg]etopt(). |
| 299 | * |
| 300 | * FUNCTION and ARGUMENTS: |
| 301 | * Implements the socket options at transport level. |
| 302 | * (cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h). |
| 303 | * (so) is the socket. |
| 304 | * (level) is SOL_TRANSPORT (see ../sys/socket.h) |
| 305 | * (optname) is the particular command or option to be set. |
| 306 | * (**mp) is an mbuf structure. |
| 307 | * |
| 308 | * RETURN VALUE: |
| 309 | * ENOTSOCK if the socket hasn't got an associated tpcb |
| 310 | * EINVAL if |
| 311 | * trying to set window too big |
| 312 | * trying to set illegal max tpdu size |
| 313 | * trying to set illegal credit fraction |
| 314 | * trying to use unknown or unimplemented class of TP |
| 315 | * structure passed to set timer values is wrong size |
| 316 | * illegal combination of command/GET-SET option, |
| 317 | * e.g., GET w/ TPOPT_CDDATA_CLEAR: |
| 318 | * EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET |
| 319 | * or if the transport-specific command is not implemented |
| 320 | * EISCONN if trying a command that isn't allowed after a connection |
| 321 | * is established |
| 322 | * ENOTCONN if trying a command that is allowed only if a connection is |
| 323 | * established |
| 324 | * EMSGSIZE if trying to give too much data on connect/disconnect |
| 325 | * |
| 326 | * SIDE EFFECTS: |
| 327 | * |
| 328 | * NOTES: |
| 329 | */ |
| 330 | ProtoHook |
| 331 | tp_ctloutput(cmd, so, level, optname, mp) |
| 332 | int cmd, level, optname; |
| 333 | struct socket *so; |
| 334 | struct mbuf **mp; |
| 335 | { |
| 336 | struct tp_pcb *tpcb = sototpcb(so); |
| 337 | int s = splnet(); |
| 338 | caddr_t value; |
| 339 | unsigned val_len; |
| 340 | int error = 0; |
| 341 | |
| 342 | IFTRACE(D_REQUEST) |
| 343 | tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp", |
| 344 | cmd, so, optname, mp); |
| 345 | ENDTRACE |
| 346 | IFDEBUG(D_REQUEST) |
| 347 | printf( |
| 348 | "tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n", |
| 349 | so, cmd, optname, mp, mp?*mp:0, tpcb); |
| 350 | ENDDEBUG |
| 351 | if( tpcb == (struct tp_pcb *)0 ) { |
| 352 | error = ENOTSOCK; goto done; |
| 353 | } |
| 354 | if(*mp == MNULL) { |
| 355 | register struct mbuf *m; |
| 356 | |
| 357 | MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */ |
| 358 | if (m == NULL) { |
| 359 | splx(s); |
| 360 | return ENOBUFS; |
| 361 | } |
| 362 | m->m_len = 0; |
| 363 | m->m_act = 0; |
| 364 | *mp = m; |
| 365 | } |
| 366 | |
| 367 | /* |
| 368 | * Hook so one can set network options via a tp socket. |
| 369 | */ |
| 370 | if ( level == SOL_NETWORK ) { |
| 371 | if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL)) |
| 372 | error = ENOTSOCK; |
| 373 | else if (tpcb->tp_nlproto->nlp_ctloutput == NULL) |
| 374 | error = EOPNOTSUPP; |
| 375 | else |
| 376 | error = (tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname, |
| 377 | tpcb->tp_npcb, *mp); |
| 378 | goto done; |
| 379 | } else if ( level != SOL_TRANSPORT ) { |
| 380 | error = EOPNOTSUPP; goto done; |
| 381 | } |
| 382 | if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) { |
| 383 | error = EOPNOTSUPP; goto done; |
| 384 | } |
| 385 | if ( so->so_error ) { |
| 386 | error = so->so_error; goto done; |
| 387 | } |
| 388 | |
| 389 | /* The only options allowed after connection is established |
| 390 | * are GET (anything) and SET DISC DATA and SET PERF MEAS |
| 391 | */ |
| 392 | if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED)) |
| 393 | && |
| 394 | (cmd == PRCO_SETOPT && |
| 395 | optname != TPOPT_DISC_DATA && |
| 396 | optname != TPOPT_CFRM_DATA && |
| 397 | optname != TPOPT_PERF_MEAS && |
| 398 | optname != TPOPT_CDDATA_CLEAR ) ) { |
| 399 | error = EISCONN; goto done; |
| 400 | } |
| 401 | /* The only options allowed after disconnection are GET DISC DATA, |
| 402 | * and TPOPT_PSTATISTICS |
| 403 | * and they're not allowed if the ref timer has gone off, because |
| 404 | * the tpcb is gone |
| 405 | */ |
| 406 | if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) == 0) { |
| 407 | if ( so->so_tpcb == (caddr_t)0 ) { |
| 408 | error = ENOTCONN; goto done; |
| 409 | } |
| 410 | if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) && |
| 411 | (optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) { |
| 412 | error = ENOTCONN; goto done; |
| 413 | } |
| 414 | } |
| 415 | |
| 416 | value = mtod(*mp, caddr_t); /* it's aligned, don't worry, |
| 417 | * but lint complains about it |
| 418 | */ |
| 419 | val_len = (*mp)->m_len; |
| 420 | |
| 421 | switch (optname) { |
| 422 | |
| 423 | case TPOPT_MY_TSEL: |
| 424 | if ( cmd == PRCO_GETOPT ) { |
| 425 | ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN ); |
| 426 | bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen); |
| 427 | (*mp)->m_len = tpcb->tp_lsuffixlen; |
| 428 | } else /* cmd == PRCO_SETOPT */ { |
| 429 | if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) { |
| 430 | printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp)); |
| 431 | error = EINVAL; |
| 432 | } else { |
| 433 | bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len); |
| 434 | tpcb->tp_lsuffixlen = val_len; |
| 435 | } |
| 436 | } |
| 437 | break; |
| 438 | |
| 439 | case TPOPT_PEER_TSEL: |
| 440 | if ( cmd == PRCO_GETOPT ) { |
| 441 | ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN ); |
| 442 | bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen); |
| 443 | (*mp)->m_len = tpcb->tp_fsuffixlen; |
| 444 | } else /* cmd == PRCO_SETOPT */ { |
| 445 | if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) { |
| 446 | printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp)); |
| 447 | error = EINVAL; |
| 448 | } else { |
| 449 | bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len); |
| 450 | tpcb->tp_fsuffixlen = val_len; |
| 451 | } |
| 452 | } |
| 453 | break; |
| 454 | |
| 455 | case TPOPT_FLAGS: |
| 456 | IFDEBUG(D_REQUEST) |
| 457 | printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n", |
| 458 | cmd==PRCO_GETOPT?"GET":"SET", |
| 459 | value, |
| 460 | *value, |
| 461 | tpcb->tp_flags); |
| 462 | ENDDEBUG |
| 463 | |
| 464 | if ( cmd == PRCO_GETOPT ) { |
| 465 | *(int *)value = (int)tpcb->tp_flags; |
| 466 | (*mp)->m_len = sizeof(u_int); |
| 467 | } else /* cmd == PRCO_SETOPT */ { |
| 468 | error = EINVAL; goto done; |
| 469 | } |
| 470 | break; |
| 471 | |
| 472 | case TPOPT_PARAMS: |
| 473 | /* This handles: |
| 474 | * timer values, |
| 475 | * class, use of transport expedited data, |
| 476 | * max tpdu size, checksum, xtd format and |
| 477 | * disconnect indications, and may get rid of connect/disc data |
| 478 | */ |
| 479 | IFDEBUG(D_SETPARAMS) |
| 480 | printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value, |
| 481 | cmd==PRCO_GETOPT?"GET":"SET"); |
| 482 | ENDDEBUG |
| 483 | IFDEBUG(D_REQUEST) |
| 484 | printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value, |
| 485 | cmd==PRCO_GETOPT?"GET":"SET"); |
| 486 | ENDDEBUG |
| 487 | |
| 488 | if ( cmd == PRCO_GETOPT ) { |
| 489 | *(struct tp_conn_param *)value = tpcb->_tp_param; |
| 490 | (*mp)->m_len = sizeof(tpcb->_tp_param); |
| 491 | } else /* cmd == PRCO_SETOPT */ { |
| 492 | if( (error = |
| 493 | tp_consistency(tpcb, TP_STRICT | TP_FORCE, |
| 494 | (struct tp_conn_param *)value))==0) { |
| 495 | /* |
| 496 | * tp_consistency doesn't copy the whole set of params |
| 497 | */ |
| 498 | tpcb->_tp_param = *(struct tp_conn_param *)value; |
| 499 | (*mp)->m_len = sizeof(tpcb->_tp_param); |
| 500 | } |
| 501 | } |
| 502 | break; |
| 503 | |
| 504 | case TPOPT_PSTATISTICS: |
| 505 | #ifdef TP_PERF_MEAS |
| 506 | if (cmd == PRCO_SETOPT) { |
| 507 | error = EINVAL; goto done; |
| 508 | } |
| 509 | IFPERF(tpcb) |
| 510 | if (*mp) { |
| 511 | struct mbuf * n; |
| 512 | do { |
| 513 | MFREE(*mp, n); |
| 514 | *mp = n; |
| 515 | } while (n); |
| 516 | } |
| 517 | *mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK); |
| 518 | ENDPERF |
| 519 | else { |
| 520 | error = EINVAL; goto done; |
| 521 | } |
| 522 | break; |
| 523 | #else |
| 524 | error = EOPNOTSUPP; |
| 525 | goto done; |
| 526 | #endif TP_PERF_MEAS |
| 527 | |
| 528 | case TPOPT_CDDATA_CLEAR: |
| 529 | if (cmd == PRCO_GETOPT) { |
| 530 | error = EINVAL; |
| 531 | } else { |
| 532 | if (tpcb->tp_ucddata) { |
| 533 | m_freem(tpcb->tp_ucddata); |
| 534 | tpcb->tp_ucddata = 0; |
| 535 | } |
| 536 | } |
| 537 | break; |
| 538 | |
| 539 | #ifdef TPOPT_NGC8_ACCEPT |
| 540 | case TPOPT_NGC8_ACCEPT: |
| 541 | if ( cmd == PRCO_GETOPT ) { |
| 542 | *(int *)value = (int)tpcb->tp_flags & TPFLAG_NGC8_ACCEPT; |
| 543 | (*mp)->m_len = sizeof(u_int); |
| 544 | } else { |
| 545 | if (*(int *)value) |
| 546 | tpcb->tp_flags |= TPFLAG_NGC8_ACCEPT; |
| 547 | else |
| 548 | tpcb->tp_flags &= ~TPFLAG_NGC8_ACCEPT; |
| 549 | } |
| 550 | break; |
| 551 | #endif |
| 552 | |
| 553 | case TPOPT_CFRM_DATA: |
| 554 | if (cmd == PRCO_SETOPT && (so->so_state & SS_ISCONFIRMING)) |
| 555 | (void) tp_confirm(tpcb); |
| 556 | /* drop through */ |
| 557 | case TPOPT_DISC_DATA: |
| 558 | /* drop through */ |
| 559 | /* sending is for debugging purposes only -- we don't pretend |
| 560 | * to support * data on connect or disconnect fully. It's a |
| 561 | * kludge at best. |
| 562 | * This data-on-connect is only for the active side. It's sort of |
| 563 | * meaningless on the passive side (because |
| 564 | * you can't reject a connect request based on the data |
| 565 | * arriving w/ the CR, this, and because you'd have to |
| 566 | * do this setsockopt system call for each accept). |
| 567 | * but you can use it if you want. |
| 568 | */ |
| 569 | case TPOPT_CONN_DATA: |
| 570 | if( tpcb->tp_class == TP_CLASS_0 ) { |
| 571 | error = EOPNOTSUPP; |
| 572 | break; |
| 573 | } |
| 574 | IFDEBUG(D_REQUEST) |
| 575 | printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data"); |
| 576 | printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n", |
| 577 | (*mp)->m_len, val_len, so->so_snd.sb_cc); |
| 578 | dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd "); |
| 579 | ENDDEBUG |
| 580 | if (cmd == PRCO_SETOPT) { |
| 581 | int len = tpcb->tp_ucddata ? tpcb->tp_ucddata->m_len : 0; |
| 582 | /* can append connect data in several calls */ |
| 583 | if (len + val_len > |
| 584 | (optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) { |
| 585 | error = EMSGSIZE; goto done; |
| 586 | } |
| 587 | (*mp)->m_next = MNULL; |
| 588 | (*mp)->m_act = 0; |
| 589 | if (tpcb->tp_ucddata) |
| 590 | m_cat(tpcb->tp_ucddata, *mp); |
| 591 | else |
| 592 | tpcb->tp_ucddata = *mp; |
| 593 | IFDEBUG(D_REQUEST) |
| 594 | dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA"); |
| 595 | ENDDEBUG |
| 596 | IFTRACE(D_REQUEST) |
| 597 | tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len", |
| 598 | tpcb->tp_flags, so->so_snd.sb_cc,val_len,0); |
| 599 | ENDTRACE |
| 600 | *mp = MNULL; /* prevent sosetopt from freeing it! */ |
| 601 | } |
| 602 | break; |
| 603 | |
| 604 | case TPOPT_PERF_MEAS: |
| 605 | #ifdef TP_PERF_MEAS |
| 606 | if (cmd == PRCO_GETOPT) { |
| 607 | *value = (u_int)tpcb->tp_perf_on; |
| 608 | (*mp)->m_len = sizeof(u_int); |
| 609 | } else if (cmd == PRCO_SETOPT) { |
| 610 | (*mp)->m_len = 0; |
| 611 | if ((*value) != 0 && (*value) != 1 ) |
| 612 | error = EINVAL; |
| 613 | else tpcb->tp_perf_on = (*value); |
| 614 | } |
| 615 | if( tpcb->tp_perf_on ) |
| 616 | error = tp_setup_perf(tpcb); |
| 617 | #else TP_PERF_MEAS |
| 618 | error = EOPNOTSUPP; |
| 619 | #endif TP_PERF_MEAS |
| 620 | break; |
| 621 | |
| 622 | default: |
| 623 | error = EOPNOTSUPP; |
| 624 | } |
| 625 | |
| 626 | done: |
| 627 | IFDEBUG(D_REQUEST) |
| 628 | dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end"); |
| 629 | dump_mbuf(*mp, "tp_ctloutput *mp"); |
| 630 | ENDDEBUG |
| 631 | /* |
| 632 | * sigh: getsockopt looks only at m_len : all output data must |
| 633 | * reside in the first mbuf |
| 634 | */ |
| 635 | if ( error && (*mp) != MNULL ) |
| 636 | (*mp)->m_len = 0; |
| 637 | if( (*mp) != MNULL ) { |
| 638 | ASSERT ( m_compress(*mp, mp) <= MLEN ); |
| 639 | IFDEBUG(D_REQUEST) |
| 640 | dump_mbuf(*mp, "tp_ctloutput *mp after compress"); |
| 641 | ENDDEBUG |
| 642 | } |
| 643 | |
| 644 | splx(s); |
| 645 | return error; |
| 646 | } |