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