/* $Id: rx.h,v 1.10 1998/10/20 02:49:53 art Exp $ */

/*
****************************************************************************
*        Copyright IBM Corporation 1988, 1989 - All Rights Reserved        *
*                                                                          *
* Permission to use, copy, modify, and distribute this software and its    *
* documentation for any purpose and without fee is hereby granted,         *
* provided that the above copyright notice appear in all copies and        *
* that both that copyright notice and this permission notice appear in     *
* supporting documentation, and that the name of IBM not be used in        *
* advertising or publicity pertaining to distribution of the software      *
* without specific, written prior permission.                              *
*                                                                          *
* IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL *
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL IBM *
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY      *
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER  *
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING   *
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.    *
****************************************************************************
*/

#ifndef	_RX_
#define _RX_

#ifdef	KERNEL
#include "../rx/rx_machdep.h"
#include "../rx/rx_kernel.h"
#include "../rx/rx_clock.h"
#include "../rx/rx_event.h"
#include "../rx/rx_queue.h"
#include "../rx/rx_packet.h"
#else				       /* KERNEL */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <atypes.h>
#include <stdio.h>
#include <sys/param.h>
#include "rx_mach.h"
#include "rx_user.h"
#include "rx_clock.h"
#include "rx_event.h"
#include "rx_pkt.h"
#endif				       /* KERNEL */


/* Configurable parameters */
#define	RX_IDLE_DEAD_TIME	60     /* default idle dead time */
#define	RX_MAX_SERVICES		20     /* Maximum number of services that may
				        * be installed */
#define	RX_DEFAULT_STACK_SIZE	16000  /* Default process stack size;
				        * overriden by rx_SetStackSize */

/* This parameter should not normally be changed */
#define	RX_PROCESS_PRIORITY	LWP_NORMAL_PRIORITY

/* backoff is fixed point binary.  Ie, units of 1/4 seconds */
#define MAXBACKOFF 0x1F

struct rx_securityClass;

/* Exported interfaces XXXX clean this up:  not all of these are exported */
int rx_Init(u_short);
struct rx_service *rx_NewService(u_short, u_short, char *, 
				 struct rx_securityClass **, int, long (*)());
struct rx_connection *rx_NewConnection(register u_long, u_short, u_short,
				       register struct rx_securityClass *,
				       int);
struct rx_call *rx_NewCall();
struct rx_call *rx_GetCall();	 /* Not normally used, but not obsolete */
long rx_EndCall();
int rx_AllocPackets();
void rx_FreePackets();
int rx_WriteProc();
int rx_ReadProc();
void rx_FlushWrite();
#ifdef RXDEBUG
void rx_PrintStats();
#else
#define rx_PrintStats(a)
#endif
void rx_PrintPeerStats();
void rx_SetArrivalProc();
void rx_Finalize();
void rx_StartServer(int);
void rx_DestroyConnection(struct rx_connection *);
void rxi_Free(void *, register int);
int rxi_GetCallNumberVector(const struct rx_connection *, int32_t *);
int rxi_SetCallNumberVector(struct rx_connection *, int32_t *);
void rx_SetEpoch(u_long);

#define	RX_WAIT	    1
#define	RX_DONTWAIT 0

#define	rx_ConnectionOf(call)		((call)->conn)
#define	rx_PeerOf(conn)			((conn)->peer)
#define	rx_HostOf(peer)			((peer)->host)
#define	rx_PortOf(peer)			((peer)->port)
#define	rx_SetLocalStatus(call, status)	((call)->localStatus = (status))
#define rx_GetLocalStatus(call, status) ((call)->localStatus)
#define	rx_GetRemoteStatus(call)	((call)->remoteStatus)
#define	rx_Error(call)			((call)->error)
#define	rx_ConnError(conn)		((conn)->error)
#define	rx_IsServerConn(conn)		((conn)->type == RX_SERVER_CONNECTION)
#define	rx_IsClientConn(conn)		((conn)->type == RX_CLIENT_CONNECTION)
/* Don't use these; use the IsServerConn style */
#define	rx_ServerConn(conn)		((conn)->type == RX_SERVER_CONNECTION)
#define	rx_ClientConn(conn)		((conn)->type == RX_CLIENT_CONNECTION)
#define rx_IsUsingPktCksum(conn)	((conn)->flags & \
					 RX_CONN_USING_PACKET_CKSUM)

/*
 * Set and get rock is applicable to both connections and calls.
 * It's used by multi rx macros for calls.
 */
#define	rx_SetRock(obj, newrock)	((obj)->rock = (void *)(newrock))
#define	rx_GetRock(obj,	type)		((type)(obj)->rock)
#define rx_ServiceIdOf(conn)		((conn)->serviceId)
#define	rx_SecurityClassOf(conn)	((conn)->securityIndex)
#define rx_SecurityObjectOf(conn)	((conn)->securityObject)

/*
 * Macros callable by the user to further define attributes of a
 * service.  Must be called before rx_StartServer
 */

