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