freebsd-nq/sys/netatm/atm_socket.c
Robert Watson bc725eafc7 Chance protocol switch method pru_detach() so that it returns void
rather than an error.  Detaches do not "fail", they other occur or
the protocol flags SS_PROTOREF to take ownership of the socket.

soclose() no longer looks at so_pcb to see if it's NULL, relying
entirely on the protocol to decide whether it's time to free the
socket or not using SS_PROTOREF.  so_pcb is now entirely owned and
managed by the protocol code.  Likewise, no longer test so_pcb in
other socket functions, such as soreceive(), which have no business
digging into protocol internals.

Protocol detach routines no longer try to free the socket on detach,
this is performed in the socket code if the protocol permits it.

In rts_detach(), no longer test for rp != NULL in detach, and
likewise in other protocols that don't permit a NULL so_pcb, reduce
the incidence of testing for it during detach.

netinet and netinet6 are not fully updated to this change, which
will be in an upcoming commit.  In their current state they may leak
memory or panic.

MFC after:	3 months
2006-04-01 15:42:02 +00:00

1313 lines
31 KiB
C

/*-
* ===================================
* HARP | Host ATM Research Platform
* ===================================
*
*
* This Host ATM Research Platform ("HARP") file (the "Software") is
* made available by Network Computing Services, Inc. ("NetworkCS")
* "AS IS". NetworkCS does not provide maintenance, improvements or
* support of any kind.
*
* NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
* INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
* SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
* In no event shall NetworkCS be responsible for any damages, including
* but not limited to consequential damages, arising from or relating to
* any use of the Software or related support.
*
* Copyright 1994-1998 Network Computing Services, Inc.
*
* Copies of this Software may be made, however, the above copyright
* notice must be reproduced on all copies.
*/
/*
* Core ATM Services
* -----------------
*
* ATM common socket protocol processing
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <netatm/port.h>
#include <netatm/queue.h>
#include <netatm/atm.h>
#include <netatm/atm_sys.h>
#include <netatm/atm_sap.h>
#include <netatm/atm_cm.h>
#include <netatm/atm_if.h>
#include <netatm/atm_sigmgr.h>
#include <netatm/atm_stack.h>
#include <netatm/atm_pcb.h>
#include <netatm/atm_var.h>
/*
* Local functions
*/
/*
* Local variables
*/
static uma_zone_t atm_pcb_zone;
static struct t_atm_cause atm_sock_cause = {
T_ATM_ITU_CODING,
T_ATM_LOC_USER,
T_ATM_CAUSE_UNSPECIFIED_NORMAL,
{0, 0, 0, 0}
};
void
atm_sock_init(void)
{
atm_pcb_zone = uma_zcreate("atm pcb", sizeof(Atm_pcb), NULL, NULL,
NULL, NULL, UMA_ALIGN_PTR, 0);
if (atm_pcb_zone == NULL)
panic("atm_sock_init: unable to initialize atm_pcb_zone");
}
/*
* Allocate resources for a new ATM socket
*
* Called at splnet.
*
* Arguments:
* so pointer to socket
* send socket send buffer maximum
* recv socket receive buffer maximum
*
* Returns:
* 0 attach successful
* errno attach failed - reason indicated
*
*/
int
atm_sock_attach(so, send, recv)
struct socket *so;
u_long send;
u_long recv;
{
Atm_pcb *atp = sotoatmpcb(so);
int err;
/*
* Make sure initialization has happened
*/
if (!atm_init)
atm_initialize();
/*
* Make sure we're not already attached
*/
if (atp)
return (EISCONN);
/*
* Reserve socket buffer space, if not already done
*/
if ((so->so_snd.sb_hiwat == 0) || (so->so_rcv.sb_hiwat == 0)) {
err = soreserve(so, send, recv);
if (err)
return (err);
}
/*
* Allocate and initialize our control block
*/
atp = uma_zalloc(atm_pcb_zone, M_ZERO | M_NOWAIT);
if (atp == NULL)
return (ENOMEM);
atp->atp_socket = so;
so->so_pcb = (caddr_t)atp;
return (0);
}
/*
* Detach from socket and free resources
*
* Called at splnet.
*
* Arguments:
* so pointer to socket
*
*/
void
atm_sock_detach(so)
struct socket *so;
{
Atm_pcb *atp = sotoatmpcb(so);
/*
* Make sure we're still attached
*/
KASSERT(atp != NULL, ("atm_sock_detach: atp == NULL"));
/*
* Terminate any (possibly pending) connection
*/
if (atp->atp_conn) {
(void) atm_sock_disconnect(so);
}
so->so_pcb = NULL;
uma_zfree(atm_pcb_zone, atp);
}
/*
* Bind local address to socket
*
* Called at splnet.
*
* Arguments:
* so pointer to socket
* addr pointer to protocol address
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
int
atm_sock_bind(so, addr)
struct socket *so;
struct sockaddr *addr;
{
Atm_pcb *atp = sotoatmpcb(so);
Atm_attributes attr;
struct sockaddr_atm *satm;
struct t_atm_sap_addr *sapadr;
struct t_atm_sap_layer2 *sapl2;
struct t_atm_sap_layer3 *sapl3;
struct t_atm_sap_appl *sapapl;
/*
* Make sure we're still attached
*/
if (atp == NULL)
return (ENOTCONN);
/*
* Can't change local address once we've started connection process
*/
if (atp->atp_conn != NULL)
return (EADDRNOTAVAIL);
/*
* Validate requested local address
*/
satm = (struct sockaddr_atm *)addr;
if (satm->satm_family != AF_ATM)
return (EAFNOSUPPORT);
sapadr = &satm->satm_addr.t_atm_sap_addr;
if (sapadr->SVE_tag_addr == T_ATM_PRESENT) {
if (sapadr->address_format == T_ATM_ENDSYS_ADDR) {
if (sapadr->SVE_tag_selector != T_ATM_PRESENT)
return (EINVAL);
} else if (sapadr->address_format == T_ATM_E164_ADDR) {
if (sapadr->SVE_tag_selector != T_ATM_ABSENT)
return (EINVAL);
} else
return (EINVAL);
} else if ((sapadr->SVE_tag_addr != T_ATM_ABSENT) &&
(sapadr->SVE_tag_addr != T_ATM_ANY))
return (EINVAL);
if (sapadr->address_length > ATM_ADDR_LEN)
return (EINVAL);
sapl2 = &satm->satm_addr.t_atm_sap_layer2;
if (sapl2->SVE_tag == T_ATM_PRESENT) {
if ((sapl2->ID_type != T_ATM_SIMPLE_ID) &&
(sapl2->ID_type != T_ATM_USER_ID))
return (EINVAL);
} else if ((sapl2->SVE_tag != T_ATM_ABSENT) &&
(sapl2->SVE_tag != T_ATM_ANY))
return (EINVAL);
sapl3 = &satm->satm_addr.t_atm_sap_layer3;
if (sapl3->SVE_tag == T_ATM_PRESENT) {
if ((sapl3->ID_type != T_ATM_SIMPLE_ID) &&
(sapl3->ID_type != T_ATM_IPI_ID) &&
(sapl3->ID_type != T_ATM_SNAP_ID) &&
(sapl3->ID_type != T_ATM_USER_ID))
return (EINVAL);
} else if ((sapl3->SVE_tag != T_ATM_ABSENT) &&
(sapl3->SVE_tag != T_ATM_ANY))
return (EINVAL);
sapapl = &satm->satm_addr.t_atm_sap_appl;
if (sapapl->SVE_tag == T_ATM_PRESENT) {
if ((sapapl->ID_type != T_ATM_ISO_APP_ID) &&
(sapapl->ID_type != T_ATM_USER_APP_ID) &&
(sapapl->ID_type != T_ATM_VENDOR_APP_ID))
return (EINVAL);
} else if ((sapapl->SVE_tag != T_ATM_ABSENT) &&
(sapapl->SVE_tag != T_ATM_ANY))
return (EINVAL);
/*
* Create temporary attributes list so that we can check out the
* new bind parameters before we modify the socket's values;
*/
attr = atp->atp_attr;
attr.called.tag = sapadr->SVE_tag_addr;
bcopy(&sapadr->address_format, &attr.called.addr, sizeof(Atm_addr));
attr.blli.tag_l2 = sapl2->SVE_tag;
if (sapl2->SVE_tag == T_ATM_PRESENT) {
attr.blli.v.layer_2_protocol.ID_type = sapl2->ID_type;
bcopy(&sapl2->ID, &attr.blli.v.layer_2_protocol.ID,
sizeof(attr.blli.v.layer_2_protocol.ID));
}
attr.blli.tag_l3 = sapl3->SVE_tag;
if (sapl3->SVE_tag == T_ATM_PRESENT) {
attr.blli.v.layer_3_protocol.ID_type = sapl3->ID_type;
bcopy(&sapl3->ID, &attr.blli.v.layer_3_protocol.ID,
sizeof(attr.blli.v.layer_3_protocol.ID));
}
attr.bhli.tag = sapapl->SVE_tag;
if (sapapl->SVE_tag == T_ATM_PRESENT) {
attr.bhli.v.ID_type = sapapl->ID_type;
bcopy(&sapapl->ID, &attr.bhli.v.ID,
sizeof(attr.bhli.v.ID));
}
/*
* Make sure we have unique listening attributes
*/
if (atm_cm_match(&attr, NULL) != NULL)
return (EADDRINUSE);
/*
* Looks good, save new attributes
*/
atp->atp_attr = attr;
return (0);
}
/*
* Listen for incoming connections
*
* Called at splnet.
*
* Arguments:
* so pointer to socket
* epp pointer to endpoint definition structure
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
int
atm_sock_listen(so, epp, backlog)
struct socket *so;
Atm_endpoint *epp;
int backlog;
{
Atm_pcb *atp = sotoatmpcb(so);
/*
* Make sure we're still attached
*/
if (atp == NULL)
return (ENOTCONN);
/*
* Start listening for incoming calls
*/
return (atm_cm_listen(so, epp, atp, &atp->atp_attr, &atp->atp_conn,
backlog));
}
/*
* Connect socket to peer
*
* Called at splnet.
*
* Arguments:
* so pointer to socket
* addr pointer to protocol address
* epp pointer to endpoint definition structure
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
int
atm_sock_connect(so, addr, epp)
struct socket *so;
struct sockaddr *addr;
Atm_endpoint *epp;
{
Atm_pcb *atp = sotoatmpcb(so);
struct sockaddr_atm *satm;
struct t_atm_sap_addr *sapadr;
struct t_atm_sap_layer2 *sapl2;
struct t_atm_sap_layer3 *sapl3;
struct t_atm_sap_appl *sapapl;
int err;
/*
* Make sure we're still attached
*/
if (atp == NULL)
return (ENOTCONN);
/*
* Validate requested peer address
*/
satm = (struct sockaddr_atm *)addr;
if (satm->satm_family != AF_ATM)
return (EAFNOSUPPORT);
sapadr = &satm->satm_addr.t_atm_sap_addr;
if (sapadr->SVE_tag_addr != T_ATM_PRESENT)
return (EINVAL);
if (sapadr->address_format == T_ATM_ENDSYS_ADDR) {
if (sapadr->SVE_tag_selector != T_ATM_PRESENT)
return (EINVAL);
} else if (sapadr->address_format == T_ATM_E164_ADDR) {
if (sapadr->SVE_tag_selector != T_ATM_ABSENT)
return (EINVAL);
} else if (sapadr->address_format == T_ATM_PVC_ADDR) {
if (sapadr->SVE_tag_selector != T_ATM_ABSENT)
return (EINVAL);
} else
return (EINVAL);
if (sapadr->address_length > ATM_ADDR_LEN)
return (EINVAL);
sapl2 = &satm->satm_addr.t_atm_sap_layer2;
if (sapl2->SVE_tag == T_ATM_PRESENT) {
if ((sapl2->ID_type != T_ATM_SIMPLE_ID) &&
(sapl2->ID_type != T_ATM_USER_ID))
return (EINVAL);
} else if (sapl2->SVE_tag != T_ATM_ABSENT)
return (EINVAL);
sapl3 = &satm->satm_addr.t_atm_sap_layer3;
if (sapl3->SVE_tag == T_ATM_PRESENT) {
if ((sapl3->ID_type != T_ATM_SIMPLE_ID) &&
(sapl3->ID_type != T_ATM_IPI_ID) &&
(sapl3->ID_type != T_ATM_SNAP_ID) &&
(sapl3->ID_type != T_ATM_USER_ID))
return (EINVAL);
} else if (sapl3->SVE_tag != T_ATM_ABSENT)
return (EINVAL);
sapapl = &satm->satm_addr.t_atm_sap_appl;
if (sapapl->SVE_tag == T_ATM_PRESENT) {
if ((sapapl->ID_type != T_ATM_ISO_APP_ID) &&
(sapapl->ID_type != T_ATM_USER_APP_ID) &&
(sapapl->ID_type != T_ATM_VENDOR_APP_ID))
return (EINVAL);
} else if (sapapl->SVE_tag != T_ATM_ABSENT)
return (EINVAL);
/*
* Select an outgoing network interface
*/
if (atp->atp_attr.nif == NULL) {
struct atm_pif *pip;
for (pip = atm_interface_head; pip != NULL;
pip = pip->pif_next) {
if (pip->pif_nif != NULL) {
atp->atp_attr.nif = pip->pif_nif;
break;
}
}
if (atp->atp_attr.nif == NULL)
return (ENXIO);
}
/*
* Set supplied connection attributes
*/
atp->atp_attr.called.tag = T_ATM_PRESENT;
bcopy(&sapadr->address_format, &atp->atp_attr.called.addr,
sizeof(Atm_addr));
atp->atp_attr.blli.tag_l2 = sapl2->SVE_tag;
if (sapl2->SVE_tag == T_ATM_PRESENT) {
atp->atp_attr.blli.v.layer_2_protocol.ID_type = sapl2->ID_type;
bcopy(&sapl2->ID, &atp->atp_attr.blli.v.layer_2_protocol.ID,
sizeof(atp->atp_attr.blli.v.layer_2_protocol.ID));
}
atp->atp_attr.blli.tag_l3 = sapl3->SVE_tag;
if (sapl3->SVE_tag == T_ATM_PRESENT) {
atp->atp_attr.blli.v.layer_3_protocol.ID_type = sapl3->ID_type;
bcopy(&sapl3->ID, &atp->atp_attr.blli.v.layer_3_protocol.ID,
sizeof(atp->atp_attr.blli.v.layer_3_protocol.ID));
}
atp->atp_attr.bhli.tag = sapapl->SVE_tag;
if (sapapl->SVE_tag == T_ATM_PRESENT) {
atp->atp_attr.bhli.v.ID_type = sapapl->ID_type;
bcopy(&sapapl->ID, &atp->atp_attr.bhli.v.ID,
sizeof(atp->atp_attr.bhli.v.ID));
}
/*
* We're finally ready to initiate the ATM connection
*/
soisconnecting(so);
atm_sock_stat.as_connreq[atp->atp_type]++;
err = atm_cm_connect(epp, atp, &atp->atp_attr, &atp->atp_conn);
if (err == 0) {
/*
* Connection is setup
*/
atm_sock_stat.as_conncomp[atp->atp_type]++;
soisconnected(so);
} else if (err == EINPROGRESS) {
/*
* We've got to wait for a connected event
*/
err = 0;
} else {
/*
* Call failed...
*/
atm_sock_stat.as_connfail[atp->atp_type]++;
soisdisconnected(so);
}
return (err);
}
/*
* Disconnect connected socket
*
* Called at splnet.
*
* Arguments:
* so pointer to socket
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
int
atm_sock_disconnect(so)
struct socket *so;
{
Atm_pcb *atp = sotoatmpcb(so);
struct t_atm_cause *cause;
int err;
/*
* Make sure we're still attached
*/
if (atp == NULL)
return (ENOTCONN);
/*
* Release the ATM connection
*/
if (atp->atp_conn) {
if (atp->atp_attr.cause.tag == T_ATM_PRESENT)
cause = &atp->atp_attr.cause.v;
else
cause = &atm_sock_cause;
err = atm_cm_release(atp->atp_conn, cause);
if (err)
log(LOG_ERR, "atm_sock_disconnect: release fail (%d)\n",
err);
atm_sock_stat.as_connrel[atp->atp_type]++;
atp->atp_conn = NULL;
}
soisdisconnected(so);
return (0);
}
/*
* Retrieve local socket address
*
* Called at splnet.
*
* Arguments:
* so pointer to socket
* addr pointer to pointer to contain protocol address
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
int
atm_sock_sockaddr(so, addr)
struct socket *so;
struct sockaddr **addr;
{
struct sockaddr_atm *satm;
struct t_atm_sap_addr *saddr;
Atm_pcb *atp = sotoatmpcb(so);
/*
* Return local interface address, if known
*/
satm = malloc(sizeof(*satm), M_SONAME, M_WAITOK | M_ZERO);
if (satm == NULL)
return (ENOMEM);
satm->satm_family = AF_ATM;
satm->satm_len = sizeof(*satm);
saddr = &satm->satm_addr.t_atm_sap_addr;
if (atp->atp_attr.nif && atp->atp_attr.nif->nif_pif->pif_siginst) {
saddr->SVE_tag_addr = T_ATM_PRESENT;
ATM_ADDR_SEL_COPY(
&atp->atp_attr.nif->nif_pif->pif_siginst->si_addr,
atp->atp_attr.nif->nif_sel, saddr);
if (saddr->address_format == T_ATM_ENDSYS_ADDR)
saddr->SVE_tag_selector = T_ATM_PRESENT;
else
saddr->SVE_tag_selector = T_ATM_ABSENT;
} else {
saddr->SVE_tag_addr = T_ATM_ABSENT;
saddr->SVE_tag_selector = T_ATM_ABSENT;
saddr->address_format = T_ATM_ABSENT;
}
satm->satm_addr.t_atm_sap_layer2.SVE_tag = T_ATM_ABSENT;
satm->satm_addr.t_atm_sap_layer3.SVE_tag = T_ATM_ABSENT;
satm->satm_addr.t_atm_sap_appl.SVE_tag = T_ATM_ABSENT;
*addr = (struct sockaddr *)satm;
return (0);
}
/*
* Retrieve peer socket address
*
* Called at splnet.
*
* Arguments:
* so pointer to socket
* addr pointer to pointer to contain protocol address
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
int
atm_sock_peeraddr(so, addr)
struct socket *so;
struct sockaddr **addr;
{
struct sockaddr_atm *satm;
struct t_atm_sap_addr *saddr;
Atm_pcb *atp = sotoatmpcb(so);
Atm_connvc *cvp;
/*
* Return remote address, if known
*/
satm = malloc(sizeof(*satm), M_SONAME, M_WAITOK | M_ZERO);
if (satm == NULL)
return (ENOMEM);
satm->satm_family = AF_ATM;
satm->satm_len = sizeof(*satm);
saddr = &satm->satm_addr.t_atm_sap_addr;
if (so->so_state & SS_ISCONNECTED) {
cvp = atp->atp_conn->co_connvc;
saddr->SVE_tag_addr = T_ATM_PRESENT;
if (cvp->cvc_flags & CVCF_CALLER) {
ATM_ADDR_COPY(&cvp->cvc_attr.called.addr, saddr);
} else {
if (cvp->cvc_attr.calling.tag == T_ATM_PRESENT) {
ATM_ADDR_COPY(&cvp->cvc_attr.calling.addr,
saddr);
} else {
saddr->SVE_tag_addr = T_ATM_ABSENT;
saddr->address_format = T_ATM_ABSENT;
}
}
if (saddr->address_format == T_ATM_ENDSYS_ADDR)
saddr->SVE_tag_selector = T_ATM_PRESENT;
else
saddr->SVE_tag_selector = T_ATM_ABSENT;
} else {
saddr->SVE_tag_addr = T_ATM_ABSENT;
saddr->SVE_tag_selector = T_ATM_ABSENT;
saddr->address_format = T_ATM_ABSENT;
}
satm->satm_addr.t_atm_sap_layer2.SVE_tag = T_ATM_ABSENT;
satm->satm_addr.t_atm_sap_layer3.SVE_tag = T_ATM_ABSENT;
satm->satm_addr.t_atm_sap_appl.SVE_tag = T_ATM_ABSENT;
*addr = (struct sockaddr *)satm;
return (0);
}
/*
* Common setsockopt processing
*
* Called at splnet.
*
* Arguments:
* so pointer to socket
* sopt pointer to socket option info
* atp pointer to ATM PCB
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
int
atm_sock_setopt(so, sopt, atp)
struct socket *so;
struct sockopt *sopt;
Atm_pcb *atp;
{
int err = 0;
union {
struct t_atm_aal5 aal5;
struct t_atm_traffic trf;
struct t_atm_bearer brr;
struct t_atm_bhli bhl;
struct t_atm_blli bll;
Atm_addr addr;
struct t_atm_cause cau;
struct t_atm_qos qos;
struct t_atm_transit trn;
struct t_atm_net_intf nif;
struct t_atm_llc llc;
struct t_atm_app_name appn;
} p;
#define MAXVAL(bits) ((1 << bits) - 1)
#define MAXMASK(bits) (~MAXVAL(bits))
switch (sopt->sopt_name) {
case T_ATM_AAL5:
err = sooptcopyin(sopt, &p.aal5, sizeof p.aal5, sizeof p.aal5);
if (err)
break;
if ((p.aal5.forward_max_SDU_size != T_ATM_ABSENT) &&
(p.aal5.forward_max_SDU_size & MAXMASK(16)))
return (EINVAL);
if ((p.aal5.backward_max_SDU_size != T_ATM_ABSENT) &&
(p.aal5.backward_max_SDU_size & MAXMASK(16)))
return (EINVAL);
if ((p.aal5.SSCS_type != T_ATM_ABSENT) &&
(p.aal5.SSCS_type != T_ATM_NULL) &&
(p.aal5.SSCS_type != T_ATM_SSCS_SSCOP_REL) &&
(p.aal5.SSCS_type != T_ATM_SSCS_SSCOP_UNREL) &&
(p.aal5.SSCS_type != T_ATM_SSCS_FR))
return (EINVAL);
if ((p.aal5.forward_max_SDU_size == T_ATM_ABSENT) &&
(p.aal5.backward_max_SDU_size == T_ATM_ABSENT) &&
(p.aal5.SSCS_type == T_ATM_ABSENT))
atp->atp_attr.aal.tag = T_ATM_ABSENT;
else {
atp->atp_attr.aal.tag = T_ATM_PRESENT;
atp->atp_attr.aal.type = ATM_AAL5;
atp->atp_attr.aal.v.aal5 = p.aal5;
}
break;
case T_ATM_TRAFFIC:
err = sooptcopyin(sopt, &p.trf, sizeof p.trf, sizeof p.trf);
if (err)
break;
if ((p.trf.forward.PCR_high_priority != T_ATM_ABSENT) &&
(p.trf.forward.PCR_high_priority & MAXMASK(24)))
return (EINVAL);
if (p.trf.forward.PCR_all_traffic & MAXMASK(24))
return (EINVAL);
if ((p.trf.forward.SCR_high_priority != T_ATM_ABSENT) &&
(p.trf.forward.SCR_high_priority & MAXMASK(24)))
return (EINVAL);
if ((p.trf.forward.SCR_all_traffic != T_ATM_ABSENT) &&
(p.trf.forward.SCR_all_traffic & MAXMASK(24)))
return (EINVAL);
if ((p.trf.forward.MBS_high_priority != T_ATM_ABSENT) &&
(p.trf.forward.MBS_high_priority & MAXMASK(24)))
return (EINVAL);
if ((p.trf.forward.MBS_all_traffic != T_ATM_ABSENT) &&
(p.trf.forward.MBS_all_traffic & MAXMASK(24)))
return (EINVAL);
if ((p.trf.forward.tagging != T_YES) &&
(p.trf.forward.tagging != T_NO))
return (EINVAL);
if ((p.trf.backward.PCR_high_priority != T_ATM_ABSENT) &&
(p.trf.backward.PCR_high_priority & MAXMASK(24)))
return (EINVAL);
if (p.trf.backward.PCR_all_traffic & MAXMASK(24))
return (EINVAL);
if ((p.trf.backward.SCR_high_priority != T_ATM_ABSENT) &&
(p.trf.backward.SCR_high_priority & MAXMASK(24)))
return (EINVAL);
if ((p.trf.backward.SCR_all_traffic != T_ATM_ABSENT) &&
(p.trf.backward.SCR_all_traffic & MAXMASK(24)))
return (EINVAL);
if ((p.trf.backward.MBS_high_priority != T_ATM_ABSENT) &&
(p.trf.backward.MBS_high_priority & MAXMASK(24)))
return (EINVAL);
if ((p.trf.backward.MBS_all_traffic != T_ATM_ABSENT) &&
(p.trf.backward.MBS_all_traffic & MAXMASK(24)))
return (EINVAL);
if ((p.trf.backward.tagging != T_YES) &&
(p.trf.backward.tagging != T_NO))
return (EINVAL);
if ((p.trf.best_effort != T_YES) &&
(p.trf.best_effort != T_NO))
return (EINVAL);
atp->atp_attr.traffic.tag = T_ATM_PRESENT;
atp->atp_attr.traffic.v = p.trf;
break;
case T_ATM_BEARER_CAP:
err = sooptcopyin(sopt, &p.brr, sizeof p.brr, sizeof p.brr);
if (err)
break;
if ((p.brr.bearer_class != T_ATM_CLASS_A) &&
(p.brr.bearer_class != T_ATM_CLASS_C) &&
(p.brr.bearer_class != T_ATM_CLASS_X))
return (EINVAL);
if ((p.brr.traffic_type != T_ATM_NULL) &&
(p.brr.traffic_type != T_ATM_CBR) &&
(p.brr.traffic_type != T_ATM_VBR) &&
(p.brr.traffic_type != T_ATM_ABR) &&
(p.brr.traffic_type != T_ATM_UBR))
return (EINVAL);
if ((p.brr.timing_requirements != T_ATM_NULL) &&
(p.brr.timing_requirements != T_ATM_END_TO_END) &&
(p.brr.timing_requirements != T_ATM_NO_END_TO_END))
return (EINVAL);
if ((p.brr.clipping_susceptibility != T_NO) &&
(p.brr.clipping_susceptibility != T_YES))
return (EINVAL);
if ((p.brr.connection_configuration != T_ATM_1_TO_1) &&
(p.brr.connection_configuration != T_ATM_1_TO_MANY))
return (EINVAL);
atp->atp_attr.bearer.tag = T_ATM_PRESENT;
atp->atp_attr.bearer.v = p.brr;
break;
case T_ATM_BHLI:
err = sooptcopyin(sopt, &p.bhl, sizeof p.bhl, sizeof p.bhl);
if (err)
break;
if ((p.bhl.ID_type != T_ATM_ABSENT) &&
(p.bhl.ID_type != T_ATM_ISO_APP_ID) &&
(p.bhl.ID_type != T_ATM_USER_APP_ID) &&
(p.bhl.ID_type != T_ATM_VENDOR_APP_ID))
return (EINVAL);
if (p.bhl.ID_type == T_ATM_ABSENT)
atp->atp_attr.bhli.tag = T_ATM_ABSENT;
else {
atp->atp_attr.bhli.tag = T_ATM_PRESENT;
atp->atp_attr.bhli.v = p.bhl;
}
break;
case T_ATM_BLLI:
err = sooptcopyin(sopt, &p.bll, sizeof p.bll, sizeof p.bll);
if (err)
break;
if ((p.bll.layer_2_protocol.ID_type != T_ATM_ABSENT) &&
(p.bll.layer_2_protocol.ID_type != T_ATM_SIMPLE_ID) &&
(p.bll.layer_2_protocol.ID_type != T_ATM_USER_ID))
return (EINVAL);
if ((p.bll.layer_2_protocol.mode != T_ATM_ABSENT) &&
(p.bll.layer_2_protocol.mode != T_ATM_BLLI_NORMAL_MODE) &&
(p.bll.layer_2_protocol.mode != T_ATM_BLLI_EXTENDED_MODE))
return (EINVAL);
if ((p.bll.layer_2_protocol.window_size != T_ATM_ABSENT) &&
(p.bll.layer_2_protocol.window_size < 1))
return (EINVAL);
if ((p.bll.layer_3_protocol.ID_type != T_ATM_ABSENT) &&
(p.bll.layer_3_protocol.ID_type != T_ATM_SIMPLE_ID) &&
(p.bll.layer_3_protocol.ID_type != T_ATM_IPI_ID) &&
(p.bll.layer_3_protocol.ID_type != T_ATM_SNAP_ID) &&
(p.bll.layer_3_protocol.ID_type != T_ATM_USER_ID))
return (EINVAL);
if ((p.bll.layer_3_protocol.mode != T_ATM_ABSENT) &&
(p.bll.layer_3_protocol.mode != T_ATM_BLLI_NORMAL_MODE) &&
(p.bll.layer_3_protocol.mode != T_ATM_BLLI_EXTENDED_MODE))
return (EINVAL);
if ((p.bll.layer_3_protocol.packet_size != T_ATM_ABSENT) &&
(p.bll.layer_3_protocol.packet_size & MAXMASK(4)))
return (EINVAL);
if ((p.bll.layer_3_protocol.window_size != T_ATM_ABSENT) &&
(p.bll.layer_3_protocol.window_size < 1))
return (EINVAL);
if (p.bll.layer_2_protocol.ID_type == T_ATM_ABSENT)
atp->atp_attr.blli.tag_l2 = T_ATM_ABSENT;
else
atp->atp_attr.blli.tag_l2 = T_ATM_PRESENT;
if (p.bll.layer_3_protocol.ID_type == T_ATM_ABSENT)
atp->atp_attr.blli.tag_l3 = T_ATM_ABSENT;
else
atp->atp_attr.blli.tag_l3 = T_ATM_PRESENT;
if ((atp->atp_attr.blli.tag_l2 == T_ATM_PRESENT) ||
(atp->atp_attr.blli.tag_l3 == T_ATM_PRESENT))
atp->atp_attr.blli.v = p.bll;
break;
case T_ATM_DEST_ADDR:
err = sooptcopyin(sopt, &p.addr, sizeof p.addr, sizeof p.addr);
if (err)
break;
if ((p.addr.address_format != T_ATM_ENDSYS_ADDR) &&
(p.addr.address_format != T_ATM_E164_ADDR))
return (EINVAL);
if (p.addr.address_length > ATM_ADDR_LEN)
return (EINVAL);
atp->atp_attr.called.tag = T_ATM_PRESENT;
atp->atp_attr.called.addr = p.addr;
break;
case T_ATM_DEST_SUB:
err = sooptcopyin(sopt, &p.addr, sizeof p.addr, sizeof p.addr);
if (err)
break;
if ((p.addr.address_format != T_ATM_ABSENT) &&
(p.addr.address_format != T_ATM_NSAP_ADDR))
return (EINVAL);
if (p.addr.address_length > ATM_ADDR_LEN)
return (EINVAL);
/* T_ATM_DEST_ADDR controls tag */
atp->atp_attr.called.subaddr = p.addr;
break;
case T_ATM_ORIG_ADDR:
return (EACCES);
case T_ATM_ORIG_SUB:
return (EACCES);
case T_ATM_CALLER_ID:
return (EACCES);
case T_ATM_CAUSE:
err = sooptcopyin(sopt, &p.cau, sizeof p.cau, sizeof p.cau);
if (err)
break;
if ((p.cau.coding_standard != T_ATM_ABSENT) &&
(p.cau.coding_standard != T_ATM_ITU_CODING) &&
(p.cau.coding_standard != T_ATM_NETWORK_CODING))
return (EINVAL);
if ((p.cau.location != T_ATM_LOC_USER) &&
(p.cau.location != T_ATM_LOC_LOCAL_PRIVATE_NET) &&
(p.cau.location != T_ATM_LOC_LOCAL_PUBLIC_NET) &&
(p.cau.location != T_ATM_LOC_TRANSIT_NET) &&
(p.cau.location != T_ATM_LOC_REMOTE_PUBLIC_NET) &&
(p.cau.location != T_ATM_LOC_REMOTE_PRIVATE_NET) &&
(p.cau.location != T_ATM_LOC_INTERNATIONAL_NET) &&
(p.cau.location != T_ATM_LOC_BEYOND_INTERWORKING))
return (EINVAL);
if (p.cau.coding_standard == T_ATM_ABSENT)
atp->atp_attr.cause.tag = T_ATM_ABSENT;
else {
atp->atp_attr.cause.tag = T_ATM_PRESENT;
atp->atp_attr.cause.v = p.cau;
}
break;
case T_ATM_QOS:
err = sooptcopyin(sopt, &p.qos, sizeof p.qos, sizeof p.qos);
if (err)
break;
if ((p.qos.coding_standard != T_ATM_ABSENT) &&
(p.qos.coding_standard != T_ATM_ITU_CODING) &&
(p.qos.coding_standard != T_ATM_NETWORK_CODING))
return (EINVAL);
if ((p.qos.forward.qos_class != T_ATM_QOS_CLASS_0) &&
(p.qos.forward.qos_class != T_ATM_QOS_CLASS_1) &&
(p.qos.forward.qos_class != T_ATM_QOS_CLASS_2) &&
(p.qos.forward.qos_class != T_ATM_QOS_CLASS_3) &&
(p.qos.forward.qos_class != T_ATM_QOS_CLASS_4))
return (EINVAL);
if ((p.qos.backward.qos_class != T_ATM_QOS_CLASS_0) &&
(p.qos.backward.qos_class != T_ATM_QOS_CLASS_1) &&
(p.qos.backward.qos_class != T_ATM_QOS_CLASS_2) &&
(p.qos.backward.qos_class != T_ATM_QOS_CLASS_3) &&
(p.qos.backward.qos_class != T_ATM_QOS_CLASS_4))
return (EINVAL);
if (p.qos.coding_standard == T_ATM_ABSENT)
atp->atp_attr.qos.tag = T_ATM_ABSENT;
else {
atp->atp_attr.qos.tag = T_ATM_PRESENT;
atp->atp_attr.qos.v = p.qos;
}
break;
case T_ATM_TRANSIT:
err = sooptcopyin(sopt, &p.trn, sizeof p.trn, sizeof p.trn);
if (err)
break;
if (p.trn.length > T_ATM_MAX_NET_ID)
return (EINVAL);
if (p.trn.length == 0)
atp->atp_attr.transit.tag = T_ATM_ABSENT;
else {
atp->atp_attr.transit.tag = T_ATM_PRESENT;
atp->atp_attr.transit.v = p.trn;
}
break;
case T_ATM_ADD_LEAF:
return (EPROTONOSUPPORT); /* XXX */
case T_ATM_DROP_LEAF:
return (EPROTONOSUPPORT); /* XXX */
case T_ATM_NET_INTF:
err = sooptcopyin(sopt, &p.nif, sizeof p.nif, sizeof p.nif);
if (err)
break;
atp->atp_attr.nif = atm_nifname(p.nif.net_intf);
if (atp->atp_attr.nif == NULL)
return (ENXIO);
break;
case T_ATM_LLC:
err = sooptcopyin(sopt, &p.llc, sizeof p.llc, sizeof p.llc);
if (err)
break;
if ((p.llc.llc_len < T_ATM_LLC_MIN_LEN) ||
(p.llc.llc_len > T_ATM_LLC_MAX_LEN))
return (EINVAL);
atp->atp_attr.llc.tag = T_ATM_PRESENT;
atp->atp_attr.llc.v = p.llc;
break;
case T_ATM_APP_NAME:
err = sooptcopyin(sopt, &p.appn, sizeof p.appn, sizeof p.appn);
if (err)
break;
strncpy(atp->atp_name, p.appn.app_name, T_ATM_APP_NAME_LEN);
break;
default:
return (ENOPROTOOPT);
}
return (err);
}
/*
* Common getsockopt processing
*
* Called at splnet.
*
* Arguments:
* so pointer to socket
* sopt pointer to socket option info
* atp pointer to ATM PCB
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
int
atm_sock_getopt(so, sopt, atp)
struct socket *so;
struct sockopt *sopt;
Atm_pcb *atp;
{
Atm_attributes *ap;
/*
* If socket is connected, return attributes for the VCC in use,
* otherwise just return what the user has setup so far.
*/
if (so->so_state & SS_ISCONNECTED)
ap = &atp->atp_conn->co_connvc->cvc_attr;
else
ap = &atp->atp_attr;
switch (sopt->sopt_name) {
case T_ATM_AAL5:
if ((ap->aal.tag == T_ATM_PRESENT) &&
(ap->aal.type == ATM_AAL5)) {
return (sooptcopyout(sopt, &ap->aal.v.aal5,
sizeof ap->aal.v.aal5));
} else {
return (ENOENT);
}
break;
case T_ATM_TRAFFIC:
if (ap->traffic.tag == T_ATM_PRESENT) {
return (sooptcopyout(sopt, &ap->traffic.v,
sizeof ap->traffic.v));
} else {
return (ENOENT);
}
break;
case T_ATM_BEARER_CAP:
if (ap->bearer.tag == T_ATM_PRESENT) {
return (sooptcopyout(sopt, &ap->bearer.v,
sizeof ap->bearer.v));
} else {
return (ENOENT);
}
break;
case T_ATM_BHLI:
if (ap->bhli.tag == T_ATM_PRESENT) {
return (sooptcopyout(sopt, &ap->bhli.v,
sizeof ap->bhli.v));
} else {
return (ENOENT);
}
break;
case T_ATM_BLLI:
if ((ap->blli.tag_l2 == T_ATM_PRESENT) ||
(ap->blli.tag_l3 == T_ATM_PRESENT)) {
return (sooptcopyout(sopt, &ap->blli.v,
sizeof ap->blli.v));
} else {
return (ENOENT);
}
break;
case T_ATM_DEST_ADDR:
if (ap->called.tag == T_ATM_PRESENT) {
return (sooptcopyout(sopt, &ap->called.addr,
sizeof ap->called.addr));
} else {
return (ENOENT);
}
break;
case T_ATM_DEST_SUB:
if (ap->called.tag == T_ATM_PRESENT) {
return (sooptcopyout(sopt, &ap->called.subaddr,
sizeof ap->called.subaddr));
} else {
return (ENOENT);
}
break;
case T_ATM_ORIG_ADDR:
if (ap->calling.tag == T_ATM_PRESENT) {
return (sooptcopyout(sopt, &ap->calling.addr,
sizeof ap->calling.addr));
} else {
return (ENOENT);
}
break;
case T_ATM_ORIG_SUB:
if (ap->calling.tag == T_ATM_PRESENT) {
return (sooptcopyout(sopt, &ap->calling.subaddr,
sizeof ap->calling.subaddr));
} else {
return (ENOENT);
}
break;
case T_ATM_CALLER_ID:
if (ap->calling.tag == T_ATM_PRESENT) {
return (sooptcopyout(sopt, &ap->calling.cid,
sizeof ap->calling.cid));
} else {
return (ENOENT);
}
break;
case T_ATM_CAUSE:
if (ap->cause.tag == T_ATM_PRESENT) {
return (sooptcopyout(sopt, &ap->cause.v,
sizeof ap->cause.v));
} else {
return (ENOENT);
}
break;
case T_ATM_QOS:
if (ap->qos.tag == T_ATM_PRESENT) {
return (sooptcopyout(sopt, &ap->qos.v,
sizeof ap->qos.v));
} else {
return (ENOENT);
}
break;
case T_ATM_TRANSIT:
if (ap->transit.tag == T_ATM_PRESENT) {
return (sooptcopyout(sopt, &ap->transit.v,
sizeof ap->transit.v));
} else {
return (ENOENT);
}
break;
case T_ATM_LEAF_IND:
return (EPROTONOSUPPORT); /* XXX */
case T_ATM_NET_INTF:
if (ap->nif) {
struct t_atm_net_intf netif;
struct ifnet *ifp;
ifp = ANIF2IFP(ap->nif);
(void) snprintf(netif.net_intf, sizeof(netif.net_intf),
"%s", ifp->if_xname);
return (sooptcopyout(sopt, &netif,
sizeof netif));
} else {
return (ENOENT);
}
break;
case T_ATM_LLC:
if (ap->llc.tag == T_ATM_PRESENT) {
return (sooptcopyout(sopt, &ap->llc.v,
sizeof ap->llc.v));
} else {
return (ENOENT);
}
break;
default:
return (ENOPROTOOPT);
}
return (0);
}
/*
* Process Socket VCC Connected Notification
*
* Arguments:
* toku owner's connection token (atm_pcb protocol block)
*
* Returns:
* none
*
*/
void
atm_sock_connected(toku)
void *toku;
{
Atm_pcb *atp = (Atm_pcb *)toku;
/*
* Connection is setup
*/
atm_sock_stat.as_conncomp[atp->atp_type]++;
soisconnected(atp->atp_socket);
}
/*
* Process Socket VCC Cleared Notification
*
* Arguments:
* toku owner's connection token (atm_pcb protocol block)
* cause pointer to cause code
*
* Returns:
* none
*
*/
void
atm_sock_cleared(toku, cause)
void *toku;
struct t_atm_cause *cause;
{
Atm_pcb *atp = (Atm_pcb *)toku;
struct socket *so;
so = atp->atp_socket;
/*
* Save call clearing cause
*/
atp->atp_attr.cause.tag = T_ATM_PRESENT;
atp->atp_attr.cause.v = *cause;
/*
* Set user error code
*/
if (so->so_state & SS_ISCONNECTED) {
so->so_error = ECONNRESET;
atm_sock_stat.as_connclr[atp->atp_type]++;
} else {
so->so_error = ECONNREFUSED;
atm_sock_stat.as_connfail[atp->atp_type]++;
}
/*
* Connection is gone
*/
atp->atp_conn = NULL;
soisdisconnected(so);
/*
* Cleanup failed incoming connection setup
*/
if (so->so_state & SS_NOFDREF) {
(void) atm_sock_detach(so);
}
}