/*
 *  Set the service stack size.  This currently just sets the stack
 * size for all processes to be the maximum seen, so far
 */
#define rx_SetStackSize(service, stackSize) \
  rx_stackSize = (((stackSize) > rx_stackSize)? stackSize: rx_stackSize)

/*
 * Set minimum number of processes guaranteed to be available for this
 * service at all times
 */
#define rx_SetMinProcs(service, min) ((service)->minProcs = (min))

/*
 * Set maximum number of processes that will be made available to this
 * service (also a guarantee that this number will be made available
 * if there is no competition)
 */
#define rx_SetMaxProcs(service, max) ((service)->maxProcs = (max))

/*
 * Define a procedure to be called just before a server connection is
 * destroyed
 */
#define rx_SetDestroyConnProc(service,proc) ((service)->destroyConnProc = (proc))

/* Define procedure to set service dead time */
#define rx_SetIdleDeadTime(service,time) ((service)->idleDeadTime = (time))

/*
 * Define procedures for getting and setting before and after execute-request
 * procs
 */
#define rx_SetAfterProc(service,proc) ((service)->afterProc = (proc))
#define rx_SetBeforeProc(service,proc) ((service)->beforeProc = (proc))
#define rx_GetAfterProc(service) ((service)->afterProc)
#define rx_GetBeforeProc(service) ((service)->beforeProc)

/* Define a procedure to be called when a server connection is created */
#define rx_SetNewConnProc(service, proc) ((service)->newConnProc = (proc))

/*
 * NOTE:  We'll probably redefine the following three routines, again,
 * sometime.
 */

/*
 * Set the connection dead time for any connections created for this service
 * (server only)
 */
#define rx_SetServiceDeadTime(service, seconds) ((service)->secondsUntilDead = (seconds))

/* Set connection dead time, for a specific client or server connection */
#define rx_SetConnDeadTime(conn, seconds) (rxi_SetConnDeadTime(conn, seconds))
extern void rxi_SetConnDeadTime();

/* Set connection hard timeout for a connection */
#define rx_SetConnHardDeadTime(conn, seconds) ((conn)->hardDeadTime = (seconds))

/*
 * Set rx default connection dead time; set on both services and
 * connections at creation time
 */
extern int rx_connDeadTime;

#define	rx_SetRxDeadTime(seconds)   (rx_connDeadTime = (seconds))

extern int rx_nPackets;

#define cpspace(call) \
  ((call)->currentPacket->wirevec[(call)->curvec].iov_len - (call)->curpos)
#define cppos(call) \
  ((call)->currentPacket->wirevec[(call)->curvec].iov_base + (call)->curpos)

#ifndef	AFS_SGIMP_ENV
/* Write nbytes of data to the call.  Returns the number of bytes written */
/* If it returns 0, the call status should be checked with rx_Error. */
#define	rx_Write(call, buf, nbytes) rx_WriteProc((call), (buf), (nbytes))
/* this shortcut is a good idea, but I don't trust it right now MTUXXX
   (((call)->nFree > (nbytes)) && (cpspace(call) > (nbytes)) ?    \
         memcpy(cppos(call), (buf), (nbytes)),			\
         (call)->nFree -= (nbytes),				\
         (call)->currentPacket->wirevec[(call)->curvec].iov_base += (nbytes), \
         (call)->curpos += (nbytes), \
         (nbytes)			\
     : rx_WriteProc((call), (buf), (nbytes)))
*/

/*
 * Read nbytes of data from the call.  Returns the number of bytes read
 */
/*
 * If it returns less than requested, the call status should be checked
 * with rx_Error
 */
#define	rx_Read(call, buf, nbytes)   rx_ReadProc((call), (buf), (nbytes))
/* this shortcut is a good idea, but I don't trust it right now MTUXXX
   (((call)->nLeft > (nbytes)) && (cpspace(call) > (nbytes)) ?             \
        memcpy((buf),  cppos(call), (nbytes)),	   \
        (call)->nLeft -= (nbytes),				           \
        (call)->currentPacket->wirevec[(call)->curvec].iov_base += (nbytes), \
        (call)->curpos += (nbytes), \
        (nbytes)			\
   : rx_ReadProc((call), (buf), (nbytes)))
*/
#endif				       /* AFS_SGIMP_ENV */

/*
 * This is the maximum size data packet that can be sent on this connection,
 * accounting for security module-specific overheads.
 */
#define	rx_MaxUserDataSize(conn) ((conn)->maxPacketSize - \
				  RX_HEADER_SIZE - \
				  (conn)->securityHeaderSize - \
				  (conn)->securityMaxTrailerSize)

struct rx_securityObjectStats {
    char type;			       /* 0:unk 1:null,2:vab 3:kad */
    char level;
    char sparec[10];		       /* force correct alignment */
    long flags;			       /* 1=>unalloc, 2=>auth, 4=>expired */
    u_long expires;
    u_long packetsReceived;
    u_long packetsSent;
    u_long bytesReceived;
    u_long bytesSent;
    short spares[4];
    long sparel[8];
};

