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