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 #define IPF_RPCB_PROXY
50 int ippr_rpcb_init
__P((void));
51 void ippr_rpcb_fini
__P((void));
52 int ippr_rpcb_new
__P((fr_info_t
*, ap_session_t
*, nat_t
*));
53 void ippr_rpcb_del
__P((ap_session_t
*));
54 int ippr_rpcb_in
__P((fr_info_t
*, ap_session_t
*, nat_t
*));
55 int ippr_rpcb_out
__P((fr_info_t
*, ap_session_t
*, nat_t
*));
57 static void ippr_rpcb_flush
__P((rpcb_session_t
*));
58 static int ippr_rpcb_decodereq
__P((fr_info_t
*, nat_t
*,
59 rpcb_session_t
*, rpc_msg_t
*));
60 static int ippr_rpcb_skipauth
__P((rpc_msg_t
*, xdr_auth_t
*, u_32_t
**));
61 static int ippr_rpcb_insert
__P((rpcb_session_t
*, rpcb_xact_t
*));
62 static int ippr_rpcb_xdrrpcb
__P((rpc_msg_t
*, u_32_t
*, rpcb_args_t
*));
63 static int ippr_rpcb_getuaddr
__P((rpc_msg_t
*, xdr_uaddr_t
*,
65 static u_int ippr_rpcb_atoi
__P((char *));
66 static int ippr_rpcb_modreq
__P((fr_info_t
*, nat_t
*, rpc_msg_t
*,
68 static int ippr_rpcb_decoderep
__P((fr_info_t
*, nat_t
*,
69 rpcb_session_t
*, rpc_msg_t
*, rpcb_xact_t
**));
70 static rpcb_xact_t
* ippr_rpcb_lookup
__P((rpcb_session_t
*, u_32_t
));
71 static void ippr_rpcb_deref
__P((rpcb_session_t
*, rpcb_xact_t
*));
72 static int ippr_rpcb_getproto
__P((rpc_msg_t
*, xdr_proto_t
*,
74 static int ippr_rpcb_getnat
__P((fr_info_t
*, nat_t
*, u_int
, u_int
));
75 static int ippr_rpcb_modv3
__P((fr_info_t
*, nat_t
*, rpc_msg_t
*,
77 static int ippr_rpcb_modv4
__P((fr_info_t
*, nat_t
*, rpc_msg_t
*,
79 static void ippr_rpcb_fixlen
__P((fr_info_t
*, int));
84 static frentry_t rpcbfr
; /* Skeleton rule for reference by entities
85 this proxy creates. */
86 static int rpcbcnt
; /* Upper bound of allocated RPCB sessions. */
87 /* XXX rpcbcnt still requires locking. */
89 int rpcb_proxy_init
= 0;
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.)
97 #define B(r) (u_32_t)ntohl(*(r))
103 /* -------------------------------------------------------------------- */
104 /* Function: ippr_rpcb_init */
105 /* Returns: int - 0 == success */
106 /* Parameters: (void) */
108 /* Initialize the filter rule entry and session limiter. */
109 /* -------------------------------------------------------------------- */
115 bzero((char *)&rpcbfr
, sizeof(rpcbfr
));
117 rpcbfr
.fr_flags
= FR_PASS
|FR_QUICK
|FR_KEEPSTATE
;
118 MUTEX_INIT(&rpcbfr
.fr_lock
, "ipf Sun RPCB proxy rule lock");
124 /* -------------------------------------------------------------------- */
125 /* Function: ippr_rpcb_fini */
127 /* Parameters: (void) */
129 /* Destroy rpcbfr's mutex to avoid a lock leak. */
130 /* -------------------------------------------------------------------- */
134 if (rpcb_proxy_init
== 1) {
135 MUTEX_DESTROY(&rpcbfr
.fr_lock
);
140 /* -------------------------------------------------------------------- */
141 /* Function: ippr_rpcb_new */
142 /* Returns: int - -1 == failure, 0 == success */
143 /* Parameters: fin(I) - pointer to packet information */
144 /* aps(I) - pointer to proxy session structure */
145 /* nat(I) - pointer to NAT session structure */
147 /* Allocate resources for per-session proxy structures. */
148 /* -------------------------------------------------------------------- */
150 ippr_rpcb_new(fin
, aps
, nat
)
157 fin
= fin
; /* LINT */
158 nat
= nat
; /* LINT */
160 KMALLOC(rs
, rpcb_session_t
*);
164 bzero((char *)rs
, sizeof(*rs
));
165 MUTEX_INIT(&rs
->rs_rxlock
, "ipf Sun RPCB proxy session lock");
172 /* -------------------------------------------------------------------- */
173 /* Function: ippr_rpcb_del */
175 /* Parameters: aps(I) - pointer to proxy session structure */
177 /* Free up a session's list of RPCB requests. */
178 /* -------------------------------------------------------------------- */
184 rs
= (rpcb_session_t
*)aps
->aps_data
;
186 MUTEX_ENTER(&rs
->rs_rxlock
);
188 MUTEX_EXIT(&rs
->rs_rxlock
);
189 MUTEX_DESTROY(&rs
->rs_rxlock
);
192 /* -------------------------------------------------------------------- */
193 /* Function: ippr_rpcb_in */
194 /* Returns: int - APR_ERR(1) == drop the packet, */
195 /* APR_ERR(2) == kill the proxy session, */
196 /* else change in packet length (in bytes) */
197 /* Parameters: fin(I) - pointer to packet information */
198 /* ip(I) - pointer to packet header */
199 /* aps(I) - pointer to proxy session structure */
200 /* nat(I) - pointer to NAT session structure */
202 /* Given a presumed RPCB request, perform some minor tests and pass off */
203 /* for decoding. Also pass packet off for a rewrite if necessary. */
204 /* -------------------------------------------------------------------- */
206 ippr_rpcb_in(fin
, aps
, nat
)
211 rpc_msg_t rpcmsg
, *rm
;
217 /* Disallow fragmented or illegally short packets. */
218 if ((fin
->fin_flx
& (FI_FRAG
|FI_SHORT
)) != 0)
221 /* Perform basic variable initialization. */
222 rs
= (rpcb_session_t
*)aps
->aps_data
;
225 off
= (char *)fin
->fin_dp
- (char *)fin
->fin_ip
;
226 off
+= sizeof(udphdr_t
) + fin
->fin_ipoff
;
227 dlen
= fin
->fin_dlen
- sizeof(udphdr_t
);
229 /* Disallow packets outside legal range for supported requests. */
230 if ((dlen
< RPCB_REQMIN
) || (dlen
> RPCB_REQMAX
))
233 /* Copy packet over to convenience buffer. */
235 bzero((char *)rm
, sizeof(*rm
));
236 COPYDATA(m
, off
, dlen
, (caddr_t
)&rm
->rm_msgbuf
);
237 rm
->rm_buflen
= dlen
;
239 /* Send off to decode request. */
240 rv
= ippr_rpcb_decodereq(fin
, nat
, rs
, rm
);
251 rv
= ippr_rpcb_modreq(fin
, nat
, rm
, m
, off
);
254 /*CONSTANTCONDITION*/
255 IPF_PANIC(1, ("illegal rv %d (ippr_rpcb_req)", rv
));
261 /* -------------------------------------------------------------------- */
262 /* Function: ippr_rpcb_out */
263 /* Returns: int - APR_ERR(1) == drop the packet, */
264 /* APR_ERR(2) == kill the proxy session, */
265 /* else change in packet length (in bytes) */
266 /* Parameters: fin(I) - pointer to packet information */
267 /* ip(I) - pointer to packet header */
268 /* aps(I) - pointer to proxy session structure */
269 /* nat(I) - pointer to NAT session structure */
271 /* Given a presumed RPCB reply, perform some minor tests and pass off */
272 /* for decoding. If the message indicates a successful request with */
273 /* valid addressing information, create NAT and state structures to */
274 /* allow direct communication between RPC client and server. */
275 /* -------------------------------------------------------------------- */
277 ippr_rpcb_out(fin
, aps
, nat
)
282 rpc_msg_t rpcmsg
, *rm
;
289 /* Disallow fragmented or illegally short packets. */
290 if ((fin
->fin_flx
& (FI_FRAG
|FI_SHORT
)) != 0)
293 /* Perform basic variable initialization. */
294 rs
= (rpcb_session_t
*)aps
->aps_data
;
298 off
= (char *)fin
->fin_dp
- (char *)fin
->fin_ip
;
299 off
+= sizeof(udphdr_t
) + fin
->fin_ipoff
;
300 dlen
= fin
->fin_dlen
- sizeof(udphdr_t
);
303 /* Disallow packets outside legal range for supported requests. */
304 if ((dlen
< RPCB_REPMIN
) || (dlen
> RPCB_REPMAX
))
307 /* Copy packet over to convenience buffer. */
309 bzero((char *)rm
, sizeof(*rm
));
310 COPYDATA(m
, off
, dlen
, (caddr_t
)&rm
->rm_msgbuf
);
311 rm
->rm_buflen
= dlen
;
313 rx
= NULL
; /* XXX gcc */
315 /* Send off to decode reply. */
316 rv
= ippr_rpcb_decoderep(fin
, nat
, rs
, rm
, &rx
);
320 case -1: /* Bad packet */
322 MUTEX_ENTER(&rs
->rs_rxlock
);
323 ippr_rpcb_deref(rs
, rx
);
324 MUTEX_EXIT(&rs
->rs_rxlock
);
329 case 0: /* Negative reply / request rejected */
331 case 1: /* Positive reply */
333 * With the IP address embedded in a GETADDR(LIST) reply,
334 * we'll need to rewrite the packet in the very possible
335 * event that the internal & external addresses aren't the
336 * same. (i.e., this box is either a router or rpcbind
337 * only listens on loopback.)
339 if (nat
->nat_inip
.s_addr
!= nat
->nat_outip
.s_addr
) {
340 if (rx
->rx_type
== RPCB_RES_STRING
)
341 diff
= ippr_rpcb_modv3(fin
, nat
, rm
, m
, off
);
342 else if (rx
->rx_type
== RPCB_RES_LIST
)
343 diff
= ippr_rpcb_modv4(fin
, nat
, rm
, m
, off
);
347 /*CONSTANTCONDITION*/
348 IPF_PANIC(1, ("illegal rv %d (ippr_rpcb_decoderep)", rv
));
352 MUTEX_ENTER(&rs
->rs_rxlock
);
353 /* XXX Gross hack - I'm overloading the reference
354 * counter to deal with both threads and retransmitted
355 * requests. One deref signals that this thread is
356 * finished with rx, and the other signals that we've
357 * processed its reply.
359 ippr_rpcb_deref(rs
, rx
);
360 ippr_rpcb_deref(rs
, rx
);
361 MUTEX_EXIT(&rs
->rs_rxlock
);
368 * Private support subroutines
371 /* -------------------------------------------------------------------- */
372 /* Function: ippr_rpcb_flush */
374 /* Parameters: rs(I) - pointer to RPCB session structure */
376 /* Simply flushes the list of outstanding transactions, if any. */
377 /* -------------------------------------------------------------------- */
382 rpcb_xact_t
*r1
, *r2
;
395 /* -------------------------------------------------------------------- */
396 /* Function: ippr_rpcb_decodereq */
397 /* Returns: int - -1 == bad request or critical failure, */
398 /* 0 == request successfully decoded, */
399 /* 1 == request successfully decoded; requires */
400 /* address rewrite/modification */
401 /* Parameters: fin(I) - pointer to packet information */
402 /* nat(I) - pointer to NAT session structure */
403 /* rs(I) - pointer to RPCB session structure */
404 /* rm(I) - pointer to RPC message structure */
406 /* Take a presumed RPCB request, decode it, and store the results in */
407 /* the transaction list. If the internal target address needs to be */
408 /* modified, store its location in ptr. */
409 /* WARNING: It's the responsibility of the caller to make sure there */
410 /* is enough room in rs_buf for the basic RPC message "preamble". */
411 /* -------------------------------------------------------------------- */
413 ippr_rpcb_decodereq(fin
, nat
, rs
, rm
)
425 p
= (u_32_t
*)rm
->rm_msgbuf
;
428 bzero((char *)&rx
, sizeof(rx
));
432 rx
.rx_xid
= B(p
++); /* Record this message's XID. */
434 /* Parse out and test the RPC header. */
435 if ((B(p
++) != RPCB_CALL
) ||
436 (B(p
++) != RPCB_MSG_VERSION
) ||
437 (B(p
++) != RPCB_PROG
))
440 /* Record the RPCB version and procedure. */
444 /* Bypass RPC authentication stuff. */
445 if (ippr_rpcb_skipauth(rm
, &rc
->rc_authcred
, &p
) != 0)
447 if (ippr_rpcb_skipauth(rm
, &rc
->rc_authverf
, &p
) != 0)
450 /* Compare RPCB version and procedure numbers. */
451 switch(B(rc
->rc_vers
))
454 /* This proxy only supports PMAP_GETPORT. */
455 if (B(rc
->rc_proc
) != RPCB_GETPORT
)
458 /* Portmap requests contain four 4 byte parameters. */
459 if (RPCB_BUF_EQ(rm
, p
, 16) == 0)
462 p
+= 2; /* Skip requested program and version numbers. */
464 /* Sanity check the requested protocol. */
466 if (!(xdr
== IPPROTO_UDP
|| xdr
== IPPROTO_TCP
))
469 rx
.rx_type
= RPCB_RES_PMAP
;
474 /* GETADDRLIST is exclusive to v4; GETADDR for v3 & v4 */
475 switch(B(rc
->rc_proc
))
478 rx
.rx_type
= RPCB_RES_STRING
;
479 rx
.rx_proto
= (u_int
)fin
->fin_p
;
481 case RPCB_GETADDRLIST
:
482 if (B(rc
->rc_vers
) != 4)
484 rx
.rx_type
= RPCB_RES_LIST
;
490 ra
= &rc
->rc_rpcbargs
;
492 /* Decode the 'struct rpcb' request. */
493 if (ippr_rpcb_xdrrpcb(rm
, p
, ra
) != 0)
496 /* Are the target address & port valid? */
497 if ((ra
->ra_maddr
.xu_ip
!= nat
->nat_outip
.s_addr
) ||
498 (ra
->ra_maddr
.xu_port
!= nat
->nat_outport
))
501 /* Do we need to rewrite this packet? */
502 if ((nat
->nat_outip
.s_addr
!= nat
->nat_inip
.s_addr
) ||
503 (nat
->nat_outport
!= nat
->nat_inport
))
510 MUTEX_ENTER(&rs
->rs_rxlock
);
511 if (ippr_rpcb_insert(rs
, &rx
) != 0) {
512 MUTEX_EXIT(&rs
->rs_rxlock
);
515 MUTEX_EXIT(&rs
->rs_rxlock
);
520 /* -------------------------------------------------------------------- */
521 /* Function: ippr_rpcb_skipauth */
522 /* Returns: int -- -1 == illegal auth parameters (lengths) */
523 /* 0 == valid parameters, pointer advanced */
524 /* Parameters: rm(I) - pointer to RPC message structure */
525 /* auth(I) - pointer to RPC auth structure */
526 /* buf(IO) - pointer to location within convenience buffer */
528 /* Record auth data length & location of auth data, then advance past */
530 /* -------------------------------------------------------------------- */
532 ippr_rpcb_skipauth(rm
, auth
, buf
)
541 /* Make sure we have enough space for expected fixed auth parms. */
542 if (RPCB_BUF_GEQ(rm
, p
, 8) == 0)
545 p
++; /* We don't care about auth_flavor. */
547 auth
->xa_string
.xs_len
= p
;
548 xdr
= B(p
++); /* Length of auth_data */
550 /* Test for absurdity / illegality of auth_data length. */
551 if ((XDRALIGN(xdr
) < xdr
) || (RPCB_BUF_GEQ(rm
, p
, XDRALIGN(xdr
)) == 0))
554 auth
->xa_string
.xs_str
= (char *)p
;
556 p
+= XDRALIGN(xdr
); /* Advance our location. */
563 /* -------------------------------------------------------------------- */
564 /* Function: ippr_rpcb_insert */
565 /* Returns: int -- -1 == list insertion failed, */
566 /* 0 == item successfully added */
567 /* Parameters: rs(I) - pointer to RPCB session structure */
568 /* rx(I) - pointer to RPCB transaction structure */
569 /* -------------------------------------------------------------------- */
571 ippr_rpcb_insert(rs
, rx
)
577 rxp
= ippr_rpcb_lookup(rs
, rx
->rx_xid
);
583 if (rpcbcnt
== RPCB_MAXREQS
)
586 KMALLOC(rxp
, rpcb_xact_t
*);
590 bcopy((char *)rx
, (char *)rxp
, sizeof(*rx
));
592 if (rs
->rs_rxlist
!= NULL
)
593 rs
->rs_rxlist
->rx_pnext
= &rxp
->rx_next
;
595 rxp
->rx_pnext
= &rs
->rs_rxlist
;
596 rxp
->rx_next
= rs
->rs_rxlist
;
606 /* -------------------------------------------------------------------- */
607 /* Function: ippr_rpcb_xdrrpcb */
608 /* Returns: int -- -1 == failure to properly decode the request */
609 /* 0 == rpcb successfully decoded */
610 /* Parameters: rs(I) - pointer to RPCB session structure */
611 /* p(I) - pointer to location within session buffer */
612 /* rpcb(O) - pointer to rpcb (xdr type) structure */
614 /* Decode a XDR encoded rpcb structure and record its contents in rpcb */
615 /* within only the context of TCP/UDP over IP networks. */
616 /* -------------------------------------------------------------------- */
618 ippr_rpcb_xdrrpcb(rm
, p
, ra
)
623 if (!RPCB_BUF_GEQ(rm
, p
, 20))
626 /* Bypass target program & version. */
629 /* Decode r_netid. Must be "tcp" or "udp". */
630 if (ippr_rpcb_getproto(rm
, &ra
->ra_netid
, &p
) != 0)
633 /* Decode r_maddr. */
634 if (ippr_rpcb_getuaddr(rm
, &ra
->ra_maddr
, &p
) != 0)
637 /* Advance to r_owner and make sure it's empty. */
638 if (!RPCB_BUF_EQ(rm
, p
, 4) || (B(p
) != 0))
644 /* -------------------------------------------------------------------- */
645 /* Function: ippr_rpcb_getuaddr */
646 /* Returns: int -- -1 == illegal string, */
647 /* 0 == string parsed; contents recorded */
648 /* Parameters: rm(I) - pointer to RPC message structure */
649 /* xu(I) - pointer to universal address structure */
650 /* p(IO) - pointer to location within message buffer */
652 /* Decode the IP address / port at p and record them in xu. */
653 /* -------------------------------------------------------------------- */
655 ippr_rpcb_getuaddr(rm
, xu
, p
)
660 char *c
, *i
, *b
, *pp
;
664 /* Test for string length. */
665 if (!RPCB_BUF_GEQ(rm
, *p
, 4))
668 xu
->xu_xslen
= (*p
)++;
669 xu
->xu_xsstr
= (char *)*p
;
673 if (l
< 11 || l
> 23 || !RPCB_BUF_GEQ(rm
, *p
, XDRALIGN(l
)))
677 *(char **)p
+= XDRALIGN(l
);
679 /* Copy string to local buffer & terminate C style */
680 bcopy(xu
->xu_xsstr
, uastr
, l
);
683 i
= (char *)&xu
->xu_ip
;
684 pp
= (char *)&xu
->xu_port
;
687 * Expected format: a.b.c.d.e.f where [a-d] correspond to bytes of
688 * an IP address and [ef] are the bytes of a L4 port.
690 if (!(ISDIGIT(uastr
[0]) && ISDIGIT(uastr
[l
-1])))
693 for (c
= &uastr
[1], d
= 0, dd
= 0; c
< &uastr
[l
-1]; c
++) {
702 /* Check for ASCII byte. */
704 t
= ippr_rpcb_atoi(b
);
708 /* Aim b at beginning of the next byte. */
711 /* Switch off IP addr vs port parsing. */
715 pp
[d
++ - 4] = t
& 0xff;
722 if (d
!= 5) /* String must contain exactly 5 periods. */
725 /* Handle the last byte (port low byte) */
726 t
= ippr_rpcb_atoi(b
);
729 pp
[d
- 4] = t
& 0xff;
734 /* -------------------------------------------------------------------- */
735 /* Function: ippr_rpcb_atoi (XXX should be generic for all proxies) */
736 /* Returns: int -- integer representation of supplied string */
737 /* Parameters: ptr(I) - input string */
739 /* Simple version of atoi(3) ripped from ip_rcmd_pxy.c. */
740 /* -------------------------------------------------------------------- */
745 register char *s
= ptr
, c
;
746 register u_int i
= 0;
748 while (((c
= *s
++) != '\0') && ISDIGIT(c
)) {
755 /* -------------------------------------------------------------------- */
756 /* Function: ippr_rpcb_modreq */
757 /* Returns: int -- change in datagram length */
758 /* APR_ERR(2) - critical failure */
759 /* Parameters: fin(I) - pointer to packet information */
760 /* nat(I) - pointer to NAT session */
761 /* rm(I) - pointer to RPC message structure */
762 /* m(I) - pointer to mbuf chain */
763 /* off(I) - current offset within mbuf chain */
765 /* When external and internal addresses differ, we rewrite the former */
766 /* with the latter. (This is exclusive to protocol versions 3 & 4). */
767 /* -------------------------------------------------------------------- */
769 ippr_rpcb_modreq(fin
, nat
, rm
, m
, off
)
776 u_int len
, xlen
, pos
, bogo
;
783 ra
= &rm
->rm_call
.rc_rpcbargs
;
784 i
= (char *)&nat
->nat_inip
.s_addr
;
785 p
= (char *)&nat
->nat_inport
;
787 /* Form new string. */
788 bzero(uaddr
, sizeof(uaddr
)); /* Just in case we need padding. */
789 #if defined(SNPRINTF) && defined(_KERNEL)
790 SNPRINTF(uaddr
, sizeof(uaddr
),
792 (void) sprintf(uaddr
,
794 "%u.%u.%u.%u.%u.%u", i
[0] & 0xff, i
[1] & 0xff,
795 i
[2] & 0xff, i
[3] & 0xff, p
[0] & 0xff, p
[1] & 0xff);
797 xlen
= XDRALIGN(len
);
799 /* Determine mbuf offset to start writing to. */
800 pos
= (char *)ra
->ra_maddr
.xu_xslen
- rm
->rm_msgbuf
;
803 /* Write new string length. */
805 COPYBACK(m
, off
, 4, (caddr_t
)&bogo
);
808 /* Write new string. */
809 COPYBACK(m
, off
, xlen
, uaddr
);
812 /* Write in zero r_owner. */
814 COPYBACK(m
, off
, 4, (caddr_t
)&bogo
);
816 /* Determine difference in data lengths. */
817 diff
= xlen
- XDRALIGN(B(ra
->ra_maddr
.xu_xslen
));
820 * If our new string has a different length, make necessary
825 udp
->uh_ulen
= htons(ntohs(udp
->uh_ulen
) + diff
);
826 fin
->fin_ip
->ip_len
+= diff
;
827 fin
->fin_dlen
+= diff
;
828 fin
->fin_plen
+= diff
;
829 /* XXX Storage lengths. */
835 /* -------------------------------------------------------------------- */
836 /* Function: ippr_rpcb_decoderep */
837 /* Returns: int - -1 == bad request or critical failure, */
838 /* 0 == valid, negative reply */
839 /* 1 == vaddlid, positive reply; needs no changes */
840 /* Parameters: fin(I) - pointer to packet information */
841 /* nat(I) - pointer to NAT session structure */
842 /* rs(I) - pointer to RPCB session structure */
843 /* rm(I) - pointer to RPC message structure */
844 /* rxp(O) - pointer to RPCB transaction structure */
846 /* Take a presumed RPCB reply, extract the XID, search for the original */
847 /* request information, and determine whether the request was accepted */
848 /* or rejected. With a valid accepted reply, go ahead and create NAT */
849 /* and state entries, and finish up by rewriting the packet as */
852 /* WARNING: It's the responsibility of the caller to make sure there */
853 /* is enough room in rs_buf for the basic RPC message "preamble". */
854 /* -------------------------------------------------------------------- */
856 ippr_rpcb_decoderep(fin
, nat
, rs
, rm
, rxp
)
870 p
= (u_32_t
*)rm
->rm_msgbuf
;
872 bzero((char *)&rx
, sizeof(rx
));
876 xdr
= B(p
++); /* Record this message's XID. */
879 MUTEX_ENTER(&rs
->rs_rxlock
);
880 if ((rx
= ippr_rpcb_lookup(rs
, xdr
)) == NULL
) {
881 MUTEX_EXIT(&rs
->rs_rxlock
);
884 ++rx
->rx_ref
; /* per thread reference */
885 MUTEX_EXIT(&rs
->rs_rxlock
);
889 /* Test call vs reply */
890 if (B(p
++) != RPCB_REPLY
)
893 /* Test reply_stat */
896 case RPCB_MSG_DENIED
:
898 case RPCB_MSG_ACCEPTED
:
904 /* Bypass RPC authentication stuff. */
905 if (ippr_rpcb_skipauth(rm
, &rr
->rr_authverf
, &p
) != 0)
908 /* Test accept status */
909 if (!RPCB_BUF_GEQ(rm
, p
, 4))
914 /* Parse out the expected reply */
918 /* There must be only one 4 byte argument. */
919 if (!RPCB_BUF_EQ(rm
, p
, 4))
925 /* Reply w/ a 0 port indicates service isn't registered */
929 /* Is the value sane? */
933 /* Create NAT & state table entries. */
934 if (ippr_rpcb_getnat(fin
, nat
, rx
->rx_proto
, (u_int
)xdr
) != 0)
937 case RPCB_RES_STRING
:
938 /* Expecting a XDR string; need 4 bytes for length */
939 if (!RPCB_BUF_GEQ(rm
, p
, 4))
942 rr
->rr_v3
.xu_str
.xs_len
= p
++;
943 rr
->rr_v3
.xu_str
.xs_str
= (char *)p
;
945 xdr
= B(rr
->rr_v3
.xu_xslen
);
947 /* A null string indicates an unregistered service */
948 if ((xdr
== 0) && RPCB_BUF_EQ(rm
, p
, 0))
951 /* Decode the target IP address / port. */
952 if (ippr_rpcb_getuaddr(rm
, &rr
->rr_v3
, &p
) != 0)
955 /* Validate the IP address and port contained. */
956 if (nat
->nat_inip
.s_addr
!= rr
->rr_v3
.xu_ip
)
959 /* Create NAT & state table entries. */
960 if (ippr_rpcb_getnat(fin
, nat
, rx
->rx_proto
,
961 (u_int
)rr
->rr_v3
.xu_port
) != 0)
965 if (!RPCB_BUF_GEQ(rm
, p
, 4))
967 /* rpcb_entry_list_ptr */
984 re
= &rl
->rl_entries
[rl
->rl_cnt
];
985 if (ippr_rpcb_getuaddr(rm
, &re
->re_maddr
, &p
) != 0)
987 if (ippr_rpcb_getproto(rm
, &re
->re_netid
, &p
) != 0)
989 /* re_semantics & re_pfamily length */
990 if (!RPCB_BUF_GEQ(rm
, p
, 12))
992 p
++; /* Skipping re_semantics. */
994 if ((xdr
!= 4) || strncmp((char *)p
, "inet", 4))
997 if (ippr_rpcb_getproto(rm
, &re
->re_proto
, &p
) != 0)
999 if (!RPCB_BUF_GEQ(rm
, p
, 4))
1002 if (B(re
->re_more
) > 1) /* 0,1 only legal values */
1006 if (B(re
->re_more
) == 0)
1008 /* Replies in max out at 2; TCP and/or UDP */
1014 for(rl
->rl_cnt
= 0; rl
->rl_cnt
< cnt
; rl
->rl_cnt
++) {
1015 re
= &rl
->rl_entries
[rl
->rl_cnt
];
1016 rv
= ippr_rpcb_getnat(fin
, nat
,
1017 re
->re_proto
.xp_proto
,
1018 (u_int
)re
->re_maddr
.xu_port
);
1024 /*CONSTANTCONDITION*/
1025 IPF_PANIC(1, ("illegal rx_type %d", rx
->rx_type
));
1031 /* -------------------------------------------------------------------- */
1032 /* Function: ippr_rpcb_lookup */
1033 /* Returns: rpcb_xact_t * - NULL == no matching record, */
1034 /* else pointer to relevant entry */
1035 /* Parameters: rs(I) - pointer to RPCB session */
1036 /* xid(I) - XID to look for */
1037 /* -------------------------------------------------------------------- */
1038 static rpcb_xact_t
*
1039 ippr_rpcb_lookup(rs
, xid
)
1045 if (rs
->rs_rxlist
== NULL
)
1048 for (rx
= rs
->rs_rxlist
; rx
!= NULL
; rx
= rx
->rx_next
)
1049 if (rx
->rx_xid
== xid
)
1055 /* -------------------------------------------------------------------- */
1056 /* Function: ippr_rpcb_deref */
1057 /* Returns: (void) */
1058 /* Parameters: rs(I) - pointer to RPCB session */
1059 /* rx(I) - pointer to RPC transaction struct to remove */
1060 /* force(I) - indicates to delete entry regardless of */
1061 /* reference count */
1062 /* Locking: rs->rs_rxlock must be held write only */
1064 /* Free the RPCB transaction record rx from the chain of entries. */
1065 /* -------------------------------------------------------------------- */
1067 ippr_rpcb_deref(rs
, rx
)
1076 if (--rx
->rx_ref
!= 0)
1079 if (rx
->rx_next
!= NULL
)
1080 rx
->rx_next
->rx_pnext
= rx
->rx_pnext
;
1082 *rx
->rx_pnext
= rx
->rx_next
;
1089 /* -------------------------------------------------------------------- */
1090 /* Function: ippr_rpcb_getproto */
1091 /* Returns: int - -1 == illegal protocol/netid, */
1092 /* 0 == legal protocol/netid */
1093 /* Parameters: rm(I) - pointer to RPC message structure */
1094 /* xp(I) - pointer to netid structure */
1095 /* p(IO) - pointer to location within packet buffer */
1097 /* Decode netid/proto stored at p and record its numeric value. */
1098 /* -------------------------------------------------------------------- */
1100 ippr_rpcb_getproto(rm
, xp
, p
)
1107 /* Must have 4 bytes for length & 4 bytes for "tcp" or "udp". */
1108 if (!RPCB_BUF_GEQ(rm
, p
, 8))
1111 xp
->xp_xslen
= (*p
)++;
1112 xp
->xp_xsstr
= (char *)*p
;
1114 /* Test the string length. */
1115 len
= B(xp
->xp_xslen
);
1119 /* Test the actual string & record the protocol accordingly. */
1120 if (!strncmp((char *)xp
->xp_xsstr
, "tcp\0", 4))
1121 xp
->xp_proto
= IPPROTO_TCP
;
1122 else if (!strncmp((char *)xp
->xp_xsstr
, "udp\0", 4))
1123 xp
->xp_proto
= IPPROTO_UDP
;
1128 /* Advance past the string. */
1134 /* -------------------------------------------------------------------- */
1135 /* Function: ippr_rpcb_getnat */
1136 /* Returns: int -- -1 == failed to create table entries, */
1138 /* Parameters: fin(I) - pointer to packet information */
1139 /* nat(I) - pointer to NAT table entry */
1140 /* proto(I) - transport protocol for new entries */
1141 /* port(I) - new port to use w/ wildcard table entries */
1143 /* Create state and NAT entries to handle an anticipated connection */
1144 /* attempt between RPC client and server. */
1145 /* -------------------------------------------------------------------- */
1147 ippr_rpcb_getnat(fin
, nat
, proto
, port
)
1153 ipnat_t
*ipn
, ipnat
;
1162 /* Generate dummy fr_info */
1163 bcopy((char *)fin
, (char *)&fi
, sizeof(fi
));
1165 fi
.fin_src
= fin
->fin_dst
;
1166 fi
.fin_dst
= nat
->nat_outip
;
1169 fi
.fin_dport
= port
& 0xffff;
1170 fi
.fin_flx
|= FI_IGNORE
;
1172 bzero((char *)&tcp
, sizeof(tcp
));
1173 tcp
.th_dport
= htons(port
);
1175 if (proto
== IPPROTO_TCP
) {
1176 tcp
.th_win
= htons(8192);
1177 TCP_OFF_A(&tcp
, sizeof(tcphdr_t
) >> 2);
1178 fi
.fin_dlen
= sizeof(tcphdr_t
);
1179 tcp
.th_flags
= TH_SYN
;
1182 fi
.fin_dlen
= sizeof(udphdr_t
);
1186 nflags
|= SI_W_SPORT
|NAT_SEARCH
;
1188 fi
.fin_plen
= fi
.fin_hlen
+ fi
.fin_dlen
;
1191 * Search for existing NAT & state entries. Pay close attention to
1192 * mutexes / locks grabbed from lookup routines, as not doing so could
1193 * lead to bad things.
1195 * If successful, fr_stlookup returns with ipf_state locked. We have
1196 * no use for this lock, so simply unlock it if necessary.
1198 is
= fr_stlookup(&fi
, &tcp
, NULL
);
1200 RWLOCK_EXIT(&ipf_state
);
1203 RWLOCK_EXIT(&ipf_nat
);
1205 WRITE_ENTER(&ipf_nat
);
1206 natl
= nat_inlookup(&fi
, nflags
, proto
, fi
.fin_src
, fi
.fin_dst
);
1208 if ((natl
!= NULL
) && (is
!= NULL
)) {
1209 MUTEX_DOWNGRADE(&ipf_nat
);
1213 /* Slightly modify the following structures for actual use in creating
1214 * NAT and/or state entries. We're primarily concerned with stripping
1215 * flags that may be detrimental to the creation process or simply
1216 * shouldn't be associated with a table entry.
1218 fi
.fin_fr
= &rpcbfr
;
1219 fi
.fin_flx
&= ~FI_IGNORE
;
1220 nflags
&= ~NAT_SEARCH
;
1223 /* XXX Since we're just copying the original ipn contents
1224 * back, would we be better off just sending a pointer to
1225 * the 'temp' copy off to nat_new instead?
1227 /* Generate template/bogus NAT rule. */
1228 bcopy((char *)ipn
, (char *)&ipnat
, sizeof(ipnat
));
1229 ipn
->in_flags
= nflags
& IPN_TCPUDP
;
1232 ipn
->in_pmin
= htons(fi
.fin_dport
);
1233 ipn
->in_pmax
= htons(fi
.fin_dport
);
1234 ipn
->in_pnext
= htons(fi
.fin_dport
);
1237 if (ipn
->in_flags
& IPN_FILTER
) {
1241 *ipn
->in_plabel
= '\0';
1243 /* Create NAT entry. return NULL if this fails. */
1244 MUTEX_ENTER(&ipf_nat_new
);
1245 natl
= nat_new(&fi
, ipn
, NULL
, nflags
|SI_CLONE
|NAT_SLAVE
,
1247 MUTEX_EXIT(&ipf_nat_new
);
1249 bcopy((char *)&ipnat
, (char *)ipn
, sizeof(ipnat
));
1252 MUTEX_DOWNGRADE(&ipf_nat
);
1257 (void) nat_proto(&fi
, natl
, nflags
);
1258 MUTEX_ENTER(&natl
->nat_lock
);
1259 nat_update(&fi
, natl
);
1260 MUTEX_EXIT(&natl
->nat_lock
);
1262 MUTEX_DOWNGRADE(&ipf_nat
);
1265 /* Create state entry. Return NULL if this fails. */
1266 fi
.fin_dst
= nat
->nat_inip
;
1267 fi
.fin_flx
|= FI_NATED
;
1268 fi
.fin_flx
&= ~FI_STATE
;
1269 nflags
&= NAT_TCPUDP
;
1270 nflags
|= SI_W_SPORT
|SI_CLONE
;
1272 is
= fr_addstate(&fi
, NULL
, nflags
);
1275 * XXX nat_delete is private to ip_nat.c. Should
1276 * check w/ Darren about this one.
1278 * nat_delete(natl, NL_EXPIRE);
1287 /* -------------------------------------------------------------------- */
1288 /* Function: ippr_rpcb_modv3 */
1289 /* Returns: int -- change in packet length */
1290 /* Parameters: fin(I) - pointer to packet information */
1291 /* nat(I) - pointer to NAT session */
1292 /* rm(I) - pointer to RPC message structure */
1293 /* m(I) - pointer to mbuf chain */
1294 /* off(I) - offset within mbuf chain */
1296 /* Write a new universal address string to this packet, adjusting */
1297 /* lengths as necessary. */
1298 /* -------------------------------------------------------------------- */
1300 ippr_rpcb_modv3(fin
, nat
, rm
, m
, off
)
1307 u_int len
, xlen
, pos
, bogo
;
1314 i
= (char *)&nat
->nat_outip
.s_addr
;
1315 p
= (char *)&rr
->rr_v3
.xu_port
;
1317 /* Form new string. */
1318 bzero(uaddr
, sizeof(uaddr
)); /* Just in case we need padding. */
1319 #if defined(SNPRINTF) && defined(_KERNEL)
1320 SNPRINTF(uaddr
, sizeof(uaddr
),
1322 (void) sprintf(uaddr
,
1324 "%u.%u.%u.%u.%u.%u", i
[0] & 0xff, i
[1] & 0xff,
1325 i
[2] & 0xff, i
[3] & 0xff, p
[0] & 0xff, p
[1] & 0xff);
1326 len
= strlen(uaddr
);
1327 xlen
= XDRALIGN(len
);
1329 /* Determine mbuf offset to write to. */
1330 pos
= (char *)rr
->rr_v3
.xu_xslen
- rm
->rm_msgbuf
;
1333 /* Write new string length. */
1335 COPYBACK(m
, off
, 4, (caddr_t
)&bogo
);
1338 /* Write new string. */
1339 COPYBACK(m
, off
, xlen
, uaddr
);
1341 /* Determine difference in data lengths. */
1342 diff
= xlen
- XDRALIGN(B(rr
->rr_v3
.xu_xslen
));
1345 * If our new string has a different length, make necessary
1349 ippr_rpcb_fixlen(fin
, diff
);
1354 /* -------------------------------------------------------------------- */
1355 /* Function: ippr_rpcb_modv4 */
1356 /* Returns: int -- change in packet length */
1357 /* Parameters: fin(I) - pointer to packet information */
1358 /* nat(I) - pointer to NAT session */
1359 /* rm(I) - pointer to RPC message structure */
1360 /* m(I) - pointer to mbuf chain */
1361 /* off(I) - offset within mbuf chain */
1363 /* Write new rpcb_entry list, adjusting lengths as necessary. */
1364 /* -------------------------------------------------------------------- */
1366 ippr_rpcb_modv4(fin
, nat
, rm
, m
, off
)
1373 u_int len
, xlen
, pos
, bogo
;
1385 i
= (char *)&nat
->nat_outip
.s_addr
;
1387 /* Determine mbuf offset to write to. */
1388 re
= &rl
->rl_entries
[0];
1389 pos
= (char *)re
->re_maddr
.xu_xslen
- rm
->rm_msgbuf
;
1392 for (cnt
= 0; cnt
< rl
->rl_cnt
; cnt
++) {
1393 re
= &rl
->rl_entries
[cnt
];
1394 p
= (char *)&re
->re_maddr
.xu_port
;
1396 /* Form new string. */
1397 bzero(uaddr
, sizeof(uaddr
)); /* Just in case we need
1399 #if defined(SNPRINTF) && defined(_KERNEL)
1400 SNPRINTF(uaddr
, sizeof(uaddr
),
1402 (void) sprintf(uaddr
,
1404 "%u.%u.%u.%u.%u.%u", i
[0] & 0xff,
1405 i
[1] & 0xff, i
[2] & 0xff, i
[3] & 0xff,
1406 p
[0] & 0xff, p
[1] & 0xff);
1407 len
= strlen(uaddr
);
1408 xlen
= XDRALIGN(len
);
1410 /* Write new string length. */
1412 COPYBACK(m
, off
, 4, (caddr_t
)&bogo
);
1415 /* Write new string. */
1416 COPYBACK(m
, off
, xlen
, uaddr
);
1419 /* Record any change in length. */
1420 diff
+= xlen
- XDRALIGN(B(re
->re_maddr
.xu_xslen
));
1422 /* If the length changed, copy back the rest of this entry. */
1423 len
= ((char *)re
->re_more
+ 4) -
1424 (char *)re
->re_netid
.xp_xslen
;
1426 COPYBACK(m
, off
, len
, (caddr_t
)re
->re_netid
.xp_xslen
);
1432 * If our new string has a different length, make necessary
1436 ippr_rpcb_fixlen(fin
, diff
);
1442 /* -------------------------------------------------------------------- */
1443 /* Function: ippr_rpcb_fixlen */
1444 /* Returns: (void) */
1445 /* Parameters: fin(I) - pointer to packet information */
1446 /* len(I) - change in packet length */
1448 /* Adjust various packet related lengths held in structure and packet */
1449 /* header fields. */
1450 /* -------------------------------------------------------------------- */
1452 ippr_rpcb_fixlen(fin
, len
)
1459 udp
->uh_ulen
= htons(ntohs(udp
->uh_ulen
) + len
);
1460 fin
->fin_ip
->ip_len
+= len
;
1461 fin
->fin_dlen
+= len
;
1462 fin
->fin_plen
+= len
;