/*
 * XXXX (rewrite this description) A security class object contains a set of
 * procedures and some private data to implement a security model for rx
 * connections.  These routines are called by rx as appropriate.  Rx knows
 * nothing about the internal details of any particular security model, or
 * about security state.  Rx does maintain state per connection on behalf of
 * the security class.  Each security class implementation is also expected to
 * provide routines to create these objects.  Rx provides a basic routine to
 * allocate one of these objects; this routine must be called by the class.
 */
struct rx_securityClass {
    struct rx_securityOps {
	int (*op_Close) ( /* obj */ );
	int (*op_NewConnection) ( /* obj, conn */ );
	int (*op_PreparePacket) ( /* obj, call, packet */ );
	int (*op_SendPacket) ( /* obj, call, packet */ );
	int (*op_CheckAuthentication) ( /* obj,conn */ );
	int (*op_CreateChallenge) ( /* obj,conn */ );
	int (*op_GetChallenge) ( /* obj,conn,packet */ );
	int (*op_GetResponse) ( /* obj,conn,packet */ );
	int (*op_CheckResponse) ( /* obj,conn,packet */ );
	int (*op_CheckPacket) ( /* obj,call,packet */ );
	int (*op_DestroyConnection) ( /* obj, conn */ );
	int (*op_GetStats) ( /* obj, conn, stats */ );
	int (*op_Spare1) ();
	int (*op_Spare2) ();
	int (*op_Spare3) ();
    } *ops;
    void *privateData;
    int refCount;
};

#if defined(__STDC__) && !defined(__HIGHC__)
#define RXS_OP(obj,op,args) ((obj->ops->op_ ## op) ? \
			     (*(obj)->ops->op_ ## op)args : 0)
#else
#define RXS_OP(obj,op,args) ((obj->ops->op_/**/op) ? \
			     (*(obj)->ops->op_/**/op)args : 0)
#endif

#define RXS_Close(obj) RXS_OP(obj,Close,(obj))
#define RXS_NewConnection(obj,conn) RXS_OP(obj,NewConnection,(obj,conn))
#define RXS_PreparePacket(obj,call,packet) RXS_OP(obj,PreparePacket,\
						  (obj,call,packet))
#define RXS_SendPacket(obj,call,packet) RXS_OP(obj,SendPacket,\
					       (obj,call,packet))
#define RXS_CheckAuthentication(obj,conn) RXS_OP(obj,CheckAuthentication,\
						 (obj,conn))
#define RXS_CreateChallenge(obj,conn) RXS_OP(obj,CreateChallenge,\
					     (obj,conn))
#define RXS_GetChallenge(obj,conn,packet) RXS_OP(obj,GetChallenge,\
						 (obj,conn,packet))
#define RXS_GetResponse(obj,conn,packet) RXS_OP(obj,GetResponse,\
						(obj,conn,packet))
#define RXS_CheckResponse(obj,conn,packet) RXS_OP(obj,CheckResponse,\
						  (obj,conn,packet))
#define RXS_CheckPacket(obj,call,packet) RXS_OP(obj,CheckPacket,\
						(obj,call,packet))
#define RXS_DestroyConnection(obj,conn) RXS_OP(obj,DestroyConnection,\
					       (obj,conn))
#define RXS_GetStats(obj,conn,stats) RXS_OP(obj,GetStats,\
					    (obj,conn,stats))

/*
 * A service is installed by rx_NewService, and specifies a service type that
 * is exported by this process.  Incoming calls are stamped with the service
 * type, and must match an installed service for the call to be accepted.
 * Each service exported has a (port,serviceId) pair to uniquely identify it.
 * It is also named:  this is intended to allow a remote statistics gathering
 * program to retrieve per service statistics without having to know the local
 * service id's.  Each service has a number of security objects (instances of
 * security classes) which implement various types of end-to-end security
 * protocols for connections made to this service.  Finally, there are two
 * parameters controlling the number of requests which may be executed in
 * parallel by this service: minProcs is the number of requests to this
 * service which are guaranteed to be able to run in parallel at any time;
 * maxProcs has two meanings: it limits the total number of requests which may
 * execute in parallel and it also guarantees that that many requests
 * may be handled in parallel if no other service is handling any
 * requests.
 */

