ONC+ Developer's Guide

Program and Procedure Numbers

The RPC call message has three unsigned fields that uniquely identify the procedure to be called:

Program numbers are administered by a central authority, as described in Program Number Registration.

The first implementation of a program most likely has version number 1. Most new protocols evolve into better, stable, and mature protocols. Therefore, a version field of the call message identifies the version of the protocol that the caller is using. Version numbers make speaking old and new protocols through the same server process possible.

The procedure number identifies the procedure to be called. These numbers are documented in the individual program's protocol specification. For example, a file service's protocol specification might state that its procedure number 5 is read and procedure number 12 is write.

Just as remote program protocols can change over several versions, the RPC message protocol itself can change. Therefore, the call message also has in it the RPC version number, which is always equal to 2 for the version of RPC described here.

The reply message to a request message has enough information to distinguish the following error conditions:

Provisions for authentication of caller to service and the reverse are provided as a part of the RPC protocol. The call message has two authentication fields, the credentials and verifier. The reply message has one authentication field, the response verifier. The RPC protocol specification defines all three fields to be the following opaque type.

enum auth_flavor {
  	AUTH_NONE = 0,
  	AUTH_SYS = 1,
  	AUTH_SHORT = 2,
  	AUTH_DES = 3,
 	AUTH_KERB = 4
  	/* and more to be defined */
  };
  struct opaque_auth {
   enum         auth_flavor;        /* style of credentials */
  	caddr_t      oa_base;          /* address of more auth stuff */
  	u_int        oa_length;        /* not to exceed MAX_AUTH_BYTES */
  };

An opaque_auth structure is an auth_flavor enumeration followed by bytes that are opaque to the RPC protocol implementation.

The interpretation and semantics of the data contained within the authentication fields are specified by individual, independent authentication protocol specifications. See Record-Marking Standard for definitions of the various authentication protocols.

If authentication parameters are rejected, the response message contains information stating why they are rejected.

Program Number Assignment

Program numbers are distributed in groups of 0x20000000, as shown in the following table.

Table B–1 RPC Program Number Assignment

Program Numbers 

Description 

00000000 - 1fffffff 

Defined by host 

20000000 - 3fffffff 

Defined by user 

40000000 - 5fffffff

Transient (reserved for customer-written applications)

60000000 - 7fffffff 

Reserved 

80000000 - 9fffffff 

Reserved 

a0000000 - bfffffff 

Reserved 

c0000000 - dfffffff 

Reserved 

e0000000 - ffffffff 

Reserved 

Sun Microsystems administers the first group of numbers, which should be identical for all customers. If a customer develops an application that might be of general interest, that application should be given an assigned number in the first range.

The second group of numbers is reserved for specific customer applications. This range is intended primarily for debugging new programs.

The third group is reserved for applications that generate program numbers dynamically.

The final groups are reserved for future use, and should not be used.

Program Number Registration

To register a protocol specification, send a request by email to rpc@sun.com, or write to: RPC Administrator, Sun Microsystems, 4150 Network Circle, Santa Clara, CA 95054.

Include a compilable rpcgen .x file describing your protocol. You are given a unique program number in return.

You can find the RPC program numbers and protocol specifications of standard RPC services in the include files in /usr/include/rpcsvc. These services, however, constitute only a small subset of those that have been registered.

Other Uses of the RPC Protocol

The intended use of this protocol is for calling remote procedures. That is, each call message is matched with a response message. However, the protocol itself is a message-passing protocol with which other non-RPC protocols can be implemented. Some of the non-RPC protocols supported by the RPC package are batching and broadcasting.

Batching

Batching enables a client to send an arbitrarily large sequence of call messages to a server. Batching typically uses reliable byte-stream protocols like TCP for its transport. In batching, the client never waits for a reply from the server, and the server does not send replies to batch requests. A sequence of batch calls is usually finished by a non-batch RPC call to flush the pipeline with positive acknowledgement. For more information, see Batching.

Broadcast RPC

In broadcast RPC, the client sends a broadcast packet to the network and waits for numerous replies. Broadcast RPC uses connectionless, packet-based protocols like UDP as its transports. Servers that support broadcast protocols only respond when the request is successfully processed, and are silent in the face of errors. Broadcast RPC uses the rpcbind service to achieve its semantics. See Broadcast RPC and rpcbind Protocol for further information.

RPC Message Protocol

This section defines the RPC message protocol in the XDR data description language. The message is defined in a top-down style, as shown in the following code example.


Example B–1 RPC Message Protocol

enum msg_type {
 	CALL = 0,
 	REPLY = 1
 };

/*
 * A reply to a call message can take on two forms: The message was
 * either accepted or rejected.
 */
 enum reply_stat {
 	MSG_ACCEPTED = 0,
 	MSG_DENIED = 1
 };

/*
 * Given that a call message was accepted, the following is the
 * status of an attempt to call a remote procedure.
 */
