www.usr.com/support/gpl/USR9107_release.1.4.tar.gz
[bcm963xx.git] / kernel / linux / net / ipv4 / netfilter / ip_nat_h323.c
1 /*
2  * H.323 'brute force' extension for NAT alteration.
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.html)
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_nat.h>
21 #include <linux/netfilter_ipv4/ip_nat_helper.h>
22 #include <linux/netfilter_ipv4/ip_nat_rule.h>
23 #include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
24 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
25 #include <linux/netfilter_ipv4/ip_conntrack_h323.h>
26
27 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
28 MODULE_DESCRIPTION("H.323 'brute force' connection tracking module");
29 MODULE_LICENSE("GPL");
30
31 DECLARE_LOCK_EXTERN(ip_h323_lock);
32 struct module *ip_nat_h323 = THIS_MODULE;
33
34 #if 0
35 #define DEBUGP printk
36 #else
37 #define DEBUGP(format, args...)
38 #endif
39
40 /* FIXME: Time out? --RR */
41
42 static unsigned int
43 h225_nat_expected(struct sk_buff **pskb,
44                   unsigned int hooknum,
45                   struct ip_conntrack *ct,
46                   struct ip_nat_info *info);
47
48 static unsigned int h225_nat_help(struct ip_conntrack *ct,
49                                   struct ip_conntrack_expect *exp,
50                                   struct ip_nat_info *info,
51                                   enum ip_conntrack_info ctinfo,
52                                   unsigned int hooknum,
53                                   struct sk_buff **pskb);
54
55 static struct ip_nat_helper h245 = {
56         .list = { NULL, NULL },
57         .name = "H.245",
58         .flags = 0,
59         .me = THIS_MODULE,
60         .tuple = { .src = { .ip = 0, 
61                             .u = { .tcp = { .port = 0 } } 
62                           }, 
63                    .dst = { .ip = 0, 
64                             .u = { .all = 0 },
65                             .protonum = IPPROTO_TCP
66                           } 
67                  },
68         .mask = { .src = { .ip = 0, 
69                            .u = { .tcp = { .port = 0xffff } } 
70                          }, 
71                   .dst = { .ip = 0, 
72                            .u = { .all = 0 },
73                            .protonum = 0xffff 
74                          } 
75                 },
76         .help = h225_nat_help,
77         .expect = h225_nat_expected
78 };
79
80 static unsigned int
81 h225_nat_expected(struct sk_buff **pskb,
82                   unsigned int hooknum,
83                   struct ip_conntrack *ct,
84                   struct ip_nat_info *info)
85 {
86         struct ip_nat_multi_range mr;
87         u_int32_t newdstip, newsrcip, newip;
88         u_int16_t port;
89         struct ip_ct_h225_expect *exp_info;
90         struct ip_ct_h225_master *master_info;
91         struct ip_conntrack *master = master_ct(ct);
92         unsigned int is_h225, ret;
93
94         IP_NF_ASSERT(info);
95         IP_NF_ASSERT(master);
96
97         IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
98
99         DEBUGP("h225_nat_expected: We have a connection!\n");
100         master_info = &ct->master->expectant->help.ct_h225_info;
101         exp_info = &ct->master->help.exp_h225_info;
102
103         LOCK_BH(&ip_h323_lock);
104
105         DEBUGP("master: ");
106         DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
107         DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_REPLY].tuple);
108         DEBUGP("conntrack: ");
109         DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
110         if (exp_info->dir == IP_CT_DIR_ORIGINAL) {
111                 /* Make connection go to the client. */
112                 newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
113                 newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
114                 DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to client)\n",
115                        NIPQUAD(newsrcip), NIPQUAD(newdstip));
116         } else {
117                 /* Make the connection go to the server */
118                 newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
119                 newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
120                 DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to server)\n",
121                        NIPQUAD(newsrcip), NIPQUAD(newdstip));
122         }
123         port = exp_info->port;
124         is_h225 = master_info->is_h225 == H225_PORT;
125         UNLOCK_BH(&ip_h323_lock);
126
127         if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
128                 newip = newsrcip;
129         else
130                 newip = newdstip;
131
132         DEBUGP("h225_nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));
133
134         /* We don't want to manip the per-protocol, just the IPs... */
135         mr.rangesize = 1;
136         mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
137         mr.range[0].min_ip = mr.range[0].max_ip = newip;
138
139         /* ... unless we're doing a MANIP_DST, in which case, make
140            sure we map to the correct port */
141         if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
142                 mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
143                 mr.range[0].min = mr.range[0].max
144                         = ((union ip_conntrack_manip_proto)
145                                 { .tcp = { port } });
146         }
147
148         ret = ip_nat_setup_info(ct, &mr, hooknum);
149
150         if (is_h225) {
151                 DEBUGP("h225_nat_expected: H.225, setting NAT helper for %p\n", ct);
152                 /* NAT expectfn called with ip_nat_lock write-locked */
153                 info->helper = &h245;
154         }
155         return ret;
156 }
157
158 static int h323_signal_address_fixup(struct ip_conntrack *ct,
159                                      struct sk_buff **pskb,
160                                      enum ip_conntrack_info ctinfo)
161 {
162         struct iphdr *iph = (*pskb)->nh.iph;
163         struct tcphdr _tcph, *tcph;
164         u_int32_t tcplen, datalen;
165         struct ip_ct_h225_master *info = &ct->help.ct_h225_info;
166         struct {
167                 u_int32_t ip;
168                 u_int16_t port;
169         } __attribute__ ((__packed__)) newdata;
170         int i;
171         int ret;
172
173         tcph = skb_header_pointer(*pskb, iph->ihl * 4, sizeof(_tcph), &_tcph);
174         if (tcph == NULL)
175                 return NF_ACCEPT;
176
177         tcplen = (*pskb)->len - iph->ihl * 4;
178         datalen = tcplen - tcph->doff * 4;
179
180         MUST_BE_LOCKED(&ip_h323_lock);
181
182         DEBUGP("h323_signal_address_fixup: %s %s\n",
183                 between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)
184                         ? "yes" : "no",
185                 between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)
186                         ? "yes" : "no");
187         if (!(between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)
188                 || between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)))
189                 return 1;
190
191         DEBUGP("h323_signal_address_fixup: offsets %u + 6  and %u + 6 in %u\n",
192                 info->offset[IP_CT_DIR_ORIGINAL],
193                 info->offset[IP_CT_DIR_REPLY],
194                 tcplen);
195         DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
196         DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
197
198         for (i = 0; i < IP_CT_DIR_MAX; i++) {
199                 DEBUGP("h323_signal_address_fixup: %s %s\n",
200                         info->dir == IP_CT_DIR_ORIGINAL ? "original" : "reply",
201                         i == IP_CT_DIR_ORIGINAL ? "caller" : "callee");
202                 if (!between(info->seq[i], ntohl(tcph->seq),
203                              ntohl(tcph->seq) + datalen))
204                         continue;
205                 if (!between(info->seq[i] + 6, ntohl(tcph->seq),
206                              ntohl(tcph->seq) + datalen)) {
207                         /* Partial retransmisison. It's a cracker being funky. */
208                         if (net_ratelimit()) {
209                                 printk("H.323_NAT: partial packet %u/6 in %u/%u\n",
210                                      info->seq[i],
211                                      ntohl(tcph->seq),
212                                      ntohl(tcph->seq) + datalen);
213                         }
214                         return 0;
215                 }
216
217                 /* Change address inside packet to match way we're mapping
218                    this connection. */
219                 if (i == IP_CT_DIR_ORIGINAL) {
220                         newdata.ip = ct->tuplehash[!info->dir].tuple.dst.ip;
221                         newdata.port = ct->tuplehash[!info->dir].tuple.dst.u.tcp.port;
222                 } else {
223                         newdata.ip = ct->tuplehash[!info->dir].tuple.src.ip;
224                         newdata.port = ct->tuplehash[!info->dir].tuple.src.u.tcp.port;
225                 }
226
227                 /* Modify the packet */
228                 ret = ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
229                                                info->seq[i] - ntohl(tcph->seq),
230                                                sizeof(newdata),
231                                                (const char*)&newdata, sizeof(newdata));
232                 if (!ret)
233                         return 0;
234         }
235
236         return 1;
237 }
238
239 static int h323_data_fixup(struct ip_ct_h225_expect *info,
240                            struct ip_conntrack *ct,
241                            struct sk_buff **pskb,
242                            enum ip_conntrack_info ctinfo,
243                            struct ip_conntrack_expect *expect)
244 {
245         struct {
246                 u_int32_t ip;
247                 u_int16_t port;
248         } __attribute__ ((__packed__)) newdata;
249         struct ip_conntrack_tuple newtuple;
250         struct iphdr *iph = (*pskb)->nh.iph;
251         struct tcphdr _tcph, *tcph;
252         u_int32_t tcplen;
253         struct ip_ct_h225_master *master_info = &ct->help.ct_h225_info;
254         int is_h225;
255         int ret;
256
257         tcph = skb_header_pointer(*pskb, iph->ihl * 4, sizeof(_tcph), &_tcph);
258         if (tcph == NULL)
259                 return NF_ACCEPT;
260
261         tcplen = (*pskb)->len - iph->ihl * 4;
262
263         MUST_BE_LOCKED(&ip_h323_lock);
264         DEBUGP("h323_data_fixup: offset %u + 6 in %u\n", info->offset, tcplen);
265         DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
266         DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
267
268         if (!between(expect->seq + 6, ntohl(tcph->seq),
269                     ntohl(tcph->seq) + tcplen - tcph->doff * 4)) {
270                 /* Partial retransmisison. It's a cracker being funky. */
271                 if (net_ratelimit()) {
272                         printk("H.323_NAT: partial packet %u/6 in %u/%u\n",
273                              expect->seq,
274                              ntohl(tcph->seq),
275                              ntohl(tcph->seq) + tcplen - tcph->doff * 4);
276                 }
277                 return 0;
278         }
279
280         /* Change address inside packet to match way we're mapping
281            this connection. */
282         if (info->dir == IP_CT_DIR_REPLY) {
283                 /* Must be where client thinks server is */
284                 newdata.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
285                 /* Expect something from client->server */
286                 newtuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
287                 newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
288         } else {
289                 /* Must be where server thinks client is */
290                 newdata.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
291                 /* Expect something from server->client */
292                 newtuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
293                 newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
294         }
295
296         is_h225 = (master_info->is_h225 == H225_PORT);
297
298         if (is_h225) {
299                 newtuple.dst.protonum = IPPROTO_TCP;
300                 newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port;
301         } else {
302                 newtuple.dst.protonum = IPPROTO_UDP;
303                 newtuple.src.u.udp.port = expect->tuple.src.u.udp.port;
304         }
305
306         /* Try to get same port: if not, try to change it. */
307         for (newdata.port = ntohs(info->port); newdata.port != 0; newdata.port++) {
308                 if (is_h225)
309                         newtuple.dst.u.tcp.port = htons(newdata.port);
310                 else
311                         newtuple.dst.u.udp.port = htons(newdata.port);
312
313                 if (ip_conntrack_change_expect(expect, &newtuple) == 0)
314                         break;
315         }
316         if (newdata.port == 0) {
317                 DEBUGP("h323_data_fixup: no free port found!\n");
318                 return 0;
319         }
320
321         newdata.port = htons(newdata.port);
322
323         /* Modify the packet */
324         ret = ip_nat_mangle_tcp_packet(pskb, ct, ctinfo,
325                                        expect->seq - ntohl(tcph->seq),
326                                        sizeof(newdata),
327                                        (const char*)&newdata, sizeof(newdata));
328         if (!ret)
329                 return 0;
330
331         return 1;
332 }
333
334 static unsigned int h225_nat_help(struct ip_conntrack *ct,
335                                   struct ip_conntrack_expect *exp,
336                                   struct ip_nat_info *info,
337                                   enum ip_conntrack_info ctinfo,
338                                   unsigned int hooknum,
339                                   struct sk_buff **pskb)
340 {
341         int dir;
342         struct ip_ct_h225_expect *exp_info;
343
344         /* Only mangle things once: original direction in POST_ROUTING
345            and reply direction on PRE_ROUTING. */
346         dir = CTINFO2DIR(ctinfo);
347         DEBUGP("nat_h323: dir %s at hook %s\n",
348                dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
349                hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
350                : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
351                : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
352         if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
353               || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
354                 DEBUGP("nat_h323: Not touching dir %s at hook %s\n",
355                        dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
356                        hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
357                        : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
358                        : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
359                 return NF_ACCEPT;
360         }
361
362         if (!exp) {
363                 LOCK_BH(&ip_h323_lock);
364                 if (!h323_signal_address_fixup(ct, pskb, ctinfo)) {
365                         UNLOCK_BH(&ip_h323_lock);
366                         return NF_DROP;
367                 }
368                 UNLOCK_BH(&ip_h323_lock);
369                 return NF_ACCEPT;
370         }
371
372         exp_info = &exp->help.exp_h225_info;
373
374         LOCK_BH(&ip_h323_lock);
375         if (!h323_data_fixup(exp_info, ct, pskb, ctinfo, exp)) {
376                 UNLOCK_BH(&ip_h323_lock);
377                 return NF_DROP;
378         }
379         UNLOCK_BH(&ip_h323_lock);
380
381         return NF_ACCEPT;
382 }
383
384 static struct ip_nat_helper h225 = {
385         .list = { NULL, NULL },
386         .name = "H.225",
387         .flags = IP_NAT_HELPER_F_ALWAYS,
388         .me = THIS_MODULE,
389         .tuple = { .src = { .ip = 0, 
390                             .u = { .tcp = { .port =  
391                                     __constant_htons(H225_PORT) } } 
392                           }, 
393                    .dst = { .ip = 0, 
394                             .u = { .all = 0 },
395                             .protonum = IPPROTO_TCP
396                           } 
397                  },
398         .mask = { .src = { .ip = 0, 
399                            .u = { .tcp = { .port = 0xffff } } 
400                          }, 
401                   .dst = { .ip = 0, 
402                            .u = { .all = 0 },
403                            .protonum = 0xffff 
404                          } 
405                 },
406         .help = h225_nat_help,
407         .expect = h225_nat_expected
408 };
409
410 static int __init init(void)
411 {
412         int ret;
413
414         printk("ip_nat_h323: initialize the module!\n");
415         ret = ip_nat_helper_register(&h225);
416
417         if (ret != 0)
418                 printk("ip_nat_h323: cannot initialize the module!\n");
419
420         return ret;
421 }
422
423 static void __exit fini(void)
424 {
425         ip_nat_helper_unregister(&h225);
426 }
427
428 NEEDS_CONNTRACK(h225);
429 NEEDS_CONNTRACK(h245);
430 module_init(init);
431 module_exit(fini);