From 691cc9529efe8ea7abaab170c452ae4470bf3ac2 Mon Sep 17 00:00:00 2001 From: Roman Yeryomin Date: Wed, 6 Feb 2013 02:59:31 +0200 Subject: Rebase files to rsdk 3.2 and refresh patches. Compilable (not by humans). Signed-off-by: Roman Yeryomin --- .../realtek/files/net/ipv4/netfilter/ipt_CONENAT.c | 655 +++++++++++++++++++++ .../files/net/ipv4/netfilter/nf_nat_ipsec.c | 622 +++++++++++++++++++ .../realtek/files/net/ipv4/netfilter/nf_nat_rtsp.c | 497 ++++++++++++++++ 3 files changed, 1774 insertions(+) create mode 100644 target/linux/realtek/files/net/ipv4/netfilter/ipt_CONENAT.c create mode 100644 target/linux/realtek/files/net/ipv4/netfilter/nf_nat_ipsec.c create mode 100644 target/linux/realtek/files/net/ipv4/netfilter/nf_nat_rtsp.c (limited to 'target/linux/realtek/files/net/ipv4') diff --git a/target/linux/realtek/files/net/ipv4/netfilter/ipt_CONENAT.c b/target/linux/realtek/files/net/ipv4/netfilter/ipt_CONENAT.c new file mode 100644 index 000000000..69f4da62a --- /dev/null +++ b/target/linux/realtek/files/net/ipv4/netfilter/ipt_CONENAT.c @@ -0,0 +1,655 @@ +/* Masquerade. Simple mapping which alters range to a local IP address + (depending on route). */ + +/* (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2004 Netfilter Core Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +//#define DEBUG 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ASSERT_READ_LOCK(x) +#define ASSERT_WRITE_LOCK(x) +#include + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Netfilter Core Team "); +MODULE_DESCRIPTION("iptables CONENAT target module"); + +#ifdef DEBUG +#undef pr_debug +#define pr_debug(fmt, args...) printk("[%s:%d] "fmt, __FUNCTION__, __LINE__, ##args) +#endif + + +/* Lock protects conenat region inside conntrack */ +static DEFINE_RWLOCK(conenat_lock); + +/* + 0, Symmetric nat + 1, full conenat + 2, restricted conenat + 3, port restricted conenat +*/ +unsigned int conenat_type = 0; + + +/****************************************************************************/ +void cone_nat_expect(struct nf_conn *ct, struct nf_conntrack_expect *exp) +{ + struct nf_nat_range range; + + /* This must be a fresh one. */ + BUG_ON(ct->status & IPS_NAT_DONE_MASK); + + /* For DST manip, map port here to where it's expected. */ + range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); + range.min = range.max = exp->saved_proto; + range.min_ip = range.max_ip + = exp->master->tuplehash[!exp->dir].tuple.src.u3.ip; + + /* hook doesn't matter, but it has to do destination manip */ + nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST); + + pr_debug("dst nat setup: %pI4:%hu\n", + &(range.min_ip), + ntohs(range.min.udp.port)); + + return; +} + +/****************************************************************************/ +static int +cone_nat_help(struct sk_buff *skb, unsigned int protoff, + struct nf_conn *ct, enum ip_conntrack_info ctinfo) +{ + int dir = CTINFO2DIR(ctinfo); + struct nf_conntrack_expect *exp; + struct nf_conntrack_tuple *tuple; + union nf_inet_addr *src_addr = NULL; + __be16 *src_port = NULL; + int ret = NF_ACCEPT; + + if (ctinfo == IP_CT_ESTABLISHED || dir != IP_CT_DIR_ORIGINAL) + return NF_ACCEPT; + + pr_debug("skb[%p] ctinfo[%d] dir[%d]\n", skb, ctinfo, dir); + pr_debug("packet[%d bytes] " + "%pI4:%hu->%pI4:%hu, " + "reply: %pI4:%hu->%pI4:%hu\n", + skb->len, + &(ct->tuplehash[dir].tuple.src.u3.ip), + ntohs(ct->tuplehash[dir].tuple.src.u.udp.port), + &(ct->tuplehash[dir].tuple.dst.u3.ip), + ntohs(ct->tuplehash[dir].tuple.dst.u.udp.port), + &(ct->tuplehash[!dir].tuple.src.u3.ip), + ntohs(ct->tuplehash[!dir].tuple.src.u.udp.port), + &(ct->tuplehash[!dir].tuple.dst.u3.ip), + ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port)); + + /* Create expect */ + if ((exp = nf_ct_expect_alloc(ct)) == NULL) + return NF_ACCEPT; + + /* + IP_CT_DIR_REPLY + 0 - symmetric nat + 1 - full, *:* -> natip:natport -> lanip:lanport + 2 - restricted, wanip:* -> natip:natport -> lanip:lanport + 3 - port restricted, wanip:wanport -> natip:natport -> lanip:lanport + */ + tuple = &ct->tuplehash[!dir].tuple; + switch (conenat_type) + { + case 1: + src_addr = NULL; + src_port = NULL; + break; + case 2: + src_addr = &tuple->src.u3; + src_port = NULL; + break; + case 3: + src_addr = &tuple->src.u3; + src_port = &tuple->src.u.udp.port; + break; + default: + src_addr = NULL; + src_port = NULL; + break; + } + nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, + nf_ct_l3num(ct), + src_addr, &tuple->dst.u3, + tuple->dst.protonum, + src_port, &tuple->dst.u.udp.port); + exp->dir = !dir; + exp->flags = NF_CT_EXPECT_PERMANENT; + exp->saved_ip = ct->tuplehash[dir].tuple.src.u3.ip; + exp->saved_proto = ct->tuplehash[dir].tuple.src.u; + exp->expectfn = cone_nat_expect; + + pr_debug("save %pI4:%hu, ", + &exp->saved_ip, ntohs(exp->saved_proto.udp.port)); + nf_ct_dump_tuple(&exp->tuple); + + /* Setup expect */ + ret = nf_ct_expect_related(exp); + nf_ct_expect_put(exp); + if (ret == 0) + { + pr_debug("expect setup, skb=%p, ret=%d.\n", skb, ret); + } + else + { + pr_debug("expect setup failed.\n"); + } + + + return NF_ACCEPT; +} + +/****************************************************************************/ +struct nf_conntrack_helper nf_conntrack_helper_cone_nat = { + .name = "CONE-NAT", + .me = THIS_MODULE, + .expect_policy = (&(struct nf_conntrack_expect_policy) { + .max_expected = 0, + .timeout = 60*60*24*365*10, + }), + .help = &cone_nat_help +}; + + +/****************************************************************************/ +static inline int +exp_cmp(const struct nf_conntrack_expect * exp, u_int32_t ip, u_int16_t port, + u_int16_t proto) +{ + pr_debug("ip[%d:%d]\n port[%d:%d]\n proto[%d:%d]\n", + exp->tuple.dst.u3.ip, ip, + exp->tuple.dst.u.udp.port, port, + exp->tuple.dst.protonum, proto); + return exp->tuple.dst.u3.ip == ip && + exp->tuple.dst.u.udp.port == port && + exp->tuple.dst.protonum == proto; +} + +/****************************************************************************/ +static inline int +exp_src_cmp(const struct nf_conntrack_expect * exp, + const struct nf_conntrack_tuple * tp) +{ + return exp->saved_ip == tp->src.u3.ip && + exp->saved_proto.udp.port == tp->src.u.udp.port && + exp->tuple.dst.protonum == tp->dst.protonum; +} + + + +/* FIXME: Multiple targets. --RR */ +static bool conenat_tg_check(const struct xt_tgchk_param *par) +{ + const struct nf_nat_multi_range_compat *mr = par->targinfo; + + if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) { + pr_debug("bad MAP_IPS.\n"); + return false; + } + if (mr->rangesize != 1) { + pr_debug("bad rangesize %u.\n", mr->rangesize); + return false; + } + + return true; +} + +unsigned int rtl_find_appropriate_newrange(struct nf_conn *ct, __be32 newsrc, const struct nf_nat_multi_range_compat *mr) +{ + struct net *net; + unsigned int ret,expectcount = net->ct.expect_count; + u_int16_t minport, maxport; + u_int16_t newport, tmpport; + struct nf_conntrack_expect *exp=NULL; + struct nf_conntrack_tuple tuple; + struct nf_nat_range newrange; + struct nf_conn_help *help = nfct_help(ct); + + net = nf_ct_net(ct); + expectcount = net->ct.expect_count; + /* Choose port */ + spin_lock_bh(&nf_conntrack_lock); + + #if 0 + exp = LIST_FIND(&nf_conntrack_expect_list, + exp_src_cmp, + struct nf_conntrack_expect *, + &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + #endif + + memset(&tuple,0,sizeof(tuple)); + + //src + tuple.src.l3num = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.l3num; + tuple.src.u3.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip; + tuple.src.u.udp.port = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port; + + //dst + tuple.dst.u3.ip = newsrc; + //tuple.dst.u.udp.port = htons(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port); + newport = htons(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port); + tuple.dst.protonum = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum; + + pr_debug("tupple1 = %pI4:%hu\n", &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip,ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port)); + + if(expectcount > 0){ + for(tmpport=0; (tmpport<=expectcount)&&(newport<=65535); tmpport++,newport++){ + tuple.dst.u.udp.port=newport; + exp = __nf_ct_expect_find_bysave(net, &tuple, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + if(exp) + break; + } + } + + if (exp) { + minport = maxport = exp->tuple.dst.u.udp.port; + pr_debug("existing mapped port = %hu\n", ntohs(minport)); + } else { + + + minport = mr->range[0].min.udp.port == 0 ? + ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port : + mr->range[0].min.udp.port; + + maxport = mr->range[0].max.udp.port == 0 ? + htons(65535) : + mr->range[0].max.udp.port; + + for (newport = ntohs(minport),tmpport = ntohs(maxport); + newport <= tmpport; newport++) { + #if 0 + exp = LIST_FIND(&ip_conntrack_expect_list, + exp_cmp, + struct nf_conntrack_expect *, + newsrc, htons(newport), ct->tuplehash[IP_CT_DIR_ORIGINAL]. + tuple.dst.protonum); + #endif + + //dst + tuple.dst.u.udp.port = htons(newport); + + exp = __nf_ct_expect_find(net, &tuple); + if (!exp) + { + pr_debug("new mapping: %pI4:%hu -> %pI4:%hu\n", + &(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip), + ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port), + &newsrc, newport); + minport = maxport = htons(newport); + break; + } + } + } + spin_unlock_bh(&nf_conntrack_lock); + + newrange.flags = mr->range[0].flags | IP_NAT_RANGE_MAP_IPS |IP_NAT_RANGE_PROTO_SPECIFIED; + newrange.min_ip = newrange.max_ip = newsrc; + newrange.min.udp.port = minport; + newrange.max.udp.port = maxport; + + /* Set ct helper */ + ret = nf_nat_setup_info(ct, &newrange, IP_NAT_MANIP_SRC); + if (ret == NF_ACCEPT) + { + rcu_read_lock(); + if (help == NULL) { + help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); + if (help == NULL) { + return NF_ACCEPT; + } + } else { + memset(&help->help, 0, sizeof(help->help)); + } + rcu_assign_pointer(help->helper, &nf_conntrack_helper_cone_nat); + rcu_read_unlock(); + } + + return ret; +} + +static unsigned int +conenat_tg(struct sk_buff *skb, const struct xt_target_param *par) +{ + struct net *net; + struct nf_conn *ct; + struct nf_conn_nat *nat; + enum ip_conntrack_info ctinfo; + struct nf_nat_range newrange; + const struct nf_nat_multi_range_compat *mr; + struct rtable *rt; + __be32 newsrc; + + NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING); + + ct = nf_ct_get(skb, &ctinfo); + nat = nfct_nat(ct); + net = nf_ct_net(ct); + + NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED + || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)); + + + /* Source address is 0.0.0.0 - locally generated packet that is + * probably not supposed to be masqueraded. + */ + if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == 0) + return NF_ACCEPT; + + mr = par->targinfo; + rt = skb->rtable; + newsrc = inet_select_addr(par->out, rt->rt_gateway, RT_SCOPE_UNIVERSE); + if (!newsrc) { + printk("CONENAT: %s ate my IP address\n", par->out->name); + return NF_DROP; + } + + write_lock_bh(&conenat_lock); + nat->masq_index = par->out->ifindex; + write_unlock_bh(&conenat_lock); + + if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == IPPROTO_UDP) + { + unsigned int ret,expectcount = net->ct.expect_count; + u_int16_t minport, maxport; + u_int16_t newport, tmpport; + struct nf_conntrack_expect *exp=NULL; + struct nf_conntrack_tuple tuple; + struct nf_conn_help *help = nfct_help(ct); + + /* Choose port */ + spin_lock_bh(&nf_conntrack_lock); + + #if 0 + exp = LIST_FIND(&nf_conntrack_expect_list, + exp_src_cmp, + struct nf_conntrack_expect *, + &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + #endif + + memset(&tuple,0,sizeof(tuple)); + + //src + tuple.src.l3num = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.l3num; + tuple.src.u3.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip; + tuple.src.u.udp.port = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port; + + //dst + tuple.dst.u3.ip = newsrc; + //tuple.dst.u.udp.port = htons(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port); + newport = htons(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port); + tuple.dst.protonum = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum; + + pr_debug("tupple1 = %pI4:%hu\n", &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip,ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port)); + + + if(expectcount > 0){ + for(tmpport=0; (tmpport<=expectcount)&&(newport<=65535); tmpport++,newport++){ + tuple.dst.u.udp.port=newport; + exp = __nf_ct_expect_find_bysave(net, &tuple, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + if(exp) + break; + } + } + + if (exp) { + minport = maxport = exp->tuple.dst.u.udp.port; + pr_debug("existing mapped port = %hu\n", ntohs(minport)); + } else { + + + minport = mr->range[0].min.udp.port == 0 ? + ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port : + mr->range[0].min.udp.port; + + maxport = mr->range[0].max.udp.port == 0 ? + htons(65535) : + mr->range[0].max.udp.port; + + for (newport = ntohs(minport),tmpport = ntohs(maxport); + newport <= tmpport; newport++) { + #if 0 + exp = LIST_FIND(&ip_conntrack_expect_list, + exp_cmp, + struct nf_conntrack_expect *, + newsrc, htons(newport), ct->tuplehash[IP_CT_DIR_ORIGINAL]. + tuple.dst.protonum); + #endif + + //dst + tuple.dst.u.udp.port = htons(newport); + + exp = __nf_ct_expect_find(net, &tuple); + if (!exp) + { + pr_debug("new mapping: %pI4:%hu -> %pI4:%hu\n", + &(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip), + ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port), + &newsrc, newport); + minport = maxport = htons(newport); + break; + } + } + } + spin_unlock_bh(&nf_conntrack_lock); + + newrange.flags = mr->range[0].flags | IP_NAT_RANGE_MAP_IPS |IP_NAT_RANGE_PROTO_SPECIFIED; + newrange.min_ip = newrange.max_ip = newsrc; + newrange.min.udp.port = minport; + newrange.max.udp.port = maxport; + + /* Set ct helper */ + ret = nf_nat_setup_info(ct, &newrange, IP_NAT_MANIP_SRC); + if (ret == NF_ACCEPT) + { + rcu_read_lock(); + if (help == NULL) { + help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); + if (help == NULL) { + return NF_ACCEPT; + } + } else { + memset(&help->help, 0, sizeof(help->help)); + } + rcu_assign_pointer(help->helper, &nf_conntrack_helper_cone_nat); + rcu_read_unlock(); + + pr_debug("helper setup, skb=%p\n", skb); + } + + return ret; + } + + /* Transfer from original range. */ + newrange = ((struct nf_nat_range) + { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, + newsrc, newsrc, + mr->range[0].min, mr->range[0].max }); + + /* Hand modified range to generic setup. */ + return nf_nat_setup_info(ct, &newrange, IP_NAT_MANIP_SRC); +} + +static int +device_cmp(struct nf_conn *i, void *ifindex) +{ + const struct nf_conn_nat *nat = nfct_nat(i); + int ret; + + if (!nat) + return 0; + + read_lock_bh(&conenat_lock); + ret = (nat->masq_index == (int)(long)ifindex); + read_unlock_bh(&conenat_lock); + + return ret; +} + +static int conenat_device_event(struct notifier_block *this, + unsigned long event, + void *ptr) +{ + const struct net_device *dev = ptr; + struct net *net = dev_net(dev); + + if (event == NETDEV_DOWN) { + /* Device was downed. Search entire table for + conntracks which were associated with that device, + and forget them. */ + NF_CT_ASSERT(dev->ifindex != 0); + + nf_ct_iterate_cleanup(net, device_cmp, + (void *)(long)dev->ifindex); + } + + return NOTIFY_DONE; +} + +static int conenat_inet_event(struct notifier_block *this, + unsigned long event, + void *ptr) +{ + struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev; + return conenat_device_event(this, event, dev); +} + +static struct notifier_block conenat_dev_notifier = { + .notifier_call = conenat_device_event, +}; + +static struct notifier_block conenat_inet_notifier = { + .notifier_call = conenat_inet_event, +}; + + +static struct proc_dir_entry *conenat_proc = NULL; +static int proc_conenat_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *out = page; + int len = 0; + + out += sprintf(out, "ConeNATType=%d\n", conenat_type); + len = out - page; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + + *start = page + off; + return len; +} + +static int proc_conenat_write( struct file *filp, const char __user *buf,unsigned long len, void *data ) +{ + int ret; + char str_buf[256]; + int val = 0; + + if(len > 255) + { + printk("Error: the value must be between 0-3\n"); + return len; + } + + copy_from_user(str_buf, buf, len); + str_buf[len] = '\0'; + + ret = sscanf(str_buf, "%d", (int*)&val); + if(ret != 1 || val < 0 || val > 3) + { + printk("Error: the value must be between 0-3\n"); + return len; + } + + conenat_type = val; + + return len; +} + + +static struct xt_target conenat_tg_reg __read_mostly = { + .name = "CONENAT", + .family = NFPROTO_IPV4, + .target = conenat_tg, + .targetsize = sizeof(struct nf_nat_multi_range_compat), + .table = "nat", + .hooks = 1 << NF_INET_POST_ROUTING, + .checkentry = conenat_tg_check, + .me = THIS_MODULE, +}; + +static int __init ipt_conenat_tg_init(void) +{ + int ret; + + conenat_proc = create_proc_entry("conenat", 0, NULL); + if (conenat_proc) { + conenat_proc->read_proc = proc_conenat_read; + conenat_proc->write_proc = proc_conenat_write; + } + + ret = xt_register_target(&conenat_tg_reg); + + if (ret == 0) { + /* Register for device down reports */ + register_netdevice_notifier(&conenat_dev_notifier); + /* Register IP address change reports */ + register_inetaddr_notifier(&conenat_inet_notifier); + } + + printk("\nipt_conenat_init for cone nat nf_conntrack \n"); + + return ret; +} + +static void __exit ipt_conenat_tg_exit(void) +{ + xt_unregister_target(&conenat_tg_reg); + unregister_netdevice_notifier(&conenat_dev_notifier); + unregister_inetaddr_notifier(&conenat_inet_notifier); +} + +module_init(ipt_conenat_tg_init); +module_exit(ipt_conenat_tg_exit); + diff --git a/target/linux/realtek/files/net/ipv4/netfilter/nf_nat_ipsec.c b/target/linux/realtek/files/net/ipv4/netfilter/nf_nat_ipsec.c new file mode 100644 index 000000000..70cde156f --- /dev/null +++ b/target/linux/realtek/files/net/ipv4/netfilter/nf_nat_ipsec.c @@ -0,0 +1,622 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP(fmt, args...) printk("%s:%s: " fmt, __FILE__, __FUNCTION__, ##args) +#else +#define DEBUGP(format, args...) +#endif + + +#if 0 +#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock) +#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock) +#endif + +struct timer_list ipsec_time; +static struct isakmp_data_s *_isakmpDb=NULL; +u_int32_t esp_addr=0; +u_int32_t esp_spi=0; +u_int16_t spi0; +u_int16_t spi1; +char ipsec_flag='1'; + +#define MAX_PORTS 8 +//lyl: in kernel 2.6.19, the tuple hash is different from kernel 2.4.* + +static void _prepend_to_hash(struct nf_conntrack_tuple_hash *tuplehash_reply,struct nf_conntrack_tuple_hash *tuplehash_orig, u_int32_t spi) +{ + u_int16_t *pu16 = (u_int16_t *)&spi; + struct nf_conntrack_tuple *tuple; + struct nf_conn *ct; + ct = nf_ct_tuplehash_to_ctrack(tuplehash_orig); + + // It's the first reply, associate to this connection + tuple = &tuplehash_reply->tuple; + + write_lock_bh(&nf_conntrack_lock); + hlist_nulls_del_rcu(&tuplehash_reply->hnnode); + hlist_nulls_del_rcu(&tuplehash_orig->hnnode); + write_unlock_bh(&nf_conntrack_lock); + + tuple->src.u.all = pu16[0]; + tuple->dst.u.all = pu16[1]; + nf_conntrack_hash_insert(ct); + DEBUGP("%s, lyl: after change ,the real conntrack hash is\n",__func__); + nf_ct_dump_tuple(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + nf_ct_dump_tuple(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); +} + +static struct isakmp_data_s* +_findEspIn(u_int32_t peer_ip,u_int32_t alias_ip,u_int32_t ispi){ + int i; + struct isakmp_data_s *tp,*new_tp; + tp = _isakmpDb; + new_tp=NULL; + + for(i = 0 ; i < MaxSession ; i++) + { + if(tp->peer_ip == peer_ip && + tp->alias_ip == alias_ip && + tp->state == IPSEC_USED && + + //(tp->ispi & ispi) == tp->ispi ) + tp->ispi == ispi ) + { + tp->idle_timer = 0; + // kaohj + //tp->ispi = ispi; + DEBUGP("Found session #%d in\n", i); + return tp; + } + tp++; + } + tp = _isakmpDb; + for(i = 0 ; i < MaxSession ; i++) + { + if(tp->peer_ip == peer_ip && + tp->alias_ip == alias_ip && + // kaohj + tp->ispi == 0 && + tp->state == IPSEC_USED) + { + // kaohj + DEBUGP("Refresh ESP session #%d on reply (new spi)\n", i); + new_tp=tp; + //lyl + break; + } + tp++; + + } + /********************* lyl chenge end *******************************/ + if(new_tp!=NULL) + { + // Here comes new spi + new_tp->idle_timer = 0; + new_tp->ispi = ispi; + + if (new_tp->pctrack != 0) + { + _prepend_to_hash(&new_tp->pctrack->tuplehash[IP_CT_DIR_REPLY],&new_tp->pctrack->tuplehash[IP_CT_DIR_ORIGINAL], ispi); + } + else + DEBUGP("should not be here!\n"); + return new_tp; + } + + return NULL; +} + + +static struct isakmp_data_s* +_findEspOut(u_int32_t local_ip,u_int32_t peer_ip, u_int32_t ospi){ + int i; + struct isakmp_data_s *tp; + tp = _isakmpDb; + + for(i = 0 ; i < MaxSession ; i++){ + if(tp->peer_ip == peer_ip && + tp->local_ip == local_ip && + + //(tp->ospi & ospi ) == tp->ospi && + tp->ospi == ospi && + tp->state == IPSEC_USED ){ + tp->idle_timer = 0; + tp->ospi = ospi; + DEBUGP("Found session #%d out\n", i); + return tp; + } + tp++; + } + + return NULL; +} + + + +static struct isakmp_data_s* +_addEsp(u_int32_t local_ip,u_int32_t peer_ip,u_int32_t ospi){ + int i; + struct isakmp_data_s *tp; + tp = _isakmpDb; + + for(i = 0 ; i < MaxSession ; i++){ + if(tp->state == IPSEC_USED && tp->peer_ip == peer_ip + && tp->local_ip == local_ip){ + // Here comes new spi + DEBUGP("New ESP session #%d out, spi=%x -> %x\n", i, tp->ospi, ospi); + tp->idle_timer = 0; + tp->ospi = ospi; + tp->ispi = 0; + return tp; + } + tp++; + } + return NULL; +} + + + + +/********************************************************************************* + * Routine Name : _findIsakmpIn + * Description : + * Input : + * Output : + * Return : + * Note : + * ThreadSafe: n + **********************************************************************************/ +static struct isakmp_data_s* +_findIsakmpIn(u_int32_t peer_ip,u_int32_t alias_ip,u_int64_t icookie,u_int64_t rcookie){ + int i; + struct isakmp_data_s *tp; + + u_int32_t *pu32; + tp = _isakmpDb; + + for(i = 0 ; i < MaxSession ; i++){ + if(tp->peer_ip == peer_ip && + // tp->alias_ip == alias_ip && + tp->icookie == icookie && + tp->state == IPSEC_USED){ + tp->idle_timer = 0; + tp->icookie = icookie; + + pu32 = (u_int32_t *)&icookie; + DEBUGP("find IKE in i=%d, local_ip=%d.%d.%d.%d, icookie=%04x%04x\n", + i, NIPQUAD(tp->local_ip), pu32[0], pu32[1]); + return tp; + } + tp++; + } + + return NULL; +} + + +/********************************************************************************* + * Routine Name : _findIsakmpOut + * Description : + * Input : + * Output : + * Return : + * Note : + * ThreadSafe: n + **********************************************************************************/ +static struct isakmp_data_s* +_findIsakmpOut(u_int32_t local_ip,u_int32_t peer_ip,u_int64_t icookie,u_int64_t rcookie){ + int i; + struct isakmp_data_s *tp = _isakmpDb; + u_int32_t *pu32; + + for(i = 0 ; i < MaxSession ; i++){ + if(tp->peer_ip == peer_ip && + tp->local_ip == local_ip && + tp->icookie == icookie && + tp->state == IPSEC_USED){ + + pu32 = (u_int32_t *)&icookie; + DEBUGP("find IKE out i=%d, local %d.%d.%d.%d peer %d.%d.%d.%d icookie=%04x%04x\n", + i,NIPQUAD(local_ip), NIPQUAD(peer_ip), pu32[0], pu32[1]); + return tp; + } + tp++; + } + + return NULL; +} + + + +/********************************************************************************* + * Routine Name : _addIsakmp + * Description : + * Input : + * Output : + * Return : + * Note : + * ThreadSafe: n + **********************************************************************************/ +static struct isakmp_data_s* +_addIsakmp(u_int32_t local_ip,u_int32_t peer_ip,u_int32_t alias_ip, + u_int64_t icookie,u_int64_t rcookie){ + int i; + struct isakmp_data_s *tp; + tp = _isakmpDb; + // find the existed one + for(i = 0 ; i < MaxSession ; i++){ + if(tp->peer_ip == peer_ip && + tp->local_ip == local_ip && + tp->state == IPSEC_USED) { + // session refresh + tp->idle_timer = 0; + tp->icookie = icookie; + tp->rcookie = rcookie; + DEBUGP("Existed session #%d been found\n", i); + return tp; + } + tp++; + } + + tp = _isakmpDb; + // if not already exists, find a new one + for(i = 0 ; i < MaxSession ; i++){ + if(tp->state == IPSEC_FREE){ + memset(tp,0,sizeof(struct isakmp_data_s)); + tp->idle_timer = 0; + tp->peer_ip = peer_ip; + tp->local_ip = local_ip; + tp->alias_ip = alias_ip; + tp->icookie = icookie; + tp->rcookie = rcookie; + tp->state = IPSEC_USED; + + DEBUGP("Free session #%d been found\n", i); + return tp; + } + tp++; + } + return NULL; +} + + +//static unsigned int esp_help(struct sk_buff **pskb, +static unsigned int esp_help(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + struct nf_conntrack_expect *exp) +{ + struct isakmp_data_s *tb; + struct nf_conntrack_tuple *tuple_temp = NULL; + struct iphdr *iph = ip_hdr(skb); + u_int32_t *spi = (void *)((char *) iph + iph->ihl * 4); + int dir = CTINFO2DIR(ctinfo); + u_int32_t s_addr, d_addr,o_spi; + o_spi= *spi; + + if(ipsec_flag=='0') + return NF_ACCEPT; + + s_addr=iph->saddr; + d_addr=iph->daddr; + DEBUGP("%d.%d.%d.%d -------> %d.%d.%d.%d, spi=%x\n", NIPQUAD(s_addr), NIPQUAD(d_addr), o_spi); + + if (dir == IP_CT_DIR_ORIGINAL) + { // original + DEBUGP("original\n"); + //printk("ori, %x, %x\n", + // (int)(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.all), + // (int)(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.all)); + //printk("rep, %x, %x\n", + // (int)(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.all), + // (int)(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.all)); + DEBUGP("findEspOut src %d.%d.%d.%d, dst %d.%d.%d.%d\n", NIPQUAD(ct->tuplehash[dir].tuple.src.ip), NIPQUAD(d_addr)); + tb = _findEspOut(ct->tuplehash[dir].tuple.src.u3.ip, d_addr, o_spi ); + if(tb==NULL) + { + tb=_addEsp(ct->tuplehash[dir].tuple.src.u3.ip,d_addr,o_spi); + if(tb != NULL) + { + // original SA been changed + if (tb->pctrack != ct){ + DEBUGP("original SA been changed, update it !\n"); + tb->pctrack = ct; + } + return NF_ACCEPT; + } + else + { + goto reply; + // maybe it's reply + //printk("can not bind to session on original\n"); + // return NF_DROP; + } + } + else + return NF_ACCEPT; + } + + else // reply + { +reply: + DEBUGP("reply\n"); + DEBUGP("findEspIn src %d.%d.%d.%d, dst %d.%d.%d.%d\n", NIPQUAD(ct->tuplehash[dir].tuple.src.u3.ip), NIPQUAD(ct->tuplehash[dir].tuple.dst.u3.ip)); + tb = _findEspIn(ct->tuplehash[dir].tuple.src.u3.ip, ct->tuplehash[dir].tuple.dst.u3.ip, o_spi); + if(tb!=NULL) + { + if (tb->pctrack != ct) + { + DEBUGP("a new ct, reply should have been refreshed\n"); + DEBUGP("modify ip from %d.%d.%d.%d to %d.%d.%d.%d\n", NIPQUAD(iph->daddr), NIPQUAD(tb->local_ip)); + skb->nfctinfo = IP_CT_RELATED; + iph->daddr=tb->local_ip; + iph->check=0; + iph->check=ip_fast_csum((unsigned char *)iph, iph->ihl); + + //lyl add the solve the first packet lose + tuple_temp = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + tuple_temp->src.u.all += 1234; + tuple_temp->dst.u.all += 1234; + tuple_temp = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; + tuple_temp->src.u.all += 1234; + tuple_temp->dst.u.all += 1234; + } + + DEBUGP("reply to localip=%x\n", tb->local_ip); + } + else + { + DEBUGP("can not bind to session on reply, drop it!\n"); + return NF_DROP; + } + } + + return NF_ACCEPT; +} + + +static unsigned int in_help(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + struct nf_conntrack_expect *exp) +{ + struct iphdr *iph = ip_hdr(skb); + struct udphdr *udph = (void *)((char *) iph + iph->ihl * 4); + int dir = CTINFO2DIR(ctinfo); + u_int32_t s_addr, d_addr; + u_int32_t *pu32; + u_int64_t *dptr,icookie, rcookie; + struct isakmp_data_s *tb; + + if(ipsec_flag=='0') + return NF_ACCEPT; + + DEBUGP("------------- In-bound helper -------------\n"); + s_addr=iph->saddr; + d_addr=iph->daddr; + dptr = (u_int64_t *) ((void *) udph + sizeof(struct udphdr)); + icookie= dptr[0]; + rcookie= dptr[1]; + DEBUGP("s_addr=%x, d_addr=%x ", s_addr, d_addr); + pu32 = (u_int32_t *)&icookie; + DEBUGP("icookie=%x, %x, \n", pu32[0], pu32[1]); + pu32 = (u_int32_t *)&rcookie; + DEBUGP("rcookie=%x, %x\n", pu32[0], pu32[1]); + + + if( rcookie!=0 || icookie==0) + { + if (ctinfo >= IP_CT_IS_REPLY) + { + DEBUGP("This is REPLY dir packet, finding in IN way\n"); + // It's reply + tb = _findIsakmpIn(ct->tuplehash[dir].tuple.src.u3.ip, ct->tuplehash[dir].tuple.dst.u3.ip, icookie, rcookie); + if(tb!=NULL) + { + if(iph->daddr != tb->local_ip) { + DEBUGP("change ip-daddr %d.%d.%d.%d to %d.%d.%d.%dn", NIPQUAD(iph->daddr),NIPQUAD( tb->local_ip)); + iph->daddr=tb->local_ip; + } + + iph->check=0; + iph->check=ip_fast_csum((unsigned char *)iph, iph->ihl); + udph->check=0; + udph->check=csum_partial((char *)udph,ntohs(udph->len),0); + udph->check=csum_tcpudp_magic(iph->saddr,iph->daddr ,ntohs(udph->len),IPPROTO_UDP,udph->check); + + DEBUGP("New local:%d.%d.%d.%d IPcheck=%x UDPcheck=%x\n",NIPQUAD(d_addr),iph->check,udph->check); + } + } + else // It's original + { + DEBUGP("This is ORIGINAL dir packet, finding in OUT way\n"); + tb = _findIsakmpOut(ct->tuplehash[dir].tuple.src.u3.ip, d_addr, icookie, rcookie); + + // Mason Yu IKE Bug + //if (tb == NULL) + if (tb == NULL && icookie!=0) + { + DEBUGP("Not found IKE session, drop it\n"); + return NF_DROP; + } + + } + } + + return NF_ACCEPT; +} + +static unsigned int out_help(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + struct nf_conntrack_expect *exp) +{ + struct iphdr *iph = ip_hdr(skb); + struct udphdr *udph = (void *)((char *) iph + iph->ihl * 4); + int dir = CTINFO2DIR(ctinfo); + u_int32_t s_addr, d_addr; + u_int32_t *pu32; + u_int64_t *dptr,icookie, rcookie; + struct isakmp_data_s *tb; + + if(ipsec_flag=='0') + return NF_ACCEPT; + + + DEBUGP("-------------- out-bound helper -----------------\n"); + s_addr=iph->saddr; + d_addr=iph->daddr; + dptr = (u_int64_t *) ((void *) udph + sizeof(struct udphdr)); + icookie= dptr[0]; + rcookie= dptr[1]; + DEBUGP("s_addr=%d.%d.%d.%d, d_addr=%d.%d.%d.%d ", NIPQUAD(s_addr), NIPQUAD(d_addr)); + pu32 = (u_int32_t *)&icookie; + DEBUGP("icookie=%x, %x, \n", pu32[0], pu32[1]); + pu32 = (u_int32_t *)&rcookie; + DEBUGP("rcookie=%x, %x\n", pu32[0], pu32[1]); + + + // lyl add this, i think it is not harm + if (dir == IP_CT_DIR_ORIGINAL) + { + if( rcookie==0 || icookie==0) + { + DEBUGP("This is ORIGINAL dir packet, finding in OUT way\n"); + tb = _findIsakmpOut(ct->tuplehash[dir].tuple.src.u3.ip, d_addr, icookie, rcookie); + + if(tb == NULL) + { + _addIsakmp(ct->tuplehash[dir].tuple.src.u3.ip,d_addr,s_addr,icookie,rcookie); + return NF_ACCEPT; + } + else + { + //printk("drop the very first IKE\n"); + // Mason Yu IKE Bug + //return NF_DROP; // first packet, should not be found + } + } + } //end dir == IP_CT_DIR_ORIGINA + + /*lyl: we must add this ,because the second PC ike packet arrive at server likes (sport:1,dport:500),but the server will + return (sport:500, dport:500), the packet will be recognize the first PC conntrack, and will SNAT to the first + conntrack, so ,we have to direct to the second conntrack mannuely. and as the help is after NAt, so we can't + compair dst ip, for it has change to the first PC ip. + */ +#if 1 + if (dir == IP_CT_DIR_REPLY) { + DEBUGP("This is REPLY dir packet, finding in IN way\n"); + // It's reply + tb = _findIsakmpIn(ct->tuplehash[dir].tuple.src.u3.ip, ct->tuplehash[dir].tuple.dst.u3.ip, icookie, rcookie); + if(tb!=NULL) + { + if(iph->daddr != tb->local_ip) { + + DEBUGP("change ip-daddr %d.%d.%d.%d to %d.%d.%d.%dn", NIPQUAD(iph->daddr),NIPQUAD( tb->local_ip)); + iph->daddr=tb->local_ip; + } + + iph->check=0; + iph->check=ip_fast_csum((unsigned char *)iph, iph->ihl); + udph->check=0; + udph->check=csum_partial((char *)udph,ntohs(udph->len),0); + udph->check=csum_tcpudp_magic(iph->saddr,iph->daddr ,ntohs(udph->len),IPPROTO_UDP,udph->check); + + DEBUGP("New local:%d.%d.%d.%d IPcheck=%x UDPcheck=%x\n",NIPQUAD(d_addr),iph->check,udph->check); + } + } +#endif + + return NF_ACCEPT; +} + + +static void check_timeout(unsigned long data) +{ + struct isakmp_data_s *tp = _isakmpDb; + int i; + + for(i=0; i < MaxSession ;i++) + { + if(tp == NULL || _isakmpDb == NULL) + break; + if(tp->state == IPSEC_FREE) + { + tp++; + continue; + } + tp->idle_timer++; + if(tp->idle_timer > IPSEC_IDLE_TIME) + { + tp->state = IPSEC_FREE; + } + tp++; + } + + ipsec_time.expires=jiffies + 100; + add_timer(&ipsec_time); +} + + +/* This function is intentionally _NOT_ defined as __exit, because + * it is needed by init() */ +static void __exit nf_nat_ipsec_fini(void) +{ + rcu_assign_pointer(nf_nat_ipsec_inbound_hook, NULL); + rcu_assign_pointer(nf_nat_ipsec_outbound_hook, NULL); + rcu_assign_pointer(nf_nat_esp_hook, NULL); + + /* Make sure noone calls it, meanwhile. */ + synchronize_net(); + + /* del timer */ + del_timer(&ipsec_time); +} + +static int __init nf_nat_ipsec_init(void) +{ + int ret = 0; + + BUG_ON(nf_nat_ipsec_inbound_hook); + rcu_assign_pointer(nf_nat_ipsec_inbound_hook, in_help); + + BUG_ON(nf_nat_ipsec_outbound_hook); + rcu_assign_pointer(nf_nat_ipsec_outbound_hook, out_help); + + BUG_ON(nf_nat_esp_hook); + rcu_assign_pointer(nf_nat_esp_hook, esp_help); + + _isakmpDb = kmalloc(MaxSession * (sizeof(struct isakmp_data_s)), GFP_KERNEL); + memset(_isakmpDb, 0, MaxSession * (sizeof(struct isakmp_data_s))); + + init_timer(&ipsec_time); + ipsec_time.expires = jiffies + 100; + ipsec_time.data = (unsigned long)_isakmpDb; + ipsec_time.function = &check_timeout; + add_timer(&ipsec_time); + + printk("nf_nat_ipsec loaded\n"); + + return ret; +} + +module_init(nf_nat_ipsec_init); +module_exit(nf_nat_ipsec_fini); + +MODULE_LICENSE("GPL"); diff --git a/target/linux/realtek/files/net/ipv4/netfilter/nf_nat_rtsp.c b/target/linux/realtek/files/net/ipv4/netfilter/nf_nat_rtsp.c new file mode 100644 index 000000000..6423661fe --- /dev/null +++ b/target/linux/realtek/files/net/ipv4/netfilter/nf_nat_rtsp.c @@ -0,0 +1,497 @@ +/* + * RTSP extension for TCP NAT alteration + * (C) 2003 by Tom Marshall + * based on ip_nat_irc.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Module load syntax: + * insmod nf_nat_rtsp.o ports=port1,port2,...port + * stunaddr=
+ * destaction=[auto|strip|none] + * + * If no ports are specified, the default will be port 554 only. + * + * stunaddr specifies the address used to detect that a client is using STUN. + * If this address is seen in the destination parameter, it is assumed that + * the client has already punched a UDP hole in the firewall, so we don't + * mangle the client_port. If none is specified, it is autodetected. It + * only needs to be set if you have multiple levels of NAT. It should be + * set to the external address that the STUN clients detect. Note that in + * this case, it will not be possible for clients to use UDP with servers + * between the NATs. + * + * If no destaction is specified, auto is used. + * destaction=auto: strip destination parameter if it is not stunaddr. + * destaction=strip: always strip destination parameter (not recommended). + * destaction=none: do not touch destination parameter (not recommended). + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#define NF_NEED_STRNCASECMP +#define NF_NEED_STRTOU16 +#include +#define NF_NEED_MIME_NEXTLINE +#include + +#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args) +#define IP_NF_RTSP_DEBUG +#ifdef IP_NF_RTSP_DEBUG +#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args) +#else +#define DEBUGP(fmt, args...) +#endif + +#define MAX_PORTS 8 +#define DSTACT_AUTO 0 +#define DSTACT_STRIP 1 +#define DSTACT_NONE 2 + +static char* stunaddr = NULL; +static char* destaction = NULL; + +static u_int32_t extip = 0; +static int dstact = 0; + +MODULE_AUTHOR("Tom Marshall "); +MODULE_DESCRIPTION("RTSP network address translation module"); +MODULE_LICENSE("GPL"); +module_param(stunaddr, charp, 0644); +MODULE_PARM_DESC(stunaddr, "Address for detecting STUN"); +module_param(destaction, charp, 0644); +MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/strip/none)"); + +#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; } + +/*** helper functions ***/ + +static void +get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint* ptcpdatalen) +{ + struct iphdr* iph = ip_hdr(skb); + struct tcphdr* tcph = (void *)iph + ip_hdrlen(skb); + + *pptcpdata = (char*)tcph + tcph->doff*4; + *ptcpdatalen = ((char*)skb_transport_header(skb) + skb->len) - *pptcpdata; +} + +/*** nat functions ***/ + +/* + * Mangle the "Transport:" header: + * - Replace all occurences of "client_port=" + * - Handle destination parameter + * + * In: + * ct, ctinfo = conntrack context + * skb = packet + * tranoff = Transport header offset from TCP data + * tranlen = Transport header length (incl. CRLF) + * rport_lo = replacement low port (host endian) + * rport_hi = replacement high port (host endian) + * + * Returns packet size difference. + * + * Assumes that a complete transport header is present, ending with CR or LF + */ +static int +rtsp_mangle_tran(enum ip_conntrack_info ctinfo, + struct nf_conntrack_expect* exp, + struct ip_ct_rtsp_expect* prtspexp, + struct sk_buff* skb, uint tranoff, uint tranlen) +{ + char* ptcp; + uint tcplen; + char* ptran; + char rbuf1[16]; /* Replacement buffer (one port) */ + uint rbuf1len; /* Replacement len (one port) */ + char rbufa[16]; /* Replacement buffer (all ports) */ + uint rbufalen; /* Replacement len (all ports) */ + u_int32_t newip; + u_int16_t loport, hiport; + uint off = 0; + uint diff; /* Number of bytes we removed */ + + struct nf_conn *ct = exp->master; + struct nf_conntrack_tuple *t; + + char szextaddr[15+1]; + uint extaddrlen; + int is_stun; + + get_skb_tcpdata(skb, &ptcp, &tcplen); + ptran = ptcp+tranoff; + + if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen || + tranlen < 10 || !iseol(ptran[tranlen-1]) || + nf_strncasecmp(ptran, "Transport:", 10) != 0) + { + pr_info("sanity check failed\n"); + return 0; + } + off += 10; + SKIP_WSPACE(ptcp+tranoff, tranlen, off); + + newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip; + t = &exp->tuple; + t->dst.u3.ip = newip; + + extaddrlen = extip ? sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(extip)) + : sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(newip)); + pr_debug("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto")); + + rbuf1len = rbufalen = 0; + switch (prtspexp->pbtype) + { + case pb_single: + for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */ + { + t->dst.u.udp.port = htons(loport); + if (nf_ct_expect_related(exp) == 0) + { + pr_debug("using port %hu\n", loport); + break; + } + } + if (loport != 0) + { + rbuf1len = sprintf(rbuf1, "%hu", loport); + rbufalen = sprintf(rbufa, "%hu", loport); + } + break; + case pb_range: + for (loport = prtspexp->loport; loport != 0; loport += 2) /* XXX: improper wrap? */ + { + t->dst.u.udp.port = htons(loport); + if (nf_ct_expect_related(exp) == 0) + { + hiport = loport + 1; //~exp->mask.dst.u.udp.port; + pr_debug("using ports %hu-%hu\n", loport, hiport); + break; + } + } + if (loport != 0) + { + rbuf1len = sprintf(rbuf1, "%hu", loport); + rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1); + } + break; + case pb_discon: + for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */ + { + t->dst.u.udp.port = htons(loport); + if (nf_ct_expect_related(exp) == 0) + { + pr_debug("using port %hu (1 of 2)\n", loport); + break; + } + } + for (hiport = prtspexp->hiport; hiport != 0; hiport++) /* XXX: improper wrap? */ + { + t->dst.u.udp.port = htons(hiport); + if (nf_ct_expect_related(exp) == 0) + { + pr_debug("using port %hu (2 of 2)\n", hiport); + break; + } + } + if (loport != 0 && hiport != 0) + { + rbuf1len = sprintf(rbuf1, "%hu", loport); + if (hiport == loport+1) + { + rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport); + } + else + { + rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport); + } + } + break; + } + + if (rbuf1len == 0) + { + return 0; /* cannot get replacement port(s) */ + } + + /* Transport: tran;field;field=val,tran;field;field=val,... */ + while (off < tranlen) + { + uint saveoff; + const char* pparamend; + uint nextparamoff; + + pparamend = memchr(ptran+off, ',', tranlen-off); + pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1; + nextparamoff = pparamend-ptcp; + + /* + * We pass over each param twice. On the first pass, we look for a + * destination= field. It is handled by the security policy. If it + * is present, allowed, and equal to our external address, we assume + * that STUN is being used and we leave the client_port= field alone. + */ + is_stun = 0; + saveoff = off; + while (off < nextparamoff) + { + const char* pfieldend; + uint nextfieldoff; + + pfieldend = memchr(ptran+off, ';', nextparamoff-off); + nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; + + if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0) + { + if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0) + { + is_stun = 1; + } + if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun)) + { + diff = nextfieldoff-off; + if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, + off, diff, NULL, 0)) + { + /* mangle failed, all we can do is bail */ + nf_ct_unexpect_related(exp); + return 0; + } + get_skb_tcpdata(skb, &ptcp, &tcplen); + ptran = ptcp+tranoff; + tranlen -= diff; + nextparamoff -= diff; + nextfieldoff -= diff; + } + } + + off = nextfieldoff; + } + if (is_stun) + { + continue; + } + off = saveoff; + while (off < nextparamoff) + { + const char* pfieldend; + uint nextfieldoff; + + pfieldend = memchr(ptran+off, ';', nextparamoff-off); + nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; + + if (strncmp(ptran+off, "client_port=", 12) == 0) + { + u_int16_t port; + uint numlen; + uint origoff; + uint origlen; + char* rbuf = rbuf1; + uint rbuflen = rbuf1len; + + off += 12; + origoff = (ptran-ptcp)+off; + origlen = 0; + numlen = nf_strtou16(ptran+off, &port); + off += numlen; + origlen += numlen; + if (port != prtspexp->loport) + { + pr_debug("multiple ports found, port %hu ignored\n", port); + } + else + { + if (ptran[off] == '-' || ptran[off] == '/') + { + off++; + origlen++; + numlen = nf_strtou16(ptran+off, &port); + off += numlen; + origlen += numlen; + rbuf = rbufa; + rbuflen = rbufalen; + } + + /* + * note we cannot just memcpy() if the sizes are the same. + * the mangle function does skb resizing, checks for a + * cloned skb, and updates the checksums. + * + * parameter 4 below is offset from start of tcp data. + */ + diff = origlen-rbuflen; + if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, + origoff, origlen, rbuf, rbuflen)) + { + /* mangle failed, all we can do is bail */ + nf_ct_unexpect_related(exp); + return 0; + } + get_skb_tcpdata(skb, &ptcp, &tcplen); + ptran = ptcp+tranoff; + tranlen -= diff; + nextparamoff -= diff; + nextfieldoff -= diff; + } + } + + off = nextfieldoff; + } + + off = nextparamoff; + } + + return 1; +} + +static uint +help_out(struct sk_buff *skb, enum ip_conntrack_info ctinfo, + unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp, + struct nf_conntrack_expect* exp) +{ + char* ptcp; + uint tcplen; + uint hdrsoff; + uint hdrslen; + uint lineoff; + uint linelen; + uint off; + + //struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph; + //struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4); + + get_skb_tcpdata(skb, &ptcp, &tcplen); + hdrsoff = matchoff;//exp->seq - ntohl(tcph->seq); + hdrslen = matchlen; + off = hdrsoff; + pr_debug("NAT rtsp help_out\n"); + + while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen)) + { + if (linelen == 0) + { + break; + } + if (off > hdrsoff+hdrslen) + { + pr_info("!! overrun !!"); + break; + } + pr_debug("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff); + + if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) + { + uint oldtcplen = tcplen; + pr_debug("hdr: Transport\n"); + if (!rtsp_mangle_tran(ctinfo, exp, prtspexp, skb, lineoff, linelen)) + { + pr_debug("hdr: Transport mangle failed"); + break; + } + get_skb_tcpdata(skb, &ptcp, &tcplen); + hdrslen -= (oldtcplen-tcplen); + off -= (oldtcplen-tcplen); + lineoff -= (oldtcplen-tcplen); + linelen -= (oldtcplen-tcplen); + pr_debug("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff); + } + } + + return NF_ACCEPT; +} + +static unsigned int +help(struct sk_buff *skb, enum ip_conntrack_info ctinfo, + unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp, + struct nf_conntrack_expect* exp) +{ + int dir = CTINFO2DIR(ctinfo); + int rc = NF_ACCEPT; + + switch (dir) + { + case IP_CT_DIR_ORIGINAL: + rc = help_out(skb, ctinfo, matchoff, matchlen, prtspexp, exp); + break; + case IP_CT_DIR_REPLY: + pr_debug("unmangle ! %u\n", ctinfo); + /* XXX: unmangle */ + rc = NF_ACCEPT; + break; + } + //UNLOCK_BH(&ip_rtsp_lock); + + return rc; +} + +static void expected(struct nf_conn* ct, struct nf_conntrack_expect *exp) +{ + struct nf_nat_multi_range_compat mr; + u_int32_t newdstip, newsrcip, newip; + + struct nf_conn *master = ct->master; + + newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; + newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; + //FIXME (how to port that ?) + //code from 2.4 : newip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) ? newsrcip : newdstip; + newip = newdstip; + + pr_debug("newsrcip=%u.%u.%u.%u, newdstip=%u.%u.%u.%u, newip=%u.%u.%u.%u\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip), 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; + + nf_nat_setup_info(ct, &mr.range[0], IP_NAT_MANIP_DST); +} + + +static void __exit fini(void) +{ + nf_nat_rtsp_hook = NULL; + nf_nat_rtsp_hook_expectfn = NULL; + synchronize_net(); +} + +static int __init init(void) +{ + printk("nf_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n"); + + BUG_ON(nf_nat_rtsp_hook); + nf_nat_rtsp_hook = help; + nf_nat_rtsp_hook_expectfn = &expected; + + if (stunaddr != NULL) + extip = in_aton(stunaddr); + + if (destaction != NULL) { + if (strcmp(destaction, "auto") == 0) + dstact = DSTACT_AUTO; + + if (strcmp(destaction, "strip") == 0) + dstact = DSTACT_STRIP; + + if (strcmp(destaction, "none") == 0) + dstact = DSTACT_NONE; + } + + return 0; +} + +module_init(init); +module_exit(fini); -- cgit v1.2.3