768 lines
21 KiB
C
768 lines
21 KiB
C
/* $NetBSD: tp_output.c,v 1.34 2007/11/09 21:00:06 plunky Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 1991, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* @(#)tp_output.c 8.2 (Berkeley) 2/9/95
|
|
*/
|
|
|
|
/***********************************************************
|
|
Copyright IBM Corporation 1987
|
|
|
|
All Rights Reserved
|
|
|
|
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
|
|
SOFTWARE.
|
|
|
|
******************************************************************/
|
|
|
|
/*
|
|
* ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
|
|
*/
|
|
/*
|
|
* In here is tp_ctloutput(), the guy called by [sg]etsockopt(),
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: tp_output.c,v 1.34 2007/11/09 21:00:06 plunky Exp $");
|
|
|
|
#include "opt_inet.h"
|
|
#include "opt_iso.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/protosw.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/time.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/kauth.h>
|
|
|
|
#include <netiso/tp_param.h>
|
|
#include <netiso/tp_var.h>
|
|
#include <netiso/tp_user.h>
|
|
#include <netiso/tp_stat.h>
|
|
#include <netiso/tp_ip.h>
|
|
#include <netiso/tp_clnp.h>
|
|
#include <netiso/tp_timer.h>
|
|
#include <netiso/argo_debug.h>
|
|
#include <netiso/tp_pcb.h>
|
|
#include <netiso/tp_trace.h>
|
|
|
|
#define TPDUSIZESHIFT 24
|
|
#define CLASSHIFT 16
|
|
|
|
/*
|
|
* NAME: tp_consistency()
|
|
*
|
|
* CALLED FROM:
|
|
* 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 arguments 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.
|
|
*
|
|
* RETURNS
|
|
* E* or EOK
|
|
* E* if the various parms aren't ok for a given class
|
|
* EOK if they are ok for a given class
|
|
*/
|
|
|
|
int
|
|
tp_consistency(struct tp_pcb *tpcb, u_int cmd, struct tp_conn_param *param)
|
|
{
|
|
int error = EOK;
|
|
int class_to_use = tp_mask_to_num(param->p_class);
|
|
|
|
#ifdef TPPT
|
|
if (tp_traceflags[D_SETPARAMS]) {
|
|
tptrace(TPPTmisc,
|
|
"tp_consist enter class_to_use dontchange param.class cmd",
|
|
class_to_use, param->p_dont_change_params, param->p_class, cmd);
|
|
}
|
|
#endif
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_SETPARAMS]) {
|
|
printf("tp_consistency %s %s\n",
|
|
cmd & TP_FORCE ? "TP_FORCE" : "",
|
|
cmd & TP_STRICT ? "TP_STRICT" : "");
|
|
}
|
|
#endif
|
|
if ((cmd & TP_FORCE) && (param->p_dont_change_params)) {
|
|
cmd &= ~TP_FORCE;
|
|
}
|
|
/*
|
|
* can switch net services within a domain, but cannot switch domains
|
|
*/
|
|
switch (param->p_netservice) {
|
|
case ISO_CONS:
|
|
case ISO_CLNS:
|
|
case ISO_COSNS:
|
|
/* param->p_netservice in ISO DOMAIN */
|
|
if (tpcb->tp_domain != AF_ISO) {
|
|
error = EINVAL;
|
|
goto done;
|
|
}
|
|
break;
|
|
case IN_CLNS:
|
|
/* param->p_netservice in INET DOMAIN */
|
|
if (tpcb->tp_domain != AF_INET) {
|
|
error = EINVAL;
|
|
goto done;
|
|
}
|
|
break;
|
|
/* no others not possible-> netservice is a 2-bit field! */
|
|
}
|
|
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_SETPARAMS]) {
|
|
printf("p_class 0x%x, class_to_use 0x%x\n", param->p_class,
|
|
class_to_use);
|
|
}
|
|
#endif
|
|
if ((param->p_netservice > TP_MAX_NETSERVICES)) {
|
|
error = EINVAL;
|
|
goto done;
|
|
}
|
|
if ((param->p_class & TP_CLASSES_IMPLEMENTED) == 0) {
|
|
error = EINVAL;
|
|
goto done;
|
|
}
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_SETPARAMS]) {
|
|
printf("Nretrans 0x%x\n", param->p_Nretrans);
|
|
}
|
|
#endif
|
|
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;
|
|
}
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_SETPARAMS]) {
|
|
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);
|
|
}
|
|
#endif
|
|
switch (class_to_use) {
|
|
|
|
case 0:
|
|
/* do not use checksums, xtd format, or XPD */
|
|
|
|
if (param->p_use_checksum | param->p_xtd_format | param->p_xpd_service) {
|
|
if (cmd & TP_STRICT) {
|
|
error = EINVAL;
|
|
} else {
|
|
param->p_use_checksum = 0;
|
|
param->p_xtd_format = 0;
|
|
param->p_xpd_service = 0;
|
|
}
|
|
break;
|
|
}
|
|
if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
|
|
if (cmd & TP_STRICT) {
|
|
error = EINVAL;
|
|
} else {
|
|
param->p_tpdusize = TP_MIN_TPDUSIZE;
|
|
}
|
|
break;
|
|
}
|
|
if (param->p_tpdusize > TP0_TPDUSIZE) {
|
|
if (cmd & TP_STRICT) {
|
|
error = EINVAL;
|
|
} else {
|
|
param->p_tpdusize = TP0_TPDUSIZE;
|
|
}
|
|
break;
|
|
}
|
|
/* connect/disc data not allowed for class 0 */
|
|
if (tpcb->tp_ucddata) {
|
|
if (cmd & TP_STRICT) {
|
|
error = EINVAL;
|
|
} else if (cmd & TP_FORCE) {
|
|
m_freem(tpcb->tp_ucddata);
|
|
tpcb->tp_ucddata = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_SETPARAMS]) {
|
|
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);
|
|
}
|
|
#endif
|
|
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))) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_SETPARAMS]) {
|
|
printf("rx_strat 0x%x\n", param->p_rx_strat);
|
|
}
|
|
#endif
|
|
if (param->p_rx_strat >
|
|
(TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART)) {
|
|
if (cmd & TP_STRICT) {
|
|
error = EINVAL;
|
|
} else {
|
|
param->p_rx_strat = TPRX_USE_CW;
|
|
}
|
|
break;
|
|
}
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_SETPARAMS]) {
|
|
printf("ack_strat 0x%x\n", param->p_ack_strat);
|
|
}
|
|
#endif
|
|
if ((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) {
|
|
if (cmd & TP_STRICT) {
|
|
error = EINVAL;
|
|
} else {
|
|
param->p_ack_strat = TPACK_WINDOW;
|
|
}
|
|
break;
|
|
}
|
|
if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
|
|
if (cmd & TP_STRICT) {
|
|
error = EINVAL;
|
|
} else {
|
|
param->p_tpdusize = TP_MIN_TPDUSIZE;
|
|
}
|
|
break;
|
|
}
|
|
if (param->p_tpdusize > TP_TPDUSIZE) {
|
|
if (cmd & TP_STRICT) {
|
|
error = EINVAL;
|
|
} else {
|
|
param->p_tpdusize = TP_TPDUSIZE;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ((error == 0) && (cmd & TP_FORCE)) {
|
|
long dusize = ((long) param->p_ptpdusize) << 7;
|
|
/* Enforce Negotation rules below */
|
|
tpcb->tp_class = param->p_class;
|
|
if (tpcb->tp_use_checksum || param->p_use_checksum)
|
|
tpcb->tp_use_checksum = 1;
|
|
if (!tpcb->tp_xpd_service || !param->p_xpd_service)
|
|
tpcb->tp_xpd_service = 0;
|
|
if (!tpcb->tp_xtd_format || !param->p_xtd_format)
|
|
tpcb->tp_xtd_format = 0;
|
|
if (dusize) {
|
|
if (tpcb->tp_l_tpdusize > dusize)
|
|
tpcb->tp_l_tpdusize = dusize;
|
|
if (tpcb->tp_ptpdusize == 0 ||
|
|
tpcb->tp_ptpdusize > param->p_ptpdusize)
|
|
tpcb->tp_ptpdusize = param->p_ptpdusize;
|
|
} else {
|
|
if (param->p_tpdusize != 0 &&
|
|
tpcb->tp_tpdusize > param->p_tpdusize)
|
|
tpcb->tp_tpdusize = param->p_tpdusize;
|
|
tpcb->tp_l_tpdusize = 1 << tpcb->tp_tpdusize;
|
|
}
|
|
}
|
|
done:
|
|
|
|
#ifdef TPPT
|
|
if (tp_traceflags[D_CONN]) {
|
|
tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd",
|
|
error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
|
|
}
|
|
#endif
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_CONN]) {
|
|
printf(
|
|
"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);
|
|
}
|
|
#endif
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* NAME: tp_ctloutput()
|
|
*
|
|
* CALLED FROM:
|
|
* [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).
|
|
* (so) is the socket.
|
|
* (level) is SOL_TRANSPORT (see ../sys/socket.h)
|
|
* (optname) is the particular command or option to be set.
|
|
* (**mp) is an mbuf structure.
|
|
*
|
|
* RETURN VALUE:
|
|
* ENOTSOCK if the socket hasn't got an associated tpcb
|
|
* EINVAL if
|
|
* 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
|
|
* is established
|
|
* ENOTCONN if trying a command that is allowed only if a connection is
|
|
* established
|
|
* EMSGSIZE if trying to give too much data on connect/disconnect
|
|
*
|
|
* SIDE EFFECTS:
|
|
*
|
|
* NOTES:
|
|
*/
|
|
int
|
|
tp_ctloutput(int cmd, struct socket *so, int level, int optname,
|
|
struct mbuf **mp)
|
|
{
|
|
struct lwp *l = curlwp; /* XXX */
|
|
struct tp_pcb *tpcb = sototpcb(so);
|
|
int s = splsoftnet();
|
|
void * value;
|
|
unsigned val_len;
|
|
int error = 0;
|
|
|
|
#ifdef TPPT
|
|
if (tp_traceflags[D_REQUEST]) {
|
|
tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
|
|
cmd, so, optname, mp);
|
|
}
|
|
#endif
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_REQUEST]) {
|
|
printf(
|
|
"tp_ctloutput so %p cmd 0x%x optname 0x%x, mp %p *mp %p tpcb %p\n",
|
|
so, cmd, optname, mp, mp ? *mp : 0, tpcb);
|
|
}
|
|
#endif
|
|
if (tpcb == (struct tp_pcb *) 0) {
|
|
error = ENOTSOCK;
|
|
goto done;
|
|
}
|
|
if (mp && *mp == NULL) {
|
|
struct mbuf *m;
|
|
|
|
MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
|
|
if (m == NULL) {
|
|
splx(s);
|
|
return ENOBUFS;
|
|
}
|
|
m->m_len = 0;
|
|
m->m_nextpkt = 0;
|
|
*mp = m;
|
|
}
|
|
/*
|
|
* Hook so one can set network options via a tp socket.
|
|
*/
|
|
if (level == SOL_NETWORK) {
|
|
if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
|
|
error = ENOTSOCK;
|
|
else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
|
|
error = EOPNOTSUPP;
|
|
else
|
|
return ((tpcb->tp_nlproto->nlp_ctloutput) (cmd, optname,
|
|
tpcb->tp_npcb, *mp));
|
|
goto done;
|
|
} else if (level == SOL_SOCKET) {
|
|
if (optname == SO_RCVBUF && cmd == PRCO_SETOPT) {
|
|
u_long old_credit = tpcb->tp_maxlcredit;
|
|
tp_rsyset(tpcb);
|
|
if (tpcb->tp_rhiwat != so->so_rcv.sb_hiwat &&
|
|
tpcb->tp_state == TP_OPEN &&
|
|
(old_credit < tpcb->tp_maxlcredit))
|
|
tp_emit(AK_TPDU_type, tpcb,
|
|
tpcb->tp_rcvnxt, 0, NULL);
|
|
tpcb->tp_rhiwat = so->so_rcv.sb_hiwat;
|
|
}
|
|
goto done;
|
|
} else if (level != SOL_TRANSPORT) {
|
|
error = EOPNOTSUPP;
|
|
goto done;
|
|
}
|
|
if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
|
|
error = EOPNOTSUPP;
|
|
goto done;
|
|
}
|
|
if (so->so_error) {
|
|
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))
|
|
&&
|
|
(cmd == PRCO_SETOPT &&
|
|
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 TPOPT_PSTATISTICS and they're not allowed if the ref timer has
|
|
* gone off, because the tpcb is gone
|
|
*/
|
|
if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) == 0) {
|
|
if (so->so_pcb == 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, void *); /* it's aligned, don't worry, but
|
|
* lint complains about it */
|
|
val_len = (*mp)->m_len;
|
|
|
|
switch (optname) {
|
|
|
|
case TPOPT_INTERCEPT:
|
|
#define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr)
|
|
#define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr)
|
|
|
|
if (l == 0 || (error = kauth_authorize_generic(l->l_cred,
|
|
KAUTH_GENERIC_ISSUSER, NULL))) {
|
|
error = EPERM;
|
|
} else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED ||
|
|
(tpcb->tp_flags & TPF_GENERAL_ADDR) ||
|
|
tpcb->tp_next == 0)
|
|
error = EINVAL;
|
|
else {
|
|
struct tp_pcb *t;
|
|
error = EADDRINUSE;
|
|
for (t = tp_listeners; t; t = t->tp_nextlisten)
|
|
if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 &&
|
|
t->tp_domain == tpcb->tp_domain)
|
|
switch (tpcb->tp_domain) {
|
|
default:
|
|
goto done;
|
|
#ifdef INET
|
|
case AF_INET:
|
|
if (INA(t) == INA(tpcb))
|
|
goto done;
|
|
continue;
|
|
#endif
|
|
#ifdef ISO
|
|
case AF_ISO:
|
|
if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr,
|
|
ISOA(t).isoa_len) == 0)
|
|
goto done;
|
|
continue;
|
|
#endif
|
|
}
|
|
tpcb->tp_lsuffixlen = 0;
|
|
tpcb->tp_state = TP_LISTENING;
|
|
error = 0;
|
|
iso_remque(tpcb);
|
|
tpcb->tp_next = tpcb->tp_prev = tpcb;
|
|
tpcb->tp_nextlisten = tp_listeners;
|
|
tp_listeners = tpcb;
|
|
}
|
|
break;
|
|
|
|
case TPOPT_MY_TSEL:
|
|
if (cmd == PRCO_GETOPT) {
|
|
ASSERT(tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN);
|
|
bcopy((void *) 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 %p\n",
|
|
val_len, (*mp));
|
|
error = EINVAL;
|
|
} else {
|
|
bcopy(value, (void *) tpcb->tp_lsuffix, val_len);
|
|
tpcb->tp_lsuffixlen = val_len;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TPOPT_PEER_TSEL:
|
|
if (cmd == PRCO_GETOPT) {
|
|
ASSERT(tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN);
|
|
bcopy((void *) 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 %p\n",
|
|
val_len, (*mp));
|
|
error = EINVAL;
|
|
} else {
|
|
bcopy(value, (void *) tpcb->tp_fsuffix, val_len);
|
|
tpcb->tp_fsuffixlen = val_len;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TPOPT_FLAGS:
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_REQUEST]) {
|
|
printf("%s TPOPT_FLAGS value %p *value 0x%x, flags 0x%x \n",
|
|
cmd == PRCO_GETOPT ? "GET" : "SET",
|
|
value,
|
|
*(unsigned char *)value,
|
|
tpcb->tp_flags);
|
|
}
|
|
#endif
|
|
|
|
if (cmd == PRCO_GETOPT) {
|
|
*(int *) value = (int) tpcb->tp_flags;
|
|
(*mp)->m_len = sizeof(u_int);
|
|
} else { /* cmd == PRCO_SETOPT */
|
|
error = EINVAL;
|
|
goto done;
|
|
}
|
|
break;
|
|
|
|
case TPOPT_PARAMS:
|
|
/*
|
|
* This handles: timer values, class, use of transport
|
|
* expedited data, max tpdu size, checksum, xtd format and
|
|
* disconnect indications, and may get rid of connect/disc
|
|
* data
|
|
*/
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_SETPARAMS]) {
|
|
printf("TPOPT_PARAMS value %p, cmd %s \n", value,
|
|
cmd == PRCO_GETOPT ? "GET" : "SET");
|
|
}
|
|
#endif
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_REQUEST]) {
|
|
printf("TPOPT_PARAMS value %p, cmd %s \n", value,
|
|
cmd == PRCO_GETOPT ? "GET" : "SET");
|
|
}
|
|
#endif
|
|
|
|
if (cmd == PRCO_GETOPT) {
|
|
*(struct tp_conn_param *) value = tpcb->_tp_param;
|
|
(*mp)->m_len = sizeof(tpcb->_tp_param);
|
|
} else { /* cmd == PRCO_SETOPT */
|
|
if ((error =
|
|
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);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TPOPT_PSTATISTICS:
|
|
#ifdef TP_PERF_MEAS
|
|
if (cmd == PRCO_SETOPT) {
|
|
error = EINVAL;
|
|
goto done;
|
|
}
|
|
if (tpcb->tp_perf_on) {
|
|
m_clget(*mp, M_WAIT);
|
|
if (((*mp)->m_flags & M_EXT) == 0) {
|
|
error = ENOBUFS; goto done;
|
|
}
|
|
(*mp)->m_len = sizeof(struct tp_pmeas);
|
|
bcopy(tpcb->tp_p_meas, mtod(*mp), sizeof(struct tp_pmeas));
|
|
}
|
|
else {
|
|
error = EINVAL;
|
|
goto done;
|
|
}
|
|
break;
|
|
#else
|
|
error = EOPNOTSUPP;
|
|
goto done;
|
|
#endif /* TP_PERF_MEAS */
|
|
|
|
case TPOPT_CDDATA_CLEAR:
|
|
if (cmd == PRCO_GETOPT) {
|
|
error = EINVAL;
|
|
} else {
|
|
if (tpcb->tp_ucddata) {
|
|
m_freem(tpcb->tp_ucddata);
|
|
tpcb->tp_ucddata = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TPOPT_CFRM_DATA:
|
|
case TPOPT_DISC_DATA:
|
|
case TPOPT_CONN_DATA:
|
|
if (tpcb->tp_class == TP_CLASS_0) {
|
|
error = EOPNOTSUPP;
|
|
break;
|
|
}
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_REQUEST]) {
|
|
printf("%s\n", optname == TPOPT_DISC_DATA ? "DISC data" : "CONN data");
|
|
printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%lx\n",
|
|
(*mp)->m_len, val_len, so->so_snd.sb_cc);
|
|
dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
|
|
}
|
|
#endif
|
|
if (cmd == PRCO_SETOPT) {
|
|
int len = tpcb->tp_ucddata ? tpcb->tp_ucddata->m_len : 0;
|
|
/* can append connect data in several calls */
|
|
if (len + val_len >
|
|
(optname == TPOPT_CONN_DATA ? TP_MAX_CR_DATA : TP_MAX_DR_DATA)) {
|
|
error = EMSGSIZE;
|
|
goto done;
|
|
}
|
|
(*mp)->m_next = NULL;
|
|
(*mp)->m_nextpkt = 0;
|
|
if (tpcb->tp_ucddata)
|
|
m_cat(tpcb->tp_ucddata, *mp);
|
|
else
|
|
tpcb->tp_ucddata = *mp;
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_REQUEST]) {
|
|
dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
|
|
}
|
|
#endif
|
|
#ifdef TPPT
|
|
if (tp_traceflags[D_REQUEST]) {
|
|
tptrace(TPPTmisc, "C/D DATA: flags snd.sbcc val_len",
|
|
tpcb->tp_flags, so->so_snd.sb_cc, val_len, 0);
|
|
}
|
|
#endif
|
|
*mp = NULL;
|
|
if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING))
|
|
(void) tp_confirm(tpcb);
|
|
}
|
|
break;
|
|
|
|
case TPOPT_PERF_MEAS:
|
|
#ifdef TP_PERF_MEAS
|
|
if (cmd == PRCO_GETOPT) {
|
|
*value = (u_int) tpcb->tp_perf_on;
|
|
(*mp)->m_len = sizeof(u_int);
|
|
} else if (cmd == PRCO_SETOPT) {
|
|
(*mp)->m_len = 0;
|
|
if ((*value) != 0 && (*value) != 1)
|
|
error = EINVAL;
|
|
else
|
|
tpcb->tp_perf_on = (*value);
|
|
}
|
|
if (tpcb->tp_perf_on)
|
|
error = tp_setup_perf(tpcb);
|
|
#else /* TP_PERF_MEAS */
|
|
error = EOPNOTSUPP;
|
|
#endif /* TP_PERF_MEAS */
|
|
break;
|
|
|
|
default:
|
|
error = EOPNOTSUPP;
|
|
}
|
|
|
|
done:
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_REQUEST]) {
|
|
dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
|
|
dump_mbuf(*mp, "tp_ctloutput *mp");
|
|
}
|
|
#endif
|
|
/*
|
|
* sigh: getsockopt looks only at m_len : all output data must reside
|
|
* in the first mbuf
|
|
*/
|
|
if (*mp) {
|
|
if (cmd == PRCO_SETOPT) {
|
|
m_freem(*mp);
|
|
*mp = NULL;
|
|
} else {
|
|
ASSERT(m_compress(*mp, mp) <= MLEN);
|
|
if (error)
|
|
(*mp)->m_len = 0;
|
|
#ifdef ARGO_DEBUG
|
|
if (argo_debug[D_REQUEST]) {
|
|
dump_mbuf(*mp, "tp_ctloutput *mp after compress");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
splx(s);
|
|
return error;
|
|
}
|