struct rx_service {
    u_short serviceId;		       /* Service number */
    u_short servicePort;	       /* UDP port for this service */
    char *serviceName;		       /* Name of the service */
    osi_socket socket;		       /* socket struct or file descriptor */
    u_short nRequestsRunning;	       /* 
					* Number of requests currently in
				        * progress 
					*/
    u_short nSecurityObjects;	       /* Number of entries in security
				        * objects array */
    struct rx_securityClass **securityObjects;	/*
						 * Array of security class
						 * objects
						 */
    long (*executeRequestProc) ();     /* Routine to call when an rpc request
				        * is received */
    void (*destroyConnProc) ();	       /* Routine to call when a server
				        * connection is destroyed */
    void (*newConnProc) ();	       /*
				        * Routine to call when a server
					* connection is created
				        */
    void (*beforeProc) ();	       /* routine to call before a call is
				        * executed */
    void (*afterProc) ();	       /* routine to call after a call is
				        * executed */
    u_short maxProcs;		       /* Maximum procs to be used for this
				        * service */
    u_short minProcs;		       /* Minimum # of requests guaranteed
				        * executable simultaneously */
    u_short connDeadTime;	       /*
			                * Secs until a client of this service
				        * will be declared dead, if it is not
				        * responding
				        */
    u_short idleDeadTime;	       /*
				        * Time a server will wait for I/O to
				        * start up again
				        */
};

/*
 * A server puts itself on an idle queue for a service using an
 * instance of the following structure.  When a call arrives, the call
 * structure pointer is placed in "newcall", the routine to execute to
 * service the request is placed in executeRequestProc, and the
 * process is woken up.  The queue entry's address is used for the
 * sleep/wakeup.
 */
struct rx_serverQueueEntry {
    struct rx_queue queueItemHeader;
    struct rx_call *newcall;
#ifdef	RX_ENABLE_LOCKS
    kmutex_t lock;
    kcondvar_t cv;
#endif
};

/* Bottom n-bits of the Call Identifier give the call number */
#define	RX_MAXCALLS 4		       /* Power of 2; max async calls per
				        * connection */
#define	RX_CIDSHIFT 2		       /* Log2(RX_MAXCALLS) */
#define	RX_CHANNELMASK (RX_MAXCALLS-1)
#define	RX_CIDMASK  (~RX_CHANNELMASK)

/*
 * A peer refers to a peer process, specified by a (host,port) pair.
 * There may be more than one peer on a given host.
 */
struct rx_peer {
    struct rx_peer *next;	       /* Next in hash conflict or free list */
    u_long host;		       /* Remote IP address, in net byte
				        * order */
    u_short port;		       /* Remote UDP port, in net byte order */
    u_short packetSize;		       /*
				        * Max packet size, if known, for this
				        * host
				        */

    /* For garbage collection */
    u_long idleWhen;		       /* When the refcountwent to zero */
    short refCount;		       /* Reference count for this structure */

    /* Congestion control parameters */
    u_char burstSize;		       /*
				        * Reinitialization size for the burst
				        * parameter
				        */
    u_char burst;		       /* Number of packets that can be
				        * transmitted right now, without
				        * pausing */
    struct clock burstWait;	       /* Delay until new burst is allowed */
    struct rx_queue congestionQueue;   /*
				        * Calls that are waiting for non-zero
				        * burst value
				        */
    int rtt;			       /*
				        * Round trip time, measured in
				        * milliseconds/8
				        */
    int rtt_dev;		       /* rtt smoothed error, in
				        * milliseconds/4 */
    struct clock timeout;	       /* Current retransmission delay */
    int nSent;			       /*
				        * Total number of distinct data packets
				        * sent, not including retransmissions
				        */
    int reSends;		       /*
				        * Total number of retransmissions for
				        * this peer, since this structure was
				        * created
				        */

/*
 * Skew: if a packet is received N packets later than expected (based
 * on packet serial numbers), then we define it to have a skew of N.
 * The maximum skew values allow us to decide when a packet hasn't
 * been received yet because it is out-of-order, as opposed to when it
 * is likely to have been dropped.
 */
    u_long inPacketSkew;	       /* Maximum skew on incoming packets */
    u_long outPacketSkew;	       /* Peer-reported max skew on our sent
				        * packets */
    int rateFlag;		       /* Flag for rate testing (-no 0yes
				        * +decrement) */
    u_short maxWindow;		       /* Maximum window size (number of
				        * packets) */
    u_short spare;		       /*
				        * we have to manually align things
					* b/c 220s crash
				        */
};

/*
 * A connection is an authenticated communication path, allowing
 * limited multiple asynchronous conversations.
 */
