freebsd-dev/sys/netatm/atm_aal5.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

929 lines
16 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 AAL5 socket protocol processing
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/lock.h>
#include <sys/protosw.h>
#include <sys/signalvar.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/sx.h>
#include <sys/systm.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_stack.h>
#include <netatm/atm_pcb.h>
#include <netatm/atm_var.h>
/*
* Global variables
*/
u_long atm_aal5_sendspace = 64 * 1024; /* XXX */
u_long atm_aal5_recvspace = 64 * 1024; /* XXX */
/*
* Local functions
*/
static int atm_aal5_attach(struct socket *, int, struct thread *td);
static void atm_aal5_detach(struct socket *);
static int atm_aal5_bind(struct socket *, struct sockaddr *,
struct thread *td);
static int atm_aal5_listen(struct socket *, int backlog,
struct thread *td);
static int atm_aal5_connect(struct socket *, struct sockaddr *,
struct thread *td);
static int atm_aal5_accept(struct socket *, struct sockaddr **);
static int atm_aal5_disconnect(struct socket *);
static int atm_aal5_shutdown(struct socket *);
static int atm_aal5_send(struct socket *, int, KBuffer *,
struct sockaddr *, KBuffer *, struct thread *td);
static void atm_aal5_abort(struct socket *);
static int atm_aal5_control(struct socket *, u_long, caddr_t,
struct ifnet *, struct thread *td);
static int atm_aal5_sense(struct socket *, struct stat *);
static int atm_aal5_sockaddr(struct socket *, struct sockaddr **);
static int atm_aal5_peeraddr(struct socket *, struct sockaddr **);
static int atm_aal5_incoming(void *, Atm_connection *,
Atm_attributes *, void **);
static void atm_aal5_cpcs_data(void *, KBuffer *);
static caddr_t atm_aal5_getname(void *);
/*
* New-style socket request routines
*/
struct pr_usrreqs atm_aal5_usrreqs = {
.pru_abort = atm_aal5_abort,
.pru_accept = atm_aal5_accept,
.pru_attach = atm_aal5_attach,
.pru_bind = atm_aal5_bind,
.pru_connect = atm_aal5_connect,
.pru_control = atm_aal5_control,
.pru_detach = atm_aal5_detach,
.pru_disconnect = atm_aal5_disconnect,
.pru_listen = atm_aal5_listen,
.pru_peeraddr = atm_aal5_peeraddr,
.pru_send = atm_aal5_send,
.pru_sense = atm_aal5_sense,
.pru_shutdown = atm_aal5_shutdown,
.pru_sockaddr = atm_aal5_sockaddr,
};
/*
* Local variables
*/
static Atm_endpoint atm_aal5_endpt = {
NULL,
ENDPT_SOCK_AAL5,
NULL,
atm_aal5_getname,
atm_sock_connected,
atm_sock_cleared,
atm_aal5_incoming,
NULL,
NULL,
NULL,
atm_aal5_cpcs_data,
NULL,
NULL,
NULL,
NULL
};
static Atm_attributes atm_aal5_defattr = {
NULL, /* nif */
CMAPI_CPCS, /* api */
0, /* api_init */
0, /* headin */
0, /* headout */
{ /* aal */
T_ATM_PRESENT,
ATM_AAL5
},
{ /* traffic */
T_ATM_ABSENT,
},
{ /* bearer */
T_ATM_ABSENT,
},
{ /* bhli */
T_ATM_ABSENT
},
{ /* blli */
T_ATM_ABSENT,
T_ATM_ABSENT,
},
{ /* llc */
T_ATM_ABSENT,
},
{ /* called */
T_ATM_ABSENT,
{
T_ATM_ABSENT,
0
},
{
T_ATM_ABSENT,
0
}
},
{ /* calling */
T_ATM_ABSENT
},
{ /* qos */
T_ATM_ABSENT,
},
{ /* transit */
T_ATM_ABSENT
},
{ /* cause */
T_ATM_ABSENT
}
};
/*
* Handy common code macros
*/
#ifdef DIAGNOSTIC
#define ATM_INTRO(f) \
int s, err = 0; \
s = splnet(); \
ATM_DEBUG2("aal5 socket %s (%p)\n", f, so); \
/* \
* Stack queue should have been drained \
*/ \
if (atm_stackq_head != NULL) \
panic("atm_aal5: stack queue not empty"); \
;
#else /* !DIAGNOSTIC */
#define ATM_INTRO(f) \
int s, err = 0; \
s = splnet(); \
;
#endif /* DIAGNOSTIC */
#define ATM_INTRO_NOERR(f) \
int s; \
s = splnet(); \
;
#define ATM_OUTRO() \
/* \
* Drain any deferred calls \
*/ \
STACK_DRAIN(); \
(void) splx(s); \
return (err); \
;
#define ATM_OUTRO_NOERR() \
/* \
* Drain any deferred calls \
*/ \
STACK_DRAIN(); \
(void) splx(s); \
;
#define ATM_RETERR(errno) { \
err = errno; \
goto out; \
}
/*
* Attach protocol to socket
*
* Arguments:
* so pointer to socket
* proto protocol identifier
* p pointer to process
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
static int
atm_aal5_attach(so, proto, td)
struct socket *so;
int proto;
struct thread *td;
{
Atm_pcb *atp;
ATM_INTRO("attach");
/*
* Do general attach stuff
*/
err = atm_sock_attach(so, atm_aal5_sendspace, atm_aal5_recvspace);
if (err)
ATM_RETERR(err);
/*
* Finish up any protocol specific stuff
*/
atp = sotoatmpcb(so);
atp->atp_type = ATPT_AAL5;
/*
* Set default connection attributes
*/
atp->atp_attr = atm_aal5_defattr;
strncpy(atp->atp_name, "(AAL5)", T_ATM_APP_NAME_LEN);
out:
ATM_OUTRO();
}
/*
* Detach protocol from socket
*
* Arguments:
* so pointer to socket
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
static void
atm_aal5_detach(so)
struct socket *so;
{
ATM_INTRO_NOERR("detach");
atm_sock_detach(so);
ATM_OUTRO_NOERR();
}
/*
* Bind address to socket
*
* Arguments:
* so pointer to socket
* addr pointer to protocol address
* p pointer to process
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
static int
atm_aal5_bind(so, addr, td)
struct socket *so;
struct sockaddr *addr;
struct thread *td;
{
ATM_INTRO("bind");
err = atm_sock_bind(so, addr);
ATM_OUTRO();
}
/*
* Listen for incoming connections
*
* Arguments:
* so pointer to socket
* p pointer to process
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
static int
atm_aal5_listen(so, backlog, td)
struct socket *so;
int backlog;
struct thread *td;
{
ATM_INTRO("listen");
err = atm_sock_listen(so, &atm_aal5_endpt, backlog);
ATM_OUTRO();
}
/*
* Connect socket to peer
*
* Arguments:
* so pointer to socket
* addr pointer to protocol address
* p pointer to process
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
static int
atm_aal5_connect(so, addr, td)
struct socket *so;
struct sockaddr *addr;
struct thread *td;
{
Atm_pcb *atp;
ATM_INTRO("connect");
atp = sotoatmpcb(so);
/*
* Resize send socket buffer to maximum sdu size
*/
if (atp->atp_attr.aal.tag == T_ATM_PRESENT) {
long size;
size = atp->atp_attr.aal.v.aal5.forward_max_SDU_size;
if (size != T_ATM_ABSENT)
if (!sbreserve(&so->so_snd, size, so, td)) {
err = ENOBUFS;
ATM_OUTRO();
}
}
/*
* Now get the socket connected
*/
err = atm_sock_connect(so, addr, &atm_aal5_endpt);
ATM_OUTRO();
}
/*
* Accept pending connection
*
* Arguments:
* so pointer to socket
* addr pointer to pointer to contain protocol address
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
static int
atm_aal5_accept(so, addr)
struct socket *so;
struct sockaddr **addr;
{
ATM_INTRO("accept");
/*
* Everything is pretty much done already, we just need to
* return the caller's address to the user.
*/
err = atm_sock_peeraddr(so, addr);
ATM_OUTRO();
}
/*
* Disconnect connected socket
*
* Arguments:
* so pointer to socket
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
static int
atm_aal5_disconnect(so)
struct socket *so;
{
ATM_INTRO("disconnect");
err = atm_sock_disconnect(so);
ATM_OUTRO();
}
/*
* Shut down socket data transmission
*
* Arguments:
* so pointer to socket
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
static int
atm_aal5_shutdown(so)
struct socket *so;
{
ATM_INTRO("shutdown");
socantsendmore(so);
ATM_OUTRO();
}
/*
* Send user data
*
* Arguments:
* so pointer to socket
* flags send data flags
* m pointer to buffer containing user data
* addr pointer to protocol address
* control pointer to buffer containing protocol control data
* p pointer to process
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
static int
atm_aal5_send(so, flags, m, addr, control, td)
struct socket *so;
int flags;
KBuffer *m;
struct sockaddr *addr;
KBuffer *control;
struct thread *td;
{
Atm_pcb *atp;
ATM_INTRO("send");
/*
* We don't support any control functions
*/
if (control) {
int clen;
clen = KB_LEN(control);
KB_FREEALL(control);
if (clen) {
KB_FREEALL(m);
ATM_RETERR(EINVAL);
}
}
/*
* We also don't support any flags or send-level addressing
*/
if (flags || addr) {
KB_FREEALL(m);
ATM_RETERR(EINVAL);
}
/*
* All we've got left is the data, so push it out
*/
atp = sotoatmpcb(so);
err = atm_cm_cpcs_data(atp->atp_conn, m);
if (err) {
/*
* Output problem, drop packet
*/
atm_sock_stat.as_outdrop[atp->atp_type]++;
KB_FREEALL(m);
}
out:
ATM_OUTRO();
}
/*
* Abnormally terminate service
*
* Arguments:
* so pointer to socket
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
static void
atm_aal5_abort(so)
struct socket *so;
{
ATM_INTRO_NOERR("abort");
so->so_error = ECONNABORTED;
atm_sock_detach(so);
ATM_OUTRO_NOERR();
}
/*
* Do control operation - ioctl system call
*
* Arguments:
* so pointer to socket
* cmd ioctl code
* data pointer to code specific parameter data area
* ifp pointer to ifnet structure if it's an interface ioctl
* p pointer to process
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
static int
atm_aal5_control(so, cmd, data, ifp, td)
struct socket *so;
u_long cmd;
caddr_t data;
struct ifnet *ifp;
struct thread *td;
{
ATM_INTRO("control");
switch (cmd) {
default:
err = EOPNOTSUPP;
}
ATM_OUTRO();
}
/*
* Sense socket status - fstat system call
*
* Arguments:
* so pointer to socket
* st pointer to file status structure
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
static int
atm_aal5_sense(so, st)
struct socket *so;
struct stat *st;
{
ATM_INTRO("sense");
/*
* Just return the max sdu size for the connection
*/
st->st_blksize = so->so_snd.sb_hiwat;
ATM_OUTRO();
}
/*
* Retrieve local socket address
*
* Arguments:
* so pointer to socket
* addr pointer to pointer to contain protocol address
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
static int
atm_aal5_sockaddr(so, addr)
struct socket *so;
struct sockaddr **addr;
{
ATM_INTRO("sockaddr");
err = atm_sock_sockaddr(so, addr);
ATM_OUTRO();
}
/*
* Retrieve peer socket address
*
* Arguments:
* so pointer to socket
* addr pointer to pointer to contain protocol address
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
static int
atm_aal5_peeraddr(so, addr)
struct socket *so;
struct sockaddr **addr;
{
ATM_INTRO("peeraddr");
err = atm_sock_peeraddr(so, addr);
ATM_OUTRO();
}
/*
* Process Incoming Calls
*
* This function will receive control when an incoming call has been matched
* to one of our registered listen parameter blocks. Assuming the call passes
* acceptance criteria and all required resources are available, we will
* create a new protocol control block and socket association. We must
* then await notification of the final SVC setup results. If any
* problems are encountered, we will just tell the connection manager to
* reject the call.
*
* Called at splnet.
*
* Arguments:
* tok owner's matched listening token
* cop pointer to incoming call's connection block
* ap pointer to incoming call's attributes
* tokp pointer to location to store our connection token
*
* Returns:
* 0 call is accepted
* errno call rejected - reason indicated
*
*/
static int
atm_aal5_incoming(tok, cop, ap, tokp)
void *tok;
Atm_connection *cop;
Atm_attributes *ap;
void **tokp;
{
Atm_pcb *atp0 = tok, *atp;
struct socket *so;
int err = 0;
/*
* Allocate a new socket and pcb for this connection.
*
* Note that our attach function will be called via sonewconn
* and it will allocate and setup most of the pcb.
*/
atm_sock_stat.as_inconn[atp0->atp_type]++;
so = sonewconn(atp0->atp_socket, 0);
if (so) {
/*
* Finish pcb setup and pass pcb back to CM
*/
atp = sotoatmpcb(so);
atp->atp_conn = cop;
atp->atp_attr = *atp0->atp_conn->co_lattr;
strncpy(atp->atp_name, atp0->atp_name, T_ATM_APP_NAME_LEN);
*tokp = atp;
} else {
err = ECONNABORTED;
atm_sock_stat.as_connfail[atp0->atp_type]++;
}
return (err);
}
/*
* Process Socket VCC Input Data
*
* Arguments:
* tok owner's connection token (atm_pcb)
* m pointer to input packet buffer chain
*
* Returns:
* none
*
*/
static void
atm_aal5_cpcs_data(tok, m)
void *tok;
KBuffer *m;
{
Atm_pcb *atp = tok;
struct socket *so;
int len;
so = atp->atp_socket;
KB_PLENGET(m, len);
/*
* Ensure that the socket is able to receive data and
* that there's room in the socket buffer
*/
if (((so->so_state & SS_ISCONNECTED) == 0) ||
(so->so_rcv.sb_state & SBS_CANTRCVMORE) ||
(len > sbspace(&so->so_rcv))) {
atm_sock_stat.as_indrop[atp->atp_type]++;
KB_FREEALL(m);
return;
}
/*
* Queue the data and notify the user
*/
sbappendrecord(&so->so_rcv, m);
sorwakeup(so);
return;
}
/*
* Process getsockopt/setsockopt system calls
*
* Arguments:
* so pointer to socket
* sopt pointer to socket option info
*
* Returns:
* 0 request processed
* errno error processing request - reason indicated
*
*/
int
atm_aal5_ctloutput(so, sopt)
struct socket *so;
struct sockopt *sopt;
{
Atm_pcb *atp;
ATM_INTRO("ctloutput");
/*
* Make sure this is for us
*/
if (sopt->sopt_level != T_ATM_SIGNALING) {
ATM_RETERR(EINVAL);
}
atp = sotoatmpcb(so);
if (atp == NULL) {
ATM_RETERR(ENOTCONN);
}
switch (sopt->sopt_dir) {
case SOPT_SET:
/*
* setsockopt()
*/
/*
* Validate socket state
*/
switch (sopt->sopt_name) {
case T_ATM_ADD_LEAF:
case T_ATM_DROP_LEAF:
if ((so->so_state & SS_ISCONNECTED) == 0) {
ATM_RETERR(ENOTCONN);
}
break;
case T_ATM_CAUSE:
case T_ATM_APP_NAME:
break;
default:
if (so->so_state & SS_ISCONNECTED) {
ATM_RETERR(EISCONN);
}
break;
}
/*
* Validate and save user-supplied option data
*/
err = atm_sock_setopt(so, sopt, atp);
break;
case SOPT_GET:
/*
* getsockopt()
*/
/*
* Return option data
*/
err = atm_sock_getopt(so, sopt, atp);
break;
}
out:
ATM_OUTRO();
}
/*
* Initialize AAL5 Sockets
*
* Arguments:
* none
*
* Returns:
* none
*
*/
void
atm_aal5_init()
{
/*
* Register our endpoint
*/
if (atm_endpoint_register(&atm_aal5_endpt))
panic("atm_aal5_init: register");
/*
* Set default connection attributes
*/
atm_aal5_defattr.aal.v.aal5.forward_max_SDU_size = T_ATM_ABSENT;
atm_aal5_defattr.aal.v.aal5.backward_max_SDU_size = T_ATM_ABSENT;
atm_aal5_defattr.aal.v.aal5.SSCS_type = T_ATM_NULL;
}
/*
* Get Connection's Application/Owner Name
*
* Arguments:
* tok owner's connection token (atm_pcb)
*
* Returns:
* addr pointer to string containing our name
*
*/
static caddr_t
atm_aal5_getname(tok)
void *tok;
{
Atm_pcb *atp = tok;
return (atp->atp_name);
}