/*#define DEBUG 1 */ #ifndef lint static char *RCSid = "$Header: /proj/freeware1.0/nfswatch/nfswatch4.1/RCS/rpcfilter.c,v 1.1 1995/08/08 04:40:50 rck Exp $"; #endif #include "os.h" /* * rpcfilter.c - filter RPC packets. * * David A. Curry Jeffrey C. Mogul * Purdue University Digital Equipment Corporation * Engineering Computer Network Western Research Laboratory * 1285 Electrical Engineering Building 250 University Avenue * West Lafayette, IN 47907-1285 Palo Alto, CA 94301 * davy@ecn.purdue.edu mogul@decwrl.dec.com * * $Log: rpcfilter.c,v $ * Revision 1.1 1995/08/08 04:40:50 rck * initial checkin * * Revision 4.9 1993/10/11 21:37:51 mogul * Avoid a few Solaris compiler warnings. * * Revision 4.8 1993/10/04 18:33:06 mogul * Another argument to Parse_fh * * Revision 4.7 93/10/04 11:01:03 mogul * Added support for "-fhdebug" * * Revision 4.6 93/10/01 10:45:54 mogul * Revert to int32, u_int32 names * * Revision 4.5 93/09/30 20:37:42 davy * More Solaris 2.2/2.3 fixes, plus int32 and u_int32 type name fixes. * * Revision 4.4 1993/09/28 23:26:35 mogul * one-behind cache to speed filesystem search * prints inferred server OS type if DEBUG defined. * * Revision 4.3 1993/09/28 21:21:52 mogul * Split file-handle parser out into a separate module. * Additional OSF/1 support * explicit data type for IP addresses * tricks to handle VMS file system names * * Revision 4.2 93/09/27 14:17:06 mogul * *** empty log message *** * * Revision 4.1 93/09/15 20:42:05 davy * Patch to understand Auspex file handles from Guy Harris (guy@auspex.com). * * Revision 4.0 1993/03/01 19:59:00 davy * NFSWATCH Version 4.0. * * Revision 3.7 1993/03/01 19:57:23 davy * Fixed some byte-order bugs. * * Revision 3.6 1993/02/24 17:44:45 davy * Added -auth mode, changes to -proc mode, -map option, -server option. * * Revision 3.5 1993/01/16 19:08:59 davy * Corrected Jeff's address. * * Revision 3.4 1993/01/15 19:33:39 davy * Miscellaneous cleanups. * * Revision 3.3 1993/01/15 15:43:36 davy * Assorted changes for porting to Solaris 2.x/SVR4. * * Revision 3.2 1993/01/13 20:18:17 davy * Put in OS-specific define scheme, and merged in Tim Hudson's code for * SGI systems (as yet untested). * * Revision 3.1 1993/01/13 13:50:59 davy * Made the file matching code work on Sun-3's, and also put a generic one in * there in hopes it'll work. * * Revision 3.0 1991/01/23 08:23:20 davy * NFSWATCH Version 3.0. * * Revision 1.5 91/01/17 10:13:02 davy * Bug fix from Jeff Mogul. * * Revision 1.7 91/01/16 15:49:12 mogul * Print server or client address in a.b.c.d notation if name not known * * Revision 1.6 91/01/07 15:35:51 mogul * Uses hash table instead of linear search on clients * One-element "hint" cache to avoid client hash lookup * * Revision 1.5 91/01/04 14:12:35 mogul * Support for client counters * Disable screen update during database upheaval * * Revision 1.4 91/01/03 17:35:00 mogul * Count per-procedure info * * Revision 1.3 90/12/04 08:22:06 davy * Fix from Dan Trinkle (trinkle@cs.purdue.edu) to determine byte order in * file handle. * * Revision 1.2 90/08/17 15:47:44 davy * NFSWATCH Version 2.0. * * Revision 1.1 88/11/29 11:20:51 davy * NFSWATCH Release 1.0 * */ #include #include #include #include #ifdef SVR4 #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #define NFSSERVER 1 #ifdef sun #include #include #endif #ifdef ultrix #include #include #include #endif #ifdef sgi #include #include "sgi.map.h" #include #endif #ifdef __alpha #include #include #include #include #endif #include "nfswatch.h" #include "externs.h" #include "rpcdefs.h" /* * NFS procedure types and XDR argument decoding routines. */ static struct nfs_proc nfs_procs[] = { /* RFS_NULL (0) */ NFS_READ, xdr_void, 0, /* RFS_GETATTR (1) */ NFS_READ, xdr_fhandle, sizeof(fhandle_t), /* RFS_SETATTR (2) */ NFS_WRITE, xdr_saargs, sizeof(struct nfssaargs), /* RFS_ROOT (3) */ NFS_READ, xdr_void, 0, /* RFS_LOOKUP (4) */ NFS_READ, xdr_diropargs, sizeof(struct nfsdiropargs), /* RFS_READLINK (5) */ NFS_READ, xdr_fhandle, sizeof(fhandle_t), /* RFS_READ (6) */ NFS_READ, xdr_readargs, sizeof(struct nfsreadargs), /* RFS_WRITECACHE (7) */ NFS_WRITE, xdr_void, 0, /* RFS_WRITE (8) */ NFS_WRITE, xdr_writeargs, sizeof(struct nfswriteargs), /* RFS_CREATE (9) */ NFS_WRITE, xdr_creatargs, sizeof(struct nfscreatargs), /* RFS_REMOVE (10) */ NFS_WRITE, xdr_diropargs, sizeof(struct nfsdiropargs), /* RFS_RENAME (11) */ NFS_WRITE, xdr_rnmargs, sizeof(struct nfsrnmargs), /* RFS_LINK (12) */ NFS_WRITE, xdr_linkargs, sizeof(struct nfslinkargs), /* RFS_SYMLINK (13) */ NFS_WRITE, xdr_slargs, sizeof(struct nfsslargs), /* RFS_MKDIR (14) */ NFS_WRITE, xdr_creatargs, sizeof(struct nfscreatargs), /* RFS_RMDIR (15) */ NFS_WRITE, xdr_diropargs, sizeof(struct nfsdiropargs), /* RFS_READDIR (16) */ NFS_READ, xdr_rddirargs, sizeof(struct nfsrddirargs), /* RFS_STATFS (17) */ NFS_READ, xdr_fhandle, sizeof(fhandle_t) }; NFSCall nfs_calls[NFSCALLHASHSIZE]; /* * rpc_filter - pass off RPC packets to other filters. */ void rpc_filter(data, length, src, dst, tstamp) register ipaddrt src, dst; struct timeval *tstamp; register u_int length; register char *data; { register struct rpc_msg *msg; msg = (struct rpc_msg *) data; /* * See which "direction" the packet is going. We * can classify RPC CALLs, but we cannot classify * REPLYs, since they no longer have the RPC * program number in them (sigh). */ switch (ntohl(msg->rm_direction)) { case CALL: /* RPC call */ rpc_callfilter(data, length, src, dst, tstamp); break; case REPLY: /* RPC reply */ rpc_replyfilter(data, length, src, dst, tstamp); break; default: /* probably not an RPC packet */ break; } } /* * rpc_callfilter - filter RPC call packets. */ void rpc_callfilter(data, length, src, dst, tstamp) register ipaddrt src, dst; struct timeval *tstamp; register u_int length; register char *data; { register struct rpc_msg *msg; msg = (struct rpc_msg *) data; /* * Decide what to do based on the program. */ switch (ntohl(msg->rm_call.cb_prog)) { case RPC_NFSPROG: nfs_filter(data, length, src, dst, tstamp); break; case RPC_YPPROG: case RPC_YPBINDPROG: case RPC_YPPASSWDPROG: case RPC_YPUPDATEPROG: case RPC_CACHEPROG: case RPC_CB_PROG: pkt_counters[PKT_YELLOWPAGES].pc_interval++; pkt_counters[PKT_YELLOWPAGES].pc_total++; break; case RPC_MOUNTPROG: pkt_counters[PKT_NFSMOUNT].pc_interval++; pkt_counters[PKT_NFSMOUNT].pc_total++; break; #ifdef notdef case RPC_PMAPPROG: case RPC_RSTATPROG: case RPC_RUSERSPROG: case RPC_DBXPROG: case RPC_WALLPROG: case RPC_ETHERSTATPROG: case RPC_RQUOTAPROG: case RPC_SPRAYPROG: case RPC_IBM3270PROG: case RPC_IBMRJEPROG: case RPC_SELNSVCPROG: case RPC_RDATABASEPROG: case RPC_REXECPROG: case RPC_ALICEPROG: case RPC_SCHEDPROG: case RPC_LOCKPROG: case RPC_NETLOCKPROG: case RPC_X25PROG: case RPC_STATMON1PROG: case RPC_STATMON2PROG: case RPC_SELNLIBPROG: case RPC_BOOTPARAMPROG: case RPC_MAZEPROG: case RPC_KEYSERVEPROG: case RPC_SECURECMDPROG: case RPC_NETFWDIPROG: case RPC_NETFWDTPROG: case RPC_SUNLINKMAP_PROG: case RPC_NETMONPROG: case RPC_DBASEPROG: case RPC_PWDAUTHPROG: case RPC_TFSPROG: case RPC_NSEPROG: case RPC_NSE_ACTIVATE_PROG: case RPC_PCNFSDPROG: case RPC_PYRAMIDLOCKINGPROG: case RPC_PYRAMIDSYS5: case RPC_CADDS_IMAGE: case RPC_ADT_RFLOCKPROG: #endif default: pkt_counters[PKT_OTHERRPC].pc_interval++; pkt_counters[PKT_OTHERRPC].pc_total++; break; } } /* * rpc_replyfilter - count RPC reply packets. */ void rpc_replyfilter(data, length, src, dst, tstamp) register ipaddrt src, dst; struct timeval *tstamp; register u_int length; register char *data; { register struct rpc_msg *msg; msg = (struct rpc_msg *) data; pkt_counters[PKT_RPCAUTH].pc_interval++; pkt_counters[PKT_RPCAUTH].pc_total++; nfs_hash_reply(msg, dst, tstamp); } /* * nfs_filter - filter NFS packets. */ void nfs_filter(data, length, src, dst, tstamp) register ipaddrt src, dst; struct timeval *tstamp; register u_int length; register char *data; { u_int proc; caddr_t args; SVCXPRT *xprt; struct rpc_msg msg; union nfs_rfsargs nfs_rfsargs; char cred_area[2*MAX_AUTH_BYTES]; msg.rm_call.cb_cred.oa_base = cred_area; msg.rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]); /* * Act as if we received this packet through RPC. */ if (!udprpc_recv(data, length, &msg, &xprt)) return; /* * Get the NFS procedure number. */ proc = msg.rm_call.cb_proc; if (proc >= RFS_NPROC) return; CountCallAuth(&msg); nfs_hash_call(&msg, src, tstamp); /* * Now decode the arguments to the procedure from * XDR format. */ args = (caddr_t) &nfs_rfsargs; (void) bzero(args, nfs_procs[proc].nfs_argsz); if (!SVC_GETARGS(xprt, nfs_procs[proc].nfs_xdrargs, args)) return; prc_counters[prc_countmap[proc]].pr_total++; prc_counters[prc_countmap[proc]].pr_interval++; CountSrc(src); /* * Now count the packet in the appropriate file system's * counters. */ switch (proc) { case RFS_NULL: break; case RFS_GETATTR: nfs_count(&nfs_rfsargs.fhandle, proc); break; case RFS_SETATTR: nfs_count(&nfs_rfsargs.nfssaargs.saa_fh, proc); break; case RFS_ROOT: break; case RFS_LOOKUP: #if defined(SUNOS5) && defined(DA_FREENAME) /* SunOS 2.2 and greater */ nfs_count(&nfs_rfsargs.nfsdiropargs.da_fhandle_buf, proc); #else nfs_count(&nfs_rfsargs.nfsdiropargs.da_fhandle, proc); #endif break; case RFS_READLINK: nfs_count(&nfs_rfsargs.fhandle, proc); break; case RFS_READ: nfs_count(&nfs_rfsargs.nfsreadargs.ra_fhandle, proc); break; case RFS_WRITECACHE: break; case RFS_WRITE: #if defined(SUNOS5) && defined(DA_FREENAME) /* SunOS 2.2 and greater */ nfs_count(&nfs_rfsargs.nfswriteargs.wa_args_buf.otw_wa_fhandle, proc); #else nfs_count(&nfs_rfsargs.nfswriteargs.wa_fhandle, proc); #endif break; case RFS_CREATE: #if defined(SUNOS5) && defined(DA_FREENAME) /* SunOS 2.2 and greater */ nfs_count(&nfs_rfsargs.nfscreatargs.ca_da.da_fhandle_buf, proc); #else nfs_count(&nfs_rfsargs.nfscreatargs.ca_da.da_fhandle, proc); #endif break; case RFS_REMOVE: #if defined(SUNOS5) && defined(DA_FREENAME) /* SunOS 2.2 and greater */ nfs_count(&nfs_rfsargs.nfsdiropargs.da_fhandle_buf, proc); #else nfs_count(&nfs_rfsargs.nfsdiropargs.da_fhandle, proc); #endif break; case RFS_RENAME: #if defined(SUNOS5) && defined(DA_FREENAME) /* SunOS 2.2 and greater */ nfs_count(&nfs_rfsargs.nfsrnmargs.rna_from.da_fhandle_buf, proc); #else nfs_count(&nfs_rfsargs.nfsrnmargs.rna_from.da_fhandle, proc); #endif break; case RFS_LINK: #if defined(SUNOS5) && defined(DA_FREENAME) /* SunOS 2.2 and greater */ nfs_count(&nfs_rfsargs.nfslinkargs.la_from_buf, proc); #else nfs_count(&nfs_rfsargs.nfslinkargs.la_from, proc); #endif break; case RFS_SYMLINK: #if defined(SUNOS5) && defined(DA_FREENAME) /* SunOS 2.2 and greater */ nfs_count(&nfs_rfsargs.nfsslargs.sla_from.da_fhandle_buf, proc); #else nfs_count(&nfs_rfsargs.nfsslargs.sla_from.da_fhandle, proc); #endif break; case RFS_MKDIR: #if defined(SUNOS5) && defined(DA_FREENAME) /* SunOS 2.2 and greater */ nfs_count(&nfs_rfsargs.nfscreatargs.ca_da.da_fhandle_buf, proc); #else nfs_count(&nfs_rfsargs.nfscreatargs.ca_da.da_fhandle, proc); #endif break; case RFS_RMDIR: #if defined(SUNOS5) && defined(DA_FREENAME) /* SunOS 2.2 and greater */ nfs_count(&nfs_rfsargs.nfsdiropargs.da_fhandle_buf, proc); #else nfs_count(&nfs_rfsargs.nfsdiropargs.da_fhandle, proc); #endif break; case RFS_READDIR: nfs_count(&nfs_rfsargs.nfsrddirargs.rda_fh, proc); break; case RFS_STATFS: nfs_count(&nfs_rfsargs.fhandle, proc); break; } /* * Decide whether it's a read or write process. */ switch (nfs_procs[proc].nfs_proctype) { case NFS_READ: pkt_counters[PKT_NFSREAD].pc_interval++; pkt_counters[PKT_NFSREAD].pc_total++; break; case NFS_WRITE: pkt_counters[PKT_NFSWRITE].pc_interval++; pkt_counters[PKT_NFSWRITE].pc_total++; break; } } /* * nfs_count - count an NFS reference to a specific file system. */ static NFSCounter *lastmatch = NULL; void nfs_count(fh, proc) register fhandle_t *fh; int proc; { my_fsid fsid; ino_t ino; char *osname = ""; char *sfsname = NULL; char *inet_ntoa(); register int i, match1, match2; /* Convert file handle to our internal representations */ #ifdef DEBUG Parse_fh(fh, &fsid, &ino, &osname, &sfsname, to_self(thisdst)); #else Parse_fh(fh, &fsid, &ino, NULL, &sfsname, to_self(thisdst)); #endif DEBUG if (fhdebugf) { /* Print file handles for specified servers */ unsigned char *fhcp = (unsigned char *)fh; struct in_addr ia; int i; if (allflag) { /* print host name if not implicit */ ia.s_addr = thisdst; (void)printf("%s: ", inet_ntoa(ia)); } for (i = 0; i < NFS_FHSIZE; i++) (void)printf("%x.", fhcp[i]); (void)printf("\n"); } /* * Run through the NFS counters looking for the matching * file system. */ match1 = 0; /* We use a 1-behind cache to speed the search */ if (lastmatch) { if (learnfs) { match1 = fsid_eq(lastmatch->nc_fsid, fsid); } else { match1 = dev_eq(lastmatch->nc_dev, fsid.fsid_dev); } if (allflag && match1) match1 = (thisdst == lastmatch->nc_ipaddr); } if (match1 == 0) { /* wasn't the same as the last packet, try the hard way */ for (i = 0; i < nnfscounters; i++) { NFSCounter *nfscp = &nfs_counters[i]; if (learnfs) { match1 = fsid_eq(nfscp->nc_fsid, fsid); } else { match1 = dev_eq(nfscp->nc_dev, fsid.fsid_dev); } /* * Check server address. */ if (allflag && match1) match1 = (thisdst == nfscp->nc_ipaddr); if (match1) { nfscp->nc_proc[proc]++; nfscp->nc_interval++; nfscp->nc_total++; lastmatch = nfscp; break; } } } else { lastmatch->nc_proc[proc]++; lastmatch->nc_interval++; lastmatch->nc_total++; } /* * We don't know about this file system, but we can * learn. */ if (!match1 && learnfs && (nnfscounters < MAXEXPORT)) { static char fsname[64], prefix[64]; int oldm; #ifdef SVR4 sighold(SIGALRM); #else oldm = sigblock(sigmask(SIGALRM)); #endif /* no redisplay while unstable */ i = nnfscounters++; nfs_counters[i].nc_fsid = fsid; nfs_counters[i].nc_dev = fsid.fsid_dev; nfs_counters[i].nc_proc[proc]++; nfs_counters[i].nc_interval++; nfs_counters[i].nc_total++; *prefix = 0; if (allflag) { struct hostent *hp; nfs_counters[i].nc_ipaddr = thisdst; hp = gethostbyaddr((char *) &thisdst, sizeof(thisdst), AF_INET); if (hp) { char *index(); char *dotp; sprintf(prefix, "%s", hp->h_name); if ((dotp = index(prefix, '.')) != NULL) *dotp = 0; } else { struct in_addr ia; ia.s_addr = thisdst; sprintf(prefix, "%s", inet_ntoa(ia)); } } if (sfsname) { /* file system ID is ASCII, not numeric, for server OS */ static char temp[17]; int i; /* Make sure string is null-terminated */ strncpy(temp, sfsname, 16); temp[16] = 0; sprintf(fsname, "%.8s:%s %s", prefix, temp, osname); } else { sprintf(fsname, "%.12s(%d,%d)%s", prefix, nfs_counters[i].nc_dev.Major, nfs_counters[i].nc_dev.Minor, osname); } if (mapfile) nfs_counters[i].nc_name = savestr(map_fs_name(fsname)); else nfs_counters[i].nc_name = savestr(fsname); sort_nfs_counters(); #ifdef SVR4 sigrelse(SIGALRM); #else (void) sigsetmask(oldm); /* permit redisplay */ #endif } if (filelist == NULL) return; /* XXX could use a 1-behind cache here, too */ /* * Run through the file counters looking for the matching * file. */ for (i = 0; i < nfilecounters; i++) { FileCounter *fcp = &(fil_counters[i]); if (dev_eq(fcp->fc_dev, fsid.fsid_dev) && (fcp->fc_ino == ino)) { fcp->fc_proc[proc]++; fcp->fc_interval++; fcp->fc_total++; break; } } } /* * CountSrc uses a hash table to speed lookups. Hash function * uses high and low octect of IP address, so as to be * fast and byte-order independent. Table is organized * as a array of linked lists. */ #define HASHSIZE 0x100 #define HASH(addr) (((addr) & 0xFF) ^ (((addr) >> 24) & 0xFF)) ClientCounter *Addr_hashtable[HASHSIZE]; /* initially all NULL ptrs */ ClientCounter *cc_hint = clnt_counters; /* one-element cache */ CountSrc(src) register ipaddrt src; { register ClientCounter *ccp; int hcode = HASH(src); char *inet_ntoa(); /* See if this is the same client as last time */ if (cc_hint->cl_ipaddr == src) { cc_hint->cl_total++; cc_hint->cl_interval++; return; } /* Search hash table */ ccp = Addr_hashtable[hcode]; while (ccp) { if (ccp->cl_ipaddr == src) { ccp->cl_total++; ccp->cl_interval++; cc_hint = ccp; return; } ccp = ccp->cl_next; } /* new client */ if (nclientcounters < MAXCLIENTS) { struct hostent *hp; static char clnt_name[64]; int oldm; #ifdef SVR4 sighold(SIGALRM); #else oldm = sigblock(sigmask(SIGALRM)); #endif /* no redisplay while unstable */ ccp = &(clnt_counters[nclientcounters]); nclientcounters++; /* Add to hash table */ ccp->cl_next = Addr_hashtable[hcode]; Addr_hashtable[hcode] = ccp; /* Fill in new ClientCounter */ ccp->cl_ipaddr = src; hp = gethostbyaddr((char *) &ccp->cl_ipaddr, sizeof(ccp->cl_ipaddr), AF_INET); if (hp) { char *index(); char *dotp; sprintf(clnt_name, "%s", hp->h_name); if ((dotp = index(clnt_name, '.')) != NULL) *dotp = 0; } else { struct in_addr ia; ia.s_addr = ccp->cl_ipaddr; sprintf(clnt_name, "%s", inet_ntoa(ia)); } ccp->cl_name = savestr(clnt_name); ccp->cl_total = 1; ccp->cl_interval = 1; sort_clnt_counters(); #ifdef SVR4 sigrelse(SIGALRM); #else (void) sigsetmask(oldm); /* permit redisplay */ #endif } } /* * Must be called after sorting the clnt_counters[] table * Should put busiest ones at front of list, but doesn't */ ClientHashRebuild() { register int i; register ClientCounter *ccp; int hcode; bzero(Addr_hashtable, sizeof(Addr_hashtable)); for (i = 0, ccp = clnt_counters; i < nclientcounters; i++, ccp++) { hcode = HASH(ccp->cl_ipaddr); ccp->cl_next = Addr_hashtable[hcode]; Addr_hashtable[hcode] = ccp; } } /* * Code to count authentication instances. */ #define UIDOFFSET 100000 /* add to a non-uid so it won't conflict with uids */ CountCallAuth(msg) struct rpc_msg *msg; { int i, len, auth; struct passwd *pw; unsigned int *oauth; static char user_name[16]; auth = msg->ru.RM_cmb.cb_cred.oa_flavor; if (auth != AUTH_UNIX) { auth += UIDOFFSET; } else { /* * Convert the opaque authorization into a uid. * Should use the XDR decoders. */ oauth = (unsigned int *) msg->ru.RM_cmb.cb_cred.oa_base; len = ntohl(oauth[1]); if ((len % 4) != 0) len = len + 4 - len % 4; auth = ntohl(oauth[2 + len / 4]); } for (i=0; i < nauthcounters; i++) { if (auth_counters[i].ac_uid == auth) { auth_counters[i].ac_interval++; auth_counters[i].ac_total++; return; } } if (nauthcounters < MAXAUTHS) { nauthcounters++; auth_counters[i].ac_uid = auth; auth_counters[i].ac_total = 1; auth_counters[i].ac_interval = 1; switch (auth) { case AUTH_NULL + UIDOFFSET: auth_counters[i].ac_name = "AUTH_NULL"; break; case AUTH_UNIX + UIDOFFSET: auth_counters[i].ac_name = "AUTH_UNIX"; break; case AUTH_SHORT + UIDOFFSET: auth_counters[i].ac_name = "AUTH_SHORT"; break; case AUTH_DES + UIDOFFSET: auth_counters[i].ac_name = "AUTH_DES"; break; default: if ((pw = getpwuid(auth)) != NULL) strcpy(user_name, pw->pw_name); else sprintf(user_name, "#%d", auth); auth_counters[i].ac_name = savestr(user_name); break; } sort_auth_counters(); } } /* * Some code to look at response times. */ void nfs_hash_call(msg, client, tstamp) struct timeval *tstamp; struct rpc_msg *msg; ipaddrt client; { int i; if (tstamp == NULL) return; for (i=0; i < NFSCALLHASHSIZE; i++) { if (nfs_calls[i].used == 0) { nfs_calls[i].used = 1; nfs_calls[i].proc = msg->rm_call.cb_proc; nfs_calls[i].client = client; nfs_calls[i].xid = msg->rm_xid; nfs_calls[i].time_sec = tstamp->tv_sec; nfs_calls[i].time_usec = tstamp->tv_usec; return; } } } void nfs_hash_reply(msg, client, tstamp) struct timeval *tstamp; struct rpc_msg *msg; ipaddrt client; { int i; double diff; u_long proc; u_int32 xid = ntohl(msg->rm_xid); if (tstamp == NULL) return; for (i=0; i < NFSCALLHASHSIZE; i++) { if (nfs_calls[i].used == 0) continue; if (nfs_calls[i].client == client && nfs_calls[i].xid == xid) { proc = prc_countmap[nfs_calls[i].proc]; diff = ((tstamp->tv_sec - nfs_calls[i].time_sec) * 1000000 + tstamp->tv_usec - nfs_calls[i].time_usec) / 1000.0; prc_counters[proc].pr_complete++; prc_counters[proc].pr_response += diff; prc_counters[proc].pr_respsqr += diff * diff; if (diff > prc_counters[proc].pr_maxresp) prc_counters[proc].pr_maxresp = diff; nfs_calls[i].used = 0; return; } } }