struct rx_connection {
    struct rx_connection *next;	       /* on hash chain _or_ free list */
    struct rx_peer *peer;
#ifdef	RX_ENABLE_LOCKS
    kmutex_t lock;
    kcondvar_t cv;
#endif
    u_int32_t epoch;		       /* Process start time of client side
				        * of connection */
    u_int32_t cid;			       /* Connection id (call channel is
				        * bottom bits) */
    long error;			       /* If this connection is in error,
				        * this is it */
    void *rock;			       /* User definable */
    struct rx_call *call[RX_MAXCALLS];
    u_int32_t callNumber[RX_MAXCALLS];    /* Current call numbers */
    u_int32_t serial;		       /* Next outgoing packet serial number */
    u_int32_t lastSerial;		       /* # of last packet received, for
				        * computing skew */
    long maxSerial;		       /* largest serial number seen on
				        * incoming packets */
    long maxPacketSize;		       /*
				        * max packet size should be
					* per-connection since peer process
					* could be restarted on us.
				        */
    struct rxevent *challengeEvent;    /* Scheduled when the server is
				        * challenging a client-- to
				        * retransmit the challenge */
    struct rx_service *service;	       /* used by servers only */
    u_short serviceId;		       /* To stamp on requests, clients only */
    short refCount;		       /* Reference count */
    u_char flags;		       /* Defined below */
    u_char type;		       /* Type of connection, defined below */
    u_char secondsUntilPing;	       /* how often to ping for each active
				        * call */
    u_char securityIndex;	       /* corresponds to the security class
				        * of the */
    /* securityObject for this conn */
    struct rx_securityClass *securityObject;	/*
					         * Security object for this
					         * connection
					         */
    void *securityData;		       /* Private data for this conn's
				        * security class */
    u_short securityHeaderSize;	       /*
				        * Length of security module's packet
				        * header data
				        */
    u_short securityMaxTrailerSize;    /*
				        * Length of security module's packet
				        * trailer data
				        */
    int timeout;		       /*
				        * Overall timeout per call (seconds)
				        * for this conn
				        */
    int lastSendTime;		       /* Last send time for this connection */
    u_short secondsUntilDead;	       /*
				        * Maximum silence from peer before
				        * RX_CALL_DEAD
				        */
    u_short hardDeadTime;	       /* hard max for call execution */
};

/* Flag bits for connection structure */
#define	RX_CONN_MAKECALL_WAITING   1   /* rx_MakeCall is waiting for a
				        * channel */
#define	RX_CONN_DESTROY_ME	   2   /*
					* Destroy *client* connection after
				        * last call
			                */
#define RX_CONN_USING_PACKET_CKSUM 4   /* non-zero header.spare field seen */
#define RX_CONN_BIG_ONES           8   /*
					* may use packets > 1500 bytes
				        * (compatibility)
					*/


/* Type of connection, client or server */
#define	RX_CLIENT_CONNECTION	0
#define	RX_SERVER_CONNECTION	1

/*
 * Call structure:  only instantiated for active calls and dallying server
 * calls. The permanent call state (i.e. the call number as well as state
 * shared with other calls associated with this connection) is maintained
 * in the connection structure.
 */
struct rx_call {
    struct rx_queue queue_item_header; /*
					* Call can be on various queues
					* (one-at-a-time)
					*/
    struct rx_queue tq;		       /* Transmit packet queue */
    struct rx_queue rq;		       /* Receive packet queue */
#ifdef	RX_ENABLE_LOCKS
    kmutex_t lock;
    kmutex_t lockw;
    kcondvar_t cv_twind;
    kmutex_t lockq;
    kcondvar_t cv_rq;
#endif
    struct rx_connection *conn;	       /* Parent connection for this call */
    u_int32_t *callNumber;		       /*
					* Pointer to call number field
					* within connection
					*/
#if 0
    char *bufPtr;		       /*
					* Next byte to fill or read in current
					* send/read packet
					*/
#endif
    u_short nLeft;		       /*
				        * Number of bytes left in first receive
				        * queue packet
				        */
    struct rx_packet *currentPacket;   /*
					* Current packet being assembled or
					* being read
					*/
    u_short curvec;		       /* current iovec in currentPacket */
    u_short curpos;		       /* current position within curvec */
    u_short nFree;		       /* Number of bytes free in last send
				        * packet */
    u_char channel;		       /* Index of call, within connection */
    u_char state;		       /* Current call state as defined below */
    u_char mode;		       /* Current mode of a call in ACTIVE
				        * state */
    u_char flags;		       /* Some random flags */
    u_char localStatus;		       /* Local user status sent out of band */
    u_char remoteStatus;	       /* Remote user status received out of
				        * band */
    long error;			       /* Error condition for this call */
    u_long timeout;		       /* High level timeout for this call */
    u_long rnext;		       /*
			                * Next sequence number expected to be
			                * read by rx_ReadData
				        */
    u_long rprev;		       /*
				        * Previous packet received; used for
				        * deciding what the next packet to be
				        * received should be in order to decide
				        * whether a negative acknowledge should
				        * be sent
				        */
    u_long rwind;		       /*
				        * The receive window:  the peer must
				        * not send packets with sequence
			                * numbers >= rnext+rwind
			                */
    u_long tfirst;		       /*
				        * First unacknowledged transmit packet
				        * number
				        */
    u_long tnext;		       /* Next transmit sequence number to
				        * use */
    u_long twind;		       /*
				        * The transmit window:  we cannot
					* assign a sequence number to a
				        * packet >= tfirst + twind
				        */
    struct rxevent *resendEvent;       /*
				        * If this is non-Null, there is a
				        * retransmission event pending
				        */
    struct rxevent *timeoutEvent;      /*
				        * If this is non-Null, then there is an
				        * overall timeout for this call
				        */
    struct rxevent *keepAliveEvent;    /*
				        * Scheduled periodically in active
				        * calls to keep call alive
				        */
    struct rxevent *delayedAckEvent;   /*
				        * Scheduled after all packets are
				        * received to send an ack if a reply
				        * or new call is not generated soon
				        */
    int lastSendTime;		       /* Last time a packet was sent on this
				        * call */
    int lastReceiveTime;	       /* Last time a packet was received for
				        * this call */
    void (*arrivalProc) ();	       /* Procedure to call when reply is
				        * received */
    void *arrivalProcHandle;	       /* Handle to pass to replyFunc */
    void *arrivalProcArg;	       /* Additional arg to pass to reply
				        * Proc */
    u_long lastAcked;		       /* last packet "hard" acked by
				        * receiver */
    u_long startTime;		       /* time the call started running */
    u_long startWait;		       /*
				        * time server began waiting for
					* input data/send quota
				        */
    struct clock traceWait;	       /*
				        * time server began waiting for input
				        * data/send quota
				        */
    struct clock traceStart;	       /* time the call started running */
};

