xref: /original-bsd/usr.sbin/amd/amd/rpc_fwd.c (revision 4092c5cc)
1398a5aebSmckusick /*
2398a5aebSmckusick  * Copyright (c) 1989 Jan-Simon Pendry
3398a5aebSmckusick  * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
4*4092c5ccSbostic  * Copyright (c) 1989, 1993
5*4092c5ccSbostic  *	The Regents of the University of California.  All rights reserved.
6398a5aebSmckusick  *
7398a5aebSmckusick  * This code is derived from software contributed to Berkeley by
8398a5aebSmckusick  * Jan-Simon Pendry at Imperial College, London.
9398a5aebSmckusick  *
10398a5aebSmckusick  * %sccs.include.redist.c%
11398a5aebSmckusick  *
12*4092c5ccSbostic  *	@(#)rpc_fwd.c	8.1 (Berkeley) 06/06/93
13c626267eSpendry  *
14cc0207dcSpendry  * $Id: rpc_fwd.c,v 5.2.2.1 1992/02/09 15:09:01 jsp beta $
15c626267eSpendry  *
16398a5aebSmckusick  */
17398a5aebSmckusick 
18398a5aebSmckusick /*
19398a5aebSmckusick  * RPC packet forwarding
20398a5aebSmckusick  */
21398a5aebSmckusick 
22398a5aebSmckusick #include "am.h"
23398a5aebSmckusick #include <sys/ioctl.h>
24398a5aebSmckusick #ifndef F_SETFL
25398a5aebSmckusick #include <fcntl.h>
26398a5aebSmckusick #endif /* F_SETFL */
27398a5aebSmckusick #ifndef FNDELAY
28398a5aebSmckusick #include <sys/file.h>
29398a5aebSmckusick #endif /* FNDELAY */
30398a5aebSmckusick 
31398a5aebSmckusick /*
32398a5aebSmckusick  * Note that the ID field in the external packet is only
33398a5aebSmckusick  * ever treated as a 32 bit opaque data object, so there
34398a5aebSmckusick  * is no need to convert to and from network byte ordering.
35398a5aebSmckusick  */
36398a5aebSmckusick 
37398a5aebSmckusick /*
38398a5aebSmckusick  * Each pending reply has an rpc_forward structure
39398a5aebSmckusick  * associated with it.  These have a 15 second lifespan.
40398a5aebSmckusick  * If a new structure is required, then an expired
41398a5aebSmckusick  * one will be re-allocated if available, otherwise a fresh
42398a5aebSmckusick  * one is allocated.  Whenever a reply is received the
43398a5aebSmckusick  * structure is discarded.
44398a5aebSmckusick  */
45398a5aebSmckusick typedef struct rpc_forward rpc_forward;
46398a5aebSmckusick struct rpc_forward {
47398a5aebSmckusick 	qelem	rf_q;		/* Linked list */
48398a5aebSmckusick 	time_t	rf_ttl;		/* Time to live */
49398a5aebSmckusick 	u_int	rf_xid;		/* Packet id */
50398a5aebSmckusick 	u_int	rf_oldid;	/* Original packet id */
51398a5aebSmckusick 	fwd_fun	rf_fwd;		/* Forwarding function */
52398a5aebSmckusick 	voidp	rf_ptr;
53398a5aebSmckusick 	struct sockaddr_in rf_sin;
54398a5aebSmckusick };
55398a5aebSmckusick 
56398a5aebSmckusick /*
57398a5aebSmckusick  * Head of list of pending replies
58398a5aebSmckusick  */
59398a5aebSmckusick extern qelem rpc_head;
60398a5aebSmckusick qelem rpc_head = { &rpc_head, &rpc_head };
61398a5aebSmckusick 
62398a5aebSmckusick static u_int xid;
63398a5aebSmckusick #define	XID_ALLOC()	(xid++)
64398a5aebSmckusick 
65398a5aebSmckusick #define	MAX_PACKET_SIZE	8192	/* Maximum UDP packet size */
66398a5aebSmckusick 
67398a5aebSmckusick int fwd_sock;
68398a5aebSmckusick 
69398a5aebSmckusick /*
70398a5aebSmckusick  * Allocate a rely structure
71398a5aebSmckusick  */
fwd_alloc()72398a5aebSmckusick static rpc_forward *fwd_alloc()
73398a5aebSmckusick {
74398a5aebSmckusick 	time_t now = clocktime();
75398a5aebSmckusick 	rpc_forward *p = 0, *p2;
76398a5aebSmckusick 
77398a5aebSmckusick #ifdef DEBUG
78398a5aebSmckusick 	/*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/
79398a5aebSmckusick #endif /* DEBUG */
80398a5aebSmckusick 	/*
81398a5aebSmckusick 	 * First search for an existing expired one.
82398a5aebSmckusick 	 */
83398a5aebSmckusick 	ITER(p2, rpc_forward, &rpc_head) {
84398a5aebSmckusick 		if (p2->rf_ttl <= now) {
85398a5aebSmckusick 			p = p2;
86398a5aebSmckusick 			break;
87398a5aebSmckusick 		}
88398a5aebSmckusick 	}
89398a5aebSmckusick 
90398a5aebSmckusick 	/*
91398a5aebSmckusick 	 * If one couldn't be found then allocate
92398a5aebSmckusick 	 * a new structure and link it at the
93398a5aebSmckusick 	 * head of the list.
94398a5aebSmckusick 	 */
95398a5aebSmckusick 	if (p) {
96398a5aebSmckusick 		/*
97398a5aebSmckusick 		 * Call forwarding function to say that
98398a5aebSmckusick 		 * this message was junked.
99398a5aebSmckusick 		 */
100398a5aebSmckusick #ifdef DEBUG
101398a5aebSmckusick 		dlog("Re-using packet forwarding slot - id %#x", p->rf_xid);
102398a5aebSmckusick #endif /* DEBUG */
103398a5aebSmckusick 		if (p->rf_fwd)
104398a5aebSmckusick 			(*p->rf_fwd)(0, 0, 0, &p->rf_sin, p->rf_ptr, FALSE);
105398a5aebSmckusick 		rem_que(&p->rf_q);
106398a5aebSmckusick 	} else {
107398a5aebSmckusick 		p = ALLOC(rpc_forward);
108398a5aebSmckusick 	}
109398a5aebSmckusick 	ins_que(&p->rf_q, &rpc_head);
110398a5aebSmckusick 
111398a5aebSmckusick 	/*
112398a5aebSmckusick 	 * Set the time to live field
113398a5aebSmckusick 	 * Timeout in 43 seconds
114398a5aebSmckusick 	 */
115398a5aebSmckusick 	p->rf_ttl = now + 43;
116398a5aebSmckusick 
117398a5aebSmckusick #ifdef DEBUG
118398a5aebSmckusick 	/*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/
119398a5aebSmckusick #endif /* DEBUG */
120398a5aebSmckusick 	return p;
121398a5aebSmckusick }
122398a5aebSmckusick 
123398a5aebSmckusick /*
124398a5aebSmckusick  * Free an allocated reply structure.
125398a5aebSmckusick  * First unlink it from the list, then
126398a5aebSmckusick  * discard it.
127398a5aebSmckusick  */
fwd_free(p)128398a5aebSmckusick static void fwd_free(p)
129398a5aebSmckusick rpc_forward *p;
130398a5aebSmckusick {
131398a5aebSmckusick #ifdef DEBUG
132398a5aebSmckusick 	/*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/
133398a5aebSmckusick #endif /* DEBUG */
134398a5aebSmckusick 	rem_que(&p->rf_q);
135398a5aebSmckusick #ifdef DEBUG
136398a5aebSmckusick 	/*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/
137398a5aebSmckusick #endif /* DEBUG */
1388a89c22cSpendry 	free((voidp) p);
139398a5aebSmckusick }
140398a5aebSmckusick 
141398a5aebSmckusick /*
142398a5aebSmckusick  * Initialise the RPC forwarder
143398a5aebSmckusick  */
fwd_init()144398a5aebSmckusick int fwd_init()
145398a5aebSmckusick {
146398a5aebSmckusick 	int on = 1;
147398a5aebSmckusick 
148398a5aebSmckusick 	/*
149398a5aebSmckusick 	 * Create ping socket
150398a5aebSmckusick 	 */
151398a5aebSmckusick 	fwd_sock = socket(AF_INET, SOCK_DGRAM, 0);
152398a5aebSmckusick 	if (fwd_sock < 0) {
153398a5aebSmckusick 		plog(XLOG_ERROR, "Unable to create RPC forwarding socket: %m");
154398a5aebSmckusick 		return errno;
155398a5aebSmckusick 	}
156398a5aebSmckusick 
157398a5aebSmckusick 	/*
158398a5aebSmckusick 	 * Some things we talk to require a priv port - so make one here
159398a5aebSmckusick 	 */
160398a5aebSmckusick 	if (bind_resv_port(fwd_sock, (unsigned short *) 0) < 0)
161398a5aebSmckusick 		plog(XLOG_ERROR, "can't bind privileged port");
162398a5aebSmckusick 
163398a5aebSmckusick 	if (fcntl(fwd_sock, F_SETFL, FNDELAY) < 0 &&
164398a5aebSmckusick 			ioctl(fwd_sock, FIONBIO, &on) < 0) {
165398a5aebSmckusick 		plog(XLOG_ERROR, "Can't set non-block on forwarding socket: %m");
166398a5aebSmckusick 		return errno;
167398a5aebSmckusick 	}
168398a5aebSmckusick 
169398a5aebSmckusick 	return 0;
170398a5aebSmckusick }
171398a5aebSmckusick 
172398a5aebSmckusick /*
173398a5aebSmckusick  * Locate a packet in the forwarding list
174398a5aebSmckusick  */
fwd_locate(id)175398a5aebSmckusick static rpc_forward *fwd_locate(id)
176398a5aebSmckusick u_int id;
177398a5aebSmckusick {
178398a5aebSmckusick 	rpc_forward *p;
179398a5aebSmckusick 
180398a5aebSmckusick 	ITER(p, rpc_forward, &rpc_head) {
181398a5aebSmckusick 		if (p->rf_xid == id)
182398a5aebSmckusick 			return p;
183398a5aebSmckusick 	}
184398a5aebSmckusick 
185398a5aebSmckusick 	return 0;
186398a5aebSmckusick }
187398a5aebSmckusick 
188398a5aebSmckusick /*
189398a5aebSmckusick  * This is called to forward a packet to another
190398a5aebSmckusick  * RPC server.  The message id is changed and noted
191398a5aebSmckusick  * so that when a reply appears we can tie it up
192398a5aebSmckusick  * correctly.  Just matching the reply's source address
193398a5aebSmckusick  * would not work because it might come from a
194398a5aebSmckusick  * different address.
195398a5aebSmckusick  */
fwd_packet(type_id,pkt,len,fwdto,replyto,i,cb)196398a5aebSmckusick int fwd_packet(type_id, pkt, len, fwdto, replyto, i, cb)
197398a5aebSmckusick int type_id;
198398a5aebSmckusick voidp pkt;
199398a5aebSmckusick int len;
200398a5aebSmckusick struct sockaddr_in *fwdto, *replyto;
201398a5aebSmckusick voidp i;
202398a5aebSmckusick fwd_fun cb;
203398a5aebSmckusick {
204398a5aebSmckusick 	rpc_forward *p;
205398a5aebSmckusick 	u_int *pkt_int;
206398a5aebSmckusick 	int error;
207398a5aebSmckusick 
208398a5aebSmckusick 	if ((int)amd_state >= (int)Finishing)
209398a5aebSmckusick 		return ENOENT;
210398a5aebSmckusick 
211398a5aebSmckusick 	/*
212398a5aebSmckusick 	 * See if the type_id is fully specified.
213398a5aebSmckusick 	 * If so, then discard any old entries
214398a5aebSmckusick 	 * for this id.
215398a5aebSmckusick 	 * Otherwise make sure the type_id is
216398a5aebSmckusick 	 * fully qualified by allocating an id here.
217398a5aebSmckusick 	 */
218398a5aebSmckusick #ifdef DEBUG
219398a5aebSmckusick 	switch (type_id & RPC_XID_MASK) {
220398a5aebSmckusick 	case RPC_XID_PORTMAP: dlog("Sending PORTMAP request"); break;
221398a5aebSmckusick 	case RPC_XID_MOUNTD: dlog("Sending MOUNTD request %#x", type_id); break;
222398a5aebSmckusick 	case RPC_XID_NFSPING: dlog("Sending NFS ping"); break;
223398a5aebSmckusick 	default: dlog("UNKNOWN RPC XID"); break;
224398a5aebSmckusick 	}
225398a5aebSmckusick #endif /* DEBUG */
226398a5aebSmckusick 
227398a5aebSmckusick 	if (type_id & ~RPC_XID_MASK) {
228398a5aebSmckusick #ifdef DEBUG
229398a5aebSmckusick 		/*dlog("Fully qualified rpc type provided");*/
230398a5aebSmckusick #endif /* DEBUG */
231398a5aebSmckusick 		p = fwd_locate(type_id);
232398a5aebSmckusick 		if (p) {
233398a5aebSmckusick #ifdef DEBUG
234398a5aebSmckusick 			dlog("Discarding earlier rpc fwd handle");
235398a5aebSmckusick #endif /* DEBUG */
236398a5aebSmckusick 			fwd_free(p);
237398a5aebSmckusick 		}
238398a5aebSmckusick 	} else {
239398a5aebSmckusick #ifdef DEBUG
240398a5aebSmckusick 		dlog("Allocating a new xid...");
241398a5aebSmckusick #endif /* DEBUG */
242398a5aebSmckusick 		type_id = MK_RPC_XID(type_id, XID_ALLOC());
243398a5aebSmckusick 	}
244398a5aebSmckusick 
245398a5aebSmckusick 	p = fwd_alloc();
246398a5aebSmckusick 	if (!p)
247398a5aebSmckusick 		return ENOBUFS;
248398a5aebSmckusick 
249398a5aebSmckusick 	error = 0;
250398a5aebSmckusick 
251398a5aebSmckusick 	pkt_int = (u_int *) pkt;
252398a5aebSmckusick 
253398a5aebSmckusick 	/*
254398a5aebSmckusick 	 * Get the original packet id
255398a5aebSmckusick 	 */
256398a5aebSmckusick 	p->rf_oldid = *pkt_int;
257398a5aebSmckusick 
258398a5aebSmckusick 	/*
259398a5aebSmckusick 	 * Replace with newly allocated id
260398a5aebSmckusick 	 */
261398a5aebSmckusick 	p->rf_xid = *pkt_int = type_id;
262398a5aebSmckusick 
263398a5aebSmckusick 	/*
264398a5aebSmckusick 	 * The sendto may fail if, for example, the route
265398a5aebSmckusick 	 * to a remote host is lost because an intermediate
266398a5aebSmckusick 	 * gateway has gone down.  Important to fill in the
267398a5aebSmckusick 	 * rest of "p" otherwise nasty things happen later...
268398a5aebSmckusick 	 */
269398a5aebSmckusick #ifdef DEBUG
2708a89c22cSpendry 	{ char dq[20];
2718a89c22cSpendry 	dlog("Sending packet id %#x to %s.%d", p->rf_xid, inet_dquad(dq, fwdto->sin_addr.s_addr), ntohs(fwdto->sin_port));
2728a89c22cSpendry 	}
273398a5aebSmckusick #endif /* DEBUG */
274398a5aebSmckusick 	if (sendto(fwd_sock, (char *) pkt, len, 0,
275398a5aebSmckusick 			(struct sockaddr *) fwdto, sizeof(*fwdto)) < 0)
276398a5aebSmckusick 		error = errno;
277398a5aebSmckusick 
278398a5aebSmckusick 	/*
279398a5aebSmckusick 	 * Save callback function and return address
280398a5aebSmckusick 	 */
281398a5aebSmckusick 	p->rf_fwd = cb;
282398a5aebSmckusick 	if (replyto)
283398a5aebSmckusick 		p->rf_sin = *replyto;
284398a5aebSmckusick 	else
285398a5aebSmckusick 		bzero((voidp) &p->rf_sin, sizeof(p->rf_sin));
286398a5aebSmckusick 	p->rf_ptr = i;
287398a5aebSmckusick 
288398a5aebSmckusick 	return error;
289398a5aebSmckusick }
290398a5aebSmckusick 
291398a5aebSmckusick /*
292398a5aebSmckusick  * Called when some data arrives on the forwarding socket
293398a5aebSmckusick  */
fwd_reply()294398a5aebSmckusick void fwd_reply()
295398a5aebSmckusick {
296398a5aebSmckusick 	int len;
297398a5aebSmckusick #ifdef DYNAMIC_BUFFERS
298398a5aebSmckusick 	voidp pkt;
299398a5aebSmckusick #else
300398a5aebSmckusick 	u_int pkt[MAX_PACKET_SIZE/sizeof(u_int)+1];
301398a5aebSmckusick #endif /* DYNAMIC_BUFFERS */
302398a5aebSmckusick 	u_int *pkt_int;
303398a5aebSmckusick 	int rc;
304398a5aebSmckusick 	rpc_forward *p;
305398a5aebSmckusick 	struct sockaddr_in src_addr;
306398a5aebSmckusick 	int src_addr_len;
307398a5aebSmckusick 
308398a5aebSmckusick 	/*
309398a5aebSmckusick 	 * Determine the length of the packet
310398a5aebSmckusick 	 */
311398a5aebSmckusick #ifdef DYNAMIC_BUFFERS
312398a5aebSmckusick 	if (ioctl(fwd_sock, FIONREAD, &len) < 0) {
313398a5aebSmckusick 		plog(XLOG_ERROR, "Error reading packet size: %m");
314398a5aebSmckusick 		return;
315398a5aebSmckusick 	}
316398a5aebSmckusick 
317398a5aebSmckusick 	/*
318398a5aebSmckusick 	 * Allocate a buffer
319398a5aebSmckusick 	 */
320398a5aebSmckusick 	pkt = (voidp) malloc((unsigned) len);
321398a5aebSmckusick 	if (!pkt) {
322398a5aebSmckusick 		plog(XLOG_ERROR, "Out of buffers in fwd_reply");
323398a5aebSmckusick 		return;
324398a5aebSmckusick 	}
325398a5aebSmckusick #else
326398a5aebSmckusick 	len = MAX_PACKET_SIZE;
327398a5aebSmckusick #endif /* DYNAMIC_BUFFERS */
328398a5aebSmckusick 
329398a5aebSmckusick 	/*
330398a5aebSmckusick 	 * Read the packet and check for validity
331398a5aebSmckusick 	 */
332398a5aebSmckusick again:
333398a5aebSmckusick 	src_addr_len = sizeof(src_addr);
334398a5aebSmckusick 	rc = recvfrom(fwd_sock, (char *) pkt, len, 0,
335398a5aebSmckusick 			(struct sockaddr *) &src_addr, &src_addr_len);
336398a5aebSmckusick 	if (rc < 0 || src_addr_len != sizeof(src_addr) ||
337398a5aebSmckusick 			src_addr.sin_family != AF_INET) {
338398a5aebSmckusick 		if (rc < 0 && errno == EINTR)
339398a5aebSmckusick 			goto again;
340398a5aebSmckusick 		plog(XLOG_ERROR, "Error reading RPC reply: %m");
341398a5aebSmckusick 		goto out;
342398a5aebSmckusick 	}
343398a5aebSmckusick 
344398a5aebSmckusick #ifdef DYNAMIC_BUFFERS
345398a5aebSmckusick 	if (rc != len) {
346398a5aebSmckusick 		plog(XLOG_ERROR, "Short read in fwd_reply");
347398a5aebSmckusick 		goto out;
348398a5aebSmckusick 	}
349398a5aebSmckusick #endif /* DYNAMIC_BUFFERS */
350398a5aebSmckusick 
351398a5aebSmckusick 	/*
352398a5aebSmckusick 	 * Do no more work if finishing soon
353398a5aebSmckusick 	 */
354398a5aebSmckusick 	if ((int)amd_state >= (int)Finishing)
355398a5aebSmckusick 		goto out;
356398a5aebSmckusick 
357398a5aebSmckusick 	/*
358398a5aebSmckusick 	 * Find packet reference
359398a5aebSmckusick 	 */
360398a5aebSmckusick 	pkt_int = (u_int *) pkt;
361398a5aebSmckusick 
362398a5aebSmckusick #ifdef DEBUG
363398a5aebSmckusick 	switch (*pkt_int & RPC_XID_MASK) {
364398a5aebSmckusick 	case RPC_XID_PORTMAP: dlog("Receiving PORTMAP reply"); break;
365398a5aebSmckusick 	case RPC_XID_MOUNTD: dlog("Receiving MOUNTD reply %#x", *pkt_int); break;
366398a5aebSmckusick 	case RPC_XID_NFSPING: dlog("Receiving NFS ping %#x", *pkt_int); break;
367398a5aebSmckusick 	default: dlog("UNKNOWN RPC XID"); break;
368398a5aebSmckusick 	}
369398a5aebSmckusick #endif /* DEBUG */
370398a5aebSmckusick 
371398a5aebSmckusick 	p = fwd_locate(*pkt_int);
372398a5aebSmckusick 	if (!p) {
373398a5aebSmckusick #ifdef DEBUG
374398a5aebSmckusick 		dlog("Can't forward reply id %#x", *pkt_int);
375398a5aebSmckusick #endif /* DEBUG */
376398a5aebSmckusick 		goto out;
377398a5aebSmckusick 	}
378398a5aebSmckusick 
379398a5aebSmckusick 	if (p->rf_fwd) {
380398a5aebSmckusick 		/*
381398a5aebSmckusick 		 * Put the original message id back
382398a5aebSmckusick 		 * into the packet.
383398a5aebSmckusick 		 */
384398a5aebSmckusick 		*pkt_int = p->rf_oldid;
385398a5aebSmckusick 
386398a5aebSmckusick 		/*
387398a5aebSmckusick 		 * Call forwarding function
388398a5aebSmckusick 		 */
3898a89c22cSpendry 		(*p->rf_fwd)((voidp) pkt, rc, &src_addr, &p->rf_sin, p->rf_ptr, TRUE);
390398a5aebSmckusick 	}
391398a5aebSmckusick 
392398a5aebSmckusick 	/*
393398a5aebSmckusick 	 * Free forwarding info
394398a5aebSmckusick 	 */
395398a5aebSmckusick 	fwd_free(p);
396398a5aebSmckusick 
397398a5aebSmckusick out:;
398398a5aebSmckusick #ifdef DYNAMIC_BUFFERS
399398a5aebSmckusick 	/*
400398a5aebSmckusick 	 * Free the packet
401398a5aebSmckusick 	 */
4028a89c22cSpendry 	free((voidp) pkt);
403398a5aebSmckusick #endif /* DYNAMIC_BUFFERS */
404398a5aebSmckusick }
405