2021-10-11 22:19:34 -03:00

383 lines
7.5 KiB
C

static char sccsid[] = "@(#)13 1.9 src/bos/kernel/net/nddsock.c, sysnet, bos411, 9433A411a 8/12/94 11:00:19";
/*
* COMPONENT_NAME: SYSNET
*
* FUNCTIONS: ndd_control
* ndd_output
* ndd_usrreq
* nddintr
* nddisr
* nddsock_init
*
*
* ORIGINS: 27
*
*
* (C) COPYRIGHT International Business Machines Corp. 1988,1993
* All Rights Reserved
* Licensed Materials - Property of IBM
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*/
#include <sys/types.h>
#include <sys/device.h>
#include <sys/domain.h> /* for struct domain */
#include <sys/socket.h>
#include <sys/protosw.h>
#include <sys/errno.h>
#include <sys/mbuf.h>
#include <sys/socketvar.h>
#include <sys/syspest.h>
#include <sys/lockl.h>
#include <sys/malloc.h>
#include <net/raw_cb.h>
#include <sys/cdli.h>
#include <sys/ndd.h>
#include <net/nd_lan.h>
#include <sys/ndd_var.h>
#include <net/netisr.h>
int ndd_output();
void nddintr();
int ndd_usrreq();
extern struct domain ndddomain;
/*
* nddintr() schedules nddisr() via the netisr queue code.
*/
struct ifqueue nddisrq;
int nddqmaxlen=50;
void nddisr();
struct protosw nddsw[] = {
{ SOCK_DGRAM, &ndddomain, 0, PR_ATOMIC,
0, ndd_output, 0, 0,
ndd_usrreq, 0, 0,
0, 0, 0, 0,
}
};
struct domain ndddomain =
{ AF_NDD, "nddnet", 0, 0, 0,
nddsw, &nddsw[sizeof(nddsw)/sizeof(nddsw[0])],
0, 0, 0, 0 };
nddsock_init()
{
domain_add(&ndddomain);
IFQ_LOCKINIT(&nddisrq);
nddisrq.ifq_maxlen = nddqmaxlen;
(void) netisr_add(NETISR_NDD, nddisr, &nddisrq, &ndddomain);
}
void
ndd_clean_isrq(struct socket *so)
{
IFQ_LOCK_DECL()
struct mbuf *m;
struct ifqueue tmpq;
struct socket *tmpso;
bzero((caddr_t)&tmpq, sizeof(tmpq));
tmpq.ifq_maxlen = nddqmaxlen;
IFQ_LOCK(&nddisrq);
do {
IF_DEQUEUE_NOLOCK(&nddisrq, m);
if (m)
IF_ENQUEUE_NOLOCK(&tmpq, m);
} while (m);
do {
IF_DEQUEUE_NOLOCK(&tmpq, m);
if (m) {
tmpso = (struct socket *)m->m_pkthdr.rcvif;
if (tmpso == so)
m_freem(m);
else
IF_ENQUEUE_NOLOCK(&nddisrq, m);
}
} while (m);
IFQ_UNLOCK(&nddisrq);
}
/*ARGSUSED*/
ndd_usrreq(so, req, m, nam, control)
struct socket *so;
int req;
struct mbuf *m, *nam, *control;
{
register int error = 0;
register struct raw_nddpcb *rp = sotorawnddpcb(so);
LOCK_ASSERT("ndd_usrreq", SOCKET_ISLOCKED(so));
switch (req) {
case PRU_ATTACH:
assert(rp == NULL);
NET_MALLOC(rp, struct raw_nddpcb *, sizeof *rp, M_PCB,
M_WAITOK);
bzero((caddr_t)rp, sizeof *rp);
so->so_pcb = (caddr_t)rp;
break;
case PRU_DETACH:
{
struct sockaddr_ndd *addr = &rp->rndd_laddr;
assert(rp != NULL);
if (rp->rndd_recvnddp != NULL) {
if (rp->rndd_rcb.rcb_laddr != NULL) {
assert(ns_del_filter(rp->rndd_recvnddp,
addr->sndd_data,
addr->sndd_filterlen) == 0);
ndd_clean_isrq(so);
}
ns_free(rp->rndd_recvnddp);
}
break;
}
case PRU_BIND:
{
struct sockaddr_ndd *addr = mtod(nam, struct sockaddr_ndd *);
struct ns_user ns_user;
if (addr->sndd_nddname == NULL)
return(EINVAL);
if (addr->sndd_filterlen < 0 ||
addr->sndd_filterlen > SNDD_MAXFILTER)
return(EINVAL);
if (rp->rndd_rcb.rcb_laddr)
return(EEXIST);
if (rp->rndd_recvnddp == NULL)
error = ns_alloc(addr->sndd_nddname, &rp->rndd_recvnddp);
if (error)
return(error);
/* XXX - wait for status block for open done? */
ns_user.isr = nddintr;
ns_user.protoq = (struct ifqueue *) NULL;
ns_user.netisr = 0;
ns_user.pkt_format = NS_INCLUDE_MAC;
ns_user.isr_data = (caddr_t) so;
ns_user.ifp = (struct ifnet *) NULL;
if (error = ns_add_filter(rp->rndd_recvnddp, addr->sndd_data,
addr->sndd_filterlen, &ns_user)) {
ns_free(rp->rndd_recvnddp);
rp->rndd_recvnddp = NULL;
return(error);
}
rp->rndd_rcb.rcb_laddr = (struct sockaddr *)&rp->rndd_laddr;
rp->rndd_laddr = *addr;
return(0);
}
case PRU_CONNECT:
{
struct sockaddr_ndd *addr = mtod(nam, struct sockaddr_ndd *);
if (rp->rndd_rcb.rcb_faddr)
return(EISCONN);
if (rp->rndd_recvnddp == NULL)
error = ns_alloc(addr->sndd_nddname, &rp->rndd_recvnddp);
if (error)
return(error);
/* XXX - wait for status block for open done? */
rp->rndd_rcb.rcb_faddr = (struct sockaddr*)&rp->rndd_faddr;
rp->rndd_faddr = *addr;
soisconnected(so);
return(0);
}
case PRU_CONTROL:
return(ndd_control(so, (int)m, (caddr_t)nam, rp->rndd_recvnddp
));
default:
break;
}
error = raw_usrreq(so, req, m, nam, control);
if (error && (req == PRU_ATTACH) && so->so_pcb)
NET_FREE(so->so_pcb, M_PCB);
return(error);
}
/*
* Generic control operations (ioctl's).
*/
/* ARGSUSED */
ndd_control(so, cmd, data, nddp)
struct socket *so;
int cmd;
caddr_t data;
register struct ndd *nddp;
{
struct nddctl *nddctl = (struct nddctl *)data;
int length;
caddr_t bufaddr;
caddr_t buf;
int error = 0;
if (data == NULL) {
if (nddp != (struct ndd *)NULL)
return((*nddp->ndd_ctl)(nddp, cmd, NULL, 0));
else
return(ENOTCONN);
}
if ( copyin((int *)&nddctl->nddctl_buflen, &length, sizeof(length)) )
return EFAULT;
if (length < 0)
return(EINVAL);
if ( copyin((int *)&nddctl->nddctl_buf, &bufaddr, sizeof(bufaddr)) )
return EFAULT;
NET_MALLOC(buf, caddr_t, length, M_TEMP, M_WAITOK);
if ( copyin((int *)bufaddr, buf, length) ) {
NET_FREE(buf, M_TEMP);
return EFAULT;
}
if (nddp != (struct ndd *)NULL) {
error = (*nddp->ndd_ctl)(nddp, cmd, buf, length);
if ( copyout(buf, (int *)bufaddr, length) )
error = EFAULT;
} else
error = ENOTCONN;
NET_FREE(buf, M_TEMP);
return(error);
}
/*
* nddintr - NDD interrupt handler
*
*/
void
nddintr(nddp, m, macp, so)
register struct ndd *nddp;
register struct mbuf *m;
caddr_t macp;
struct socket *so;
{
struct raw_nddpcb *rp;
struct sockaddr_ndd_8022 *sa;
struct mbuf *n;
IFQ_LOCK_DECL()
/*
* For TAP users, we must copy the mbuf. If this fails, we
* bail...
*/
rp = (struct raw_nddpcb *)(so->so_pcb);
sa = (struct sockaddr_ndd_8022 *)(&rp->rndd_laddr);
if (sa->sndd_8022_filtertype == NS_TAP) {
n = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
if (n == NULL)
return;
m = n;
}
/*
* Save the socket ptr in the rcvif field of the mbuf so the
* ISR handler can use it.
*/
m->m_pkthdr.rcvif = (struct ifnet *)so;
IFQ_LOCK(&nddisrq);
if (IF_QFULL(&nddisrq)) {
IF_DROP(&nddisrq);
m_freem(m);
} else {
IF_ENQUEUE_NOLOCK(&nddisrq, m);
}
IFQ_UNLOCK(&nddisrq);
schednetisr(NETISR_NDD);
}
void
nddisr()
{
IFQ_LOCK_DECL()
struct mbuf *m;
struct socket *so;
do {
IF_DEQUEUE(&nddisrq, m);
if (m) {
so = (struct socket *)m->m_pkthdr.rcvif;
SOCKET_LOCK(so);
SOCKBUF_LOCK(&so->so_rcv);
if (m->m_pkthdr.len > sbspace(&so->so_rcv))
m_freem(m);
else {
sbappendrecord(&so->so_rcv, m);
sorwakeup(so);
}
SOCKBUF_LOCK(&so->so_rcv);
SOCKET_UNLOCK(so);
}
} while (m);
}
/*
* RAW NDD output
*/
ndd_output(m0, so)
struct mbuf *m0;
struct socket *so;
{
int error=0;
struct mbuf *m;
struct ndd *nddp;
struct raw_nddpcb *rp = sotorawnddpcb(so);
LOCK_ASSERT("ndd_output", SOCKET_ISLOCKED(so));
nddp = rp->rndd_recvnddp;
if (nddp == (struct ndd *) NULL) {
error = ENOTCONN;
goto bad;
}
for (m = m0; m; m = m0) {
m0 = m->m_nextpkt;
m->m_nextpkt = 0;
if (error == 0) {
if ( (m->m_pkthdr.len < nddp->ndd_mintu) &&
(m->m_pkthdr.len > nddp->ndd_mtu) )
error = EMSGSIZE;
else
error = (*nddp->ndd_output)(nddp, m);
if (error)
m_freem(m);
} else
m_freem(m);
}
return (error);
bad:
m_freem(m0);
return (error);
}