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