ip_rpcb_pxy.c revision 369245
1/*
2 * Copyright (C) 2002-2012 by Ryan Beasley <ryanb@goddamnbastard.org>
3 *
4 * See the IPFILTER.LICENCE file for details on licencing.
5 */
6/*
7 * Overview:
8 *   This is an in-kernel application proxy for Sun's RPCBIND (nee portmap)
9 *   protocol as defined in RFC1833.  It is far from complete, mostly
10 *   lacking in less-likely corner cases, but it's definitely functional.
11 *
12 *   Invocation:
13 *     rdr <int> <e_ip>/32 port <e_p> -> <i_ip> port <i_p> udp proxy rpcbu
14 *
15 *   If the host running IP Filter is the same as the RPC server, it's
16 *   perfectly legal for both the internal and external addresses and ports
17 *   to match.
18 *
19 *   When triggered by appropriate IP NAT rules, this proxy works by
20 *   examining data contained in received packets.  Requests and replies are
21 *   modified, NAT and state table entries created, etc., as necessary.
22 */
23/*
24 * TODO / NOTES
25 *
26 *   o Must implement locking to protect proxy session data.
27 *   o Fragmentation isn't supported.
28 *   o Only supports UDP.
29 *   o Doesn't support multiple RPC records in a single request.
30 *   o Errors should be more fine-grained.  (e.g., malloc failure vs.
31 *     illegal RPCB request / reply)
32 *   o Even with the limit on the total amount of recorded transactions,
33 *     should there be a timeout on transaction removal?
34 *   o There is a potential collision between cloning, wildcard NAT and
35 *     state entries.  There should be an appr_getport routine for
36 *     to avoid this.
37 *   o The enclosed hack of STREAMS support is pretty sick and most likely
38 *     broken.
39 *
40 *	$Id$
41 */
42#define	IPF_RPCB_PROXY
43
44/*
45 * Function prototypes
46 */
47void	ipf_p_rpcb_main_load(void);
48void	ipf_p_rpcb_main_unload(void);
49int	ipf_p_rpcb_new(void *, fr_info_t *, ap_session_t *, nat_t *);
50void	ipf_p_rpcb_del(ipf_main_softc_t *, ap_session_t *);
51int	ipf_p_rpcb_in(void *, fr_info_t *, ap_session_t *, nat_t *);
52int	ipf_p_rpcb_out(void *, fr_info_t *, ap_session_t *, nat_t *);
53
54static void	ipf_p_rpcb_flush(rpcb_session_t *);
55static int	ipf_p_rpcb_decodereq(fr_info_t *, nat_t *,
56	rpcb_session_t *, rpc_msg_t *);
57static int	ipf_p_rpcb_skipauth(rpc_msg_t *, xdr_auth_t *, u_32_t **);
58static int	ipf_p_rpcb_insert(rpcb_session_t *, rpcb_xact_t *);
59static int	ipf_p_rpcb_xdrrpcb(rpc_msg_t *, u_32_t *, rpcb_args_t *);
60static int	ipf_p_rpcb_getuaddr(rpc_msg_t *, xdr_uaddr_t *,
61	u_32_t **);
62static u_int	ipf_p_rpcb_atoi(char *);
63static int	ipf_p_rpcb_modreq(fr_info_t *, nat_t *, rpc_msg_t *,
64	mb_t *, u_int);
65static int	ipf_p_rpcb_decoderep(fr_info_t *, nat_t *,
66	rpcb_session_t *, rpc_msg_t *, rpcb_xact_t **);
67static rpcb_xact_t *	ipf_p_rpcb_lookup(rpcb_session_t *, u_32_t);
68static void	ipf_p_rpcb_deref(rpcb_session_t *, rpcb_xact_t *);
69static int	ipf_p_rpcb_getproto(rpc_msg_t *, xdr_proto_t *,
70	u_32_t **);
71static int	ipf_p_rpcb_getnat(fr_info_t *, nat_t *, u_int, u_int);
72static int	ipf_p_rpcb_modv3(fr_info_t *, nat_t *, rpc_msg_t *,
73	mb_t *, u_int);
74static int	ipf_p_rpcb_modv4(fr_info_t *, nat_t *, rpc_msg_t *,
75	mb_t *, u_int);
76static void     ipf_p_rpcb_fixlen(fr_info_t *, int);
77
78/*
79 * Global variables
80 */
81static	frentry_t	rpcbfr;	/* Skeleton rule for reference by entities
82				   this proxy creates. */
83static	VNET_DEFINE(int,	rpcbcnt);
84#define	V_rpcbcnt		VNET(rpcbcnt)
85				/* Upper bound of allocated RPCB sessions. */
86				/* XXX rpcbcnt still requires locking. */
87
88static	int	rpcb_proxy_init = 0;
89
90
91/*
92 * Since rpc_msg contains only pointers, one should use this macro as a
93 * handy way to get to the goods.  (In case you're wondering about the name,
94 * this started as BYTEREF -> BREF -> B.)
95 */
96#define	B(r)	(u_32_t)ntohl(*(r))
97
98/*
99 * Public subroutines
100 */
101
102/* -------------------------------------------------------------------- */
103/* Function:    ipf_p_rpcb_main_load                                    */
104/* Returns:     void                                                    */
105/* Parameters:  (void)                                                  */
106/*                                                                      */
107/* Initialize the filter rule entry and session limiter.                */
108/* -------------------------------------------------------------------- */
109void
110ipf_p_rpcb_main_load()
111{
112	V_rpcbcnt = 0;
113
114	bzero((char *)&rpcbfr, sizeof(rpcbfr));
115	rpcbfr.fr_ref = 1;
116	rpcbfr.fr_flags = FR_PASS|FR_QUICK|FR_KEEPSTATE;
117	MUTEX_INIT(&rpcbfr.fr_lock, "ipf Sun RPCB proxy rule lock");
118	rpcb_proxy_init = 1;
119}
120
121/* -------------------------------------------------------------------- */
122/* Function:    ipf_p_rpcb_main_unload                                  */
123/* Returns:     void                                                    */
124/* Parameters:  (void)                                                  */
125/*                                                                      */
126/* Destroy rpcbfr's mutex to avoid a lock leak.                         */
127/* -------------------------------------------------------------------- */
128void
129ipf_p_rpcb_main_unload()
130{
131	if (rpcb_proxy_init == 1) {
132		MUTEX_DESTROY(&rpcbfr.fr_lock);
133		rpcb_proxy_init = 0;
134	}
135}
136
137/* --------------------------------------------------------------------	*/
138/* Function:	ipf_p_rpcb_new						*/
139/* Returns:	int - -1 == failure, 0 == success			*/
140/* Parameters:	fin(I)	- pointer to packet information			*/
141/*		aps(I)	- pointer to proxy session structure		*/
142/*		nat(I)	- pointer to NAT session structure		*/
143/*									*/
144/* Allocate resources for per-session proxy structures.			*/
145/* --------------------------------------------------------------------	*/
146int
147ipf_p_rpcb_new(arg, fin, aps, nat)
148	void *arg;
149	fr_info_t *fin;
150	ap_session_t *aps;
151	nat_t *nat;
152{
153	rpcb_session_t *rs;
154
155	nat = nat;	/* LINT */
156
157	if (fin->fin_v != 4)
158		return -1;
159
160	KMALLOC(rs, rpcb_session_t *);
161	if (rs == NULL)
162		return(-1);
163
164	bzero((char *)rs, sizeof(*rs));
165	MUTEX_INIT(&rs->rs_rxlock, "ipf Sun RPCB proxy session lock");
166
167	aps->aps_data = rs;
168
169	return(0);
170}
171
172/* --------------------------------------------------------------------	*/
173/* Function:	ipf_p_rpcb_del						*/
174/* Returns:	void							*/
175/* Parameters:	aps(I)	- pointer to proxy session structure		*/
176/*									*/
177/* Free up a session's list of RPCB requests.				*/
178/* --------------------------------------------------------------------	*/
179void
180ipf_p_rpcb_del(softc, aps)
181	ipf_main_softc_t *softc;
182	ap_session_t *aps;
183{
184	rpcb_session_t *rs;
185	rs = (rpcb_session_t *)aps->aps_data;
186
187	MUTEX_ENTER(&rs->rs_rxlock);
188	ipf_p_rpcb_flush(rs);
189	MUTEX_EXIT(&rs->rs_rxlock);
190	MUTEX_DESTROY(&rs->rs_rxlock);
191}
192
193/* --------------------------------------------------------------------	*/
194/* Function:	ipf_p_rpcb_in						*/
195/* Returns:	int - APR_ERR(1) == drop the packet, 			*/
196/*		      APR_ERR(2) == kill the proxy session,		*/
197/*		      else change in packet length (in bytes)		*/
198/* Parameters:	fin(I)	- pointer to packet information			*/
199/*		ip(I)	- pointer to packet header			*/
200/*		aps(I)	- pointer to proxy session structure		*/
201/*		nat(I)	- pointer to NAT session structure		*/
202/*									*/
203/* Given a presumed RPCB request, perform some minor tests and pass off */
204/* for decoding.  Also pass packet off for a rewrite if necessary.	*/
205/* --------------------------------------------------------------------	*/
206int
207ipf_p_rpcb_in(arg, fin, aps, nat)
208	void *arg;
209	fr_info_t *fin;
210	ap_session_t *aps;
211	nat_t *nat;
212{
213	rpc_msg_t rpcmsg, *rm;
214	rpcb_session_t *rs;
215	u_int off, dlen;
216	mb_t *m;
217	int rv;
218
219	/* Disallow fragmented or illegally short packets. */
220	if ((fin->fin_flx & (FI_FRAG|FI_SHORT)) != 0)
221		return(APR_ERR(1));
222
223	/* Perform basic variable initialization. */
224	rs = (rpcb_session_t *)aps->aps_data;
225
226	m = fin->fin_m;
227	off = (char *)fin->fin_dp - (char *)fin->fin_ip;
228	off += sizeof(udphdr_t) + fin->fin_ipoff;
229	dlen = fin->fin_dlen - sizeof(udphdr_t);
230
231	/* Disallow packets outside legal range for supported requests. */
232	if ((dlen < RPCB_REQMIN) || (dlen > RPCB_REQMAX))
233		return(APR_ERR(1));
234
235	/* Copy packet over to convenience buffer. */
236	rm = &rpcmsg;
237	bzero((char *)rm, sizeof(*rm));
238	COPYDATA(m, off, dlen, (caddr_t)&rm->rm_msgbuf);
239	rm->rm_buflen = dlen;
240
241	/* Send off to decode request. */
242	rv = ipf_p_rpcb_decodereq(fin, nat, rs, rm);
243
244	switch(rv)
245	{
246	case -1:
247		return(APR_ERR(1));
248		/*NOTREACHED*/
249		break;
250	case 0:
251		break;
252	case 1:
253		rv = ipf_p_rpcb_modreq(fin, nat, rm, m, off);
254		break;
255	default:
256		/*CONSTANTCONDITION*/
257		IPF_PANIC(1, ("illegal rv %d (ipf_p_rpcb_req)", rv));
258	}
259
260	return(rv);
261}
262
263/* --------------------------------------------------------------------	*/
264/* Function:	ipf_p_rpcb_out						*/
265/* Returns:	int - APR_ERR(1) == drop the packet, 			*/
266/*		      APR_ERR(2) == kill the proxy session,		*/
267/*		      else change in packet length (in bytes)		*/
268/* Parameters:	fin(I)	- pointer to packet information			*/
269/*		ip(I)	- pointer to packet header			*/
270/*		aps(I)	- pointer to proxy session structure		*/
271/*		nat(I)	- pointer to NAT session structure		*/
272/*									*/
273/* Given a presumed RPCB reply, perform some minor tests and pass off	*/
274/* for decoding.  If the message indicates a successful request with	*/
275/* valid addressing information, create NAT and state structures to	*/
276/* allow direct communication between RPC client and server.		*/
277/* --------------------------------------------------------------------	*/
278int
279ipf_p_rpcb_out(arg, fin, aps, nat)
280	void *arg;
281	fr_info_t *fin;
282	ap_session_t *aps;
283	nat_t *nat;
284{
285	rpc_msg_t rpcmsg, *rm;
286	rpcb_session_t *rs;
287	rpcb_xact_t *rx;
288	u_int off, dlen;
289	int rv, diff;
290	mb_t *m;
291
292	/* Disallow fragmented or illegally short packets. */
293	if ((fin->fin_flx & (FI_FRAG|FI_SHORT)) != 0)
294		return(APR_ERR(1));
295
296	/* Perform basic variable initialization. */
297	rs = (rpcb_session_t *)aps->aps_data;
298	rx = NULL;
299
300	m = fin->fin_m;
301	off = (char *)fin->fin_dp - (char *)fin->fin_ip;
302	off += sizeof(udphdr_t) + fin->fin_ipoff;
303	dlen = fin->fin_dlen - sizeof(udphdr_t);
304	diff = 0;
305
306	/* Disallow packets outside legal range for supported requests. */
307	if ((dlen < RPCB_REPMIN) || (dlen > RPCB_REPMAX))
308		return(APR_ERR(1));
309
310	/* Copy packet over to convenience buffer. */
311	rm = &rpcmsg;
312	bzero((char *)rm, sizeof(*rm));
313	COPYDATA(m, off, dlen, (caddr_t)&rm->rm_msgbuf);
314	rm->rm_buflen = dlen;
315
316	rx = NULL;		/* XXX gcc */
317
318	/* Send off to decode reply. */
319	rv = ipf_p_rpcb_decoderep(fin, nat, rs, rm, &rx);
320
321	switch(rv)
322	{
323	case -1: /* Bad packet */
324                if (rx != NULL) {
325                        MUTEX_ENTER(&rs->rs_rxlock);
326                        ipf_p_rpcb_deref(rs, rx);
327                        MUTEX_EXIT(&rs->rs_rxlock);
328                }
329		return(APR_ERR(1));
330		/*NOTREACHED*/
331		break;
332	case  0: /* Negative reply / request rejected */
333		break;
334	case  1: /* Positive reply */
335		/*
336		 * With the IP address embedded in a GETADDR(LIST) reply,
337		 * we'll need to rewrite the packet in the very possible
338		 * event that the internal & external addresses aren't the
339		 * same.  (i.e., this box is either a router or rpcbind
340		 * only listens on loopback.)
341		 */
342		if (nat->nat_odstaddr != nat->nat_ndstaddr) {
343			if (rx->rx_type == RPCB_RES_STRING)
344				diff = ipf_p_rpcb_modv3(fin, nat, rm, m, off);
345			else if (rx->rx_type == RPCB_RES_LIST)
346				diff = ipf_p_rpcb_modv4(fin, nat, rm, m, off);
347		}
348		break;
349	default:
350		/*CONSTANTCONDITION*/
351		IPF_PANIC(1, ("illegal rv %d (ipf_p_rpcb_decoderep)", rv));
352	}
353
354	if (rx != NULL) {
355                MUTEX_ENTER(&rs->rs_rxlock);
356                /* XXX Gross hack - I'm overloading the reference
357                 * counter to deal with both threads and retransmitted
358                 * requests.  One deref signals that this thread is
359                 * finished with rx, and the other signals that we've
360                 * processed its reply.
361                 */
362                ipf_p_rpcb_deref(rs, rx);
363                ipf_p_rpcb_deref(rs, rx);
364                MUTEX_EXIT(&rs->rs_rxlock);
365	}
366
367	return(diff);
368}
369
370/*
371 * Private support subroutines
372 */
373
374/* --------------------------------------------------------------------	*/
375/* Function:	ipf_p_rpcb_flush						*/
376/* Returns:	void							*/
377/* Parameters:	rs(I)	- pointer to RPCB session structure		*/
378/*									*/
379/* Simply flushes the list of outstanding transactions, if any.		*/
380/* --------------------------------------------------------------------	*/
381static void
382ipf_p_rpcb_flush(rs)
383	rpcb_session_t *rs;
384{
385	rpcb_xact_t *r1, *r2;
386
387	r1 = rs->rs_rxlist;
388	if (r1 == NULL)
389		return;
390
391	while (r1 != NULL) {
392		r2 = r1;
393		r1 = r1->rx_next;
394		KFREE(r2);
395	}
396}
397
398/* --------------------------------------------------------------------	*/
399/* Function:	ipf_p_rpcb_decodereq					*/
400/* Returns:	int - -1 == bad request or critical failure,		*/
401/*		       0 == request successfully decoded,		*/
402/*		       1 == request successfully decoded; requires	*/
403/*			    address rewrite/modification		*/
404/* Parameters:	fin(I)	- pointer to packet information			*/
405/*		nat(I)	- pointer to NAT session structure		*/
406/*		rs(I)	- pointer to RPCB session structure		*/
407/*		rm(I)	- pointer to RPC message structure		*/
408/*									*/
409/* Take a presumed RPCB request, decode it, and store the results in	*/
410/* the transaction list.  If the internal target address needs to be	*/
411/* modified, store its location in ptr.					*/
412/* WARNING:  It's the responsibility of the caller to make sure there	*/
413/* is enough room in rs_buf for the basic RPC message "preamble".	*/
414/* --------------------------------------------------------------------	*/
415static int
416ipf_p_rpcb_decodereq(fin, nat, rs, rm)
417	fr_info_t *fin;
418	nat_t *nat;
419	rpcb_session_t *rs;
420	rpc_msg_t *rm;
421{
422	rpcb_args_t *ra;
423	u_32_t xdr, *p;
424	rpc_call_t *rc;
425	rpcb_xact_t rx;
426	int mod;
427
428	p = (u_32_t *)rm->rm_msgbuf;
429	mod = 0;
430
431	bzero((char *)&rx, sizeof(rx));
432	rc = &rm->rm_call;
433
434	rm->rm_xid = p;
435	rx.rx_xid = B(p++);	/* Record this message's XID. */
436
437	/* Parse out and test the RPC header. */
438	if ((B(p++) != RPCB_CALL) ||
439	    (B(p++) != RPCB_MSG_VERSION) ||
440	    (B(p++) != RPCB_PROG))
441		return(-1);
442
443	/* Record the RPCB version and procedure. */
444	rc->rc_vers = p++;
445	rc->rc_proc = p++;
446
447	/* Bypass RPC authentication stuff. */
448	if (ipf_p_rpcb_skipauth(rm, &rc->rc_authcred, &p) != 0)
449		return(-1);
450	if (ipf_p_rpcb_skipauth(rm, &rc->rc_authverf, &p) != 0)
451		return(-1);
452
453	/* Compare RPCB version and procedure numbers. */
454	switch(B(rc->rc_vers))
455	{
456	case 2:
457		/* This proxy only supports PMAP_GETPORT. */
458		if (B(rc->rc_proc) != RPCB_GETPORT)
459			return(-1);
460
461		/* Portmap requests contain four 4 byte parameters. */
462		if (RPCB_BUF_EQ(rm, p, 16) == 0)
463			return(-1);
464
465		p += 2; /* Skip requested program and version numbers. */
466
467		/* Sanity check the requested protocol. */
468		xdr = B(p);
469		if (!(xdr == IPPROTO_UDP || xdr == IPPROTO_TCP))
470			return(-1);
471
472		rx.rx_type = RPCB_RES_PMAP;
473		rx.rx_proto = xdr;
474		break;
475	case 3:
476	case 4:
477		/* GETADDRLIST is exclusive to v4; GETADDR for v3 & v4 */
478		switch(B(rc->rc_proc))
479		{
480		case RPCB_GETADDR:
481			rx.rx_type = RPCB_RES_STRING;
482			rx.rx_proto = (u_int)fin->fin_p;
483			break;
484		case RPCB_GETADDRLIST:
485			if (B(rc->rc_vers) != 4)
486				return(-1);
487			rx.rx_type = RPCB_RES_LIST;
488			break;
489		default:
490			return(-1);
491		}
492
493		ra = &rc->rc_rpcbargs;
494
495		/* Decode the 'struct rpcb' request. */
496		if (ipf_p_rpcb_xdrrpcb(rm, p, ra) != 0)
497			return(-1);
498
499		/* Are the target address & port valid? */
500		if ((ra->ra_maddr.xu_ip != nat->nat_ndstaddr) ||
501		    (ra->ra_maddr.xu_port != nat->nat_ndport))
502		    	return(-1);
503
504		/* Do we need to rewrite this packet? */
505		if ((nat->nat_ndstaddr != nat->nat_odstaddr) ||
506		    (nat->nat_ndport != nat->nat_odport))
507		    	mod = 1;
508		break;
509	default:
510		return(-1);
511	}
512
513        MUTEX_ENTER(&rs->rs_rxlock);
514	if (ipf_p_rpcb_insert(rs, &rx) != 0) {
515                MUTEX_EXIT(&rs->rs_rxlock);
516		return(-1);
517	}
518        MUTEX_EXIT(&rs->rs_rxlock);
519
520	return(mod);
521}
522
523/* --------------------------------------------------------------------	*/
524/* Function:	ipf_p_rpcb_skipauth					*/
525/* Returns:	int -- -1 == illegal auth parameters (lengths)		*/
526/*			0 == valid parameters, pointer advanced		*/
527/* Parameters:	rm(I)	- pointer to RPC message structure		*/
528/*		auth(I)	- pointer to RPC auth structure			*/
529/*		buf(IO)	- pointer to location within convenience buffer	*/
530/*									*/
531/* Record auth data length & location of auth data, then advance past	*/
532/* it.									*/
533/* --------------------------------------------------------------------	*/
534static int
535ipf_p_rpcb_skipauth(rm, auth, buf)
536	rpc_msg_t *rm;
537	xdr_auth_t *auth;
538	u_32_t **buf;
539{
540	u_32_t *p, xdr;
541
542	p = *buf;
543
544	/* Make sure we have enough space for expected fixed auth parms. */
545	if (RPCB_BUF_GEQ(rm, p, 8) == 0)
546		return(-1);
547
548	p++; /* We don't care about auth_flavor. */
549
550	auth->xa_string.xs_len = p;
551	xdr = B(p++);		/* Length of auth_data */
552
553	/* Test for absurdity / illegality of auth_data length. */
554	if ((XDRALIGN(xdr) < xdr) || (RPCB_BUF_GEQ(rm, p, XDRALIGN(xdr)) == 0))
555		return(-1);
556
557	auth->xa_string.xs_str = (char *)p;
558
559	p += XDRALIGN(xdr);	/* Advance our location. */
560
561	*buf = (u_32_t *)p;
562
563	return(0);
564}
565
566/* --------------------------------------------------------------------	*/
567/* Function:	ipf_p_rpcb_insert					*/
568/* Returns:	int -- -1 == list insertion failed,			*/
569/*			0 == item successfully added			*/
570/* Parameters:	rs(I)	- pointer to RPCB session structure		*/
571/*		rx(I)	- pointer to RPCB transaction structure		*/
572/* --------------------------------------------------------------------	*/
573static int
574ipf_p_rpcb_insert(rs, rx)
575	rpcb_session_t *rs;
576	rpcb_xact_t *rx;
577{
578	rpcb_xact_t *rxp;
579
580	rxp = ipf_p_rpcb_lookup(rs, rx->rx_xid);
581	if (rxp != NULL) {
582                ++rxp->rx_ref;
583		return(0);
584        }
585
586	if (V_rpcbcnt == RPCB_MAXREQS)
587		return(-1);
588
589	KMALLOC(rxp, rpcb_xact_t *);
590	if (rxp == NULL)
591		return(-1);
592
593	bcopy((char *)rx, (char *)rxp, sizeof(*rx));
594
595	if (rs->rs_rxlist != NULL)
596		rs->rs_rxlist->rx_pnext = &rxp->rx_next;
597
598	rxp->rx_pnext = &rs->rs_rxlist;
599	rxp->rx_next = rs->rs_rxlist;
600	rs->rs_rxlist = rxp;
601
602	rxp->rx_ref = 1;
603
604	++V_rpcbcnt;
605
606	return(0);
607}
608
609/* --------------------------------------------------------------------	*/
610/* Function:	ipf_p_rpcb_xdrrpcb					*/
611/* Returns:	int -- -1 == failure to properly decode the request	*/
612/*			0 == rpcb successfully decoded			*/
613/* Parameters:	rs(I)	- pointer to RPCB session structure		*/
614/*		p(I)	- pointer to location within session buffer	*/
615/*		rpcb(O)	- pointer to rpcb (xdr type) structure		*/
616/*									*/
617/* Decode a XDR encoded rpcb structure and record its contents in rpcb  */
618/* within only the context of TCP/UDP over IP networks.			*/
619/* --------------------------------------------------------------------	*/
620static int
621ipf_p_rpcb_xdrrpcb(rm, p, ra)
622	rpc_msg_t *rm;
623	u_32_t *p;
624	rpcb_args_t *ra;
625{
626	if (!RPCB_BUF_GEQ(rm, p, 20))
627		return(-1);
628
629	/* Bypass target program & version. */
630	p += 2;
631
632	/* Decode r_netid.  Must be "tcp" or "udp". */
633	if (ipf_p_rpcb_getproto(rm, &ra->ra_netid, &p) != 0)
634		return(-1);
635
636	/* Decode r_maddr. */
637	if (ipf_p_rpcb_getuaddr(rm, &ra->ra_maddr, &p) != 0)
638		return(-1);
639
640	/* Advance to r_owner and make sure it's empty. */
641	if (!RPCB_BUF_EQ(rm, p, 4) || (B(p) != 0))
642		return(-1);
643
644	return(0);
645}
646
647/* --------------------------------------------------------------------	*/
648/* Function:	ipf_p_rpcb_getuaddr					*/
649/* Returns:	int -- -1 == illegal string,				*/
650/*			0 == string parsed; contents recorded		*/
651/* Parameters:	rm(I)	- pointer to RPC message structure		*/
652/*		xu(I)	- pointer to universal address structure	*/
653/*		p(IO)	- pointer to location within message buffer	*/
654/*									*/
655/* Decode the IP address / port at p and record them in xu.		*/
656/* --------------------------------------------------------------------	*/
657static int
658ipf_p_rpcb_getuaddr(rm, xu, p)
659	rpc_msg_t *rm;
660	xdr_uaddr_t *xu;
661	u_32_t **p;
662{
663	char *c, *i, *b, *pp;
664	u_int d, dd, l, t;
665	char uastr[24];
666
667	/* Test for string length. */
668	if (!RPCB_BUF_GEQ(rm, *p, 4))
669		return(-1);
670
671	xu->xu_xslen = (*p)++;
672	xu->xu_xsstr = (char *)*p;
673
674	/* Length check */
675	l = B(xu->xu_xslen);
676	if (l < 11 || l > 23 || !RPCB_BUF_GEQ(rm, *p, XDRALIGN(l)))
677		return(-1);
678
679	/* Advance p */
680	*(char **)p += XDRALIGN(l);
681
682	/* Copy string to local buffer & terminate C style */
683	bcopy(xu->xu_xsstr, uastr, l);
684	uastr[l] = '\0';
685
686	i = (char *)&xu->xu_ip;
687	pp = (char *)&xu->xu_port;
688
689	/*
690	 * Expected format: a.b.c.d.e.f where [a-d] correspond to bytes of
691	 * an IP address and [ef] are the bytes of a L4 port.
692	 */
693	if (!(ISDIGIT(uastr[0]) && ISDIGIT(uastr[l-1])))
694		return(-1);
695	b = uastr;
696	for (c = &uastr[1], d = 0, dd = 0; c < &uastr[l-1]; c++) {
697		if (ISDIGIT(*c)) {
698			dd = 0;
699			continue;
700		}
701		if (*c == '.') {
702			if (dd != 0)
703				return(-1);
704
705			/* Check for ASCII byte. */
706			*c = '\0';
707			t = ipf_p_rpcb_atoi(b);
708			if (t > 255)
709				return(-1);
710
711			/* Aim b at beginning of the next byte. */
712			b = c + 1;
713
714			/* Switch off IP addr vs port parsing. */
715			if (d < 4)
716				i[d++] = t & 0xff;
717			else
718				pp[d++ - 4] = t & 0xff;
719
720			dd = 1;
721			continue;
722		}
723		return(-1);
724	}
725	if (d != 5) /* String must contain exactly 5 periods. */
726		return(-1);
727
728	/* Handle the last byte (port low byte) */
729	t = ipf_p_rpcb_atoi(b);
730	if (t > 255)
731		return(-1);
732	pp[d - 4] = t & 0xff;
733
734	return(0);
735}
736
737/* --------------------------------------------------------------------	*/
738/* Function:	ipf_p_rpcb_atoi (XXX should be generic for all proxies)	*/
739/* Returns:	int -- integer representation of supplied string	*/
740/* Parameters:	ptr(I)	- input string					*/
741/*									*/
742/* Simple version of atoi(3) ripped from ip_rcmd_pxy.c.			*/
743/* --------------------------------------------------------------------	*/
744static u_int
745ipf_p_rpcb_atoi(ptr)
746	char *ptr;
747{
748	register char *s = ptr, c;
749	register u_int i = 0;
750
751	while (((c = *s++) != '\0') && ISDIGIT(c)) {
752		i *= 10;
753		i += c - '0';
754	}
755	return i;
756}
757
758/* --------------------------------------------------------------------	*/
759/* Function:	ipf_p_rpcb_modreq					*/
760/* Returns:	int -- change in datagram length			*/
761/*			APR_ERR(2) - critical failure			*/
762/* Parameters:	fin(I)	- pointer to packet information			*/
763/*		nat(I)	- pointer to NAT session			*/
764/*		rm(I)	- pointer to RPC message structure		*/
765/*		m(I)	- pointer to mbuf chain				*/
766/*		off(I)	- current offset within mbuf chain		*/
767/*									*/
768/* When external and internal addresses differ, we rewrite the former	*/
769/* with the latter.  (This is exclusive to protocol versions 3 & 4).	*/
770/* --------------------------------------------------------------------	*/
771static int
772ipf_p_rpcb_modreq(fin, nat, rm, m, off)
773	fr_info_t *fin;
774	nat_t *nat;
775	rpc_msg_t *rm;
776	mb_t *m;
777	u_int off;
778{
779	u_int len, xlen, pos, bogo;
780	rpcb_args_t *ra;
781	char uaddr[24];
782	udphdr_t *udp;
783	char *i, *p;
784	int diff;
785
786	ra = &rm->rm_call.rc_rpcbargs;
787	i = (char *)&nat->nat_odstaddr;
788	p = (char *)&nat->nat_odport;
789
790	/* Form new string. */
791	bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */
792#if defined(SNPRINTF) && defined(_KERNEL)
793	SNPRINTF(uaddr, sizeof(uaddr),
794#else
795	(void) sprintf(uaddr,
796#endif
797		       "%u.%u.%u.%u.%u.%u", i[0] & 0xff, i[1] & 0xff,
798		       i[2] & 0xff, i[3] & 0xff, p[0] & 0xff, p[1] & 0xff);
799	len = strlen(uaddr);
800	xlen = XDRALIGN(len);
801
802	/* Determine mbuf offset to start writing to. */
803	pos = (char *)ra->ra_maddr.xu_xslen - rm->rm_msgbuf;
804	off += pos;
805
806	/* Write new string length. */
807	bogo = htonl(len);
808	COPYBACK(m, off, 4, (caddr_t)&bogo);
809	off += 4;
810
811	/* Write new string. */
812	COPYBACK(m, off, xlen, uaddr);
813	off += xlen;
814
815	/* Write in zero r_owner. */
816	bogo = 0;
817	COPYBACK(m, off, 4, (caddr_t)&bogo);
818
819	/* Determine difference in data lengths. */
820	diff = xlen - XDRALIGN(B(ra->ra_maddr.xu_xslen));
821
822	/*
823	 * If our new string has a different length, make necessary
824	 * adjustments.
825	 */
826	if (diff != 0) {
827		udp = fin->fin_dp;
828		udp->uh_ulen = htons(ntohs(udp->uh_ulen) + diff);
829		fin->fin_plen += diff;
830		fin->fin_ip->ip_len = htons(fin->fin_plen);
831		fin->fin_dlen += diff;
832		/* XXX Storage lengths. */
833	}
834
835	return(diff);
836}
837
838/* --------------------------------------------------------------------	*/
839/* Function:	ipf_p_rpcb_decoderep					*/
840/* Returns:	int - -1 == bad request or critical failure,		*/
841/*		       0 == valid, negative reply			*/
842/*		       1 == vaddlid, positive reply; needs no changes	*/
843/* Parameters:	fin(I)	- pointer to packet information			*/
844/*		nat(I)	- pointer to NAT session structure		*/
845/*		rs(I)	- pointer to RPCB session structure		*/
846/*		rm(I)	- pointer to RPC message structure		*/
847/*		rxp(O)	- pointer to RPCB transaction structure		*/
848/*									*/
849/* Take a presumed RPCB reply, extract the XID, search for the original */
850/* request information, and determine whether the request was accepted	*/
851/* or rejected.  With a valid accepted reply, go ahead and create NAT	*/
852/* and state entries, and finish up by rewriting the packet as 		*/
853/* required.								*/
854/*									*/
855/* WARNING:  It's the responsibility of the caller to make sure there	*/
856/* is enough room in rs_buf for the basic RPC message "preamble".	*/
857/* --------------------------------------------------------------------	*/
858static int
859ipf_p_rpcb_decoderep(fin, nat, rs, rm, rxp)
860	fr_info_t *fin;
861	nat_t *nat;
862	rpcb_session_t *rs;
863	rpc_msg_t *rm;
864	rpcb_xact_t **rxp;
865{
866	rpcb_listp_t *rl;
867	rpcb_entry_t *re;
868	rpcb_xact_t *rx;
869	u_32_t xdr, *p;
870	rpc_resp_t *rr;
871	int rv, cnt;
872
873	p = (u_32_t *)rm->rm_msgbuf;
874
875	bzero((char *)&rx, sizeof(rx));
876	rr = &rm->rm_resp;
877
878	rm->rm_xid = p;
879	xdr = B(p++);		/* Record this message's XID. */
880
881	/* Lookup XID */
882        MUTEX_ENTER(&rs->rs_rxlock);
883	if ((rx = ipf_p_rpcb_lookup(rs, xdr)) == NULL) {
884                MUTEX_EXIT(&rs->rs_rxlock);
885		return(-1);
886        }
887        ++rx->rx_ref;        /* per thread reference */
888        MUTEX_EXIT(&rs->rs_rxlock);
889
890	*rxp = rx;
891
892	/* Test call vs reply */
893	if (B(p++) != RPCB_REPLY)
894		return(-1);
895
896	/* Test reply_stat */
897	switch(B(p++))
898	{
899	case RPCB_MSG_DENIED:
900		return(0);
901	case RPCB_MSG_ACCEPTED:
902		break;
903	default:
904		return(-1);
905	}
906
907	/* Bypass RPC authentication stuff. */
908	if (ipf_p_rpcb_skipauth(rm, &rr->rr_authverf, &p) != 0)
909		return(-1);
910
911	/* Test accept status */
912	if (!RPCB_BUF_GEQ(rm, p, 4))
913		return(-1);
914	if (B(p++) != 0)
915		return(0);
916
917	/* Parse out the expected reply */
918	switch(rx->rx_type)
919	{
920	case RPCB_RES_PMAP:
921		/* There must be only one 4 byte argument. */
922		if (!RPCB_BUF_EQ(rm, p, 4))
923			return(-1);
924
925		rr->rr_v2 = p;
926		xdr = B(rr->rr_v2);
927
928		/* Reply w/ a 0 port indicates service isn't registered */
929		if (xdr == 0)
930			return(0);
931
932		/* Is the value sane? */
933		if (xdr > 65535)
934			return(-1);
935
936		/* Create NAT & state table entries. */
937		if (ipf_p_rpcb_getnat(fin, nat, rx->rx_proto, (u_int)xdr) != 0)
938			return(-1);
939		break;
940	case RPCB_RES_STRING:
941		/* Expecting a XDR string; need 4 bytes for length */
942		if (!RPCB_BUF_GEQ(rm, p, 4))
943			return(-1);
944
945		rr->rr_v3.xu_str.xs_len = p++;
946		rr->rr_v3.xu_str.xs_str = (char *)p;
947
948		xdr = B(rr->rr_v3.xu_xslen);
949
950		/* A null string indicates an unregistered service */
951		if ((xdr == 0) && RPCB_BUF_EQ(rm, p, 0))
952			return(0);
953
954		/* Decode the target IP address / port. */
955		if (ipf_p_rpcb_getuaddr(rm, &rr->rr_v3, &p) != 0)
956			return(-1);
957
958		/* Validate the IP address and port contained. */
959		if (nat->nat_odstaddr != rr->rr_v3.xu_ip)
960			return(-1);
961
962		/* Create NAT & state table entries. */
963		if (ipf_p_rpcb_getnat(fin, nat, rx->rx_proto,
964				     (u_int)rr->rr_v3.xu_port) != 0)
965			return(-1);
966		break;
967	case RPCB_RES_LIST:
968		if (!RPCB_BUF_GEQ(rm, p, 4))
969			return(-1);
970		/* rpcb_entry_list_ptr */
971		switch(B(p))
972		{
973		case 0:
974			return(0);
975			/*NOTREACHED*/
976			break;
977		case 1:
978			break;
979		default:
980			return(-1);
981		}
982		rl = &rr->rr_v4;
983		rl->rl_list = p++;
984		cnt = 0;
985
986		for(;;) {
987			re = &rl->rl_entries[rl->rl_cnt];
988			if (ipf_p_rpcb_getuaddr(rm, &re->re_maddr, &p) != 0)
989				return(-1);
990			if (ipf_p_rpcb_getproto(rm, &re->re_netid, &p) != 0)
991				return(-1);
992			/* re_semantics & re_pfamily length */
993			if (!RPCB_BUF_GEQ(rm, p, 12))
994				return(-1);
995			p++; /* Skipping re_semantics. */
996			xdr = B(p++);
997			if ((xdr != 4) || strncmp((char *)p, "inet", 4))
998				return(-1);
999			p++;
1000			if (ipf_p_rpcb_getproto(rm, &re->re_proto, &p) != 0)
1001				return(-1);
1002			if (!RPCB_BUF_GEQ(rm, p, 4))
1003				return(-1);
1004			re->re_more = p;
1005			if (B(re->re_more) > 1) /* 0,1 only legal values */
1006				return(-1);
1007			++rl->rl_cnt;
1008			++cnt;
1009			if (B(re->re_more) == 0)
1010				break;
1011			/* Replies in  max out at 2; TCP and/or UDP */
1012			if (cnt > 2)
1013				return(-1);
1014			p++;
1015		}
1016
1017		for(rl->rl_cnt = 0; rl->rl_cnt < cnt; rl->rl_cnt++) {
1018			re = &rl->rl_entries[rl->rl_cnt];
1019			rv = ipf_p_rpcb_getnat(fin, nat,
1020			                      re->re_proto.xp_proto,
1021				              (u_int)re->re_maddr.xu_port);
1022			if (rv != 0)
1023				return(-1);
1024		}
1025		break;
1026	default:
1027		/*CONSTANTCONDITION*/
1028		IPF_PANIC(1, ("illegal rx_type %d", rx->rx_type));
1029	}
1030
1031	return(1);
1032}
1033
1034/* --------------------------------------------------------------------	*/
1035/* Function:	ipf_p_rpcb_lookup					*/
1036/* Returns:	rpcb_xact_t * 	- NULL == no matching record,		*/
1037/*				  else pointer to relevant entry	*/
1038/* Parameters:	rs(I)	- pointer to RPCB session			*/
1039/*		xid(I)	- XID to look for				*/
1040/* --------------------------------------------------------------------	*/
1041static rpcb_xact_t *
1042ipf_p_rpcb_lookup(rs, xid)
1043	rpcb_session_t *rs;
1044	u_32_t xid;
1045{
1046	rpcb_xact_t *rx;
1047
1048	if (rs->rs_rxlist == NULL)
1049		return(NULL);
1050
1051	for (rx = rs->rs_rxlist; rx != NULL; rx = rx->rx_next)
1052		if (rx->rx_xid == xid)
1053			break;
1054
1055	return(rx);
1056}
1057
1058/* --------------------------------------------------------------------	*/
1059/* Function:	ipf_p_rpcb_deref					        */
1060/* Returns:	(void)							*/
1061/* Parameters:	rs(I)	- pointer to RPCB session			*/
1062/*		rx(I)	- pointer to RPC transaction struct to remove	*/
1063/*              force(I) - indicates to delete entry regardless of      */
1064/*                         reference count                              */
1065/* Locking:	rs->rs_rxlock must be held write only			*/
1066/*									*/
1067/* Free the RPCB transaction record rx from the chain of entries.	*/
1068/* --------------------------------------------------------------------	*/
1069static void
1070ipf_p_rpcb_deref(rs, rx)
1071	rpcb_session_t *rs;
1072	rpcb_xact_t *rx;
1073{
1074	rs = rs;	/* LINT */
1075
1076	if (rx == NULL)
1077		return;
1078
1079	if (--rx->rx_ref != 0)
1080		return;
1081
1082	if (rx->rx_next != NULL)
1083		rx->rx_next->rx_pnext = rx->rx_pnext;
1084
1085	*rx->rx_pnext = rx->rx_next;
1086
1087	KFREE(rx);
1088
1089	--V_rpcbcnt;
1090}
1091
1092/* --------------------------------------------------------------------	*/
1093/* Function:	ipf_p_rpcb_getproto					*/
1094/* Returns:	int - -1 == illegal protocol/netid,			*/
1095/*		       0 == legal protocol/netid			*/
1096/* Parameters:	rm(I)	- pointer to RPC message structure		*/
1097/*		xp(I)	- pointer to netid structure			*/
1098/*		p(IO)	- pointer to location within packet buffer	*/
1099/* 									*/
1100/* Decode netid/proto stored at p and record its numeric value.	 	*/
1101/* --------------------------------------------------------------------	*/
1102static int
1103ipf_p_rpcb_getproto(rm, xp, p)
1104	rpc_msg_t *rm;
1105	xdr_proto_t *xp;
1106	u_32_t **p;
1107{
1108	u_int len;
1109
1110	/* Must have 4 bytes for length & 4 bytes for "tcp" or "udp". */
1111	if (!RPCB_BUF_GEQ(rm, p, 8))
1112		return(-1);
1113
1114	xp->xp_xslen = (*p)++;
1115	xp->xp_xsstr = (char *)*p;
1116
1117	/* Test the string length. */
1118	len = B(xp->xp_xslen);
1119	if (len != 3)
1120	 	return(-1);
1121
1122	/* Test the actual string & record the protocol accordingly. */
1123	if (!strncmp((char *)xp->xp_xsstr, "tcp\0", 4))
1124		xp->xp_proto = IPPROTO_TCP;
1125	else if (!strncmp((char *)xp->xp_xsstr, "udp\0", 4))
1126		xp->xp_proto = IPPROTO_UDP;
1127	else {
1128		return(-1);
1129	}
1130
1131	/* Advance past the string. */
1132	(*p)++;
1133
1134	return(0);
1135}
1136
1137/* --------------------------------------------------------------------	*/
1138/* Function:	ipf_p_rpcb_getnat					*/
1139/* Returns:	int -- -1 == failed to create table entries,		*/
1140/*			0 == success					*/
1141/* Parameters:	fin(I)	- pointer to packet information			*/
1142/*		nat(I)	- pointer to NAT table entry			*/
1143/*		proto(I) - transport protocol for new entries		*/
1144/*		port(I)	- new port to use w/ wildcard table entries	*/
1145/*									*/
1146/* Create state and NAT entries to handle an anticipated connection	*/
1147/* attempt between RPC client and server.				*/
1148/* --------------------------------------------------------------------	*/
1149static int
1150ipf_p_rpcb_getnat(fin, nat, proto, port)
1151	fr_info_t *fin;
1152	nat_t *nat;
1153	u_int proto;
1154	u_int port;
1155{
1156	ipf_main_softc_t *softc = fin->fin_main_soft;
1157	ipnat_t *ipn, ipnat;
1158	tcphdr_t tcp;
1159	ipstate_t *is;
1160	fr_info_t fi;
1161	nat_t *natl;
1162	int nflags;
1163
1164	ipn = nat->nat_ptr;
1165
1166	/* Generate dummy fr_info */
1167	bcopy((char *)fin, (char *)&fi, sizeof(fi));
1168	fi.fin_out = 0;
1169	fi.fin_p = proto;
1170	fi.fin_sport = 0;
1171	fi.fin_dport = port & 0xffff;
1172	fi.fin_flx |= FI_IGNORE;
1173	fi.fin_saddr = nat->nat_osrcaddr;
1174	fi.fin_daddr = nat->nat_odstaddr;
1175
1176	bzero((char *)&tcp, sizeof(tcp));
1177	tcp.th_dport = htons(port);
1178
1179	if (proto == IPPROTO_TCP) {
1180		tcp.th_win = htons(8192);
1181		TCP_OFF_A(&tcp, sizeof(tcphdr_t) >> 2);
1182		fi.fin_dlen = sizeof(tcphdr_t);
1183		tcp.th_flags = TH_SYN;
1184		nflags = NAT_TCP;
1185	} else {
1186		fi.fin_dlen = sizeof(udphdr_t);
1187		nflags = NAT_UDP;
1188	}
1189
1190	nflags |= SI_W_SPORT|NAT_SEARCH;
1191	fi.fin_dp = &tcp;
1192	fi.fin_plen = fi.fin_hlen + fi.fin_dlen;
1193
1194	/*
1195	 * Search for existing NAT & state entries.  Pay close attention to
1196	 * mutexes / locks grabbed from lookup routines, as not doing so could
1197	 * lead to bad things.
1198	 *
1199	 * If successful, fr_stlookup returns with ipf_state locked.  We have
1200	 * no use for this lock, so simply unlock it if necessary.
1201	 */
1202	is = ipf_state_lookup(&fi, &tcp, NULL);
1203	if (is != NULL) {
1204		RWLOCK_EXIT(&softc->ipf_state);
1205	}
1206
1207	RWLOCK_EXIT(&softc->ipf_nat);
1208
1209	WRITE_ENTER(&softc->ipf_nat);
1210	natl = ipf_nat_inlookup(&fi, nflags, proto, fi.fin_src, fi.fin_dst);
1211
1212	if ((natl != NULL) && (is != NULL)) {
1213		MUTEX_DOWNGRADE(&softc->ipf_nat);
1214		return(0);
1215	}
1216
1217	/* Slightly modify the following structures for actual use in creating
1218	 * NAT and/or state entries.  We're primarily concerned with stripping
1219	 * flags that may be detrimental to the creation process or simply
1220	 * shouldn't be associated with a table entry.
1221	 */
1222	fi.fin_fr = &rpcbfr;
1223	fi.fin_flx &= ~FI_IGNORE;
1224	nflags &= ~NAT_SEARCH;
1225
1226	if (natl == NULL) {
1227#ifdef USE_MUTEXES
1228		ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1229#endif
1230
1231		/* XXX Since we're just copying the original ipn contents
1232		 * back, would we be better off just sending a pointer to
1233		 * the 'temp' copy off to nat_new instead?
1234		 */
1235		/* Generate template/bogus NAT rule. */
1236		bcopy((char *)ipn, (char *)&ipnat, sizeof(ipnat));
1237		ipn->in_flags = nflags & IPN_TCPUDP;
1238		ipn->in_apr = NULL;
1239		ipn->in_pr[0] = proto;
1240		ipn->in_pr[1] = proto;
1241		ipn->in_dpmin = fi.fin_dport;
1242		ipn->in_dpmax = fi.fin_dport;
1243		ipn->in_dpnext = fi.fin_dport;
1244		ipn->in_space = 1;
1245		ipn->in_ippip = 1;
1246		if (ipn->in_flags & IPN_FILTER) {
1247			ipn->in_scmp = 0;
1248			ipn->in_dcmp = 0;
1249		}
1250		ipn->in_plabel = -1;
1251
1252		/* Create NAT entry.  return NULL if this fails. */
1253		MUTEX_ENTER(&softn->ipf_nat_new);
1254		natl = ipf_nat_add(&fi, ipn, NULL, nflags|SI_CLONE|NAT_SLAVE,
1255			       NAT_INBOUND);
1256		MUTEX_EXIT(&softn->ipf_nat_new);
1257
1258		bcopy((char *)&ipnat, (char *)ipn, sizeof(ipnat));
1259
1260		if (natl == NULL) {
1261			MUTEX_DOWNGRADE(&softc->ipf_nat);
1262			return(-1);
1263		}
1264
1265		natl->nat_ptr = ipn;
1266		fi.fin_saddr = natl->nat_nsrcaddr;
1267		fi.fin_daddr = natl->nat_ndstaddr;
1268		ipn->in_use++;
1269		(void) ipf_nat_proto(&fi, natl, nflags);
1270		MUTEX_ENTER(&natl->nat_lock);
1271		ipf_nat_update(&fi, natl);
1272		MUTEX_EXIT(&natl->nat_lock);
1273	}
1274	MUTEX_DOWNGRADE(&softc->ipf_nat);
1275
1276	if (is == NULL) {
1277		/* Create state entry.  Return NULL if this fails. */
1278		fi.fin_flx |= FI_NATED;
1279		fi.fin_flx &= ~FI_STATE;
1280		nflags &= NAT_TCPUDP;
1281		nflags |= SI_W_SPORT|SI_CLONE;
1282
1283		if (ipf_state_add(softc, &fi, NULL, nflags) != 0) {
1284			/*
1285			 * XXX nat_delete is private to ip_nat.c.  Should
1286			 * check w/ Darren about this one.
1287			 *
1288			 * nat_delete(natl, NL_EXPIRE);
1289			 */
1290			return(-1);
1291		}
1292	}
1293
1294	return(0);
1295}
1296
1297/* --------------------------------------------------------------------	*/
1298/* Function:	ipf_p_rpcb_modv3						*/
1299/* Returns:	int -- change in packet length				*/
1300/* Parameters:	fin(I)	- pointer to packet information			*/
1301/*		nat(I)	- pointer to NAT session			*/
1302/*		rm(I)	- pointer to RPC message structure		*/
1303/*		m(I)	- pointer to mbuf chain				*/
1304/*		off(I)	- offset within mbuf chain			*/
1305/*									*/
1306/* Write a new universal address string to this packet, adjusting	*/
1307/* lengths as necessary.						*/
1308/* --------------------------------------------------------------------	*/
1309static int
1310ipf_p_rpcb_modv3(fin, nat, rm, m, off)
1311	fr_info_t *fin;
1312	nat_t *nat;
1313	rpc_msg_t *rm;
1314	mb_t *m;
1315	u_int off;
1316{
1317	u_int len, xlen, pos, bogo;
1318	rpc_resp_t *rr;
1319	char uaddr[24];
1320	char *i, *p;
1321	int diff;
1322
1323	rr = &rm->rm_resp;
1324	i = (char *)&nat->nat_ndstaddr;
1325	p = (char *)&rr->rr_v3.xu_port;
1326
1327	/* Form new string. */
1328	bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */
1329#if defined(SNPRINTF) && defined(_KERNEL)
1330	SNPRINTF(uaddr, sizeof(uaddr),
1331#else
1332	(void) sprintf(uaddr,
1333#endif
1334		       "%u.%u.%u.%u.%u.%u", i[0] & 0xff, i[1] & 0xff,
1335		       i[2] & 0xff, i[3] & 0xff, p[0] & 0xff, p[1] & 0xff);
1336	len = strlen(uaddr);
1337	xlen = XDRALIGN(len);
1338
1339	/* Determine mbuf offset to write to. */
1340	pos = (char *)rr->rr_v3.xu_xslen - rm->rm_msgbuf;
1341	off += pos;
1342
1343	/* Write new string length. */
1344	bogo = htonl(len);
1345	COPYBACK(m, off, 4, (caddr_t)&bogo);
1346	off += 4;
1347
1348	/* Write new string. */
1349	COPYBACK(m, off, xlen, uaddr);
1350
1351	/* Determine difference in data lengths. */
1352	diff = xlen - XDRALIGN(B(rr->rr_v3.xu_xslen));
1353
1354	/*
1355	 * If our new string has a different length, make necessary
1356	 * adjustments.
1357	 */
1358	if (diff != 0)
1359		ipf_p_rpcb_fixlen(fin, diff);
1360
1361	return(diff);
1362}
1363
1364/* --------------------------------------------------------------------	*/
1365/* Function:	ipf_p_rpcb_modv4						*/
1366/* Returns:	int -- change in packet length				*/
1367/* Parameters:	fin(I)	- pointer to packet information			*/
1368/*		nat(I)	- pointer to NAT session			*/
1369/*		rm(I)	- pointer to RPC message structure		*/
1370/*		m(I)	- pointer to mbuf chain				*/
1371/*		off(I)	- offset within mbuf chain			*/
1372/*									*/
1373/* Write new rpcb_entry list, adjusting	lengths as necessary.		*/
1374/* --------------------------------------------------------------------	*/
1375static int
1376ipf_p_rpcb_modv4(fin, nat, rm, m, off)
1377	fr_info_t *fin;
1378	nat_t *nat;
1379	rpc_msg_t *rm;
1380	mb_t *m;
1381	u_int off;
1382{
1383	u_int len, xlen, pos, bogo;
1384	rpcb_listp_t *rl;
1385	rpcb_entry_t *re;
1386	rpc_resp_t *rr;
1387	char uaddr[24];
1388	int diff, cnt;
1389	char *i, *p;
1390
1391	diff = 0;
1392	rr = &rm->rm_resp;
1393	rl = &rr->rr_v4;
1394
1395	i = (char *)&nat->nat_ndstaddr;
1396
1397	/* Determine mbuf offset to write to. */
1398	re = &rl->rl_entries[0];
1399	pos = (char *)re->re_maddr.xu_xslen - rm->rm_msgbuf;
1400	off += pos;
1401
1402	for (cnt = 0; cnt < rl->rl_cnt; cnt++) {
1403		re = &rl->rl_entries[cnt];
1404		p = (char *)&re->re_maddr.xu_port;
1405
1406		/* Form new string. */
1407		bzero(uaddr, sizeof(uaddr)); /* Just in case we need
1408						padding. */
1409#if defined(SNPRINTF) && defined(_KERNEL)
1410		SNPRINTF(uaddr, sizeof(uaddr),
1411#else
1412		(void) sprintf(uaddr,
1413#endif
1414			       "%u.%u.%u.%u.%u.%u", i[0] & 0xff,
1415			       i[1] & 0xff, i[2] & 0xff, i[3] & 0xff,
1416			       p[0] & 0xff, p[1] & 0xff);
1417		len = strlen(uaddr);
1418		xlen = XDRALIGN(len);
1419
1420		/* Write new string length. */
1421		bogo = htonl(len);
1422		COPYBACK(m, off, 4, (caddr_t)&bogo);
1423		off += 4;
1424
1425		/* Write new string. */
1426		COPYBACK(m, off, xlen, uaddr);
1427		off += xlen;
1428
1429		/* Record any change in length. */
1430		diff += xlen - XDRALIGN(B(re->re_maddr.xu_xslen));
1431
1432		/* If the length changed, copy back the rest of this entry. */
1433		len = ((char *)re->re_more + 4) -
1434		       (char *)re->re_netid.xp_xslen;
1435		if (diff != 0) {
1436			COPYBACK(m, off, len, (caddr_t)re->re_netid.xp_xslen);
1437		}
1438		off += len;
1439	}
1440
1441	/*
1442	 * If our new string has a different length, make necessary
1443	 * adjustments.
1444	 */
1445	if (diff != 0)
1446		ipf_p_rpcb_fixlen(fin, diff);
1447
1448	return(diff);
1449}
1450
1451
1452/* --------------------------------------------------------------------	*/
1453/* Function:    ipf_p_rpcb_fixlen                                        */
1454/* Returns:     (void)                                                  */
1455/* Parameters:  fin(I)  - pointer to packet information                 */
1456/*              len(I)  - change in packet length                       */
1457/*                                                                      */
1458/* Adjust various packet related lengths held in structure and packet   */
1459/* header fields.                                                       */
1460/* --------------------------------------------------------------------	*/
1461static void
1462ipf_p_rpcb_fixlen(fin, len)
1463        fr_info_t *fin;
1464        int len;
1465{
1466        udphdr_t *udp;
1467
1468        udp = fin->fin_dp;
1469        udp->uh_ulen = htons(ntohs(udp->uh_ulen) + len);
1470        fin->fin_plen += len;
1471        fin->fin_ip->ip_len = htons(fin->fin_plen);
1472        fin->fin_dlen += len;
1473}
1474
1475#undef B
1476