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