freebsd-dev/sys/netatm/atm_socket.c

1324 lines
31 KiB
C
Raw Normal View History

/*-
* ===================================
* 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
*/
2003-06-11 07:00:30 +00:00
#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
*
* Returns:
* 0 detach successful
* errno detach failed - reason indicated
*
*/
int
atm_sock_detach(so)
struct socket *so;
{
Atm_pcb *atp = sotoatmpcb(so);
/*
* Make sure we're still attached
*/
if (atp == NULL)
return (ENOTCONN);
/*
* Terminate any (possibly pending) connection
*/
if (atp->atp_conn) {
(void) atm_sock_disconnect(so);
}
/*
* Break links and free control blocks
*/
Push acquisition of the accept mutex out of sofree() into the caller (sorele()/sotryfree()): - This permits the caller to acquire the accept mutex before the socket mutex, avoiding sofree() having to drop the socket mutex and re-order, which could lead to races permitting more than one thread to enter sofree() after a socket is ready to be free'd. - This also covers clearing of the so_pcb weak socket reference from the protocol to the socket, preventing races in clearing and evaluation of the reference such that sofree() might be called more than once on the same socket. This appears to close a race I was able to easily trigger by repeatedly opening and resetting TCP connections to a host, in which the tcp_close() code called as a result of the RST raced with the close() of the accepted socket in the user process resulting in simultaneous attempts to de-allocate the same socket. The new locking increases the overhead for operations that may potentially free the socket, so we will want to revise the synchronization strategy here as we normalize the reference counting model for sockets. The use of the accept mutex in freeing of sockets that are not listen sockets is primarily motivated by the potential need to remove the socket from the incomplete connection queue on its parent (listen) socket, so cleaning up the reference model here may allow us to substantially weaken the synchronization requirements. RELENG_5_3 candidate. MFC after: 3 days Reviewed by: dwhite Discussed with: gnn, dwhite, green Reported by: Marc UBM Bocklet <ubm at u-boot-man dot de> Reported by: Vlad <marchenko at gmail dot com>
2004-10-18 22:19:43 +00:00
ACCEPT_LOCK();
SOCK_LOCK(so);
so->so_pcb = NULL;
sotryfree(so);
uma_zfree(atm_pcb_zone, atp);
return (0);
}
/*
* 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)
struct socket *so;
Atm_endpoint *epp;
{
Atm_pcb *atp = sotoatmpcb(so);
/*
* Make sure we're still attached
*/
if (atp == NULL)
return (ENOTCONN);
/*
* Start listening for incoming calls
*/
In the current world order, solisten() implements the state transition of a socket from a regular socket to a listening socket able to accept new connections. As part of this state transition, solisten() calls into the protocol to update protocol-layer state. There were several bugs in this implementation that could result in a race wherein a TCP SYN received in the interval between the protocol state transition and the shortly following socket layer transition would result in a panic in the TCP code, as the socket would be in the TCPS_LISTEN state, but the socket would not have the SO_ACCEPTCONN flag set. This change does the following: - Pushes the socket state transition from the socket layer solisten() to to socket "library" routines called from the protocol. This permits the socket routines to be called while holding the protocol mutexes, preventing a race exposing the incomplete socket state transition to TCP after the TCP state transition has completed. The check for a socket layer state transition is performed by solisten_proto_check(), and the actual transition is performed by solisten_proto(). - Holds the socket lock for the duration of the socket state test and set, and over the protocol layer state transition, which is now possible as the socket lock is acquired by the protocol layer, rather than vice versa. This prevents additional state related races in the socket layer. This permits the dual transition of socket layer and protocol layer state to occur while holding locks for both layers, making the two changes atomic with respect to one another. Similar changes are likely require elsewhere in the socket/protocol code. Reported by: Peter Holm <peter@holm.cc> Review and fixes from: emax, Antoine Brodin <antoine.brodin@laposte.net> Philosophical head nod: gnn
2005-02-21 21:58:17 +00:00
return (atm_cm_listen(so, epp, atp, &atp->atp_attr, &atp->atp_conn));
}
/*
* 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 = &ap->nif->nif_if;
(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);
}
}