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