enum accept_stat {
 	SUCCESS = 0,       /* RPC executed successfully */
 	PROG_UNAVAIL = 1,  /* remote service hasn't exported prog */
 	PROG_MISMATCH = 2, /* remote service can't support versn # */
 	PROC_UNAVAIL = 3,  /* program can't support proc */
 	GARBAGE_ARGS = 4   /* procedure can't decode params */
};

/*
 * Reasons a call message was rejected:
 */
enum reject_stat {
 	RPC_MISMATCH = 0,  /* RPC version number != 2 */
 	AUTH_ERROR = 1     /* remote can't authenticate caller */
};
/*
 * Why authentication failed:
 */
enum auth_stat {
 	AUTH_BADCRED = 1,       /* bad credentials */
 	AUTH_REJECTEDCRED = 2,  /* clnt must do new session */
 	AUTH_BADVERF = 3,       /* bad verifier */
 	AUTH_REJECTEDVERF = 4,  /* verif expired or replayed */
 	AUTH_TOOWEAK = 5        /* rejected for security */
};

/*
 * The RPC message:
 * All messages start with a transaction identifier, xid, followed
 * by a two-armed discriminated union. The union's discriminant is
 * a msg_type which switches to one of the two types of the
 * message.
 * The xid of a REPLY message always matches that of the
 * initiating CALL message. NB: The xid field is only used for
 * clients matching reply messages with call messages or for servers
 * detecting retransmissions; the service side cannot treat this id as
 * any type of sequence number.
 */
struct rpc_msg {
 	unsigned int xid;
 	union switch (msg_type mtype) {
 		case CALL:
 			call_body cbody;
 		case REPLY:
 			reply_body rbody;
 	} body;
};

/*
 * Body of an RPC request call:
 * In version 2 of the RPC protocol specification, rpcvers must be
 * equal to 2. The fields prog, vers, and proc specify the remote
 * program, its version number, and the procedure within the
 * remote program to be called. After these fields are two 
 * authentication parameters: cred (authentication credentials) and 
 * verf (authentication verifier). The two authentication parameters
 * are followed by the parameters to the remote procedure, which are
 * specified by the specific program protocol.
 */
struct call_body {
 	unsigned int rpcvers; /* must be equal to two (2) */
 	unsigned int prog;
 	unsigned int vers;
 	unsigned int proc;
 	opaque_auth cred;
 	opaque_auth verf;
 	/* procedure specific parameters start here */
 };

/*
 * Body of a reply to an RPC request:
 * The call message was either accepted or rejected.
 */
union reply_body switch (reply_stat stat) {
 	case MSG_ACCEPTED:
 		accepted_reply areply;
 	case MSG_DENIED:
 		rejected_reply rreply;
} reply;

/*
 * Reply to an RPC request that was accepted by the server: there
 * could be an error even though the request was accepted. The
 * first field is an authentication verifier that the server
 * generates in order to validate itself to the caller. It is 
 * followed by a union whose discriminant is an enum accept_stat.
 * The SUCCESS arm of the union is protocol specific. 
 * The PROG_UNAVAIL, PROC_UNAVAIL, and GARBAGE_ARGP arms of 
 * the union are void. The PROG_MISMATCH arm specifies the lowest
 * and highest version numbers of the remote program supported by
 * the server.
 */
struct accepted_reply {
 	opaque_auth verf;
 	union switch (accept_stat stat) {
 		case SUCCESS:
 			opaque results[0];
 			/* procedure-specific results start here */
 		case PROG_MISMATCH:
 			struct {
 				unsigned int low;
 				unsigned int high;
 			} mismatch_info;
 		default:
 			/*
 			 * Void. Cases include PROG_UNAVAIL, PROC_UNAVAIL, and
			    * GARBAGE_ARGS.
 			 */
 			void;
 	} reply_data;
 };

/*
 * Reply to an RPC request that was rejected by the server:
 * The request can be rejected for two reasons: either the server
 * is not running a compatible version of the RPC protocol
 * (RPC_MISMATCH), or the server refuses to authenticate the
 * caller AUTH_ERROR). In case of an RPC version mismatch,
 * the server returns the lowest and highest supported RPC
 * version numbers. In case of refused authentication, failure
 * status is returned.
 */
union rejected_reply switch (reject_stat stat) {
 	case RPC_MISMATCH:
 		struct {
 			unsigned int low;
 			unsigned int high;
 		} mismatch_info;
 	case AUTH_ERROR:
 		auth_stat stat;
};

Record-Marking Standard

When RPC messages are passed on top of a byte-stream transport like TCP, you should try to delimit one message from another to detect and possibly recover from user protocol errors. This is called record marking (RM). One RPC message fits into one RM record.

A record is composed of one or more record fragments. A record fragment is a 4-byte header followed by 0 to (2**31) - 1 bytes of fragment data. The bytes encode an unsigned binary number. As with XDR integers, the byte order is the network byte order.

The header encodes two values: