/* * * =================================== * 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. * * @(#) $FreeBSD: src/sys/netatm/atm_aal5.c,v 1.6 1999/10/09 23:24:59 green Exp $ * @(#) $DragonFly: src/sys/netproto/atm/atm_aal5.c,v 1.7 2004/03/05 19:17:25 hsu Exp $ */ /* * Core ATM Services * ----------------- * * ATM AAL5 socket protocol processing * */ #include "kern_include.h" #include /* * 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 pru_attach_info *); static int atm_aal5_detach (struct socket *); static int atm_aal5_bind (struct socket *, struct sockaddr *, struct thread *); static int atm_aal5_listen (struct socket *, struct thread *); static int atm_aal5_connect (struct socket *, struct sockaddr *, struct thread *); 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 *); static int atm_aal5_abort (struct socket *); static int atm_aal5_control (struct socket *, u_long, caddr_t, struct ifnet *, struct thread *); 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 *); #if (defined(__DragonFly__) && (BSD >= 199506)) /* * New-style socket request routines */ struct pr_usrreqs atm_aal5_usrreqs = { atm_aal5_abort, /* pru_abort */ atm_aal5_accept, /* pru_accept */ atm_aal5_attach, /* pru_attach */ atm_aal5_bind, /* pru_bind */ atm_aal5_connect, /* pru_connect */ pru_connect2_notsupp, /* pru_connect2 */ atm_aal5_control, /* pru_control */ atm_aal5_detach, /* pru_detach */ atm_aal5_disconnect, /* pru_disconnect */ atm_aal5_listen, /* pru_listen */ atm_aal5_peeraddr, /* pru_peeraddr */ pru_rcvd_notsupp, /* pru_rcvd */ pru_rcvoob_notsupp, /* pru_rcvoob */ atm_aal5_send, /* pru_send */ atm_aal5_sense, /* pru_sense */ atm_aal5_shutdown, /* pru_shutdown */ atm_aal5_sockaddr, /* pru_sockaddr */ sosend, /* pru_sosend */ soreceive, /* pru_soreceive */ sopoll /* pru_sopoll */ }; #endif /* * 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_OUTRO() \ /* \ * Drain any deferred calls \ */ \ STACK_DRAIN(); \ (void) splx(s); \ return (err); \ ; #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, ai) struct socket *so; int proto; struct pru_attach_info *ai; { Atm_pcb *atp; ATM_INTRO("attach"); /* * Do general attach stuff */ err = atm_sock_attach(so, atm_aal5_sendspace, atm_aal5_recvspace, ai->sb_rlimit); 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 int atm_aal5_detach(so) struct socket *so; { ATM_INTRO("detach"); err = atm_sock_detach(so); ATM_OUTRO(); } /* * 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(struct socket *so, struct thread *td) { ATM_INTRO("listen"); err = atm_sock_listen(so, &atm_aal5_endpt); 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(struct socket *so, struct sockaddr *addr, thread_t 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->td_proc->p_rlimit[RLIMIT_SBSIZE])) { 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(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(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(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( 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 int atm_aal5_abort(struct socket *so) { ATM_INTRO("abort"); so->so_error = ECONNABORTED; err = atm_sock_detach(so); ATM_OUTRO(); } /* * 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( 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(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(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(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]++; #if (defined(BSD) && (BSD >= 199103)) so = sonewconn(atp0->atp_socket, 0); #else so = sonewconn(atp0->atp_socket); #endif 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_state & SS_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(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); }