diff options
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.c | 421 |
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); |