1 /*
2 * Copyright 2001, QNX Software Systems Ltd. All Rights Reserved
3 *
4 * This source code has been published by QNX Software Systems Ltd. (QSSL).
5 * However, any use, reproduction, modification, distribution or transfer of
6 * this software, or any software which includes or is based upon any of this
7 * code, is only permitted under the terms of the QNX Open Community License
8 * version 1.0 (see licensing.qnx.com for details) or as otherwise expressly
9 * authorized by a written license agreement from QSSL. For more information,
10 * please email licensing@qnx.com.
11 *
12 * For more details, see QNX_OCL.txt provided with this distribution.
13 *
14 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
15 * Use is subject to license terms.
16 */
17
18 /*
19 * Simple H.323 proxy
20 *
21 * by xtang@canada.com
22 * ported to ipfilter 3.4.20 by Michael Grant mg-ipf@grant.org
23 */
24
25 #pragma ident "%Z%%M% %I% %E% SMI"
26
27 #if __FreeBSD_version >= 220000 && defined(_KERNEL)
28 # include <sys/fcntl.h>
29 # include <sys/filio.h>
30 #else
31 # ifndef linux
32 # include <sys/ioctl.h>
33 # endif
34 #endif
35
36 #define IPF_H323_PROXY
37
38 typedef struct ifs_h323pxy {
39 frentry_t h323_fr;
40 int h323_proxy_init;
41 } ifs_h323pxy_t;
42
43 int ippr_h323_init __P((void **, ipf_stack_t *));
44 void ippr_h323_fini __P((void **, ipf_stack_t *));
45 int ippr_h323_new __P((fr_info_t *, ap_session_t *, nat_t *, void *));
46 void ippr_h323_del __P((ap_session_t *, void *, ipf_stack_t *));
47 int ippr_h323_out __P((fr_info_t *, ap_session_t *, nat_t *, void *));
48 int ippr_h323_in __P((fr_info_t *, ap_session_t *, nat_t *, void *));
49
50 int ippr_h245_new __P((fr_info_t *, ap_session_t *, nat_t *, void *));
51 int ippr_h245_out __P((fr_info_t *, ap_session_t *, nat_t *, void *));
52 int ippr_h245_in __P((fr_info_t *, ap_session_t *, nat_t *, void *));
53
54 static int find_port __P((int, caddr_t, int datlen, int *, u_short *));
55
56
find_port(ipaddr,data,datlen,off,port)57 static int find_port(ipaddr, data, datlen, off, port)
58 int ipaddr;
59 caddr_t data;
60 int datlen, *off;
61 unsigned short *port;
62 {
63 u_32_t addr, netaddr;
64 u_char *dp;
65 int offset;
66
67 if (datlen < 6)
68 return -1;
69
70 *port = 0;
71 offset = *off;
72 dp = (u_char *)data;
73 netaddr = ntohl(ipaddr);
74
75 for (offset = 0; offset <= datlen - 6; offset++, dp++) {
76 addr = (dp[0] << 24) | (dp[1] << 16) | (dp[2] << 8) | dp[3];
77 if (netaddr == addr)
78 {
79 *port = (*(dp + 4) << 8) | *(dp + 5);
80 break;
81 }
82 }
83 *off = offset;
84 return (offset > datlen - 6) ? -1 : 0;
85 }
86
87 /*
88 * Initialize local structures.
89 */
90 /*ARGSUSED*/
ippr_h323_init(private,ifs)91 int ippr_h323_init(private, ifs)
92 void **private;
93 ipf_stack_t *ifs;
94 {
95 ifs_h323pxy_t *ifsh323;
96
97 KMALLOC(ifsh323, ifs_h323pxy_t *);
98 if (ifsh323 == NULL)
99 return -1;
100
101 ifsh323->h323_fr.fr_ref = 1;
102 ifsh323->h323_fr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
103 MUTEX_INIT(&ifsh323->h323_fr.fr_lock, "H323 proxy rule lock");
104 ifsh323->h323_proxy_init = 1;
105
106 *private = (void *)ifsh323;
107
108 return 0;
109 }
110
111
112 /*ARGSUSED*/
ippr_h323_fini(private,ifs)113 void ippr_h323_fini(private, ifs)
114 void **private;
115 ipf_stack_t *ifs;
116 {
117 ifs_h323pxy_t *ifsh323 = *((ifs_h323pxy_t **)private);
118
119 if (ifsh323->h323_proxy_init == 1) {
120 MUTEX_DESTROY(&ifsh323->h323_fr.fr_lock);
121 ifsh323->h323_proxy_init = 0;
122 }
123
124 KFREE(ifsh323);
125 *private = NULL;
126 }
127
128 /*ARGSUSED*/
ippr_h323_new(fin,aps,nat,private)129 int ippr_h323_new(fin, aps, nat, private)
130 fr_info_t *fin;
131 ap_session_t *aps;
132 nat_t *nat;
133 void *private;
134 {
135 fin = fin; /* LINT */
136 nat = nat; /* LINT */
137
138 aps->aps_data = NULL;
139 aps->aps_psiz = 0;
140
141 return 0;
142 }
143
144 /*ARGSUSED*/
ippr_h323_del(aps,private,ifs)145 void ippr_h323_del(aps, private, ifs)
146 ap_session_t *aps;
147 void *private;
148 ipf_stack_t *ifs;
149 {
150 int i;
151 ipnat_t *ipn;
152
153 if (aps->aps_data) {
154 for (i = 0, ipn = aps->aps_data;
155 i < (aps->aps_psiz / sizeof(ipnat_t));
156 i++, ipn = (ipnat_t *)((char *)ipn + sizeof(*ipn)))
157 {
158 /*
159 * Check the comment in ippr_h323_in() function,
160 * just above fr_nat_ioctl() call.
161 * We are lucky here because this function is not
162 * called with ipf_nat locked.
163 */
164 if (fr_nat_ioctl((caddr_t)ipn, SIOCRMNAT, NAT_SYSSPACE|
165 NAT_LOCKHELD|FWRITE, 0, NULL, ifs) == -1) {
166 /*EMPTY*/;
167 /* log the error */
168 }
169 }
170 KFREES(aps->aps_data, aps->aps_psiz);
171 /* avoid double free */
172 aps->aps_data = NULL;
173 aps->aps_psiz = 0;
174 }
175 return;
176 }
177
178
179 /*ARGSUSED*/
ippr_h323_in(fin,aps,nat,private)180 int ippr_h323_in(fin, aps, nat, private)
181 fr_info_t *fin;
182 ap_session_t *aps;
183 nat_t *nat;
184 void *private;
185 {
186 int ipaddr, off, datlen;
187 unsigned short port;
188 caddr_t data;
189 tcphdr_t *tcp;
190 ip_t *ip;
191 ipf_stack_t *ifs = fin->fin_ifs;
192
193 ip = fin->fin_ip;
194 tcp = (tcphdr_t *)fin->fin_dp;
195 ipaddr = ip->ip_src.s_addr;
196
197 data = (caddr_t)tcp + (TCP_OFF(tcp) << 2);
198 datlen = fin->fin_dlen - (TCP_OFF(tcp) << 2);
199 if (find_port(ipaddr, data, datlen, &off, &port) == 0) {
200 ipnat_t *ipn;
201 char *newarray;
202
203 /* setup a nat rule to set a h245 proxy on tcp-port "port"
204 * it's like:
205 * map <if> <inter_ip>/<mask> -> <gate_ip>/<mask> proxy port <port> <port>/tcp
206 */
207 KMALLOCS(newarray, char *, aps->aps_psiz + sizeof(*ipn));
208 if (newarray == NULL) {
209 return -1;
210 }
211 ipn = (ipnat_t *)&newarray[aps->aps_psiz];
212 bcopy((caddr_t)nat->nat_ptr, (caddr_t)ipn, sizeof(ipnat_t));
213 (void) strncpy(ipn->in_plabel, "h245", APR_LABELLEN);
214
215 ipn->in_inip = nat->nat_inip.s_addr;
216 ipn->in_inmsk = 0xffffffff;
217 ipn->in_dport = htons(port);
218 /*
219 * we got a problem here. we need to call fr_nat_ioctl() to add
220 * the h245 proxy rule, but since we already hold (READ locked)
221 * the nat table rwlock (ipf_nat), if we go into fr_nat_ioctl(),
222 * it will try to WRITE lock it. This will causing dead lock
223 * on RTP.
224 *
225 * The quick & dirty solution here is release the read lock,
226 * call fr_nat_ioctl() and re-lock it.
227 * A (maybe better) solution is do a UPGRADE(), and instead
228 * of calling fr_nat_ioctl(), we add the nat rule ourself.
229 */
230 RWLOCK_EXIT(&ifs->ifs_ipf_nat);
231 if (fr_nat_ioctl((caddr_t)ipn, SIOCADNAT,
232 NAT_SYSSPACE|FWRITE, 0, NULL, ifs) == -1) {
233 READ_ENTER(&ifs->ifs_ipf_nat);
234 return -1;
235 }
236 READ_ENTER(&ifs->ifs_ipf_nat);
237 if (aps->aps_data != NULL && aps->aps_psiz > 0) {
238 bcopy(aps->aps_data, newarray, aps->aps_psiz);
239 KFREES(aps->aps_data, aps->aps_psiz);
240 }
241 aps->aps_data = newarray;
242 aps->aps_psiz += sizeof(*ipn);
243 }
244 return 0;
245 }
246
247
248 /*ARGSUSED*/
ippr_h245_new(fin,aps,nat,private)249 int ippr_h245_new(fin, aps, nat, private)
250 fr_info_t *fin;
251 ap_session_t *aps;
252 nat_t *nat;
253 void *private;
254 {
255 fin = fin; /* LINT */
256 nat = nat; /* LINT */
257
258 aps->aps_data = NULL;
259 aps->aps_psiz = 0;
260 return 0;
261 }
262
263
264 /*ARGSUSED*/
ippr_h245_out(fin,aps,nat,private)265 int ippr_h245_out(fin, aps, nat, private)
266 fr_info_t *fin;
267 ap_session_t *aps;
268 nat_t *nat;
269 void *private;
270 {
271 int ipaddr, off, datlen;
272 tcphdr_t *tcp;
273 caddr_t data;
274 u_short port;
275 ip_t *ip;
276 ipf_stack_t *ifs = fin->fin_ifs;
277
278 aps = aps; /* LINT */
279
280 ip = fin->fin_ip;
281 tcp = (tcphdr_t *)fin->fin_dp;
282 ipaddr = nat->nat_inip.s_addr;
283 data = (caddr_t)tcp + (TCP_OFF(tcp) << 2);
284 datlen = fin->fin_dlen - (TCP_OFF(tcp) << 2);
285 if (find_port(ipaddr, data, datlen, &off, &port) == 0) {
286 fr_info_t fi;
287 nat_t *nat2;
288
289 /* port = htons(port); */
290 nat2 = nat_outlookup(fin->fin_ifp, IPN_UDP, IPPROTO_UDP,
291 ip->ip_src, ip->ip_dst);
292 if (nat2 == NULL) {
293 struct ip newip;
294 struct udphdr udp;
295
296 bcopy((caddr_t)ip, (caddr_t)&newip, sizeof(newip));
297 newip.ip_len = fin->fin_hlen + sizeof(udp);
298 newip.ip_p = IPPROTO_UDP;
299 newip.ip_src = nat->nat_inip;
300
301 bzero((char *)&udp, sizeof(udp));
302 udp.uh_sport = port;
303
304 bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi));
305 fi.fin_fi.fi_p = IPPROTO_UDP;
306 fi.fin_data[0] = port;
307 fi.fin_data[1] = 0;
308 fi.fin_dp = (char *)&udp;
309
310 nat2 = nat_new(&fi, nat->nat_ptr, NULL,
311 NAT_SLAVE|IPN_UDP|SI_W_DPORT,
312 NAT_OUTBOUND);
313 if (nat2 != NULL) {
314 (void) nat_proto(&fi, nat2, IPN_UDP);
315 nat_update(&fi, nat2, nat2->nat_ptr);
316
317 nat2->nat_ptr->in_hits++;
318 #ifdef IPFILTER_LOG
319 nat_log(nat2, (u_int)(nat->nat_ptr->in_redir),
320 ifs);
321 #endif
322 bcopy((caddr_t)&ip->ip_src.s_addr,
323 data + off, 4);
324 bcopy((caddr_t)&nat2->nat_outport,
325 data + off + 4, 2);
326 }
327 }
328 }
329 return 0;
330 }
331