summaryrefslogtreecommitdiffstats
path: root/target/linux/realtek/files/net/ipv4
diff options
context:
space:
mode:
authorRoman Yeryomin <roman@advem.lv>2013-02-06 02:59:31 +0200
committerRoman Yeryomin <roman@advem.lv>2013-02-06 02:59:31 +0200
commit691cc9529efe8ea7abaab170c452ae4470bf3ac2 (patch)
tree8d18d131720975fc63c8c2abc7bd933efe503e5f /target/linux/realtek/files/net/ipv4
parent62da0fe6152d0025e570ca41a6f9ae68df7da89b (diff)
Rebase files to rsdk 3.2 and refresh patches. Compilable (not by humans).
Signed-off-by: Roman Yeryomin <roman@advem.lv>
Diffstat (limited to 'target/linux/realtek/files/net/ipv4')
-rw-r--r--target/linux/realtek/files/net/ipv4/netfilter/ipt_CONENAT.c655
-rw-r--r--target/linux/realtek/files/net/ipv4/netfilter/nf_nat_ipsec.c622
-rw-r--r--target/linux/realtek/files/net/ipv4/netfilter/nf_nat_rtsp.c497
3 files changed, 1774 insertions, 0 deletions
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 <coreteam@netfilter.org>
+ *
+ * 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 <linux/autoconf.h>
+#include <linux/types.h>
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/timer.h>
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <net/protocol.h>
+#include <net/ip.h>
+#include <net/checksum.h>
+#include <net/route.h>
+#include <linux/netdevice.h>
+#include <linux/netfilter.h>
+
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_core.h>
+#include <net/netfilter/nf_conntrack_helper.h>
+#include <net/netfilter/nf_conntrack_tuple.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+#include <net/netfilter/nf_nat.h>
+#include <net/netfilter/nf_nat_rule.h>
+#include <net/netfilter/nf_nat_helper.h>
+
+#define ASSERT_READ_LOCK(x)
+#define ASSERT_WRITE_LOCK(x)
+#include <linux/netfilter_ipv4/listhelp.h>
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
+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 <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/kernel.h>
+#include <net/tcp.h>
+
+#include <net/netfilter/nf_conntrack_core.h>
+#include <net/netfilter/nf_nat.h>
+#include <net/netfilter/nf_nat_helper.h>
+#include <net/netfilter/nf_nat_rule.h>
+#include <net/netfilter/nf_conntrack_helper.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+#include <linux/netfilter/nf_conntrack_ipsec.h>
+
+#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 <tmarshall at real.com>
+ * 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<MAX_PORTS>
+ * stunaddr=<address>
+ * 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 <linux/module.h>
+#include <net/tcp.h>
+#include <net/netfilter/nf_nat_helper.h>
+#include <net/netfilter/nf_nat_rule.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+#include <linux/netfilter/nf_conntrack_rtsp.h>
+
+#include <linux/inet.h>
+#include <linux/ctype.h>
+#define NF_NEED_STRNCASECMP
+#define NF_NEED_STRTOU16
+#include <linux/netfilter_helpers.h>
+#define NF_NEED_MIME_NEXTLINE
+#include <linux/netfilter_mime.h>
+
+#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 <tmarshall at real.com>");
+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=<spec>"
+ * - 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);