* $Id: rpc_fwd.c,v 5.2.1.2 91/03/03 20:46:57 jsp Alpha $
* Copyright (c) 1989 Jan-Simon Pendry
* Copyright (c) 1989 Imperial College of Science, Technology & Medicine
* Copyright (c) 1989 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Jan-Simon Pendry at Imperial College, London.
* %sccs.include.redist.c%
* @(#)rpc_fwd.c 5.2 (Berkeley) %G%
* Note that the ID field in the external packet is only
* ever treated as a 32 bit opaque data object, so there
* is no need to convert to and from network byte ordering.
* Each pending reply has an rpc_forward structure
* associated with it. These have a 15 second lifespan.
* If a new structure is required, then an expired
* one will be re-allocated if available, otherwise a fresh
* one is allocated. Whenever a reply is received the
* structure is discarded.
typedef struct rpc_forward rpc_forward
;
qelem rf_q
; /* Linked list */
time_t rf_ttl
; /* Time to live */
u_int rf_xid
; /* Packet id */
u_int rf_oldid
; /* Original packet id */
fwd_fun rf_fwd
; /* Forwarding function */
struct sockaddr_in rf_sin
;
* Head of list of pending replies
qelem rpc_head
= { &rpc_head
, &rpc_head
};
#define XID_ALLOC() (xid++)
#define MAX_PACKET_SIZE 8192 /* Maximum UDP packet size */
* Allocate a rely structure
static rpc_forward
*fwd_alloc()
time_t now
= clocktime();
/*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/
* First search for an existing expired one.
ITER(p2
, rpc_forward
, &rpc_head
) {
* If one couldn't be found then allocate
* a new structure and link it at the
* Call forwarding function to say that
* this message was junked.
dlog("Re-using packet forwarding slot - id %#x", p
->rf_xid
);
(*p
->rf_fwd
)(0, 0, 0, &p
->rf_sin
, p
->rf_ptr
, FALSE
);
ins_que(&p
->rf_q
, &rpc_head
);
* Set the time to live field
/*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/
* Free an allocated reply structure.
* First unlink it from the list, then
/*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/
/*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/
* Initialise the RPC forwarder
fwd_sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
plog(XLOG_ERROR
, "Unable to create RPC forwarding socket: %m");
* Some things we talk to require a priv port - so make one here
if (bind_resv_port(fwd_sock
, (unsigned short *) 0) < 0)
plog(XLOG_ERROR
, "can't bind privileged port");
if (fcntl(fwd_sock
, F_SETFL
, FNDELAY
) < 0 &&
ioctl(fwd_sock
, FIONBIO
, &on
) < 0) {
plog(XLOG_ERROR
, "Can't set non-block on forwarding socket: %m");
* Locate a packet in the forwarding list
static rpc_forward
*fwd_locate(id
)
ITER(p
, rpc_forward
, &rpc_head
) {
* This is called to forward a packet to another
* RPC server. The message id is changed and noted
* so that when a reply appears we can tie it up
* correctly. Just matching the reply's source address
* would not work because it might come from a
int fwd_packet(type_id
, pkt
, len
, fwdto
, replyto
, i
, cb
)
struct sockaddr_in
*fwdto
, *replyto
;
if ((int)amd_state
>= (int)Finishing
)
* See if the type_id is fully specified.
* If so, then discard any old entries
* Otherwise make sure the type_id is
* fully qualified by allocating an id here.
switch (type_id
& RPC_XID_MASK
) {
case RPC_XID_PORTMAP
: dlog("Sending PORTMAP request"); break;
case RPC_XID_MOUNTD
: dlog("Sending MOUNTD request %#x", type_id
); break;
case RPC_XID_NFSPING
: dlog("Sending NFS ping"); break;
default: dlog("UNKNOWN RPC XID"); break;
if (type_id
& ~RPC_XID_MASK
) {
/*dlog("Fully qualified rpc type provided");*/
dlog("Discarding earlier rpc fwd handle");
dlog("Allocating a new xid...");
type_id
= MK_RPC_XID(type_id
, XID_ALLOC());
* Get the original packet id
* Replace with newly allocated id
p
->rf_xid
= *pkt_int
= type_id
;
* The sendto may fail if, for example, the route
* to a remote host is lost because an intermediate
* gateway has gone down. Important to fill in the
* rest of "p" otherwise nasty things happen later...
dlog("Sending packet id %#x to %s.%d", p
->rf_xid
, inet_dquad(dq
, fwdto
->sin_addr
.s_addr
), ntohs(fwdto
->sin_port
));
if (sendto(fwd_sock
, (char *) pkt
, len
, 0,
(struct sockaddr
*) fwdto
, sizeof(*fwdto
)) < 0)
* Save callback function and return address
bzero((voidp
) &p
->rf_sin
, sizeof(p
->rf_sin
));
* Called when some data arrives on the forwarding socket
u_int pkt
[MAX_PACKET_SIZE
/sizeof(u_int
)+1];
#endif /* DYNAMIC_BUFFERS */
struct sockaddr_in src_addr
;
* Determine the length of the packet
if (ioctl(fwd_sock
, FIONREAD
, &len
) < 0) {
plog(XLOG_ERROR
, "Error reading packet size: %m");
pkt
= (voidp
) malloc((unsigned) len
);
plog(XLOG_ERROR
, "Out of buffers in fwd_reply");
#endif /* DYNAMIC_BUFFERS */
* Read the packet and check for validity
src_addr_len
= sizeof(src_addr
);
rc
= recvfrom(fwd_sock
, (char *) pkt
, len
, 0,
(struct sockaddr
*) &src_addr
, &src_addr_len
);
if (rc
< 0 || src_addr_len
!= sizeof(src_addr
) ||
src_addr
.sin_family
!= AF_INET
) {
if (rc
< 0 && errno
== EINTR
)
plog(XLOG_ERROR
, "Error reading RPC reply: %m");
plog(XLOG_ERROR
, "Short read in fwd_reply");
#endif /* DYNAMIC_BUFFERS */
* Do no more work if finishing soon
if ((int)amd_state
>= (int)Finishing
)
switch (*pkt_int
& RPC_XID_MASK
) {
case RPC_XID_PORTMAP
: dlog("Receiving PORTMAP reply"); break;
case RPC_XID_MOUNTD
: dlog("Receiving MOUNTD reply %#x", *pkt_int
); break;
case RPC_XID_NFSPING
: dlog("Receiving NFS ping %#x", *pkt_int
); break;
default: dlog("UNKNOWN RPC XID"); break;
p
= fwd_locate(*pkt_int
);
dlog("Can't forward reply id %#x", *pkt_int
);
* Put the original message id back
* Call forwarding function
(*p
->rf_fwd
)((voidp
) pkt
, rc
, &src_addr
, &p
->rf_sin
, p
->rf_ptr
, TRUE
);
#endif /* DYNAMIC_BUFFERS */