/* Major call states */
#define	RX_STATE_NOTINIT  0	       /* Call structure has never been
				        * initialized */
#define	RX_STATE_PRECALL  1	       /*
					* Server-only:  call is not in
					* progress, but packets have arrived
					*/
#define	RX_STATE_ACTIVE	  2	       /*
	       		                * An active call; a process is dealing
					* with this call
					*/
#define	RX_STATE_DALLY	  3	       /* Dallying after process is done with
				        * call */

/*
 * Call modes:  the modes of a call in RX_STATE_ACTIVE state (process attached)
 */
#define	RX_MODE_SENDING	  1	       /* Sending or ready to send */
#define	RX_MODE_RECEIVING 2	       /* Receiving or ready to receive */
#define	RX_MODE_ERROR	  3	       /* Something in error for current
				        * conversation */
#define	RX_MODE_EOF	  4	       /*
				        * Server has flushed (or client has
					* read) last reply packet
					*/

/* Flags */
#define	RX_CALL_READER_WAIT	   1   /* Reader is waiting for next packet */
#define	RX_CALL_WAIT_WINDOW_ALLOC  2   /*
	       			        * Sender is waiting for window to
				        * allocate buffers
	     			        */
#define	RX_CALL_WAIT_WINDOW_SEND   4   /*
		       		        * Sender is waiting for window to
				        * send buffers
					*/
#define	RX_CALL_WAIT_PACKETS	   8   /*
					* Sender is waiting for packet buffers
					*/
#define	RX_CALL_WAIT_PROC	  16   /*
					* Waiting for a process to be assigned
					*/
#define	RX_CALL_RECEIVE_DONE	  32   /* All packets received on this call */
#define	RX_CALL_CLEARED		  64   /*
					* Receive queue cleared in precall
					* state
					*/
#define	RX_CALL_TQ_BUSY		  128  /*
					* Call's Xmit Queue is busy;
					* don't modify
					*/

/* Maximum number of acknowledgements in an acknowledge packet */
#define	RX_MAXACKS	    255

/*
 * The structure of the data portion of an acknowledge packet: An acknowledge
 * packet is in network byte order at all times.  An acknowledgement is always
 * prompted for a specific reason by a specific incoming packet.  This reason
 * is reported in "reason" and the packet's sequence number in the packet
 * header.seq.  In addition to this information, all of the current
 * acknowledgement information about this call is placed in the packet.
 * "FirstPacket" is the sequence number of the first packet represented in an
 * array of bytes, "acks", containing acknowledgement information for a number
 * of consecutive packets.  All packets prior to FirstPacket are implicitly
 * acknowledged: the sender need no longer be concerned about them.  Packets
 * from firstPacket+nAcks and on are not acknowledged.  Packets in the range
 * [firstPacket,firstPacket+nAcks) are each acknowledged explicitly.  The
 * acknowledgement may be RX_NACK if the packet is not (currently) at the
 * receiver (it may have never been received, or received and then later
 * dropped), or it may be RX_ACK if the packet is queued up waiting to be read
 * by the upper level software.  RX_ACK does not imply that the packet may not
 * be dropped before it is read; it does imply that the sender should stop
 * retransmitting the packet until notified otherwise.  The field
 * previousPacket identifies the previous packet received by the peer.  This
 * was used in a previous version of this software, and could be used in the
 * future.  The serial number in the data part of the ack packet corresponds to
 * the serial number oof the packet which prompted the acknowledge.  Any
 * packets which are explicitly not acknowledged, and which were last
 * transmitted with a serial number less than the provided serial number,
 * should be retransmitted immediately.  Actually, this is slightly inaccurate:
 * packets are not necessarily received in order.  When packets are habitually
 * transmitted out of order, this is allowed for in the retransmission
 * algorithm by introducing the notion of maximum packet skew: the degree of
 * out-of-orderness of the packets received on the wire.  This number is
 * communicated from the receiver to the sender in ack packets.
 */

