/***********************************************************
Copyright IBM Corporation 1987
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of IBM not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
******************************************************************/
* ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
* $Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $
* $Source: /usr/argo/sys/netiso/RCS/tp_output.c,v $
* @(#)tp_output.c 7.5 (Berkeley) %G% *
* In here is tp_ctloutput(), the guy called by [sg]etsockopt(),
static char *rcsid
= "$Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $";
#define USERFLAGSMASK_G 0x0f00643b
#define USERFLAGSMASK_S 0x0f000432
* tp_ctloutput(), tp_input()
* FUNCTION and ARGUMENTS:
* Checks the consistency of options and tpdusize with class,
* using the parameters passed in via (param).
* (cmd) may be TP_STRICT or TP_FORCE or both.
* Force means it will set all the values in (tpcb) to those in
* the input arguements iff no errors were encountered.
* Strict means that no inconsistency will be tolerated. If it's
* not used, checksum and tpdusize inconsistencies will be tolerated.
* The reason for this is that in some cases, when we're negotiating down
* from class 4, these options should be changed but should not
* cause negotiation to fail.
* E* if the various parms aren't ok for a given class
* EOK if they are ok for a given class
tp_consistency( tpcb
, cmd
, param
)
struct tp_conn_param
*param
;
register int error
= EOK
;
int class_to_use
= tp_mask_to_num(param
->p_class
);
"tp_consist enter class_to_use dontchange param.class cmd",
class_to_use
, param
->p_dont_change_params
, param
->p_class
, cmd
);
printf("tp_consistency %s %s\n",
cmd
& TP_FORCE
? "TP_FORCE": "",
cmd
& TP_STRICT
? "TP_STRICT":"");
if ((cmd
& TP_FORCE
) && (param
->p_dont_change_params
)) {
/* can switch net services within a domain, but
switch( param
->p_netservice
) {
/* param->p_netservice in ISO DOMAIN */
if(tpcb
->tp_domain
!= AF_ISO
) {
error
= EINVAL
; goto done
;
/* param->p_netservice in INET DOMAIN */
if( tpcb
->tp_domain
!= AF_INET
) {
error
= EINVAL
; goto done
;
/* no others not possible-> netservice is a 2-bit field! */
printf("p_class 0x%x, class_to_use 0x%x\n", param
->p_class
,
if((param
->p_netservice
< 0) || (param
->p_netservice
> TP_MAX_NETSERVICES
)){
error
= EINVAL
; goto done
;
if( (param
->p_class
& TP_CLASSES_IMPLEMENTED
) == 0 ) {
error
= EINVAL
; goto done
;
printf("Nretrans 0x%x\n", param
->p_Nretrans
);
if( ( param
->p_Nretrans
< 1 ) ||
(param
->p_cr_ticks
< 1) || (param
->p_cc_ticks
< 1) ) {
/* bad for any class because negot has to be done a la class 4 */
error
= EINVAL
; goto done
;
printf("winsize 0x%x\n", param
->p_winsize
);
if( (param
->p_winsize
< 128 ) ||
(param
->p_winsize
< param
->p_tpdusize
) ||
(param
->p_winsize
> ((1+SB_MAX
)>>2 /* 1/4 of the max */)) ) {
error
= EINVAL
; goto done
;
if( tpcb
->tp_state
== TP_CLOSED
)
soreserve(tpcb
->tp_sock
, (u_long
)param
->p_winsize
,
(u_long
)param
->p_winsize
);
printf("use_csum 0x%x\n", param
->p_use_checksum
);
printf("xtd_format 0x%x\n", param
->p_xtd_format
);
printf("xpd_service 0x%x\n", param
->p_xpd_service
);
printf("tpdusize 0x%x\n", param
->p_tpdusize
);
printf("tpcb->flags 0x%x\n", tpcb
->tp_flags
);
/* do not use checksums, xtd format, or XPD */
if( param
->p_use_checksum
| param
->p_xtd_format
| param
->p_xpd_service
) {
param
->p_use_checksum
= 0;
param
->p_xpd_service
= 0;
if (param
->p_tpdusize
< TP_MIN_TPDUSIZE
) {
param
->p_tpdusize
= TP_MIN_TPDUSIZE
;
if (param
->p_tpdusize
> TP0_TPDUSIZE
) {
param
->p_tpdusize
= TP0_TPDUSIZE
;
/* connect/disc data not allowed for class 0 */
} else if(cmd
& TP_FORCE
) {
m_freem(tpcb
->tp_ucddata
);
printf("dt_ticks 0x%x\n", param
->p_dt_ticks
);
printf("x_ticks 0x%x\n", param
->p_x_ticks
);
printf("dr_ticks 0x%x\n", param
->p_dr_ticks
);
printf("keepalive 0x%x\n", param
->p_keepalive_ticks
);
printf("sendack 0x%x\n", param
->p_sendack_ticks
);
printf("inact 0x%x\n", param
->p_inact_ticks
);
printf("ref 0x%x\n", param
->p_ref_ticks
);
if( (param
->p_class
& TP_CLASS_4
) && (
(param
->p_dt_ticks
< 1) || (param
->p_dr_ticks
< 1) ||
(param
->p_x_ticks
< 1) || (param
->p_keepalive_ticks
< 1) ||
(param
->p_sendack_ticks
< 1) || (param
->p_ref_ticks
< 1) ||
(param
->p_inact_ticks
< 1) ) ) {
printf("rx_strat 0x%x\n", param
->p_rx_strat
);
( TPRX_USE_CW
| TPRX_EACH
| TPRX_FASTSTART
) ) {
param
->p_rx_strat
= TPRX_USE_CW
;
printf("ack_strat 0x%x\n", param
->p_ack_strat
);
if((param
->p_ack_strat
!= 0) && (param
->p_ack_strat
!= 1)) {
param
->p_ack_strat
= TPACK_WINDOW
;
if (param
->p_tpdusize
< TP_MIN_TPDUSIZE
) {
param
->p_tpdusize
= TP_MIN_TPDUSIZE
;
if (param
->p_tpdusize
> TP_TPDUSIZE
) {
param
->p_tpdusize
= TP_TPDUSIZE
;
if ((error
==0) && (cmd
& TP_FORCE
)) {
tpcb
->tp_tpdusize
= param
->p_tpdusize
;
tpcb
->tp_class
= param
->p_class
;
tpcb
->tp_use_checksum
= param
->p_use_checksum
;
tpcb
->tp_xpd_service
= param
->p_xpd_service
;
tpcb
->tp_xtd_format
= param
->p_xtd_format
;
tptrace(TPPTmisc
, "tp_consist returns class xtdfmt cmd",
error
, tpcb
->tp_class
, tpcb
->tp_xtd_format
, cmd
);
"tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n",
error
, tpcb
->tp_class
, tpcb
->tp_xtd_format
, cmd
);
* [sg]etsockopt(), via so[sg]etopt().
* FUNCTION and ARGUMENTS:
* Implements the socket options at transport level.
* (cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h).
* (level) is SOL_TRANSPORT (see ../sys/socket.h)
* (optname) is the particular command or option to be set.
* (**mp) is an mbuf structure.
* ENOTSOCK if the socket hasn't got an associated tpcb
* trying to set window too big
* trying to set illegal max tpdu size
* trying to set illegal credit fraction
* trying to use unknown or unimplemented class of TP
* structure passed to set timer values is wrong size
* illegal combination of command/GET-SET option,
* e.g., GET w/ TPOPT_CDDATA_CLEAR:
* EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
* or if the transport-specific command is not implemented
* EISCONN if trying a command that isn't allowed after a connection
* ENOTCONN if trying a command that is allowed only if a connection is
* EMSGSIZE if trying to give too much data on connect/disconnect
tp_ctloutput(cmd
, so
, level
, optname
, mp
)
struct tp_pcb
*tpcb
= sototpcb(so
);
tptrace(TPPTmisc
, "tp_ctloutput cmd so optname mp",
"tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n",
so
, cmd
, optname
, mp
, mp
?*mp
:0, tpcb
);
if( tpcb
== (struct tp_pcb
*)0 ) {
error
= ENOTSOCK
; goto done
;
MGET(m
, M_DONTWAIT
, TPMT_SONAME
); /* does off, type, next */
* Hook so one can set network options via a tp socket.
if ( level
== SOL_NETWORK
) {
if ((tpcb
->tp_nlproto
== NULL
) || (tpcb
->tp_npcb
== NULL
))
else if (tpcb
->tp_nlproto
->nlp_ctloutput
== NULL
)
error
= (tpcb
->tp_nlproto
->nlp_ctloutput
)(cmd
, optname
,
} else if ( level
!= SOL_TRANSPORT
) {
error
= EOPNOTSUPP
; goto done
;
if (cmd
!= PRCO_GETOPT
&& cmd
!= PRCO_SETOPT
) {
error
= EOPNOTSUPP
; goto done
;
error
= so
->so_error
; goto done
;
/* The only options allowed after connection is established
* are GET (anything) and SET DISC DATA and SET PERF MEAS
if ( ((so
->so_state
& SS_ISCONNECTING
)||(so
->so_state
& SS_ISCONNECTED
))
optname
!= TPOPT_DISC_DATA
&&
optname
!= TPOPT_CFRM_DATA
&&
optname
!= TPOPT_PERF_MEAS
&&
optname
!= TPOPT_CDDATA_CLEAR
) ) {
error
= EISCONN
; goto done
;
/* The only options allowed after disconnection are GET DISC DATA,
* and they're not allowed if the ref timer has gone off, because
if ((so
->so_state
& (SS_ISCONNECTED
| SS_ISCONFIRMING
)) == 0) {
if ( so
->so_tpcb
== (caddr_t
)0 ) {
error
= ENOTCONN
; goto done
;
if ( (tpcb
->tp_state
== TP_REFWAIT
|| tpcb
->tp_state
== TP_CLOSING
) &&
(optname
!= TPOPT_DISC_DATA
&& optname
!= TPOPT_PSTATISTICS
)) {
error
= ENOTCONN
; goto done
;
value
= mtod(*mp
, caddr_t
); /* it's aligned, don't worry,
* but lint complains about it
if ( cmd
== PRCO_GETOPT
) {
ASSERT( tpcb
->tp_lsuffixlen
<= MAX_TSAP_SEL_LEN
);
bcopy((caddr_t
)tpcb
->tp_lsuffix
, value
, tpcb
->tp_lsuffixlen
);
(*mp
)->m_len
= tpcb
->tp_lsuffixlen
;
} else /* cmd == PRCO_SETOPT */ {
if( (val_len
> MAX_TSAP_SEL_LEN
) || (val_len
<= 0 )) {
printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len
, (*mp
));
bcopy(value
, (caddr_t
)tpcb
->tp_lsuffix
, val_len
);
tpcb
->tp_lsuffixlen
= val_len
;
if ( cmd
== PRCO_GETOPT
) {
ASSERT( tpcb
->tp_fsuffixlen
<= MAX_TSAP_SEL_LEN
);
bcopy((caddr_t
)tpcb
->tp_fsuffix
, value
, tpcb
->tp_fsuffixlen
);
(*mp
)->m_len
= tpcb
->tp_fsuffixlen
;
} else /* cmd == PRCO_SETOPT */ {
if( (val_len
> MAX_TSAP_SEL_LEN
) || (val_len
<= 0 )) {
printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len
, (*mp
));
bcopy(value
, (caddr_t
)tpcb
->tp_fsuffix
, val_len
);
tpcb
->tp_fsuffixlen
= val_len
;
printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n",
cmd
==PRCO_GETOPT
?"GET":"SET",
if ( cmd
== PRCO_GETOPT
) {
*(int *)value
= (int)tpcb
->tp_flags
;
(*mp
)->m_len
= sizeof(u_int
);
} else /* cmd == PRCO_SETOPT */ {
error
= EINVAL
; goto done
;
* class, use of transport expedited data,
* max tpdu size, checksum, xtd format and
* disconnect indications, and may get rid of connect/disc data
printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value
,
cmd
==PRCO_GETOPT
?"GET":"SET");
printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value
,
cmd
==PRCO_GETOPT
?"GET":"SET");
if ( cmd
== PRCO_GETOPT
) {
*(struct tp_conn_param
*)value
= tpcb
->_tp_param
;
(*mp
)->m_len
= sizeof(tpcb
->_tp_param
);
} else /* cmd == PRCO_SETOPT */ {
tp_consistency(tpcb
, TP_STRICT
| TP_FORCE
,
(struct tp_conn_param
*)value
))==0) {
* tp_consistency doesn't copy the whole set of params
tpcb
->_tp_param
= *(struct tp_conn_param
*)value
;
(*mp
)->m_len
= sizeof(tpcb
->_tp_param
);
if (cmd
== PRCO_SETOPT
) {
error
= EINVAL
; goto done
;
*mp
= m_copym(tpcb
->tp_p_mbuf
, (int)M_COPYALL
, M_WAITOK
);
error
= EINVAL
; goto done
;
if (cmd
== PRCO_GETOPT
) {
m_freem(tpcb
->tp_ucddata
);
if( tpcb
->tp_class
== TP_CLASS_0
) {
printf("%s\n", optname
==TPOPT_DISC_DATA
?"DISC data":"CONN data");
printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n",
(*mp
)->m_len
, val_len
, so
->so_snd
.sb_cc
);
dump_mbuf(so
->so_snd
.sb_mb
, "tp_ctloutput: sosnd ");
if (cmd
== PRCO_SETOPT
) {
int len
= tpcb
->tp_ucddata
? tpcb
->tp_ucddata
->m_len
: 0;
/* can append connect data in several calls */
(optname
==TPOPT_CONN_DATA
?TP_MAX_CR_DATA
:TP_MAX_DR_DATA
) ) {
error
= EMSGSIZE
; goto done
;
m_cat(tpcb
->tp_ucddata
, *mp
);
dump_mbuf(tpcb
->tp_ucddata
, "tp_ctloutput after CONN_DATA");
tptrace(TPPTmisc
,"C/D DATA: flags snd.sbcc val_len",
tpcb
->tp_flags
, so
->so_snd
.sb_cc
,val_len
,0);
*mp
= MNULL
; /* prevent sosetopt from freeing it! */
if (optname
== TPOPT_CFRM_DATA
&& (so
->so_state
& SS_ISCONFIRMING
))
if (cmd
== PRCO_GETOPT
) {
*value
= (u_int
)tpcb
->tp_perf_on
;
(*mp
)->m_len
= sizeof(u_int
);
} else if (cmd
== PRCO_SETOPT
) {
if ((*value
) != 0 && (*value
) != 1 )
else tpcb
->tp_perf_on
= (*value
);
error
= tp_setup_perf(tpcb
);
dump_mbuf(so
->so_snd
.sb_mb
, "tp_ctloutput sosnd at end");
dump_mbuf(*mp
, "tp_ctloutput *mp");
* sigh: getsockopt looks only at m_len : all output data must
* reside in the first mbuf
if ( error
&& (*mp
) != MNULL
)
ASSERT ( m_compress(*mp
, mp
) <= MLEN
);
dump_mbuf(*mp
, "tp_ctloutput *mp after compress");