freebsd-dev/sys/netncp/ncp_sock.c
bmilekic 4c48d530e1 * Rename M_WAIT mbuf subsystem flag to M_TRYWAIT.
This is because calls with M_WAIT (now M_TRYWAIT) may not wait
  forever when nothing is available for allocation, and may end up
  returning NULL. Hopefully we now communicate more of the right thing
  to developers and make it very clear that it's necessary to check whether
  calls with M_(TRY)WAIT also resulted in a failed allocation.
  M_TRYWAIT basically means "try harder, block if necessary, but don't
  necessarily wait forever." The time spent blocking is tunable with
  the kern.ipc.mbuf_wait sysctl.
  M_WAIT is now deprecated but still defined for the next little while.

* Fix a typo in a comment in mbuf.h

* Fix some code that was actually passing the mbuf subsystem's M_WAIT to
  malloc(). Made it pass M_WAITOK instead. If we were ever to redefine the
  value of the M_WAIT flag, this could have became a big problem.
2000-12-21 21:44:31 +00:00

440 lines
11 KiB
C

/*
* Copyright (c) 1999, Boris Popov
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Boris Popov.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*
* Low level socket routines
*/
#include "opt_inet.h"
#include "opt_ipx.h"
#if !defined(INET) && !defined(IPX)
#error "NCP requeires either INET of IPX protocol family"
#endif
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>
#include <sys/kernel.h>
#include <sys/uio.h>
#include <sys/syslog.h>
#include <sys/mbuf.h>
#include <net/route.h>
#ifdef IPX
#include <netipx/ipx.h>
#include <netipx/ipx_pcb.h>
#endif
#include <netncp/ncp.h>
#include <netncp/ncp_conn.h>
#include <netncp/ncp_sock.h>
#include <netncp/ncp_subr.h>
#include <netncp/ncp_rq.h>
#ifdef IPX
#define ipx_setnullnet(x) ((x).x_net.s_net[0]=0); ((x).x_net.s_net[1]=0);
#define ipx_setnullhost(x) ((x).x_host.s_host[0] = 0); \
((x).x_host.s_host[1] = 0); ((x).x_host.s_host[2] = 0);
#endif
/*int ncp_poll(struct socket *so, int events);*/
/*static int ncp_getsockname(struct socket *so, caddr_t asa, int *alen);*/
static int ncp_soconnect(struct socket *so,struct sockaddr *target, struct proc *p);
/* This will need only if native IP used, or (unlikely) NCP will be
* implemented on the socket level
*/
static int
ncp_soconnect(struct socket *so,struct sockaddr *target, struct proc *p) {
int error,s;
error = soconnect(so, (struct sockaddr*)target, p);
if (error)
return error;
/*
* Wait for the connection to complete. Cribbed from the
* connect system call but with the wait timing out so
* that interruptible mounts don't hang here for a long time.
*/
error = EIO;
s = splnet();
while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
(void) tsleep((caddr_t)&so->so_timeo, PSOCK, "ncpcon", 2 * hz);
if ((so->so_state & SS_ISCONNECTING) &&
so->so_error == 0 /*&& rep &&*/) {
so->so_state &= ~SS_ISCONNECTING;
splx(s);
goto bad;
}
}
if (so->so_error) {
error = so->so_error;
so->so_error = 0;
splx(s);
goto bad;
}
splx(s);
error=0;
bad:
return error;
}
#ifdef notyet
static int
ncp_getsockname(struct socket *so, caddr_t asa, int *alen) {
struct sockaddr *sa;
int len=0, error;
sa = 0;
error = (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, &sa);
if (error==0) {
if (sa) {
len = min(len, sa->sa_len);
bcopy(sa, (caddr_t)asa, (u_int)len);
}
*alen=len;
}
if (sa)
FREE(sa, M_SONAME);
return (error);
}
#endif
int ncp_sock_recv(struct socket *so, struct mbuf **mp, int *rlen)
{
struct uio auio;
struct proc *p=curproc; /* XXX */
int error,flags,len;
auio.uio_resid = len = 1000000;
auio.uio_procp = p;
flags = MSG_DONTWAIT;
/* error = so->so_proto->pr_usrreqs->pru_soreceive(so, 0, &auio,
(struct mbuf **)0, (struct mbuf **)0, &flags);*/
error = so->so_proto->pr_usrreqs->pru_soreceive(so, 0, &auio,
mp, (struct mbuf **)0, &flags);
*rlen = len - auio.uio_resid;
/* if (!error) {
*rlen=iov.iov_len;
} else
*rlen=0;*/
#ifdef NCP_SOCKET_DEBUG
if (error)
printf("ncp_recv: err=%d\n", error);
#endif
return (error);
}
int
ncp_sock_send(struct socket *so, struct mbuf *top, struct ncp_rq *rqp)
{
struct proc *p = curproc; /* XXX */
struct sockaddr *to = 0;
struct ncp_conn *conn = rqp->conn;
struct mbuf *m;
int error, flags=0;
int sendwait;
for(;;) {
m = m_copym(top, 0, M_COPYALL, M_TRYWAIT);
/* NCPDDEBUG(m);*/
error = so->so_proto->pr_usrreqs->pru_sosend(so, to, 0, m, 0, flags, p);
if (error == 0 || error == EINTR || error == ENETDOWN)
break;
if (rqp->rexmit == 0) break;
rqp->rexmit--;
tsleep(&sendwait, PWAIT, "ncprsn", conn->li.timeout * hz);
error = ncp_chkintr(conn, p);
if (error == EINTR) break;
}
if (error) {
log(LOG_INFO, "ncp_send: error %d for server %s", error, conn->li.server);
}
return error;
}
int
ncp_poll(struct socket *so, int events){
struct proc *p = curproc;
struct ucred *cred=NULL;
return so->so_proto->pr_usrreqs->pru_sopoll(so, events, cred, p);
}
int
ncp_sock_rselect(struct socket *so,struct proc *p, struct timeval *tv, int events)
{
struct timeval atv,rtv,ttv;
int s,timo,error=0;
if (tv) {
atv=*tv;
if (itimerfix(&atv)) {
error = EINVAL;
goto done;
}
getmicrouptime(&rtv);
timevaladd(&atv, &rtv);
}
timo = 0;
retry:
p->p_flag |= P_SELECT;
error = ncp_poll(so, events);
if (error) {
error = 0;
goto done;
}
if (tv) {
getmicrouptime(&rtv);
if (timevalcmp(&rtv, &atv, >=))
goto done;
ttv=atv;
timevalsub(&ttv, &rtv);
timo = tvtohz(&ttv);
}
s = splhigh();
if ((p->p_flag & P_SELECT) == 0) {
splx(s);
goto retry;
}
p->p_flag &= ~P_SELECT;
error = tsleep((caddr_t)&selwait, PSOCK, "ncpslt", timo);
splx(s);
done:
p->p_flag &= ~P_SELECT;
if (error == ERESTART) {
/* printf("Signal: %x", CURSIG(p));*/
error = 0;
}
return (error);
}
#ifdef IPX
/*
* Connect to specified server via IPX
*/
int
ncp_sock_connect_ipx(struct ncp_conn *conn) {
struct sockaddr_ipx sipx;
struct ipxpcb *npcb;
struct proc *p = conn->procp;
int addrlen, error, count;
sipx.sipx_port = htons(0);
for (count = 0;;count++) {
if (count > (IPXPORT_WELLKNOWN-IPXPORT_RESERVED)*2) {
error = EADDRINUSE;
goto bad;
}
conn->ncp_so = conn->wdg_so = NULL;
checkbad(socreate(AF_IPX, &conn->ncp_so, SOCK_DGRAM, 0, p));
if (conn->li.opt & NCP_OPT_WDOG)
checkbad(socreate(AF_IPX, &conn->wdg_so, SOCK_DGRAM,0,p));
addrlen = sizeof(sipx);
sipx.sipx_family = AF_IPX;
ipx_setnullnet(sipx.sipx_addr);
ipx_setnullhost(sipx.sipx_addr);
sipx.sipx_len = addrlen;
error = sobind(conn->ncp_so, (struct sockaddr *)&sipx, p);
if (error == 0) {
if ((conn->li.opt & NCP_OPT_WDOG) == 0)
break;
sipx.sipx_addr = sotoipxpcb(conn->ncp_so)->ipxp_laddr;
sipx.sipx_port = htons(ntohs(sipx.sipx_port) + 1);
ipx_setnullnet(sipx.sipx_addr);
ipx_setnullhost(sipx.sipx_addr);
error = sobind(conn->wdg_so, (struct sockaddr *)&sipx, p);
}
if (!error) break;
if (error != EADDRINUSE) goto bad;
sipx.sipx_port = htons((ntohs(sipx.sipx_port)+4) & 0xfff8);
soclose(conn->ncp_so);
if (conn->wdg_so)
soclose(conn->wdg_so);
}
npcb = sotoipxpcb(conn->ncp_so);
npcb->ipxp_dpt = IPXPROTO_NCP;
/* IPXrouted must be running, i.e. route must be presented */
conn->li.ipxaddr.sipx_len = sizeof(struct sockaddr_ipx);
checkbad(ncp_soconnect(conn->ncp_so, &conn->li.saddr, p));
if (conn->wdg_so) {
sotoipxpcb(conn->wdg_so)->ipxp_laddr.x_net = npcb->ipxp_laddr.x_net;
sotoipxpcb(conn->wdg_so)->ipxp_laddr.x_host= npcb->ipxp_laddr.x_host;
}
if (!error) {
conn->flags |= NCPFL_SOCONN;
}
#ifdef NCPBURST
if (ncp_burst_enabled) {
checkbad(socreate(AF_IPX, &conn->bc_so, SOCK_DGRAM, 0, p));
bzero(&sipx, sizeof(sipx));
sipx.sipx_len = sizeof(sipx);
checkbad(sobind(conn->bc_so, (struct sockaddr *)&sipx, p));
checkbad(ncp_soconnect(conn->bc_so, &conn->li.saddr, p));
}
#endif
if (!error) {
conn->flags |= NCPFL_SOCONN;
ncp_sock_checksum(conn, 0);
}
return error;
bad:
ncp_sock_disconnect(conn);
return (error);
}
int
ncp_sock_checksum(struct ncp_conn *conn, int enable) {
#ifdef SO_IPX_CHECKSUM
if (enable) {
sotoipxpcb(conn->ncp_so)->ipxp_flags |= IPXP_CHECKSUM;
} else {
sotoipxpcb(conn->ncp_so)->ipxp_flags &= ~IPXP_CHECKSUM;
}
#endif
return 0;
}
#endif
#ifdef INET
/*
* Connect to specified server via IP
*/
int
ncp_sock_connect_in(struct ncp_conn *conn) {
struct sockaddr_in sin;
struct proc *p=conn->procp;
int addrlen=sizeof(sin), error;
conn->flags = 0;
bzero(&sin,addrlen);
conn->ncp_so = conn->wdg_so = NULL;
checkbad(socreate(AF_INET, &conn->ncp_so, SOCK_DGRAM, IPPROTO_UDP, p));
sin.sin_family = AF_INET;
sin.sin_len = addrlen;
checkbad(sobind(conn->ncp_so, (struct sockaddr *)&sin, p));
checkbad(ncp_soconnect(conn->ncp_so,(struct sockaddr*)&conn->li.addr, p));
if (!error)
conn->flags |= NCPFL_SOCONN;
return error;
bad:
ncp_sock_disconnect(conn);
return (error);
}
#endif
/*
* Connection expected to be locked
*/
int
ncp_sock_disconnect(struct ncp_conn *conn) {
register struct socket *so;
conn->flags &= ~(NCPFL_SOCONN | NCPFL_ATTACHED | NCPFL_LOGGED);
if (conn->ncp_so) {
so = conn->ncp_so;
conn->ncp_so = (struct socket *)0;
soshutdown(so, 2);
soclose(so);
}
if (conn->wdg_so) {
so = conn->wdg_so;
conn->wdg_so = (struct socket *)0;
soshutdown(so, 2);
soclose(so);
}
#ifdef NCPBURST
if (conn->bc_so) {
so = conn->bc_so;
conn->bc_so = (struct socket *)NULL;
soshutdown(so, 2);
soclose(so);
}
#endif
return 0;
}
#ifdef IPX
static void
ncp_watchdog(struct ncp_conn *conn) {
char *buf;
struct mbuf *m;
int error, len, flags;
struct socket *so;
struct sockaddr *sa;
struct uio auio;
sa = NULL;
while (conn->wdg_so) { /* not a loop */
so = conn->wdg_so;
auio.uio_resid = len = 1000000;
auio.uio_procp = curproc;
flags = MSG_DONTWAIT;
error = so->so_proto->pr_usrreqs->pru_soreceive(so,
(struct sockaddr**)&sa, &auio, &m, (struct mbuf**)0, &flags);
if (error) break;
len -= auio.uio_resid;
NCPSDEBUG("got watch dog %d\n",len);
if (len != 2) break;
buf = mtod(m, char*);
if (buf[1] != '?') break;
buf[1] = 'Y';
error = so->so_proto->pr_usrreqs->pru_sosend(so, (struct sockaddr*)sa, 0, m, 0, 0, curproc);
NCPSDEBUG("send watch dog %d\n",error);
break;
}
if (sa) FREE(sa, M_SONAME);
return;
}
#endif /* IPX */
void
ncp_check_conn(struct ncp_conn *conn) {
int s;
if (conn == NULL || !(conn->flags & NCPFL_ATTACHED))
return;
s = splnet();
ncp_check_rq(conn);
splx(s);
#ifdef IPX
ncp_watchdog(conn);
#endif
}