struct rx_ackPacket {
    u_short bufferSpace;	       /*
				        * Number of packet buffers available.
					* That is: the number of buffers that
					* the sender of the ack packet is
					* willing to provide for data,
				        * on this or subsequent calls. Lying is
				        * permissable.
				        */
    u_short maxSkew;		       /*
				        * Maximum difference between serial# of
				        * packet acknowledged and highest
					* packet yet received
				        */
    u_long firstPacket;		       /*
				        * The first packet in the list of
				        * acknowledged packets
				        */
    u_long previousPacket;	       /*
				        * The previous packet number received
					* (obsolete?)
				        */
    u_long serial;		       /*
				        * Serial number of the packet which
				        * prompted the acknowledge
				        */
    u_char reason;		       /*
				        * Reason for the acknowledge of
					* ackPacket,  defined below
				        */
    u_char nAcks;		       /* Number of acknowledgements */
    u_char acks[RX_MAXACKS];	       /*
				        * Up to RX_MAXACKS packet ack's,
				        * defined below
				        */
    /*
     * Packets <firstPacket are implicitly acknowledged and may be discarded
     * by the sender.  Packets >= firstPacket+nAcks are implicitly NOT
     * acknowledged.  No packets with sequence numbers >= firstPacket should
     * be discarded by the sender (they may thrown out at any time by the
     * receiver)
     */
};

#define FIRSTACKOFFSET 4

/* Reason for acknowledge message */
#define	RX_ACK_REQUESTED	1      /* Peer requested an ack on this
				        * packet */
#define	RX_ACK_DUPLICATE	2      /* Duplicate packet */
#define	RX_ACK_OUT_OF_SEQUENCE	3      /* Packet out of sequence */
#define	RX_ACK_EXCEEDS_WINDOW	4      /*
					* Packet sequence number higher than
					* window; discarded
					*/
#define	RX_ACK_NOSPACE		5      /* No buffer space at all */
#define	RX_ACK_PING		6      /* This is a keep-alive ack */
#define	RX_ACK_PING_RESPONSE	7      /* Ack'ing because we were pinged */
#define	RX_ACK_DELAY		8      /*
					* Ack generated since nothing has
					* happened since receiving packet
					*/

/* Packet acknowledgement type */
#define	RX_ACK_TYPE_NACK	0      /* I Don't have this packet */
#define	RX_ACK_TYPE_ACK		1      /*
					* I have this packet, although I may
					* discard it later
					*/

/*
 * The packet size transmitted for an acknowledge is adjusted to reflect the
 * actual size of the acks array.  This macro defines the size
 */
#define rx_AckDataSize(nAcks) (18 + (nAcks))

#define	RX_CHALLENGE_TIMEOUT	2      /*
					* Number of seconds before another
					* authentication request packet is
					* generated
					*/

/*
 * RX error codes.  RX uses error codes from -1 to -64.  Rxgen may use other
 * error codes < -64; user programs are expected to return positive error
 * codes
 */

/* Something bad happened to the connection; temporary loss of communication */
#define	RX_CALL_DEAD		    (-1)

/*
 * An invalid operation, such as a client attempting to send data after
 * having received the beginning of a reply from the server
 */
#define	RX_INVALID_OPERATION	    (-2)

/* An optional timeout per call may be specified */
#define	RX_CALL_TIMEOUT		    (-3)

/* End of data on a read */
#define	RX_EOF			    (-4)

/* Some sort of low-level protocol error */
#define	RX_PROTOCOL_ERROR	    (-5)

/*
 * Generic user abort code; used when no more specific error code needs to
 * be communicated.  For example, multi rx clients use this code to abort a
 * multi rx call
 */
#define	RX_USER_ABORT		    (-6)

/* Port already in use (from rx_Init) */
#define RX_ADDRINUSE		    (-7)

/* EMSGSIZE returned from network.  Packet too big, must fragment */
#define RX_MSGSIZE		    (-8)

/*
 * Structure for keeping rx statistics.  Note that this structure is returned
 * by rxdebug, so, for compatibility reasons, new fields should be appended (or
 * spares used), the rxdebug protocol checked, if necessary, and the PrintStats
 * code should be updated as well.
 *
 * Clearly we assume that ntohl will work on these structures so sizeof(int)
 * must equal sizeof(long).
 */

