4 * Copyright (C) 2002-2003 by Ryan Beasley <ryanb@goddamnbastard.org>
6 * See the IPFILTER.LICENCE file for details on licencing.
10 * This is an in-kernel application proxy for Sun's RPCBIND (nee portmap)
11 * protocol as defined in RFC1833. It is far from complete, mostly
12 * lacking in less-likely corner cases, but it's definitely functional.
15 * rdr <int> <e_ip>/32 port <e_p> -> <i_ip> port <i_p> udp proxy rpcbu
17 * If the host running IP Filter is the same as the RPC server, it's
18 * perfectly legal for both the internal and external addresses and ports
21 * When triggered by appropriate IP NAT rules, this proxy works by
22 * examining data contained in received packets. Requests and replies are
23 * modified, NAT and state table entries created, etc., as necessary.
28 * o Must implement locking to protect proxy session data.
29 * o Fragmentation isn't supported.
30 * o Only supports UDP.
31 * o Doesn't support multiple RPC records in a single request.
32 * o Errors should be more fine-grained. (e.g., malloc failure vs.
33 * illegal RPCB request / reply)
34 * o Even with the limit on the total amount of recorded transactions,
35 * should there be a timeout on transaction removal?
36 * o There is a potential collision between cloning, wildcard NAT and
37 * state entries. There should be an appr_getport routine for
39 * o The enclosed hack of STREAMS support is pretty sick and most likely
42 * Id: ip_rpcb_pxy.c,v 2.25.2.12 2008/11/06 21:18:36 darrenr Exp
45 #include <sys/cdefs.h>
46 __KERNEL_RCSID(1, "$NetBSD$");
48 #define IPF_RPCB_PROXY
53 int ippr_rpcb_init
__P((void));
54 void ippr_rpcb_fini
__P((void));
55 int ippr_rpcb_new
__P((fr_info_t
*, ap_session_t
*, nat_t
*));
56 void ippr_rpcb_del
__P((ap_session_t
*));
57 int ippr_rpcb_in
__P((fr_info_t
*, ap_session_t
*, nat_t
*));
58 int ippr_rpcb_out
__P((fr_info_t
*, ap_session_t
*, nat_t
*));
60 static void ippr_rpcb_flush
__P((rpcb_session_t
*));
61 static int ippr_rpcb_decodereq
__P((fr_info_t
*, nat_t
*,
62 rpcb_session_t
*, rpc_msg_t
*));
63 static int ippr_rpcb_skipauth
__P((rpc_msg_t
*, xdr_auth_t
*, u_32_t
**));
64 static int ippr_rpcb_insert
__P((rpcb_session_t
*, rpcb_xact_t
*));
65 static int ippr_rpcb_xdrrpcb
__P((rpc_msg_t
*, u_32_t
*, rpcb_args_t
*));
66 static int ippr_rpcb_getuaddr
__P((rpc_msg_t
*, xdr_uaddr_t
*,
68 static u_int ippr_rpcb_atoi
__P((char *));
69 static int ippr_rpcb_modreq
__P((fr_info_t
*, nat_t
*, rpc_msg_t
*,
71 static int ippr_rpcb_decoderep
__P((fr_info_t
*, nat_t
*,
72 rpcb_session_t
*, rpc_msg_t
*, rpcb_xact_t
**));
73 static rpcb_xact_t
* ippr_rpcb_lookup
__P((rpcb_session_t
*, u_32_t
));
74 static void ippr_rpcb_deref
__P((rpcb_session_t
*, rpcb_xact_t
*));
75 static int ippr_rpcb_getproto
__P((rpc_msg_t
*, xdr_proto_t
*,
77 static int ippr_rpcb_getnat
__P((fr_info_t
*, nat_t
*, u_int
, u_int
));
78 static int ippr_rpcb_modv3
__P((fr_info_t
*, nat_t
*, rpc_msg_t
*,
80 static int ippr_rpcb_modv4
__P((fr_info_t
*, nat_t
*, rpc_msg_t
*,
82 static void ippr_rpcb_fixlen
__P((fr_info_t
*, int));
87 static frentry_t rpcbfr
; /* Skeleton rule for reference by entities
88 this proxy creates. */
89 static int rpcbcnt
; /* Upper bound of allocated RPCB sessions. */
90 /* XXX rpcbcnt still requires locking. */
92 int rpcb_proxy_init
= 0;
96 * Since rpc_msg contains only pointers, one should use this macro as a
97 * handy way to get to the goods. (In case you're wondering about the name,
98 * this started as BYTEREF -> BREF -> B.)
100 #define B(r) (u_32_t)ntohl(*(r))
106 /* -------------------------------------------------------------------- */
107 /* Function: ippr_rpcb_init */
108 /* Returns: int - 0 == success */
109 /* Parameters: (void) */
111 /* Initialize the filter rule entry and session limiter. */
112 /* -------------------------------------------------------------------- */
118 bzero((char *)&rpcbfr
, sizeof(rpcbfr
));
120 rpcbfr
.fr_flags
= FR_PASS
|FR_QUICK
|FR_KEEPSTATE
;
121 MUTEX_INIT(&rpcbfr
.fr_lock
, "ipf Sun RPCB proxy rule lock");
127 /* -------------------------------------------------------------------- */
128 /* Function: ippr_rpcb_fini */
130 /* Parameters: (void) */
132 /* Destroy rpcbfr's mutex to avoid a lock leak. */
133 /* -------------------------------------------------------------------- */
137 if (rpcb_proxy_init
== 1) {
138 MUTEX_DESTROY(&rpcbfr
.fr_lock
);
143 /* -------------------------------------------------------------------- */
144 /* Function: ippr_rpcb_new */
145 /* Returns: int - -1 == failure, 0 == success */
146 /* Parameters: fin(I) - pointer to packet information */
147 /* aps(I) - pointer to proxy session structure */
148 /* nat(I) - pointer to NAT session structure */
150 /* Allocate resources for per-session proxy structures. */
151 /* -------------------------------------------------------------------- */
153 ippr_rpcb_new(fin
, aps
, nat
)
160 fin
= fin
; /* LINT */
161 nat
= nat
; /* LINT */
163 KMALLOC(rs
, rpcb_session_t
*);
167 bzero((char *)rs
, sizeof(*rs
));
168 MUTEX_INIT(&rs
->rs_rxlock
, "ipf Sun RPCB proxy session lock");
175 /* -------------------------------------------------------------------- */
176 /* Function: ippr_rpcb_del */
178 /* Parameters: aps(I) - pointer to proxy session structure */
180 /* Free up a session's list of RPCB requests. */
181 /* -------------------------------------------------------------------- */
187 rs
= (rpcb_session_t
*)aps
->aps_data
;
189 MUTEX_ENTER(&rs
->rs_rxlock
);
191 MUTEX_EXIT(&rs
->rs_rxlock
);
192 MUTEX_DESTROY(&rs
->rs_rxlock
);
195 /* -------------------------------------------------------------------- */
196 /* Function: ippr_rpcb_in */
197 /* Returns: int - APR_ERR(1) == drop the packet, */
198 /* APR_ERR(2) == kill the proxy session, */
199 /* else change in packet length (in bytes) */
200 /* Parameters: fin(I) - pointer to packet information */
201 /* ip(I) - pointer to packet header */
202 /* aps(I) - pointer to proxy session structure */
203 /* nat(I) - pointer to NAT session structure */
205 /* Given a presumed RPCB request, perform some minor tests and pass off */
206 /* for decoding. Also pass packet off for a rewrite if necessary. */
207 /* -------------------------------------------------------------------- */
209 ippr_rpcb_in(fin
, aps
, nat
)
214 rpc_msg_t rpcmsg
, *rm
;
220 /* Disallow fragmented or illegally short packets. */
221 if ((fin
->fin_flx
& (FI_FRAG
|FI_SHORT
)) != 0)
224 /* Perform basic variable initialization. */
225 rs
= (rpcb_session_t
*)aps
->aps_data
;
228 off
= (char *)fin
->fin_dp
- (char *)fin
->fin_ip
;
229 off
+= sizeof(udphdr_t
) + fin
->fin_ipoff
;
230 dlen
= fin
->fin_dlen
- sizeof(udphdr_t
);
232 /* Disallow packets outside legal range for supported requests. */
233 if ((dlen
< RPCB_REQMIN
) || (dlen
> RPCB_REQMAX
))
236 /* Copy packet over to convenience buffer. */
238 bzero((char *)rm
, sizeof(*rm
));
239 COPYDATA(m
, off
, dlen
, (void *)&rm
->rm_msgbuf
);
240 rm
->rm_buflen
= dlen
;
242 /* Send off to decode request. */
243 rv
= ippr_rpcb_decodereq(fin
, nat
, rs
, rm
);
254 rv
= ippr_rpcb_modreq(fin
, nat
, rm
, m
, off
);
257 /*CONSTANTCONDITION*/
258 IPF_PANIC(1, ("illegal rv %d (ippr_rpcb_req)", rv
));
264 /* -------------------------------------------------------------------- */
265 /* Function: ippr_rpcb_out */
266 /* Returns: int - APR_ERR(1) == drop the packet, */
267 /* APR_ERR(2) == kill the proxy session, */
268 /* else change in packet length (in bytes) */
269 /* Parameters: fin(I) - pointer to packet information */
270 /* ip(I) - pointer to packet header */
271 /* aps(I) - pointer to proxy session structure */
272 /* nat(I) - pointer to NAT session structure */
274 /* Given a presumed RPCB reply, perform some minor tests and pass off */
275 /* for decoding. If the message indicates a successful request with */
276 /* valid addressing information, create NAT and state structures to */
277 /* allow direct communication between RPC client and server. */
278 /* -------------------------------------------------------------------- */
280 ippr_rpcb_out(fin
, aps
, nat
)
285 rpc_msg_t rpcmsg
, *rm
;
292 rx
= NULL
; /* XXX gcc */
294 /* Disallow fragmented or illegally short packets. */
295 if ((fin
->fin_flx
& (FI_FRAG
|FI_SHORT
)) != 0)
298 /* Perform basic variable initialization. */
299 rs
= (rpcb_session_t
*)aps
->aps_data
;
303 off
= (char *)fin
->fin_dp
- (char *)fin
->fin_ip
;
304 off
+= sizeof(udphdr_t
) + fin
->fin_ipoff
;
305 dlen
= fin
->fin_dlen
- sizeof(udphdr_t
);
308 /* Disallow packets outside legal range for supported requests. */
309 if ((dlen
< RPCB_REPMIN
) || (dlen
> RPCB_REPMAX
))
312 /* Copy packet over to convenience buffer. */
314 bzero((char *)rm
, sizeof(*rm
));
315 COPYDATA(m
, off
, dlen
, (void *)&rm
->rm_msgbuf
);
316 rm
->rm_buflen
= dlen
;
318 rx
= NULL
; /* XXX gcc */
320 /* Send off to decode reply. */
321 rv
= ippr_rpcb_decoderep(fin
, nat
, rs
, rm
, &rx
);
325 case -1: /* Bad packet */
327 MUTEX_ENTER(&rs
->rs_rxlock
);
328 ippr_rpcb_deref(rs
, rx
);
329 MUTEX_EXIT(&rs
->rs_rxlock
);
334 case 0: /* Negative reply / request rejected */
336 case 1: /* Positive reply */
338 * With the IP address embedded in a GETADDR(LIST) reply,
339 * we'll need to rewrite the packet in the very possible
340 * event that the internal & external addresses aren't the
341 * same. (i.e., this box is either a router or rpcbind
342 * only listens on loopback.)
344 if (nat
->nat_inip
.s_addr
!= nat
->nat_outip
.s_addr
) {
345 if (rx
->rx_type
== RPCB_RES_STRING
)
346 diff
= ippr_rpcb_modv3(fin
, nat
, rm
, m
, off
);
347 else if (rx
->rx_type
== RPCB_RES_LIST
)
348 diff
= ippr_rpcb_modv4(fin
, nat
, rm
, m
, off
);
352 /*CONSTANTCONDITION*/
353 IPF_PANIC(1, ("illegal rv %d (ippr_rpcb_decoderep)", rv
));
357 MUTEX_ENTER(&rs
->rs_rxlock
);
358 /* XXX Gross hack - I'm overloading the reference
359 * counter to deal with both threads and retransmitted
360 * requests. One deref signals that this thread is
361 * finished with rx, and the other signals that we've
362 * processed its reply.
364 ippr_rpcb_deref(rs
, rx
);
365 ippr_rpcb_deref(rs
, rx
);
366 MUTEX_EXIT(&rs
->rs_rxlock
);
373 * Private support subroutines
376 /* -------------------------------------------------------------------- */
377 /* Function: ippr_rpcb_flush */
379 /* Parameters: rs(I) - pointer to RPCB session structure */
381 /* Simply flushes the list of outstanding transactions, if any. */
382 /* -------------------------------------------------------------------- */
387 rpcb_xact_t
*r1
, *r2
;
400 /* -------------------------------------------------------------------- */
401 /* Function: ippr_rpcb_decodereq */
402 /* Returns: int - -1 == bad request or critical failure, */
403 /* 0 == request successfully decoded, */
404 /* 1 == request successfully decoded; requires */
405 /* address rewrite/modification */
406 /* Parameters: fin(I) - pointer to packet information */
407 /* nat(I) - pointer to NAT session structure */
408 /* rs(I) - pointer to RPCB session structure */
409 /* rm(I) - pointer to RPC message structure */
411 /* Take a presumed RPCB request, decode it, and store the results in */
412 /* the transaction list. If the internal target address needs to be */
413 /* modified, store its location in ptr. */
414 /* WARNING: It's the responsibility of the caller to make sure there */
415 /* is enough room in rs_buf for the basic RPC message "preamble". */
416 /* -------------------------------------------------------------------- */
418 ippr_rpcb_decodereq(fin
, nat
, rs
, rm
)
430 p
= (u_32_t
*)rm
->rm_msgbuf
;
433 bzero((char *)&rx
, sizeof(rx
));
437 rx
.rx_xid
= B(p
++); /* Record this message's XID. */
439 /* Parse out and test the RPC header. */
440 if ((B(p
++) != RPCB_CALL
) ||
441 (B(p
++) != RPCB_MSG_VERSION
) ||
442 (B(p
++) != RPCB_PROG
))
445 /* Record the RPCB version and procedure. */
449 /* Bypass RPC authentication stuff. */
450 if (ippr_rpcb_skipauth(rm
, &rc
->rc_authcred
, &p
) != 0)
452 if (ippr_rpcb_skipauth(rm
, &rc
->rc_authverf
, &p
) != 0)
455 /* Compare RPCB version and procedure numbers. */
456 switch(B(rc
->rc_vers
))
459 /* This proxy only supports PMAP_GETPORT. */
460 if (B(rc
->rc_proc
) != RPCB_GETPORT
)
463 /* Portmap requests contain four 4 byte parameters. */
464 if (RPCB_BUF_EQ(rm
, p
, 16) == 0)
467 p
+= 2; /* Skip requested program and version numbers. */
469 /* Sanity check the requested protocol. */
471 if (!(xdr
== IPPROTO_UDP
|| xdr
== IPPROTO_TCP
))
474 rx
.rx_type
= RPCB_RES_PMAP
;
479 /* GETADDRLIST is exclusive to v4; GETADDR for v3 & v4 */
480 switch(B(rc
->rc_proc
))
483 rx
.rx_type
= RPCB_RES_STRING
;
484 rx
.rx_proto
= (u_int
)fin
->fin_p
;
486 case RPCB_GETADDRLIST
:
487 if (B(rc
->rc_vers
) != 4)
489 rx
.rx_type
= RPCB_RES_LIST
;
495 ra
= &rc
->rc_rpcbargs
;
497 /* Decode the 'struct rpcb' request. */
498 if (ippr_rpcb_xdrrpcb(rm
, p
, ra
) != 0)
501 /* Are the target address & port valid? */
502 if ((ra
->ra_maddr
.xu_ip
!= nat
->nat_outip
.s_addr
) ||
503 (ra
->ra_maddr
.xu_port
!= nat
->nat_outport
))
506 /* Do we need to rewrite this packet? */
507 if ((nat
->nat_outip
.s_addr
!= nat
->nat_inip
.s_addr
) ||
508 (nat
->nat_outport
!= nat
->nat_inport
))
515 MUTEX_ENTER(&rs
->rs_rxlock
);
516 if (ippr_rpcb_insert(rs
, &rx
) != 0) {
517 MUTEX_EXIT(&rs
->rs_rxlock
);
520 MUTEX_EXIT(&rs
->rs_rxlock
);
525 /* -------------------------------------------------------------------- */
526 /* Function: ippr_rpcb_skipauth */
527 /* Returns: int -- -1 == illegal auth parameters (lengths) */
528 /* 0 == valid parameters, pointer advanced */
529 /* Parameters: rm(I) - pointer to RPC message structure */
530 /* auth(I) - pointer to RPC auth structure */
531 /* buf(IO) - pointer to location within convenience buffer */
533 /* Record auth data length & location of auth data, then advance past */
535 /* -------------------------------------------------------------------- */
537 ippr_rpcb_skipauth(rm
, auth
, buf
)
546 /* Make sure we have enough space for expected fixed auth parms. */
547 if (RPCB_BUF_GEQ(rm
, p
, 8) == 0)
550 p
++; /* We don't care about auth_flavor. */
552 auth
->xa_string
.xs_len
= p
;
553 xdr
= B(p
++); /* Length of auth_data */
555 /* Test for absurdity / illegality of auth_data length. */
556 if ((XDRALIGN(xdr
) < xdr
) || (RPCB_BUF_GEQ(rm
, p
, XDRALIGN(xdr
)) == 0))
559 auth
->xa_string
.xs_str
= (char *)p
;
561 p
+= XDRALIGN(xdr
); /* Advance our location. */
568 /* -------------------------------------------------------------------- */
569 /* Function: ippr_rpcb_insert */
570 /* Returns: int -- -1 == list insertion failed, */
571 /* 0 == item successfully added */
572 /* Parameters: rs(I) - pointer to RPCB session structure */
573 /* rx(I) - pointer to RPCB transaction structure */
574 /* -------------------------------------------------------------------- */
576 ippr_rpcb_insert(rs
, rx
)
582 rxp
= ippr_rpcb_lookup(rs
, rx
->rx_xid
);
588 if (rpcbcnt
== RPCB_MAXREQS
)
591 KMALLOC(rxp
, rpcb_xact_t
*);
595 bcopy((char *)rx
, (char *)rxp
, sizeof(*rx
));
597 if (rs
->rs_rxlist
!= NULL
)
598 rs
->rs_rxlist
->rx_pnext
= &rxp
->rx_next
;
600 rxp
->rx_pnext
= &rs
->rs_rxlist
;
601 rxp
->rx_next
= rs
->rs_rxlist
;
611 /* -------------------------------------------------------------------- */
612 /* Function: ippr_rpcb_xdrrpcb */
613 /* Returns: int -- -1 == failure to properly decode the request */
614 /* 0 == rpcb successfully decoded */
615 /* Parameters: rs(I) - pointer to RPCB session structure */
616 /* p(I) - pointer to location within session buffer */
617 /* rpcb(O) - pointer to rpcb (xdr type) structure */
619 /* Decode a XDR encoded rpcb structure and record its contents in rpcb */
620 /* within only the context of TCP/UDP over IP networks. */
621 /* -------------------------------------------------------------------- */
623 ippr_rpcb_xdrrpcb(rm
, p
, ra
)
628 if (!RPCB_BUF_GEQ(rm
, p
, 20))
631 /* Bypass target program & version. */
634 /* Decode r_netid. Must be "tcp" or "udp". */
635 if (ippr_rpcb_getproto(rm
, &ra
->ra_netid
, &p
) != 0)
638 /* Decode r_maddr. */
639 if (ippr_rpcb_getuaddr(rm
, &ra
->ra_maddr
, &p
) != 0)
642 /* Advance to r_owner and make sure it's empty. */
643 if (!RPCB_BUF_EQ(rm
, p
, 4) || (B(p
) != 0))
649 /* -------------------------------------------------------------------- */
650 /* Function: ippr_rpcb_getuaddr */
651 /* Returns: int -- -1 == illegal string, */
652 /* 0 == string parsed; contents recorded */
653 /* Parameters: rm(I) - pointer to RPC message structure */
654 /* xu(I) - pointer to universal address structure */
655 /* p(IO) - pointer to location within message buffer */
657 /* Decode the IP address / port at p and record them in xu. */
658 /* -------------------------------------------------------------------- */
660 ippr_rpcb_getuaddr(rm
, xu
, p
)
665 char *c
, *i
, *b
, *pp
;
669 /* Test for string length. */
670 if (!RPCB_BUF_GEQ(rm
, *p
, 4))
673 xu
->xu_xslen
= (*p
)++;
674 xu
->xu_xsstr
= (char *)*p
;
678 if (l
< 11 || l
> 23 || !RPCB_BUF_GEQ(rm
, *p
, XDRALIGN(l
)))
682 *(char **)p
+= XDRALIGN(l
);
684 /* Copy string to local buffer & terminate C style */
685 bcopy(xu
->xu_xsstr
, uastr
, l
);
688 i
= (char *)&xu
->xu_ip
;
689 pp
= (char *)&xu
->xu_port
;
692 * Expected format: a.b.c.d.e.f where [a-d] correspond to bytes of
693 * an IP address and [ef] are the bytes of a L4 port.
695 if (!(ISDIGIT(uastr
[0]) && ISDIGIT(uastr
[l
-1])))
698 for (c
= &uastr
[1], d
= 0, dd
= 0; c
< &uastr
[l
-1]; c
++) {
707 /* Check for ASCII byte. */
709 t
= ippr_rpcb_atoi(b
);
713 /* Aim b at beginning of the next byte. */
716 /* Switch off IP addr vs port parsing. */
720 pp
[d
++ - 4] = t
& 0xff;
727 if (d
!= 5) /* String must contain exactly 5 periods. */
730 /* Handle the last byte (port low byte) */
731 t
= ippr_rpcb_atoi(b
);
734 pp
[d
- 4] = t
& 0xff;
739 /* -------------------------------------------------------------------- */
740 /* Function: ippr_rpcb_atoi (XXX should be generic for all proxies) */
741 /* Returns: int -- integer representation of supplied string */
742 /* Parameters: ptr(I) - input string */
744 /* Simple version of atoi(3) ripped from ip_rcmd_pxy.c. */
745 /* -------------------------------------------------------------------- */
750 register char *s
= ptr
, c
;
751 register u_int i
= 0;
753 while (((c
= *s
++) != '\0') && ISDIGIT(c
)) {
760 /* -------------------------------------------------------------------- */
761 /* Function: ippr_rpcb_modreq */
762 /* Returns: int -- change in datagram length */
763 /* APR_ERR(2) - critical failure */
764 /* Parameters: fin(I) - pointer to packet information */
765 /* nat(I) - pointer to NAT session */
766 /* rm(I) - pointer to RPC message structure */
767 /* m(I) - pointer to mbuf chain */
768 /* off(I) - current offset within mbuf chain */
770 /* When external and internal addresses differ, we rewrite the former */
771 /* with the latter. (This is exclusive to protocol versions 3 & 4). */
772 /* -------------------------------------------------------------------- */
774 ippr_rpcb_modreq(fin
, nat
, rm
, m
, off
)
781 u_int len
, xlen
, pos
, bogo
;
788 ra
= &rm
->rm_call
.rc_rpcbargs
;
789 i
= (char *)&nat
->nat_inip
.s_addr
;
790 p
= (char *)&nat
->nat_inport
;
792 /* Form new string. */
793 bzero(uaddr
, sizeof(uaddr
)); /* Just in case we need padding. */
794 #if defined(SNPRINTF) && defined(_KERNEL)
795 SNPRINTF(uaddr
, sizeof(uaddr
),
797 (void) sprintf(uaddr
,
799 "%u.%u.%u.%u.%u.%u", i
[0] & 0xff, i
[1] & 0xff,
800 i
[2] & 0xff, i
[3] & 0xff, p
[0] & 0xff, p
[1] & 0xff);
802 xlen
= XDRALIGN(len
);
804 /* Determine mbuf offset to start writing to. */
805 pos
= (char *)ra
->ra_maddr
.xu_xslen
- rm
->rm_msgbuf
;
808 /* Write new string length. */
810 COPYBACK(m
, off
, 4, (void *)&bogo
);
813 /* Write new string. */
814 COPYBACK(m
, off
, xlen
, uaddr
);
817 /* Write in zero r_owner. */
819 COPYBACK(m
, off
, 4, (void *)&bogo
);
821 /* Determine difference in data lengths. */
822 diff
= xlen
- XDRALIGN(B(ra
->ra_maddr
.xu_xslen
));
825 * If our new string has a different length, make necessary
830 udp
->uh_ulen
= htons(ntohs(udp
->uh_ulen
) + diff
);
831 fin
->fin_ip
->ip_len
+= diff
;
832 fin
->fin_dlen
+= diff
;
833 fin
->fin_plen
+= diff
;
834 /* XXX Storage lengths. */
840 /* -------------------------------------------------------------------- */
841 /* Function: ippr_rpcb_decoderep */
842 /* Returns: int - -1 == bad request or critical failure, */
843 /* 0 == valid, negative reply */
844 /* 1 == vaddlid, positive reply; needs no changes */
845 /* Parameters: fin(I) - pointer to packet information */
846 /* nat(I) - pointer to NAT session structure */
847 /* rs(I) - pointer to RPCB session structure */
848 /* rm(I) - pointer to RPC message structure */
849 /* rxp(O) - pointer to RPCB transaction structure */
851 /* Take a presumed RPCB reply, extract the XID, search for the original */
852 /* request information, and determine whether the request was accepted */
853 /* or rejected. With a valid accepted reply, go ahead and create NAT */
854 /* and state entries, and finish up by rewriting the packet as */
857 /* WARNING: It's the responsibility of the caller to make sure there */
858 /* is enough room in rs_buf for the basic RPC message "preamble". */
859 /* -------------------------------------------------------------------- */
861 ippr_rpcb_decoderep(fin
, nat
, rs
, rm
, rxp
)
875 p
= (u_32_t
*)rm
->rm_msgbuf
;
877 bzero((char *)&rx
, sizeof(rx
));
881 xdr
= B(p
++); /* Record this message's XID. */
884 MUTEX_ENTER(&rs
->rs_rxlock
);
885 if ((rx
= ippr_rpcb_lookup(rs
, xdr
)) == NULL
) {
886 MUTEX_EXIT(&rs
->rs_rxlock
);
889 ++rx
->rx_ref
; /* per thread reference */
890 MUTEX_EXIT(&rs
->rs_rxlock
);
894 /* Test call vs reply */
895 if (B(p
++) != RPCB_REPLY
)
898 /* Test reply_stat */
901 case RPCB_MSG_DENIED
:
903 case RPCB_MSG_ACCEPTED
:
909 /* Bypass RPC authentication stuff. */
910 if (ippr_rpcb_skipauth(rm
, &rr
->rr_authverf
, &p
) != 0)
913 /* Test accept status */
914 if (!RPCB_BUF_GEQ(rm
, p
, 4))
919 /* Parse out the expected reply */
923 /* There must be only one 4 byte argument. */
924 if (!RPCB_BUF_EQ(rm
, p
, 4))
930 /* Reply w/ a 0 port indicates service isn't registered */
934 /* Is the value sane? */
938 /* Create NAT & state table entries. */
939 if (ippr_rpcb_getnat(fin
, nat
, rx
->rx_proto
, (u_int
)xdr
) != 0)
942 case RPCB_RES_STRING
:
943 /* Expecting a XDR string; need 4 bytes for length */
944 if (!RPCB_BUF_GEQ(rm
, p
, 4))
947 rr
->rr_v3
.xu_str
.xs_len
= p
++;
948 rr
->rr_v3
.xu_str
.xs_str
= (char *)p
;
950 xdr
= B(rr
->rr_v3
.xu_xslen
);
952 /* A null string indicates an unregistered service */
953 if ((xdr
== 0) && RPCB_BUF_EQ(rm
, p
, 0))
956 /* Decode the target IP address / port. */
957 if (ippr_rpcb_getuaddr(rm
, &rr
->rr_v3
, &p
) != 0)
960 /* Validate the IP address and port contained. */
961 if (nat
->nat_inip
.s_addr
!= rr
->rr_v3
.xu_ip
)
964 /* Create NAT & state table entries. */
965 if (ippr_rpcb_getnat(fin
, nat
, rx
->rx_proto
,
966 (u_int
)rr
->rr_v3
.xu_port
) != 0)
970 if (!RPCB_BUF_GEQ(rm
, p
, 4))
972 /* rpcb_entry_list_ptr */
989 re
= &rl
->rl_entries
[rl
->rl_cnt
];
990 if (ippr_rpcb_getuaddr(rm
, &re
->re_maddr
, &p
) != 0)
992 if (ippr_rpcb_getproto(rm
, &re
->re_netid
, &p
) != 0)
994 /* re_semantics & re_pfamily length */
995 if (!RPCB_BUF_GEQ(rm
, p
, 12))
997 p
++; /* Skipping re_semantics. */
999 if ((xdr
!= 4) || strncmp((char *)p
, "inet", 4))
1002 if (ippr_rpcb_getproto(rm
, &re
->re_proto
, &p
) != 0)
1004 if (!RPCB_BUF_GEQ(rm
, p
, 4))
1007 if (B(re
->re_more
) > 1) /* 0,1 only legal values */
1011 if (B(re
->re_more
) == 0)
1013 /* Replies in max out at 2; TCP and/or UDP */
1019 for(rl
->rl_cnt
= 0; rl
->rl_cnt
< cnt
; rl
->rl_cnt
++) {
1020 re
= &rl
->rl_entries
[rl
->rl_cnt
];
1021 rv
= ippr_rpcb_getnat(fin
, nat
,
1022 re
->re_proto
.xp_proto
,
1023 (u_int
)re
->re_maddr
.xu_port
);
1029 /*CONSTANTCONDITION*/
1030 IPF_PANIC(1, ("illegal rx_type %d", rx
->rx_type
));
1036 /* -------------------------------------------------------------------- */
1037 /* Function: ippr_rpcb_lookup */
1038 /* Returns: rpcb_xact_t * - NULL == no matching record, */
1039 /* else pointer to relevant entry */
1040 /* Parameters: rs(I) - pointer to RPCB session */
1041 /* xid(I) - XID to look for */
1042 /* -------------------------------------------------------------------- */
1043 static rpcb_xact_t
*
1044 ippr_rpcb_lookup(rs
, xid
)
1050 if (rs
->rs_rxlist
== NULL
)
1053 for (rx
= rs
->rs_rxlist
; rx
!= NULL
; rx
= rx
->rx_next
)
1054 if (rx
->rx_xid
== xid
)
1060 /* -------------------------------------------------------------------- */
1061 /* Function: ippr_rpcb_deref */
1062 /* Returns: (void) */
1063 /* Parameters: rs(I) - pointer to RPCB session */
1064 /* rx(I) - pointer to RPC transaction struct to remove */
1065 /* force(I) - indicates to delete entry regardless of */
1066 /* reference count */
1067 /* Locking: rs->rs_rxlock must be held write only */
1069 /* Free the RPCB transaction record rx from the chain of entries. */
1070 /* -------------------------------------------------------------------- */
1072 ippr_rpcb_deref(rs
, rx
)
1081 if (--rx
->rx_ref
!= 0)
1084 if (rx
->rx_next
!= NULL
)
1085 rx
->rx_next
->rx_pnext
= rx
->rx_pnext
;
1087 *rx
->rx_pnext
= rx
->rx_next
;
1094 /* -------------------------------------------------------------------- */
1095 /* Function: ippr_rpcb_getproto */
1096 /* Returns: int - -1 == illegal protocol/netid, */
1097 /* 0 == legal protocol/netid */
1098 /* Parameters: rm(I) - pointer to RPC message structure */
1099 /* xp(I) - pointer to netid structure */
1100 /* p(IO) - pointer to location within packet buffer */
1102 /* Decode netid/proto stored at p and record its numeric value. */
1103 /* -------------------------------------------------------------------- */
1105 ippr_rpcb_getproto(rm
, xp
, p
)
1112 /* Must have 4 bytes for length & 4 bytes for "tcp" or "udp". */
1113 if (!RPCB_BUF_GEQ(rm
, p
, 8))
1116 xp
->xp_xslen
= (*p
)++;
1117 xp
->xp_xsstr
= (char *)*p
;
1119 /* Test the string length. */
1120 len
= B(xp
->xp_xslen
);
1124 /* Test the actual string & record the protocol accordingly. */
1125 if (!strncmp((char *)xp
->xp_xsstr
, "tcp\0", 4))
1126 xp
->xp_proto
= IPPROTO_TCP
;
1127 else if (!strncmp((char *)xp
->xp_xsstr
, "udp\0", 4))
1128 xp
->xp_proto
= IPPROTO_UDP
;
1133 /* Advance past the string. */
1139 /* -------------------------------------------------------------------- */
1140 /* Function: ippr_rpcb_getnat */
1141 /* Returns: int -- -1 == failed to create table entries, */
1143 /* Parameters: fin(I) - pointer to packet information */
1144 /* nat(I) - pointer to NAT table entry */
1145 /* proto(I) - transport protocol for new entries */
1146 /* port(I) - new port to use w/ wildcard table entries */
1148 /* Create state and NAT entries to handle an anticipated connection */
1149 /* attempt between RPC client and server. */
1150 /* -------------------------------------------------------------------- */
1152 ippr_rpcb_getnat(fin
, nat
, proto
, port
)
1158 ipnat_t
*ipn
, ipnat
;
1167 /* Generate dummy fr_info */
1168 bcopy((char *)fin
, (char *)&fi
, sizeof(fi
));
1170 fi
.fin_src
= fin
->fin_dst
;
1171 fi
.fin_dst
= nat
->nat_outip
;
1174 fi
.fin_dport
= port
& 0xffff;
1175 fi
.fin_flx
|= FI_IGNORE
;
1177 bzero((char *)&tcp
, sizeof(tcp
));
1178 tcp
.th_dport
= htons(port
);
1180 if (proto
== IPPROTO_TCP
) {
1181 tcp
.th_win
= htons(8192);
1182 TCP_OFF_A(&tcp
, sizeof(tcphdr_t
) >> 2);
1183 fi
.fin_dlen
= sizeof(tcphdr_t
);
1184 tcp
.th_flags
= TH_SYN
;
1187 fi
.fin_dlen
= sizeof(udphdr_t
);
1191 nflags
|= SI_W_SPORT
|NAT_SEARCH
;
1193 fi
.fin_plen
= fi
.fin_hlen
+ fi
.fin_dlen
;
1196 * Search for existing NAT & state entries. Pay close attention to
1197 * mutexes / locks grabbed from lookup routines, as not doing so could
1198 * lead to bad things.
1200 * If successful, fr_stlookup returns with ipf_state locked. We have
1201 * no use for this lock, so simply unlock it if necessary.
1203 is
= fr_stlookup(&fi
, &tcp
, NULL
);
1205 RWLOCK_EXIT(&ipf_state
);
1208 RWLOCK_EXIT(&ipf_nat
);
1210 WRITE_ENTER(&ipf_nat
);
1211 natl
= nat_inlookup(&fi
, nflags
, proto
, fi
.fin_src
, fi
.fin_dst
);
1213 if ((natl
!= NULL
) && (is
!= NULL
)) {
1214 MUTEX_DOWNGRADE(&ipf_nat
);
1218 /* Slightly modify the following structures for actual use in creating
1219 * NAT and/or state entries. We're primarily concerned with stripping
1220 * flags that may be detrimental to the creation process or simply
1221 * shouldn't be associated with a table entry.
1223 fi
.fin_fr
= &rpcbfr
;
1224 fi
.fin_flx
&= ~FI_IGNORE
;
1225 nflags
&= ~NAT_SEARCH
;
1228 /* XXX Since we're just copying the original ipn contents
1229 * back, would we be better off just sending a pointer to
1230 * the 'temp' copy off to nat_new instead?
1232 /* Generate template/bogus NAT rule. */
1233 bcopy((char *)ipn
, (char *)&ipnat
, sizeof(ipnat
));
1234 ipn
->in_flags
= nflags
& IPN_TCPUDP
;
1237 ipn
->in_pmin
= htons(fi
.fin_dport
);
1238 ipn
->in_pmax
= htons(fi
.fin_dport
);
1239 ipn
->in_pnext
= htons(fi
.fin_dport
);
1242 if (ipn
->in_flags
& IPN_FILTER
) {
1246 *ipn
->in_plabel
= '\0';
1248 /* Create NAT entry. return NULL if this fails. */
1249 MUTEX_ENTER(&ipf_nat_new
);
1250 natl
= nat_new(&fi
, ipn
, NULL
, nflags
|SI_CLONE
|NAT_SLAVE
,
1252 MUTEX_EXIT(&ipf_nat_new
);
1254 bcopy((char *)&ipnat
, (char *)ipn
, sizeof(ipnat
));
1257 MUTEX_DOWNGRADE(&ipf_nat
);
1262 (void) nat_proto(&fi
, natl
, nflags
);
1263 MUTEX_ENTER(&natl
->nat_lock
);
1264 nat_update(&fi
, natl
);
1265 MUTEX_EXIT(&natl
->nat_lock
);
1267 MUTEX_DOWNGRADE(&ipf_nat
);
1270 /* Create state entry. Return NULL if this fails. */
1271 fi
.fin_dst
= nat
->nat_inip
;
1272 fi
.fin_flx
|= FI_NATED
;
1273 fi
.fin_flx
&= ~FI_STATE
;
1274 nflags
&= NAT_TCPUDP
;
1275 nflags
|= SI_W_SPORT
|SI_CLONE
;
1277 is
= fr_addstate(&fi
, NULL
, nflags
);
1280 * XXX nat_delete is private to ip_nat.c. Should
1281 * check w/ Darren about this one.
1283 * nat_delete(natl, NL_EXPIRE);
1292 /* -------------------------------------------------------------------- */
1293 /* Function: ippr_rpcb_modv3 */
1294 /* Returns: int -- change in packet length */
1295 /* Parameters: fin(I) - pointer to packet information */
1296 /* nat(I) - pointer to NAT session */
1297 /* rm(I) - pointer to RPC message structure */
1298 /* m(I) - pointer to mbuf chain */
1299 /* off(I) - offset within mbuf chain */
1301 /* Write a new universal address string to this packet, adjusting */
1302 /* lengths as necessary. */
1303 /* -------------------------------------------------------------------- */
1305 ippr_rpcb_modv3(fin
, nat
, rm
, m
, off
)
1312 u_int len
, xlen
, pos
, bogo
;
1319 i
= (char *)&nat
->nat_outip
.s_addr
;
1320 p
= (char *)&rr
->rr_v3
.xu_port
;
1322 /* Form new string. */
1323 bzero(uaddr
, sizeof(uaddr
)); /* Just in case we need padding. */
1324 #if defined(SNPRINTF) && defined(_KERNEL)
1325 SNPRINTF(uaddr
, sizeof(uaddr
),
1327 (void) sprintf(uaddr
,
1329 "%u.%u.%u.%u.%u.%u", i
[0] & 0xff, i
[1] & 0xff,
1330 i
[2] & 0xff, i
[3] & 0xff, p
[0] & 0xff, p
[1] & 0xff);
1331 len
= strlen(uaddr
);
1332 xlen
= XDRALIGN(len
);
1334 /* Determine mbuf offset to write to. */
1335 pos
= (char *)rr
->rr_v3
.xu_xslen
- rm
->rm_msgbuf
;
1338 /* Write new string length. */
1340 COPYBACK(m
, off
, 4, (void *)&bogo
);
1343 /* Write new string. */
1344 COPYBACK(m
, off
, xlen
, uaddr
);
1346 /* Determine difference in data lengths. */
1347 diff
= xlen
- XDRALIGN(B(rr
->rr_v3
.xu_xslen
));
1350 * If our new string has a different length, make necessary
1354 ippr_rpcb_fixlen(fin
, diff
);
1359 /* -------------------------------------------------------------------- */
1360 /* Function: ippr_rpcb_modv4 */
1361 /* Returns: int -- change in packet length */
1362 /* Parameters: fin(I) - pointer to packet information */
1363 /* nat(I) - pointer to NAT session */
1364 /* rm(I) - pointer to RPC message structure */
1365 /* m(I) - pointer to mbuf chain */
1366 /* off(I) - offset within mbuf chain */
1368 /* Write new rpcb_entry list, adjusting lengths as necessary. */
1369 /* -------------------------------------------------------------------- */
1371 ippr_rpcb_modv4(fin
, nat
, rm
, m
, off
)
1378 u_int len
, xlen
, pos
, bogo
;
1390 i
= (char *)&nat
->nat_outip
.s_addr
;
1392 /* Determine mbuf offset to write to. */
1393 re
= &rl
->rl_entries
[0];
1394 pos
= (char *)re
->re_maddr
.xu_xslen
- rm
->rm_msgbuf
;
1397 for (cnt
= 0; cnt
< rl
->rl_cnt
; cnt
++) {
1398 re
= &rl
->rl_entries
[cnt
];
1399 p
= (char *)&re
->re_maddr
.xu_port
;
1401 /* Form new string. */
1402 bzero(uaddr
, sizeof(uaddr
)); /* Just in case we need
1404 #if defined(SNPRINTF) && defined(_KERNEL)
1405 SNPRINTF(uaddr
, sizeof(uaddr
),
1407 (void) sprintf(uaddr
,
1409 "%u.%u.%u.%u.%u.%u", i
[0] & 0xff,
1410 i
[1] & 0xff, i
[2] & 0xff, i
[3] & 0xff,
1411 p
[0] & 0xff, p
[1] & 0xff);
1412 len
= strlen(uaddr
);
1413 xlen
= XDRALIGN(len
);
1415 /* Write new string length. */
1417 COPYBACK(m
, off
, 4, (void *)&bogo
);
1420 /* Write new string. */
1421 COPYBACK(m
, off
, xlen
, uaddr
);
1424 /* Record any change in length. */
1425 diff
+= xlen
- XDRALIGN(B(re
->re_maddr
.xu_xslen
));
1427 /* If the length changed, copy back the rest of this entry. */
1428 len
= ((char *)re
->re_more
+ 4) -
1429 (char *)re
->re_netid
.xp_xslen
;
1431 COPYBACK(m
, off
, len
, (void *)re
->re_netid
.xp_xslen
);
1437 * If our new string has a different length, make necessary
1441 ippr_rpcb_fixlen(fin
, diff
);
1447 /* -------------------------------------------------------------------- */
1448 /* Function: ippr_rpcb_fixlen */
1449 /* Returns: (void) */
1450 /* Parameters: fin(I) - pointer to packet information */
1451 /* len(I) - change in packet length */
1453 /* Adjust various packet related lengths held in structure and packet */
1454 /* header fields. */
1455 /* -------------------------------------------------------------------- */
1457 ippr_rpcb_fixlen(fin
, len
)
1464 udp
->uh_ulen
= htons(ntohs(udp
->uh_ulen
) + len
);
1465 fin
->fin_ip
->ip_len
+= len
;
1466 fin
->fin_dlen
+= len
;
1467 fin
->fin_plen
+= len
;