1 /*
2  * Copyright (C) 1998-2003 by Darren Reed
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  *
6  * $Id: ip_rcmd_pxy.c,v 1.41.2.4 2005/02/04 10:22:55 darrenr Exp $
7  *
8  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
9  * Use is subject to license terms.
10  *
11  * Simple RCMD transparent proxy for in-kernel use.  For use with the NAT
12  * code.
13  */
14 
15 #define	IPF_RCMD_PROXY
16 
17 typedef struct ifs_rcmdpxy {
18 	frentry_t	rcmdfr;
19 	int		rcmd_proxy_init;
20 } ifs_rcmdpxy_t;
21 
22 int ippr_rcmd_init __P((void **, ipf_stack_t *));
23 void ippr_rcmd_fini __P((void **, ipf_stack_t *));
24 int ippr_rcmd_new __P((fr_info_t *, ap_session_t *, nat_t *, void *));
25 int ippr_rcmd_out __P((fr_info_t *, ap_session_t *, nat_t *, void *));
26 int ippr_rcmd_in __P((fr_info_t *, ap_session_t *, nat_t *, void *));
27 u_short ipf_rcmd_atoi __P((char *));
28 int ippr_rcmd_portmsg __P((fr_info_t *, ap_session_t *, nat_t *, ifs_rcmdpxy_t *));
29 
30 /*
31  * RCMD application proxy initialization.
32  */
33 /*ARGSUSED*/
ippr_rcmd_init(private,ifs)34 int ippr_rcmd_init(private, ifs)
35 void **private;
36 ipf_stack_t *ifs;
37 {
38 	ifs_rcmdpxy_t *ifsrcmd;
39 
40 	KMALLOC(ifsrcmd, ifs_rcmdpxy_t *);
41 	if (ifsrcmd == NULL)
42 		return -1;
43 
44 	bzero((char *)&ifsrcmd->rcmdfr, sizeof(ifsrcmd->rcmdfr));
45 	ifsrcmd->rcmdfr.fr_ref = 1;
46 	ifsrcmd->rcmdfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
47 	MUTEX_INIT(&ifsrcmd->rcmdfr.fr_lock, "RCMD proxy rule lock");
48 	ifsrcmd->rcmd_proxy_init = 1;
49 
50 	*private = (void *)ifsrcmd;
51 
52 	return 0;
53 }
54 
55 
56 /*ARGSUSED*/
ippr_rcmd_fini(private,ifs)57 void ippr_rcmd_fini(private, ifs)
58 void **private;
59 ipf_stack_t *ifs;
60 {
61 	ifs_rcmdpxy_t *ifsrcmd = *((ifs_rcmdpxy_t **)private);
62 
63 	if (ifsrcmd->rcmd_proxy_init == 1) {
64 		MUTEX_DESTROY(&ifsrcmd->rcmdfr.fr_lock);
65 		ifsrcmd->rcmd_proxy_init = 0;
66 	}
67 
68 	KFREE(ifsrcmd);
69 	*private = NULL;
70 }
71 
72 
73 /*
74  * Setup for a new RCMD proxy.
75  */
76 /*ARGSUSED*/
ippr_rcmd_new(fin,aps,nat,private)77 int ippr_rcmd_new(fin, aps, nat, private)
78 fr_info_t *fin;
79 ap_session_t *aps;
80 nat_t *nat;
81 void *private;
82 {
83 	tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp;
84 
85 	fin = fin;	/* LINT */
86 	nat = nat;	/* LINT */
87 
88 	aps->aps_psiz = sizeof(u_32_t);
89 	KMALLOCS(aps->aps_data, u_32_t *, sizeof(u_32_t));
90 	if (aps->aps_data == NULL) {
91 #ifdef IP_RCMD_PROXY_DEBUG
92 		printf("ippr_rcmd_new:KMALLOCS(%d) failed\n", sizeof(u_32_t));
93 #endif
94 		return -1;
95 	}
96 	*(u_32_t *)aps->aps_data = 0;
97 	aps->aps_sport = tcp->th_sport;
98 	aps->aps_dport = tcp->th_dport;
99 	return 0;
100 }
101 
102 
103 /*
104  * ipf_rcmd_atoi - implement a simple version of atoi
105  */
ipf_rcmd_atoi(ptr)106 u_short ipf_rcmd_atoi(ptr)
107 char *ptr;
108 {
109 	register char *s = ptr, c;
110 	register u_short i = 0;
111 
112 	while (((c = *s++) != '\0') && ISDIGIT(c)) {
113 		i *= 10;
114 		i += c - '0';
115 	}
116 	return i;
117 }
118 
119 
ippr_rcmd_portmsg(fin,aps,nat,ifsrcmd)120 int ippr_rcmd_portmsg(fin, aps, nat, ifsrcmd)
121 fr_info_t *fin;
122 ap_session_t *aps;
123 nat_t *nat;
124 ifs_rcmdpxy_t *ifsrcmd;
125 {
126 	tcphdr_t *tcp, tcph, *tcp2 = &tcph;
127 	struct in_addr swip, swip2;
128 	int off, dlen, nflags;
129 	char portbuf[8], *s;
130 	fr_info_t fi;
131 	u_short sp;
132 	nat_t *nat2;
133 	ip_t *ip;
134 	mb_t *m;
135 
136 	tcp = (tcphdr_t *)fin->fin_dp;
137 
138 	if (tcp->th_flags & TH_SYN) {
139 		*(u_32_t *)aps->aps_data = htonl(ntohl(tcp->th_seq) + 1);
140 		return 0;
141 	}
142 
143 	if ((*(u_32_t *)aps->aps_data != 0) &&
144 	    (tcp->th_seq != *(u_32_t *)aps->aps_data))
145 		return 0;
146 
147 	m = fin->fin_m;
148 	ip = fin->fin_ip;
149 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
150 
151 #ifdef __sgi
152 	dlen = fin->fin_plen - off;
153 #else
154 	dlen = MSGDSIZE(m) - off;
155 #endif
156 	if (dlen <= 0)
157 		return 0;
158 
159 	bzero(portbuf, sizeof(portbuf));
160 	COPYDATA(m, off, MIN(sizeof(portbuf), dlen), portbuf);
161 
162 	portbuf[sizeof(portbuf) - 1] = '\0';
163 	s = portbuf;
164 	sp = ipf_rcmd_atoi(s);
165 	if (sp == 0) {
166 #ifdef IP_RCMD_PROXY_DEBUG
167 		printf("ippr_rcmd_portmsg:sp == 0 dlen %d [%s]\n",
168 		       dlen, portbuf);
169 #endif
170 		return 0;
171 	}
172 
173 	/*
174 	 * Add skeleton NAT entry for connection which will come back the
175 	 * other way.
176 	 */
177 	bcopy((char *)fin, (char *)&fi, sizeof(fi));
178 	fi.fin_flx |= FI_IGNORE;
179 	fi.fin_data[0] = sp;
180 	fi.fin_data[1] = 0;
181 	if (nat->nat_dir == NAT_OUTBOUND)
182 		nat2 = nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p,
183 				     nat->nat_inip, nat->nat_oip);
184 	else
185 		nat2 = nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p,
186 				    nat->nat_inip, nat->nat_oip);
187 	if (nat2 == NULL) {
188 		int slen;
189 
190 		slen = ip->ip_len;
191 		ip->ip_len = fin->fin_hlen + sizeof(*tcp);
192 		bzero((char *)tcp2, sizeof(*tcp2));
193 		tcp2->th_win = htons(8192);
194 		tcp2->th_sport = htons(sp);
195 		tcp2->th_dport = 0; /* XXX - don't specify remote port */
196 		TCP_OFF_A(tcp2, 5);
197 		tcp2->th_flags = TH_SYN;
198 		fi.fin_dp = (char *)tcp2;
199 		fi.fin_fr = &ifsrcmd->rcmdfr;
200 		fi.fin_dlen = sizeof(*tcp2);
201 		fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
202 		fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
203 		nflags = NAT_SLAVE|IPN_TCP|SI_W_DPORT;
204 
205 		swip = ip->ip_src;
206 		swip2 = ip->ip_dst;
207 
208 		if (nat->nat_dir == NAT_OUTBOUND) {
209 			fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
210 			ip->ip_src = nat->nat_inip;
211 		} else {
212 			fi.fin_fi.fi_saddr = nat->nat_oip.s_addr;
213 			ip->ip_src = nat->nat_oip;
214 			nflags |= NAT_NOTRULEPORT;
215 		}
216 
217 		nat2 = nat_new(&fi, nat->nat_ptr, NULL, nflags, nat->nat_dir);
218 
219 		if (nat2 != NULL) {
220 			(void) nat_proto(&fi, nat2, IPN_TCP);
221 			nat_update(&fi, nat2, nat2->nat_ptr);
222 			fi.fin_ifp = NULL;
223 			if (nat->nat_dir == NAT_INBOUND) {
224 				fi.fin_fi.fi_daddr = nat->nat_inip.s_addr;
225 				ip->ip_dst = nat->nat_inip;
226 			}
227 			(void) fr_addstate(&fi, &nat2->nat_state, SI_W_DPORT);
228 		}
229 		ip->ip_len = slen;
230 		ip->ip_src = swip;
231 		ip->ip_dst = swip2;
232 	}
233 	return 0;
234 }
235 
236 
ippr_rcmd_out(fin,aps,nat,private)237 int ippr_rcmd_out(fin, aps, nat, private)
238 fr_info_t *fin;
239 ap_session_t *aps;
240 nat_t *nat;
241 void *private;
242 {
243 	if (nat->nat_dir == NAT_OUTBOUND)
244 		return ippr_rcmd_portmsg(fin, aps, nat, (ifs_rcmdpxy_t *)private);
245 	return 0;
246 }
247 
248 
ippr_rcmd_in(fin,aps,nat,private)249 int ippr_rcmd_in(fin, aps, nat, private)
250 fr_info_t *fin;
251 ap_session_t *aps;
252 nat_t *nat;
253 void *private;
254 {
255 	if (nat->nat_dir == NAT_INBOUND)
256 		return ippr_rcmd_portmsg(fin, aps, nat, (ifs_rcmdpxy_t *)private);
257 	return 0;
258 }
259