summaryrefslogtreecommitdiff
path: root/release/src/linux/linux/net/ipv4/netfilter/ip_nat_h323.c
diff options
context:
space:
mode:
Diffstat (limited to 'release/src/linux/linux/net/ipv4/netfilter/ip_nat_h323.c')
-rw-r--r--release/src/linux/linux/net/ipv4/netfilter/ip_nat_h323.c421
1 files changed, 421 insertions, 0 deletions
diff --git a/release/src/linux/linux/net/ipv4/netfilter/ip_nat_h323.c b/release/src/linux/linux/net/ipv4/netfilter/ip_nat_h323.c
new file mode 100644
index 00000000..2c080cf3
--- /dev/null
+++ b/release/src/linux/linux/net/ipv4/netfilter/ip_nat_h323.c
@@ -0,0 +1,421 @@
+/*
+ * H.323 'brute force' extension for NAT alteration.
+ * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * Based on ip_masq_h323.c for 2.2 kernels from CoRiTel, Sofia project.
+ * (http://www.coritel.it/projects/sofia/nat.html)
+ * Uses Sampsa Ranta's excellent idea on using expectfn to 'bind'
+ * the unregistered helpers to the conntrack entries.
+ */
+
+
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <net/checksum.h>
+#include <net/tcp.h>
+
+#include <linux/netfilter_ipv4/lockhelp.h>
+#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_helper.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/ip_conntrack_h323.h>
+
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
+MODULE_DESCRIPTION("H.323 'brute force' connection tracking module");
+MODULE_LICENSE("GPL");
+
+DECLARE_LOCK_EXTERN(ip_h323_lock);
+struct module *ip_nat_h323 = THIS_MODULE;
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+/* FIXME: Time out? --RR */
+
+static unsigned int
+h225_nat_expected(struct sk_buff **pskb,
+ unsigned int hooknum,
+ struct ip_conntrack *ct,
+ struct ip_nat_info *info);
+
+static unsigned int h225_nat_help(struct ip_conntrack *ct,
+ struct ip_conntrack_expect *exp,
+ struct ip_nat_info *info,
+ enum ip_conntrack_info ctinfo,
+ unsigned int hooknum,
+ struct sk_buff **pskb);
+
+static struct ip_nat_helper h245 =
+ { { NULL, NULL },
+ "H.245", /* name */
+ 0, /* flags */
+ NULL, /* module */
+ { { 0, { 0 } }, /* tuple */
+ { 0, { 0 }, IPPROTO_TCP } },
+ { { 0, { 0xFFFF } }, /* mask */
+ { 0, { 0 }, 0xFFFF } },
+ h225_nat_help, /* helper */
+ h225_nat_expected /* expectfn */
+ };
+
+static unsigned int
+h225_nat_expected(struct sk_buff **pskb,
+ unsigned int hooknum,
+ struct ip_conntrack *ct,
+ struct ip_nat_info *info)
+{
+ struct ip_nat_multi_range mr;
+ u_int32_t newdstip, newsrcip, newip;
+ u_int16_t port;
+ struct ip_ct_h225_expect *exp_info;
+ struct ip_ct_h225_master *master_info;
+ struct ip_conntrack *master = master_ct(ct);
+ unsigned int is_h225, ret;
+
+ IP_NF_ASSERT(info);
+ IP_NF_ASSERT(master);
+
+ IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
+
+ DEBUGP("h225_nat_expected: We have a connection!\n");
+ master_info = &ct->master->expectant->help.ct_h225_info;
+ exp_info = &ct->master->help.exp_h225_info;
+
+ LOCK_BH(&ip_h323_lock);
+
+ DEBUGP("master: ");
+ DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+ DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_REPLY].tuple);
+ DEBUGP("conntrack: ");
+ DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+ if (exp_info->dir == IP_CT_DIR_ORIGINAL) {
+ /* Make connection go to the client. */
+ newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
+ newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
+ DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to client)\n",
+ NIPQUAD(newsrcip), NIPQUAD(newdstip));
+ } else {
+ /* Make the connection go to the server */
+ newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
+ newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
+ DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to server)\n",
+ NIPQUAD(newsrcip), NIPQUAD(newdstip));
+ }
+ port = exp_info->port;
+ is_h225 = master_info->is_h225 == H225_PORT;
+ UNLOCK_BH(&ip_h323_lock);
+
+ if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
+ newip = newsrcip;
+ else
+ newip = newdstip;
+
+ DEBUGP("h225_nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));
+
+ mr.rangesize = 1;
+ /* We don't want to manip the per-protocol, just the IPs... */
+ mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
+ mr.range[0].min_ip = mr.range[0].max_ip = newip;
+
+ /* ... unless we're doing a MANIP_DST, in which case, make
+ sure we map to the correct port */
+ if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
+ mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+ mr.range[0].min = mr.range[0].max
+ = ((union ip_conntrack_manip_proto)
+ { port });
+ }
+
+ ret = ip_nat_setup_info(ct, &mr, hooknum);
+
+ if (is_h225) {
+ DEBUGP("h225_nat_expected: H.225, setting NAT helper for %p\n", ct);
+ /* NAT expectfn called with ip_nat_lock write-locked */
+ info->helper = &h245;
+ }
+ return ret;
+}
+
+static int h323_signal_address_fixup(struct ip_conntrack *ct,
+ struct sk_buff **pskb,
+ enum ip_conntrack_info ctinfo)
+{
+ struct iphdr *iph = (*pskb)->nh.iph;
+ struct tcphdr *tcph = (void *)iph + iph->ihl*4;
+ char *data = (char *) tcph + tcph->doff * 4;
+ u_int32_t tcplen = (*pskb)->len - iph->ihl*4;
+ u_int32_t datalen = tcplen - tcph->doff*4;
+ struct ip_ct_h225_master *info = &ct->help.ct_h225_info;
+ u_int32_t newip;
+ u_int16_t port;
+ int i;
+
+ MUST_BE_LOCKED(&ip_h323_lock);
+
+ DEBUGP("h323_signal_address_fixup: %s %s\n",
+ between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)
+ ? "yes" : "no",
+ between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)
+ ? "yes" : "no");
+ if (!(between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)
+ || between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen)))
+ return 1;
+
+ DEBUGP("h323_signal_address_fixup: offsets %u + 6 and %u + 6 in %u\n",
+ info->offset[IP_CT_DIR_ORIGINAL],
+ info->offset[IP_CT_DIR_REPLY],
+ tcplen);
+ DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+ DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+
+ for (i = 0; i < IP_CT_DIR_MAX; i++) {
+ DEBUGP("h323_signal_address_fixup: %s %s\n",
+ info->dir == IP_CT_DIR_ORIGINAL ? "original" : "reply",
+ i == IP_CT_DIR_ORIGINAL ? "caller" : "callee");
+ if (!between(info->seq[i], ntohl(tcph->seq),
+ ntohl(tcph->seq) + datalen))
+ continue;
+ if (!between(info->seq[i] + 6, ntohl(tcph->seq),
+ ntohl(tcph->seq) + datalen)) {
+ /* Partial retransmisison. It's a cracker being funky. */
+ if (net_ratelimit()) {
+ printk("H.323_NAT: partial packet %u/6 in %u/%u\n",
+ info->seq[i],
+ ntohl(tcph->seq),
+ ntohl(tcph->seq) + datalen);
+ }
+ return 0;
+ }
+
+ /* Change address inside packet to match way we're mapping
+ this connection. */
+ if (i == IP_CT_DIR_ORIGINAL) {
+ newip = ct->tuplehash[!info->dir].tuple.dst.ip;
+ port = ct->tuplehash[!info->dir].tuple.dst.u.tcp.port;
+ } else {
+ newip = ct->tuplehash[!info->dir].tuple.src.ip;
+ port = ct->tuplehash[!info->dir].tuple.src.u.tcp.port;
+ }
+
+ DEBUGP("h323_signal_address_fixup: orig %s IP:port %u.%u.%u.%u:%u\n",
+ i == IP_CT_DIR_ORIGINAL ? "source" : "dest ",
+ NIPQUAD(*((u_int32_t *)(data + info->offset[i]))),
+ ntohs(*((u_int16_t *)(data + info->offset[i] + 4))));
+
+ /* Modify the packet */
+ *(u_int32_t *)(data + info->offset[i]) = newip;
+ *(u_int16_t *)(data + info->offset[i] + 4) = port;
+
+ DEBUGP("h323_signal_address_fixup: new %s IP:port %u.%u.%u.%u:%u\n",
+ i == IP_CT_DIR_ORIGINAL ? "source" : "dest ",
+ NIPQUAD(*((u_int32_t *)(data + info->offset[i]))),
+ ntohs(*((u_int16_t *)(data + info->offset[i] + 4))));
+ }
+
+ /* fix checksum information */
+
+ (*pskb)->csum = csum_partial((char *)tcph + tcph->doff*4,
+ datalen, 0);
+
+ tcph->check = 0;
+ tcph->check = tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
+ csum_partial((char *)tcph, tcph->doff*4,
+ (*pskb)->csum));
+ ip_send_check(iph);
+
+ return 1;
+}
+
+static int h323_data_fixup(struct ip_ct_h225_expect *info,
+ struct ip_conntrack *ct,
+ struct sk_buff **pskb,
+ enum ip_conntrack_info ctinfo,
+ struct ip_conntrack_expect *expect)
+{
+ u_int32_t newip;
+ u_int16_t port;
+ struct ip_conntrack_tuple newtuple;
+ struct iphdr *iph = (*pskb)->nh.iph;
+ struct tcphdr *tcph = (void *)iph + iph->ihl*4;
+ char *data = (char *) tcph + tcph->doff * 4;
+ u_int32_t tcplen = (*pskb)->len - iph->ihl*4;
+ struct ip_ct_h225_master *master_info = &ct->help.ct_h225_info;
+ int is_h225;
+
+ MUST_BE_LOCKED(&ip_h323_lock);
+ DEBUGP("h323_data_fixup: offset %u + 6 in %u\n", info->offset, tcplen);
+ DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+ DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+
+ memset(&newtuple, 0, sizeof(newtuple));
+
+ if (!between(expect->seq + 6, ntohl(tcph->seq),
+ ntohl(tcph->seq) + tcplen - tcph->doff * 4)) {
+ /* Partial retransmisison. It's a cracker being funky. */
+ if (net_ratelimit()) {
+ printk("H.323_NAT: partial packet %u/6 in %u/%u\n",
+ expect->seq,
+ ntohl(tcph->seq),
+ ntohl(tcph->seq) + tcplen - tcph->doff * 4);
+ }
+ return 0;
+ }
+
+ /* Change address inside packet to match way we're mapping
+ this connection. */
+ if (info->dir == IP_CT_DIR_REPLY) {
+ /* Must be where client thinks server is */
+ newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
+ /* Expect something from client->server */
+ newtuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
+ newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
+ } else {
+ /* Must be where server thinks client is */
+ newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
+ /* Expect something from server->client */
+ newtuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
+ newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
+ }
+
+ is_h225 = (master_info->is_h225 == H225_PORT);
+
+ if (is_h225) {
+ newtuple.dst.protonum = IPPROTO_TCP;
+ newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port;
+ } else {
+ newtuple.dst.protonum = IPPROTO_UDP;
+ newtuple.src.u.udp.port = expect->tuple.src.u.udp.port;
+ }
+
+ /* Try to get same port: if not, try to change it. */
+ for (port = ntohs(info->port); port != 0; port++) {
+ if (is_h225)
+ newtuple.dst.u.tcp.port = htons(port);
+ else
+ newtuple.dst.u.udp.port = htons(port);
+
+ if (ip_conntrack_change_expect(expect, &newtuple) == 0)
+ break;
+ }
+ if (port == 0) {
+ DEBUGP("h323_data_fixup: no free port found!\n");
+ return 0;
+ }
+
+ port = htons(port);
+
+ DEBUGP("h323_data_fixup: orig IP:port %u.%u.%u.%u:%u\n",
+ NIPQUAD(*((u_int32_t *)(data + info->offset))),
+ ntohs(*((u_int16_t *)(data + info->offset + 4))));
+
+ /* Modify the packet */
+ *(u_int32_t *)(data + info->offset) = newip;
+ *(u_int16_t *)(data + info->offset + 4) = port;
+
+ DEBUGP("h323_data_fixup: new IP:port %u.%u.%u.%u:%u\n",
+ NIPQUAD(*((u_int32_t *)(data + info->offset))),
+ ntohs(*((u_int16_t *)(data + info->offset + 4))));
+
+ /* fix checksum information */
+ /* FIXME: usually repeated multiple times in the case of H.245! */
+
+ (*pskb)->csum = csum_partial((char *)tcph + tcph->doff*4,
+ tcplen - tcph->doff*4, 0);
+
+ tcph->check = 0;
+ tcph->check = tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
+ csum_partial((char *)tcph, tcph->doff*4,
+ (*pskb)->csum));
+ ip_send_check(iph);
+
+ return 1;
+}
+
+static unsigned int h225_nat_help(struct ip_conntrack *ct,
+ struct ip_conntrack_expect *exp,
+ struct ip_nat_info *info,
+ enum ip_conntrack_info ctinfo,
+ unsigned int hooknum,
+ struct sk_buff **pskb)
+{
+ int dir;
+ struct ip_ct_h225_expect *exp_info;
+
+ /* Only mangle things once: original direction in POST_ROUTING
+ and reply direction on PRE_ROUTING. */
+ dir = CTINFO2DIR(ctinfo);
+ DEBUGP("nat_h323: dir %s at hook %s\n",
+ dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
+ hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
+ : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
+ : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
+ if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
+ || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
+ DEBUGP("nat_h323: Not touching dir %s at hook %s\n",
+ dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
+ hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
+ : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
+ : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
+ return NF_ACCEPT;
+ }
+
+ if (!exp) {
+ LOCK_BH(&ip_h323_lock);
+ if (!h323_signal_address_fixup(ct, pskb, ctinfo)) {
+ UNLOCK_BH(&ip_h323_lock);
+ return NF_DROP;
+ }
+ UNLOCK_BH(&ip_h323_lock);
+ return NF_ACCEPT;
+ }
+
+ exp_info = &exp->help.exp_h225_info;
+
+ LOCK_BH(&ip_h323_lock);
+ if (!h323_data_fixup(exp_info, ct, pskb, ctinfo, exp)) {
+ UNLOCK_BH(&ip_h323_lock);
+ return NF_DROP;
+ }
+ UNLOCK_BH(&ip_h323_lock);
+
+ return NF_ACCEPT;
+}
+
+static struct ip_nat_helper h225 =
+ { { NULL, NULL },
+ "H.225", /* name */
+ IP_NAT_HELPER_F_ALWAYS, /* flags */
+ THIS_MODULE, /* module */
+ { { 0, { __constant_htons(H225_PORT) } }, /* tuple */
+ { 0, { 0 }, IPPROTO_TCP } },
+ { { 0, { 0xFFFF } }, /* mask */
+ { 0, { 0 }, 0xFFFF } },
+ h225_nat_help, /* helper */
+ h225_nat_expected /* expectfn */
+ };
+
+static int __init init(void)
+{
+ int ret;
+
+ ret = ip_nat_helper_register(&h225);
+
+ if (ret != 0)
+ printk("ip_nat_h323: cannot initialize the module!\n");
+
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ ip_nat_helper_unregister(&h225);
+}
+
+module_init(init);
+module_exit(fini);