4.3BSD-UWisc/src/sys/rpc/svc_kudp.c
#ifndef lint
static char rcs_id[] =
{"$Header: svc_kudp.c,v 1.1 86/09/05 09:17:53 tadl Exp $"};
#endif not lint
/*
* RCS Info
* $Locker: tadl $
*/
/* NFSSRC @(#)svc_kudp.c 2.1 86/04/14 */
/* @(#)svc_kudp.c 1.1 86/02/03 SMI */
/*
* svc_kudp.c,
* Server side for UDP/IP based RPC in the kernel.
*
* Copyright (C) 1984, Sun Microsystems, Inc.
*/
#include "../h/param.h"
#include "../h/systm.h"
#include "../rpc/types.h"
#include "../netinet/in.h"
#include "../rpc/xdr.h"
#include "../rpc/auth.h"
#include "../rpc/clnt.h"
#include "../rpc/rpc_msg.h"
#include "../rpc/svc.h"
#include "../h/socket.h"
#include "../h/socketvar.h"
#include "../h/mbuf.h"
#define rpc_buffer(xprt) ((xprt)->xp_p1)
/*
* Routines exported through ops vector.
*/
bool_t svckudp_recv();
bool_t svckudp_send();
enum xprt_stat svckudp_stat();
bool_t svckudp_getargs();
bool_t svckudp_freeargs();
void svckudp_destroy();
/*
* Server transport operations vector.
*/
struct xp_ops svckudp_op = {
svckudp_recv, /* Get requests */
svckudp_stat, /* Return status */
svckudp_getargs, /* Deserialize arguments */
svckudp_send, /* Send reply */
svckudp_freeargs, /* Free argument data space */
svckudp_destroy /* Destroy transport handle */
};
struct mbuf *ku_recvfrom();
void xdrmbuf_init();
/*
* Transport private data.
* Kept in xprt->xp_p2.
*/
struct udp_data {
int ud_flags; /* flag bits, see below */
u_long ud_xid; /* id */
struct mbuf *ud_inmbuf; /* input mbuf chain */
XDR ud_xdrin; /* input xdr stream */
XDR ud_xdrout; /* output xdr stream */
char ud_verfbody[MAX_AUTH_BYTES]; /* verifier */
};
/*
* Flags
*/
#define UD_BUSY 0x001 /* buffer is busy */
#define UD_WANTED 0x002 /* buffer wanted */
/*
* Server statistics
*/
struct {
int rscalls;
int rsbadcalls;
int rsnullrecv;
int rsbadlen;
int rsxdrcall;
} rsstat;
/*
* Create a transport record.
* The transport record, output buffer, and private data structure
* are allocated. The output buffer is serialized into using xdrmem.
* There is one transport record per user process which implements a
* set of services.
*/
SVCXPRT *
svckudp_create(sock, port)
struct socket *sock;
u_short port;
{
register SVCXPRT *xprt;
register struct udp_data *ud;
#ifdef RPCDEBUG
rpc_debug(4, "svckudp_create so = %x, port = %d\n", sock, port);
#endif
xprt = (SVCXPRT *)kmem_alloc((u_int)sizeof(SVCXPRT));
rpc_buffer(xprt) = (caddr_t)kmem_alloc((u_int)UDPMSGSIZE);
ud = (struct udp_data *)kmem_alloc((u_int)sizeof(struct udp_data));
bzero((caddr_t)ud, sizeof(*ud));
xprt->xp_addrlen = 0;
xprt->xp_p2 = (caddr_t)ud;
xprt->xp_verf.oa_base = ud->ud_verfbody;
xprt->xp_ops = &svckudp_op;
xprt->xp_port = port;
xprt->xp_sock = sock;
xprt_register(xprt);
return (xprt);
}
/*
* Destroy a transport record.
* Frees the space allocated for a transport record.
*/
void
svckudp_destroy(xprt)
register SVCXPRT *xprt;
{
register struct udp_data *ud = (struct udp_data *)xprt->xp_p2;
#ifdef RPCDEBUG
rpc_debug(4, "usr_destroy %x\n", xprt);
#endif
if (ud->ud_inmbuf) {
m_freem(ud->ud_inmbuf);
}
kmem_free((caddr_t)ud, (u_int)sizeof(struct udp_data));
kmem_free((caddr_t)rpc_buffer(xprt), (u_int)UDPMSGSIZE);
kmem_free((caddr_t)xprt, (u_int)sizeof(SVCXPRT));
}
/*
* Receive rpc requests.
* Pulls a request in off the socket, checks if the packet is intact,
* and deserializes the call packet.
*/
bool_t
svckudp_recv(xprt, msg)
register SVCXPRT *xprt;
struct rpc_msg *msg;
{
register struct udp_data *ud = (struct udp_data *)xprt->xp_p2;
register XDR *xdrs = &(ud->ud_xdrin);
register struct mbuf *m;
int s;
#ifdef RPCDEBUG
rpc_debug(4, "svckudp_recv %x\n", xprt);
#endif
rsstat.rscalls++;
s = splnet();
m = ku_recvfrom(xprt->xp_sock, &(xprt->xp_raddr));
(void) splx(s);
if (m == NULL) {
rsstat.rsnullrecv++;
return (FALSE);
}
if (m->m_len < 4*sizeof(u_long)) {
rsstat.rsbadlen++;
goto bad;
}
xdrmbuf_init(&ud->ud_xdrin, m, XDR_DECODE);
if (! xdr_callmsg(xdrs, msg)) {
rsstat.rsxdrcall++;
goto bad;
}
ud->ud_xid = msg->rm_xid;
ud->ud_inmbuf = m;
#ifdef RPCDEBUG
rpc_debug(5, "svckudp_recv done\n");
#endif
return (TRUE);
bad:
m_freem(m);
ud->ud_inmbuf = NULL;
rsstat.rsbadcalls++;
return (FALSE);
}
#ifndef lint
/*
* 9/11/86 tadl
* Why is this here?
*/
static
noop()
{
}
#endif !lint
static
buffree(ud)
register struct udp_data *ud;
{
ud->ud_flags &= ~UD_BUSY;
if (ud->ud_flags & UD_WANTED) {
ud->ud_flags &= ~UD_WANTED;
wakeup((caddr_t)ud);
}
}
/*
* Send rpc reply.
* Serialize the reply packet into the output buffer then
* call ku_sendto to make an mbuf out of it and send it.
*/
bool_t
/* ARGSUSED */
svckudp_send(xprt, msg)
register SVCXPRT *xprt;
struct rpc_msg *msg;
{
register struct udp_data *ud = (struct udp_data *)xprt->xp_p2;
register XDR *xdrs = &(ud->ud_xdrout);
register int slen;
register int stat = FALSE;
int s;
struct mbuf *m, *mclgetx();
#ifdef RPCDEBUG
rpc_debug(4, "svckudp_send %x\n", xprt);
#endif
s = splimp();
while (ud->ud_flags & UD_BUSY) {
ud->ud_flags |= UD_WANTED;
sleep((caddr_t)ud, PZERO-2);
}
ud->ud_flags |= UD_BUSY;
(void) splx(s);
m = mclgetx(buffree, (caddr_t)ud, rpc_buffer(xprt), UDPMSGSIZE, M_WAIT);
if (m == NULL) {
buffree(ud);
return (stat);
}
xdrmbuf_init(&ud->ud_xdrout, m, XDR_ENCODE);
msg->rm_xid = ud->ud_xid;
if (xdr_replymsg(xdrs, msg)) {
slen = (int)XDR_GETPOS(xdrs);
if (m->m_next == 0) { /* XXX */
m->m_len = slen;
}
if (!ku_sendto_mbuf(xprt->xp_sock, m, &xprt->xp_raddr))
stat = TRUE;
} else {
printf("svckudp_send: xdr_replymsg failed\n");
m_freem(m);
}
/*
* This is completely disgusting. If public is set it is
* a pointer to a structure whose first field is the address
* of the function to free that structure and any related
* stuff. (see rrokfree in nfs_xdr.c).
*/
if (xdrs->x_public) {
(**((int (**)())xdrs->x_public))(xdrs->x_public);
}
#ifdef RPCDEBUG
rpc_debug(5, "svckudp_send done\n");
#endif
return (stat);
}
/*
* Return transport status.
*/
/*ARGSUSED*/
enum xprt_stat
svckudp_stat(xprt)
SVCXPRT *xprt;
{
return (XPRT_IDLE);
}
/*
* Deserialize arguments.
*/
bool_t
svckudp_getargs(xprt, xdr_args, args_ptr)
SVCXPRT *xprt;
xdrproc_t xdr_args;
caddr_t args_ptr;
{
return ((*xdr_args)(&(((struct udp_data *)(xprt->xp_p2))->ud_xdrin), args_ptr));
}
bool_t
svckudp_freeargs(xprt, xdr_args, args_ptr)
SVCXPRT *xprt;
xdrproc_t xdr_args;
caddr_t args_ptr;
{
register XDR *xdrs =
&(((struct udp_data *)(xprt->xp_p2))->ud_xdrin);
register struct udp_data *ud = (struct udp_data *)xprt->xp_p2;
if (ud->ud_inmbuf) {
m_freem(ud->ud_inmbuf);
}
ud->ud_inmbuf = (struct mbuf *)0;
if (args_ptr) {
xdrs->x_op = XDR_FREE;
return ((*xdr_args)(xdrs, args_ptr));
} else {
return (TRUE);
}
}
/*
* the dup cacheing routines below provide a cache of non-failure
* transaction id's. rpc service routines can use this to detect
* retransmissions and re-send a non-failure response.
*/
struct dupreq {
u_long dr_xid;
struct sockaddr_in dr_addr;
u_long dr_proc;
u_long dr_vers;
u_long dr_prog;
struct dupreq *dr_next;
struct dupreq *dr_chain;
};
/*
* MAXDUPREQS is the number of cached items. It should be adjusted
* to the service load so that there is likely to be a response entry
* when the first retransmission comes in.
*/
#define MAXDUPREQS 400
#define DUPREQSZ (sizeof(struct dupreq) - 2*sizeof(caddr_t))
#define DRHASHSZ 32
#define XIDHASH(xid) ((xid) & (DRHASHSZ-1))
#define DRHASH(dr) XIDHASH((dr)->dr_xid)
#define REQTOXID(req) ((struct udp_data *)((req)->rq_xprt->xp_p2))->ud_xid
int ndupreqs;
int dupreqs;
int dupchecks;
struct dupreq *drhashtbl[DRHASHSZ];
/*
* drmru points to the head of a circular linked list in lru order.
* drmru->dr_next == drlru
*/
struct dupreq *drmru;
svckudp_dupsave(req)
register struct svc_req *req;
{
register struct dupreq *dr;
if (ndupreqs < MAXDUPREQS) {
dr = (struct dupreq *)kmem_alloc(sizeof(*dr));
if (drmru) {
dr->dr_next = drmru->dr_next;
drmru->dr_next = dr;
} else {
dr->dr_next = dr;
}
ndupreqs++;
} else {
dr = drmru->dr_next;
unhash(dr);
}
drmru = dr;
dr->dr_xid = REQTOXID(req);
dr->dr_prog = req->rq_prog;
dr->dr_vers = req->rq_vers;
dr->dr_proc = req->rq_proc;
dr->dr_addr = req->rq_xprt->xp_raddr;
dr->dr_chain = drhashtbl[DRHASH(dr)];
drhashtbl[DRHASH(dr)] = dr;
}
svckudp_dup(req)
register struct svc_req *req;
{
register struct dupreq *dr;
u_long xid;
dupchecks++;
xid = REQTOXID(req);
dr = drhashtbl[XIDHASH(xid)];
while (dr != NULL) {
if (dr->dr_xid != xid ||
dr->dr_prog != req->rq_prog ||
dr->dr_vers != req->rq_vers ||
dr->dr_proc != req->rq_proc ||
bcmp((caddr_t)&dr->dr_addr,
(caddr_t)&req->rq_xprt->xp_raddr,
sizeof(dr->dr_addr)) != 0) {
dr = dr->dr_chain;
continue;
} else {
dupreqs++;
return (1);
}
}
return (0);
}
static
unhash(dr)
struct dupreq *dr;
{
struct dupreq *drt;
struct dupreq *drtprev = NULL;
drt = drhashtbl[DRHASH(dr)];
while (drt != NULL) {
if (drt == dr) {
if (drtprev == NULL) {
drhashtbl[DRHASH(dr)] = drt->dr_chain;
} else {
drtprev->dr_chain = drt->dr_chain;
}
return;
}
drtprev = drt;
drt = drt->dr_chain;
}
}