c1ccd28e19cf5a466b3e785b1dcbba9587925d6f
[bcm963xx.git] / kernel / linux / net / ipv4 / netfilter / ip_conntrack_h323.c
1 /*
2  * H.323 'brute force' extension for H.323 connection tracking.
3  * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
4  * (c) 2005 Max Kellermann <max@duempel.org>
5  *
6  * Based on ip_masq_h323.c for 2.2 kernels from CoRiTel, Sofia project.
7  * (http://www.coritel.it/projects/sofia/nat/)
8  * Uses Sampsa Ranta's excellent idea on using expectfn to 'bind'
9  * the unregistered helpers to the conntrack entries.
10  */
11
12
13 #include <linux/module.h>
14 #include <linux/netfilter.h>
15 #include <linux/ip.h>
16 #include <net/checksum.h>
17 #include <net/tcp.h>
18
19 #include <linux/netfilter_ipv4/lockhelp.h>
20 #include <linux/netfilter_ipv4/ip_conntrack.h>
21 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
22 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
23 #include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
24 #include <linux/netfilter_ipv4/ip_conntrack_h323.h>
25
26 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
27 MODULE_DESCRIPTION("H.323 'brute force' connection tracking module");
28 MODULE_LICENSE("GPL");
29
30 /* This is slow, but it's simple. --RR */
31 static char h323_buffer[65536];
32 static DECLARE_LOCK(h323_buffer_lock);
33
34 DECLARE_LOCK(ip_h323_lock);
35 struct module *ip_conntrack_h323 = THIS_MODULE;
36
37 #if 0
38 #define DEBUGP printk
39 #else
40 #define DEBUGP(format, args...)
41 #endif
42
43 /* FIXME: This should be in userspace.  Later. */
44 static int h245_help(struct sk_buff *skb,
45                      struct ip_conntrack *ct,
46                      enum ip_conntrack_info ctinfo)
47 {
48         struct iphdr *iph = skb->nh.iph;
49         struct tcphdr _tcph, *tcph;
50         unsigned char *data;
51         unsigned char *data_limit;
52         unsigned dataoff, datalen;
53         int dir = CTINFO2DIR(ctinfo);
54         struct ip_ct_h225_master *info = &ct->help.ct_h225_info;
55         struct ip_conntrack_expect *exp;
56         struct ip_ct_h225_expect *exp_info;
57         u_int16_t data_port;
58         u_int32_t data_ip;
59         unsigned int i;
60         int ret;
61
62         /* Until there's been traffic both ways, don't look in packets. */
63         if (ctinfo != IP_CT_ESTABLISHED
64             && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
65                 DEBUGP("ct_h245_help: Conntrackinfo = %u\n", ctinfo);
66                 return NF_ACCEPT;
67         }
68
69         tcph = skb_header_pointer(skb, skb->nh.iph->ihl*4,
70                                   sizeof(_tcph), &_tcph);
71         if (tcph == NULL)
72                 return NF_ACCEPT;
73
74         DEBUGP("ct_h245_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
75                 NIPQUAD(iph->saddr), ntohs(tcph->source),
76                 NIPQUAD(iph->daddr), ntohs(tcph->dest));
77
78         dataoff = skb->nh.iph->ihl*4 + tcph->doff*4;
79         /* No data? */
80         if (dataoff >= skb->len) {
81                 DEBUGP("ct_h245_help: skblen = %u\n", skb->len);
82                 return NF_ACCEPT;
83         }
84         datalen = skb->len - dataoff;
85
86         LOCK_BH(&h323_buffer_lock);
87         data = skb_header_pointer(skb, dataoff,
88                                   datalen, h323_buffer);
89         BUG_ON(data == NULL);
90
91         data_limit = data + datalen - 6;
92         /* bytes: 0123   45
93                   ipadrr port */
94         for (i = 0; data <= data_limit; data++, i++) {
95                 data_ip = *((u_int32_t *)data);
96                 if (data_ip == iph->saddr) {
97                         data_port = *((u_int16_t *)(data + 4));
98
99                         /* update the H.225 info */
100                         DEBUGP("ct_h245_help: new RTCP/RTP requested %u.%u.%u.%u:->%u.%u.%u.%u:%u\n",
101                                 NIPQUAD(ct->tuplehash[!dir].tuple.src.ip),
102                                 NIPQUAD(iph->saddr), ntohs(data_port));
103
104                         exp = ip_conntrack_expect_alloc();
105                         if (exp == NULL) {
106                                 ret = NF_ACCEPT;
107                                 goto out;
108                         }
109
110                         exp_info = &exp->help.exp_h225_info;
111
112                         LOCK_BH(&ip_h323_lock);
113                         info->is_h225 = H225_PORT + 1;
114                         exp_info->port = data_port;
115                         exp_info->dir = dir;
116                         exp_info->offset = i;
117
118                         exp->seq = ntohl(tcph->seq) + i;
119
120                         exp->tuple = ((struct ip_conntrack_tuple)
121                                 { { ct->tuplehash[!dir].tuple.src.ip,
122                                     { 0 } },
123                                   { data_ip,
124                                     { .tcp = { data_port } },
125                                     IPPROTO_UDP }});
126                         exp->mask = ((struct ip_conntrack_tuple)
127                                 { { 0xFFFFFFFF, { 0 } },
128                                   { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }});
129
130                         exp->expectfn = NULL;
131
132                         /* Ignore failure; should only happen with NAT */
133                         ip_conntrack_expect_related(exp, ct);
134
135                         UNLOCK_BH(&ip_h323_lock);
136                 }
137         }
138
139         ret = NF_ACCEPT;
140  out:
141         UNLOCK_BH(&h323_buffer_lock);
142         return ret;
143 }
144
145 /* H.245 helper is not registered! */
146 static struct ip_conntrack_helper h245 = {
147         .name = "H.245",
148         .flags = IP_CT_HELPER_F_REUSE_EXPECT,
149         .max_expected = 8,
150         .timeout = 1,
151         .tuple = { .dst = { .protonum = IPPROTO_TCP } },
152         .mask = { .src = { .u = { 0xFFFF } },
153                   .dst = { .protonum = 0xFFFF } },
154         .help = h245_help
155 };
156
157 static int h225_expect(struct ip_conntrack *ct)
158 {
159         WRITE_LOCK(&ip_conntrack_lock);
160         ct->helper = &h245;
161         DEBUGP("h225_expect: helper for %p added\n", ct);
162         WRITE_UNLOCK(&ip_conntrack_lock);
163
164         return NF_ACCEPT;       /* unused */
165 }
166
167 /* FIXME: This should be in userspace.  Later. */
168 static int h225_help(struct sk_buff *skb,
169                      struct ip_conntrack *ct,
170                      enum ip_conntrack_info ctinfo)
171 {
172         struct iphdr *iph = skb->nh.iph;
173         struct tcphdr _tcph, *tcph;
174         unsigned char *data;
175         unsigned char *data_limit;
176         unsigned dataoff, datalen;
177         int dir = CTINFO2DIR(ctinfo);
178         struct ip_ct_h225_master *info = &ct->help.ct_h225_info;
179         struct ip_conntrack_expect *exp;
180         struct ip_ct_h225_expect *exp_info;
181         u_int16_t data_port;
182         u_int32_t data_ip;
183         unsigned int i;
184         int ret;
185
186         /* Until there's been traffic both ways, don't look in packets. */
187         if (ctinfo != IP_CT_ESTABLISHED
188             && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) {
189                 DEBUGP("ct_h225_help: Conntrackinfo = %u\n", ctinfo);
190                 return NF_ACCEPT;
191         }
192
193         tcph = skb_header_pointer(skb, skb->nh.iph->ihl*4,
194                                   sizeof(_tcph), &_tcph);
195         if (tcph == NULL)
196                 return NF_ACCEPT;
197
198         DEBUGP("ct_h225_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n",
199                 NIPQUAD(iph->saddr), ntohs(tcph->source),
200                 NIPQUAD(iph->daddr), ntohs(tcph->dest));
201
202         dataoff = skb->nh.iph->ihl*4 + tcph->doff*4;
203         /* No data? */
204         if (dataoff >= skb->len) {
205                 DEBUGP("ct_h225_help: skblen = %u\n", skb->len);
206                 return NF_ACCEPT;
207         }
208         datalen = skb->len - dataoff;
209
210         LOCK_BH(&h323_buffer_lock);
211         data = skb_header_pointer(skb, dataoff,
212                                   datalen, h323_buffer);
213         BUG_ON(data == NULL);
214
215         data_limit = data + datalen - 6;
216         /* bytes: 0123   45
217                   ipadrr port */
218         for (i = 0; data <= data_limit; data++, i++) {
219                 data_ip = *((u_int32_t *)data);
220                 if (data_ip == iph->saddr) {
221                         data_port = *((u_int16_t *)(data + 4));
222                         if (data_port == tcph->source) {
223                                 /* Signal address */
224                                 DEBUGP("ct_h225_help: sourceCallSignalAddress from %u.%u.%u.%u\n",
225                                         NIPQUAD(iph->saddr));
226                                 /* Update the H.225 info so that NAT can mangle the address/port
227                                    even when we have no expected connection! */
228 #ifdef CONFIG_IP_NF_NAT_NEEDED
229                                 LOCK_BH(&ip_h323_lock);
230                                 info->dir = dir;
231                                 info->seq[IP_CT_DIR_ORIGINAL] = ntohl(tcph->seq) + i;
232                                 info->offset[IP_CT_DIR_ORIGINAL] = i;
233                                 UNLOCK_BH(&ip_h323_lock);
234 #endif
235                         } else {
236                                 /* update the H.225 info */
237                                 exp = ip_conntrack_expect_alloc();
238                                 if (exp == NULL) {
239                                         ret = NF_ACCEPT;
240                                         goto out;
241                                 }
242
243                                 exp_info = &exp->help.exp_h225_info;
244
245                                 LOCK_BH(&ip_h323_lock);
246                                 info->is_h225 = H225_PORT;
247                                 exp_info->port = data_port;
248                                 exp_info->dir = dir;
249                                 exp_info->offset = i;
250
251                                 exp->seq = ntohl(tcph->seq) + i;
252
253                                 exp->tuple = ((struct ip_conntrack_tuple)
254                                         { { ct->tuplehash[!dir].tuple.src.ip,
255                                             { 0 } },
256                                           { data_ip,
257                                             { .tcp = { data_port } },
258                                             IPPROTO_TCP }});
259                                 exp->mask = ((struct ip_conntrack_tuple)
260                                         { { 0xFFFFFFFF, { 0 } },
261                                           { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }});
262
263                                 exp->expectfn = h225_expect;
264
265                                 /* Ignore failure */
266                                 ip_conntrack_expect_related(exp, ct);
267
268                                 DEBUGP("ct_h225_help: new H.245 requested %u.%u.%u.%u->%u.%u.%u.%u:%u\n",
269                                         NIPQUAD(ct->tuplehash[!dir].tuple.src.ip),
270                                         NIPQUAD(iph->saddr), ntohs(data_port));
271
272                                 UNLOCK_BH(&ip_h323_lock);
273                         }
274 #ifdef CONFIG_IP_NF_NAT_NEEDED
275                 } else if (data_ip == iph->daddr) {
276                         data_port = *((u_int16_t *)(data + 4));
277                         if (data_port == tcph->dest) {
278                                 /* Signal address */
279                                 DEBUGP("ct_h225_help: destCallSignalAddress %u.%u.%u.%u\n",
280                                         NIPQUAD(iph->daddr));
281                                 /* Update the H.225 info so that NAT can mangle the address/port
282                                    even when we have no expected connection! */
283                                 LOCK_BH(&ip_h323_lock);
284                                 info->dir = dir;
285                                 info->seq[IP_CT_DIR_REPLY] = ntohl(tcph->seq) + i;
286                                 info->offset[IP_CT_DIR_REPLY] = i;
287                                 UNLOCK_BH(&ip_h323_lock);
288                         }
289 #endif
290                 }
291         }
292
293         ret = NF_ACCEPT;
294  out:
295         UNLOCK_BH(&h323_buffer_lock);
296         return ret;
297 }
298
299 static struct ip_conntrack_helper h225 = {
300         .name = "H.225",
301         .flags = IP_CT_HELPER_F_REUSE_EXPECT,
302         .me = THIS_MODULE,
303         .max_expected = 2,
304         .timeout = 240,
305         .tuple = { .src = { .ip = 0, 
306                             .u = { .tcp = { .port =  
307                                     __constant_htons(H225_PORT) } } 
308                           }, 
309                    .dst = { .ip = 0, 
310                             .u = { .all = 0 },
311                             .protonum = IPPROTO_TCP
312                           } 
313                  },
314         .mask = { .src = { .ip = 0, 
315                            .u = { .tcp = { .port = 0xffff } } 
316                          }, 
317                   .dst = { .ip = 0, 
318                            .u = { .all = 0 },
319                            .protonum = 0xffff 
320                          } 
321                 },
322         .help = h225_help
323 };
324
325 static int __init init(void)
326 {
327         printk("ip_conntrack_h323: init \n");
328         return ip_conntrack_helper_register(&h225);
329 }
330
331 static void __exit fini(void)
332 {
333         /* Unregister H.225 helper */
334         ip_conntrack_helper_unregister(&h225);
335 }
336
337 EXPORT_SYMBOL(ip_h323_lock);
338
339 PROVIDES_CONNTRACK(h225);
340 PROVIDES_CONNTRACK(h245);
341 module_init(init);
342 module_exit(fini);