struct rx_stats {		       /* General rx statistics */
    int packetRequests;		       /* Number of packet allocation
				        * requests */
    int noPackets[RX_N_PACKET_CLASSES];/*
				        * Number of failed packet requests,
				        * per allocation class
				        */
    int socketGreedy;		       /* Whether SO_GREEDY succeeded */
    int bogusPacketOnRead;	       /*
				        * Number of inappropriately short
					* packets  received
				        */
    int bogusHost;		       /* Host address from bogus packets */
    int noPacketOnRead;		       /*
				        * Number of read packets attempted
					* when there was actually no packet
				        * to read off the wire
				        */
    int noPacketBuffersOnRead;	       /*
					* Number of dropped data packets due
					* to lack of packet buffers
					*/
    int selects;		       /*
				        * Number of selects waiting for packet
					* or timeout
					*/
    int sendSelects;		       /*
					* Number of selects forced when
					* sending packet
					*/
    int packetsRead[RX_N_PACKET_TYPES];/*
				        * Total number of packets read, per
				        * type
				        */
    int dataPacketsRead;	       /*
				        * Number of unique data packets read
					* off the wire
				        */
    int ackPacketsRead;		       /* Number of ack packets read */
    int dupPacketsRead;		       /* Number of duplicate data packets
				        * read */
    int spuriousPacketsRead;	       /* Number of inappropriate data
				        * packets */
    int packetsSent[RX_N_PACKET_TYPES];/*
				        * Number of rxi_Sends: packets sent
				        * over the wire, per type
				        */
    int ackPacketsSent;		       /* Number of acks sent */
    int pingPacketsSent;	       /* Total number of ping packets sent */
    int abortPacketsSent;	       /* Total number of aborts */
    int busyPacketsSent;	       /* Total number of busies sent
				        * received */
    int dataPacketsSent;	       /* Number of unique data packets sent */
    int dataPacketsReSent;	       /* Number of retransmissions */
    int dataPacketsPushed;	       /*
				        * Number of retransmissions pushed early by
				        * a NACK
				        */
    int ignoreAckedPacket;	       /*
				        * Number of packets with acked flag,
					* on rxi_Start
				        */
    struct clock totalRtt;	       /*
				        * Total round trip time measured
					* (use to compute average)
				        */
    struct clock minRtt;	       /* Minimum round trip time measured */
    struct clock maxRtt;	       /* Maximum round trip time measured */
    int nRttSamples;		       /* Total number of round trip samples */
    int nServerConns;		       /* Total number of server connections */
    int nClientConns;		       /* Total number of client connections */
    int nPeerStructs;		       /* Total number of peer structures */
    int nCallStructs;		       /* Total number of call structures
				        * allocated */
    int nFreeCallStructs;	       /*
				        * Total number of previously allocated
					* free call structures
				        */
    int netSendFailures;
    long fatalErrors;
    int spares[8];
};

/* structures for debug input and output packets */

/* debug input types */
struct rx_debugIn {
    long type;
    long index;
};

/* Invalid rx debug package type */
#define RX_DEBUGI_BADTYPE     (-8)

#define RX_DEBUGI_VERSION_MINIMUM ('L')/* earliest real version */
#define RX_DEBUGI_VERSION     ('N')    /* Latest version */
 /* first version w/ secStats */
#define RX_DEBUGI_VERSION_W_SECSTATS ('L')
 /* version M is first supporting GETALLCONN and RXSTATS type */
#define RX_DEBUGI_VERSION_W_GETALLCONN ('M')
#define RX_DEBUGI_VERSION_W_RXSTATS ('M')
 /* last version with unaligned debugConn */
#define RX_DEBUGI_VERSION_W_UNALIGNED_CONN ('L')
#define RX_DEBUGI_VERSION_W_WAITERS ('N')

#define	RX_DEBUGI_GETSTATS	1      /* get basic rx stats */
#define	RX_DEBUGI_GETCONN	2      /* get connection info */
#define	RX_DEBUGI_GETALLCONN	3      /* get even uninteresting conns */
#define	RX_DEBUGI_RXSTATS	4      /* get all rx stats */

struct rx_debugStats {
    long nFreePackets;
    long packetReclaims;
    long callsExecuted;
    char waitingForPackets;
    char usedFDs;
    char version;
    char spare1;
    long nWaiting;
    long spare2[9];
};

struct rx_debugConn_vL {
    long host;
    long cid;
    long serial;
    long callNumber[RX_MAXCALLS];
    long error;
    short port;
    char flags;
    char type;
    char securityIndex;
    char callState[RX_MAXCALLS];
    char callMode[RX_MAXCALLS];
    char callFlags[RX_MAXCALLS];
    char callOther[RX_MAXCALLS];
    /* old style getconn stops here */
    struct rx_securityObjectStats secStats;
    long sparel[10];
};

struct rx_debugConn {
    long host;
    long cid;
    long serial;
    long callNumber[RX_MAXCALLS];
    long error;
    short port;
    char flags;
    char type;
    char securityIndex;
    char sparec[3];		       /* force correct alignment */
    char callState[RX_MAXCALLS];
    char callMode[RX_MAXCALLS];
    char callFlags[RX_MAXCALLS];
    char callOther[RX_MAXCALLS];
    /* old style getconn stops here */
    struct rx_securityObjectStats secStats;
    long epoch;
    long maxPacketSize;
    long sparel[9];
};

#define	RX_OTHER_IN	1	       /* packets avail in in queue */
#define	RX_OTHER_OUT	2	       /* packets avail in out queue */

#endif				       /* _RX_	 End of rx.h */
