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