summaryrefslogtreecommitdiffstats
path: root/target/linux/realtek/files/net/ipv4
diff options
context:
space:
mode:
authorRoman Yeryomin <roman@advem.lv>2013-05-17 20:40:24 +0300
committerRoman Yeryomin <roman@advem.lv>2013-05-17 20:40:24 +0300
commite6d87036412b952cb083eff2dc716aee97a771f2 (patch)
tree273dd3daaa85553832d3cc6d48276229dc7fbe09 /target/linux/realtek/files/net/ipv4
parenta18fec42221baa52fff4c5ffd45ec8f32e3add36 (diff)
Move to rsdk 3.2.4. Compiles cleanly.
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_TRIGGER.c525
1 files changed, 525 insertions, 0 deletions
diff --git a/target/linux/realtek/files/net/ipv4/netfilter/ipt_TRIGGER.c b/target/linux/realtek/files/net/ipv4/netfilter/ipt_TRIGGER.c
new file mode 100644
index 000000000..1885ab624
--- /dev/null
+++ b/target/linux/realtek/files/net/ipv4/netfilter/ipt_TRIGGER.c
@@ -0,0 +1,525 @@
+/* Kernel module to match the port-ranges, trigger related port-ranges,
+ * and alters the destination to a local IP address.
+ *
+ * Copyright (C) 2003, CyberTAN Corporation
+ * All Rights Reserved.
+ *
+ * Description:
+ * This is kernel module for port-triggering.
+ *
+ * The module follows the Netfilter framework, called extended packet
+ * matching modules.
+ */
+
+//#define DEBUG 1
+
+#include <linux/autoconf.h>
+#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/timer.h>
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/inetdevice.h>
+#include <net/protocol.h>
+#include <net/checksum.h>
+
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_TRIGGER.h>
+
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_core.h>
+#include <net/netfilter/nf_conntrack_tuple.h>
+#include <net/netfilter/nf_conntrack_expect.h>
+#include <net/netfilter/nf_conntrack_ecache.h>
+#include <net/netfilter/nf_conntrack_helper.h>
+#include <net/netfilter/nf_nat.h>
+
+
+#ifdef DEBUG
+#undef pr_debug
+#define pr_debug(fmt, args...) printk("[%s:%d] "fmt, __FUNCTION__, __LINE__, ##args)
+#endif
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
+MODULE_DESCRIPTION("iptables trigger target module");
+
+/* This rwlock protects the main hash table, protocol/helper/expected
+ * registrations, conntrack timers*/
+#define ASSERT_READ_LOCK(x)
+#define ASSERT_WRITE_LOCK(x)
+#include <linux/netfilter_ipv4/listhelp.h>
+
+struct ipt_trigger {
+ struct list_head list; /* Trigger list */
+ struct timer_list timeout; /* Timer for list destroying */
+ u_int32_t srcip; /* Outgoing source address */
+ u_int32_t dstip; /* Outgoing destination address */
+ u_int16_t mproto; /* Trigger protocol */
+ u_int16_t rproto; /* Related protocol */
+ struct ipt_trigger_ports ports; /* Trigger and related ports */
+ u_int8_t reply; /* Confirm a reply connection */
+};
+
+static LIST_HEAD(trigger_list);
+static DEFINE_RWLOCK(ip_trigger_lock);
+static unsigned int trigger_ip_timeout = 600; /* 600s */
+
+static void trigger_refresh(struct ipt_trigger *trig, unsigned long extra_jiffies)
+{
+ NF_CT_ASSERT(trig);
+ write_lock_bh(&ip_trigger_lock);
+
+ pr_debug("Refresh trigger, srcip= %pI4, dstip= %pI4, mproto= %d, mport[0..1]=%d-%d, rproto= %d, rport[0..1]=%d-%d.\n",
+ &(trig->srcip), &(trig->dstip),
+ trig->mproto, trig->ports.mport[0], trig->ports.mport[1],
+ trig->rproto, trig->ports.rport[0], trig->ports.rport[1]);
+
+ /* Need del_timer for race avoidance (may already be dying). */
+ if (del_timer(&trig->timeout)) {
+ trig->timeout.expires = jiffies + extra_jiffies;
+ add_timer(&trig->timeout);
+ }
+
+ write_unlock_bh(&ip_trigger_lock);
+}
+
+static void __del_trigger(struct ipt_trigger *trig)
+{
+ NF_CT_ASSERT(trig);
+ ASSERT_WRITE_LOCK(&ip_trigger_lock);
+
+ pr_debug("Del trigger, srcip= %pI4, dstip= %pI4, mproto= %d, mport[0..1]=%d-%d, rproto= %d, rport[0..1]=%d-%d.\n",
+ &(trig->srcip), &(trig->dstip),
+ trig->mproto, trig->ports.mport[0], trig->ports.mport[1],
+ trig->rproto, trig->ports.rport[0], trig->ports.rport[1]);
+
+ /* delete from 'trigger_list' */
+ list_del(&trig->list);
+ kfree(trig);
+}
+
+static void trigger_timeout(unsigned long ul_trig)
+{
+ struct ipt_trigger *trig= (void *) ul_trig;
+
+ pr_debug("trigger list %p timed out.\n", trig);
+ write_lock_bh(&ip_trigger_lock);
+ __del_trigger(trig);
+ write_unlock_bh(&ip_trigger_lock);
+}
+
+static unsigned int
+add_new_trigger(struct ipt_trigger *trig)
+{
+ struct ipt_trigger *new;
+
+ pr_debug("Add trigger, srcip= %pI4, dstip= %pI4, mproto= %d, mport[0..1]=%d-%d, rproto= %d, rport[0..1]=%d-%d.\n",
+ &(trig->srcip), &(trig->dstip),
+ trig->mproto, trig->ports.mport[0], trig->ports.mport[1],
+ trig->rproto, trig->ports.rport[0], trig->ports.rport[1]);
+
+ write_lock_bh(&ip_trigger_lock);
+ new = (struct ipt_trigger *)kmalloc(sizeof(struct ipt_trigger), GFP_ATOMIC);
+ if (!new) {
+ write_unlock_bh(&ip_trigger_lock);
+ pr_debug("OOM allocating trigger list\n");
+ return -ENOMEM;
+ }
+
+ memset(new, 0, sizeof(*trig));
+ INIT_LIST_HEAD(&new->list);
+ memcpy(new, trig, sizeof(*trig));
+
+ /* add to global table of trigger */
+ list_prepend(&trigger_list, &new->list);
+
+ /* add and start timer if required */
+ init_timer(&new->timeout);
+ new->timeout.data = (unsigned long)new;
+ new->timeout.function = trigger_timeout;
+ new->timeout.expires = jiffies + (trigger_ip_timeout * HZ);
+ add_timer(&new->timeout);
+
+ write_unlock_bh(&ip_trigger_lock);
+
+ return 0;
+}
+
+static inline int trigger_out_matched(const struct ipt_trigger *i,
+ const u_int16_t proto, const u_int16_t dport, const struct ipt_trigger_info *info)
+{
+ pr_debug("i=%p, proto=%d, dport=%d, rproto=%d, rport[0..1]=%d-%d.\n",
+ i, proto, dport, info->rproto, info->ports.rport[0], info->ports.rport[1]);
+ pr_debug("Got one, mproto= %d, mport[0..1]=%d-%d.\n",
+ i->mproto, i->ports.mport[0], i->ports.mport[1]);
+
+ return ((i->mproto == 0 || i->mproto == proto)
+ && ((i->ports.mport[0] <= dport) && (i->ports.mport[1] >= dport))
+ && (i->rproto == info->rproto)
+ && ((i->ports.rport[0] == info->ports.rport[0]) && (i->ports.rport[1] == info->ports.rport[1]))
+ );
+}
+
+static inline int trigger_in_matched(const struct ipt_trigger *i,
+ const u_int16_t proto, const u_int16_t dport)
+{
+ pr_debug("i=%p, proto= %d, dport=%d.\n", i, proto, dport);
+ pr_debug("Got one, rproto= %d, rport[0..1]=%d-%d.\n",
+ i->rproto, i->ports.rport[0], i->ports.rport[1]);
+
+ return ((i->rproto ==0 || i->rproto == proto)
+ && ((i->ports.rport[0] <= dport) && (i->ports.rport[1] >= dport))
+ );
+}
+
+
+static unsigned int
+trigger_out(struct sk_buff *skb, const struct xt_target_param *par)
+{
+ const struct ipt_trigger_info *info = par->targinfo;
+ struct ipt_trigger trig, *found;
+ const struct iphdr *iph = ip_hdr(skb);
+ struct tcphdr *tcph = (void *)iph + iph->ihl*4;
+
+ /* Check if the trigger range has already existed in 'trigger_list'. */
+ found = LIST_FIND(&trigger_list, trigger_out_matched, struct ipt_trigger *,
+ iph->protocol, ntohs(tcph->dest), info);
+
+ pr_debug("############# out found ############\n");
+
+ if (found) {
+ /* Yeah, it exists. We need to update(delay) the destroying timer. */
+ trigger_refresh(found, trigger_ip_timeout * HZ);
+
+ /* In order to allow multiple hosts use the same port range, we update
+ the 'saddr' after previous trigger has a reply connection. */
+ if (found->reply)
+ found->srcip = iph->saddr;
+ }
+ else {
+ /* Create new trigger */
+ memset(&trig, 0, sizeof(trig));
+ trig.srcip = iph->saddr;
+ trig.mproto = info->mproto;
+ trig.rproto = info->rproto;
+ memcpy(&trig.ports, &info->ports, sizeof(struct ipt_trigger_ports));
+
+ /* Add the new 'trig' to list 'trigger_list'. */
+ add_new_trigger(&trig);
+
+ pr_debug("new trigger:srcip=%pI4 dstip=%pI4 mproto=%d rproto=%d mport[%d-%d] rport[%d-%d]\n",
+ &(trig.srcip), &(trig.dstip), trig.mproto, trig.rproto,
+ trig.ports.mport[0], trig.ports.mport[1],
+ trig.ports.rport[0], trig.ports.rport[1]);
+ }
+
+ /* We don't block any packet. */
+ return XT_CONTINUE;
+}
+
+static unsigned int
+trigger_in(struct sk_buff *skb, const struct xt_target_param *par)
+{
+ struct ipt_trigger *found;
+ const struct iphdr *iph = ip_hdr(skb);
+ struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
+
+ /* Check if the trigger-ed range has already existed in 'trigger_list'. */
+ found = LIST_FIND(&trigger_list, trigger_in_matched,
+ struct ipt_trigger *, iph->protocol, ntohs(tcph->dest));
+ if (found) {
+ pr_debug("############# in found ############\n");
+
+ /* Yeah, it exists. We need to update(delay) the destroying timer. */
+ trigger_refresh(found, trigger_ip_timeout * HZ);
+
+ /* Accept it, or the incoming packet could be dropped in the FORWARD chain */
+ return NF_ACCEPT;
+ }
+
+ return XT_CONTINUE; /* Our job is the interception. */
+}
+
+static unsigned int
+trigger_dnat(struct sk_buff *skb, const struct xt_target_param *par)
+{
+ struct ipt_trigger *found;
+ const struct iphdr *iph = ip_hdr(skb);
+ struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
+ struct nf_conn *ct;
+ enum ip_conntrack_info ctinfo;
+ struct nf_nat_range newrange;
+
+ NF_CT_ASSERT(par->hooknum == NF_IP_PRE_ROUTING);
+
+ /* Check if the trigger-ed range has already existed in 'trigger_list'. */
+ found = LIST_FIND(&trigger_list, trigger_in_matched,
+ struct ipt_trigger *, iph->protocol, ntohs(tcph->dest));
+ if (!found || !found->srcip)
+ {
+ /* We don't block any packet. */
+ return XT_CONTINUE;
+ }
+
+ pr_debug("############# dnat found ############\n");
+
+ /* Confirm there has been a reply connection. */
+ found->reply = 1;
+ ct = nf_ct_get(skb, &ctinfo);
+ NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW));
+
+ pr_debug("hooknum=%d got ", par->hooknum);
+ nf_ct_dump_tuple(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+
+ /* This must be a fresh one. */
+ BUG_ON(ct->status & IPS_NAT_DONE_MASK);
+
+ /* Alter the destination of imcoming packet. */
+ newrange = ((struct nf_nat_range)
+ {
+ IP_NAT_RANGE_MAP_IPS,
+ found->srcip, found->srcip,
+ { 0 }, { 0 }
+ });
+
+ //add by ramen to let the packet through our modem 2008/03/06
+ skb->nfctinfo = IP_CT_RELATED;
+
+ /* Hand modified range to generic setup. */
+ return nf_nat_setup_info(ct, &newrange, IP_NAT_MANIP_DST);
+}
+
+static unsigned int
+trigger_tg(struct sk_buff *skb, const struct xt_target_param *par)
+{
+ const struct ipt_trigger_info *info = par->targinfo;
+ const struct iphdr *iph = ip_hdr(skb);
+ struct tcphdr *tcph = (void *)iph + iph->ihl*4; /* Might be TCP, UDP */
+
+ /* The Port-trigger only supports TCP and UDP. */
+ if ((iph->protocol != IPPROTO_TCP) && (iph->protocol != IPPROTO_UDP))
+ return XT_CONTINUE;
+
+ pr_debug("type=%s, protocol=%s, dport=%d\n",
+ (info->type == IPT_TRIGGER_DNAT) ? "dnat"
+ :(info->type == IPT_TRIGGER_IN) ? "in" : "out",
+ (iph->protocol == IPPROTO_TCP) ? "tcp" : "udp",
+ ntohs(tcph->dest));
+
+ if (info->type == IPT_TRIGGER_OUT)
+ {
+ /* Check if the protocol equal to trigger protocol
+ and if the destport is in trigger port range
+ */
+ if ((info->mproto != 0 && info->mproto != iph->protocol)
+ || (ntohs(tcph->dest) < info->ports.mport[0] || ntohs(tcph->dest) > info->ports.mport[1])
+ )
+ {
+ return XT_CONTINUE;
+ }
+
+ return trigger_out(skb, par);
+ }
+ else
+ {
+ /* Check if the protocol equal to related protocol
+ and if the destport is in related port range
+ */
+ if (info->ports.rport[0])
+ {
+ if ((info->rproto != 0 && info->rproto != iph->protocol)
+ || (ntohs(tcph->dest) < info->ports.rport[0] || ntohs(tcph->dest) > info->ports.rport[1])
+ )
+ {
+ return XT_CONTINUE;
+ }
+ }
+
+ if (info->type == IPT_TRIGGER_IN)
+ {
+ return trigger_in(skb, par);
+ }
+ else if (info->type == IPT_TRIGGER_DNAT)
+ {
+ return trigger_dnat(skb, par);
+ }
+ }
+
+ return XT_CONTINUE;
+}
+
+static bool trigger_tg_check(const struct xt_tgchk_param *par)
+{
+ const struct ipt_trigger_info *info = par->targinfo;
+
+ if ((strcmp(par->table, "mangle") == 0)) {
+ pr_debug("bad table `%s'.\n", par->table);
+ return false;
+ }
+ if (par->hook_mask & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_FORWARD))) {
+ pr_debug("bad hooks %x, %x.\n", par->hook_mask, ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_FORWARD)));
+ return false;
+ }
+ if (info->mproto) {
+ if (info->mproto != IPPROTO_TCP && info->mproto != IPPROTO_UDP) {
+ pr_debug("bad mproto %d.\n", info->mproto);
+ return false;
+ }
+ }
+ if (info->rproto) {
+ if (info->rproto != IPPROTO_TCP && info->rproto != IPPROTO_UDP) {
+ pr_debug("bad rproto %d.\n", info->rproto);
+ return false;
+ }
+ }
+ if (info->type == IPT_TRIGGER_OUT) {
+ if (!info->ports.mport[0] || !info->ports.rport[0]) {
+ pr_debug("Try 'iptables -j TRIGGER -h' for help.\n");
+ return false;
+ }
+ }
+ else
+ {
+ if (info->mproto || info->ports.mport[0]) {
+ pr_debug("When trigger-type is 'in' or 'dnat', mproto and mport is not needed\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* drop reference count of cluster config when rule is deleted */
+static void trigger_tg_destory(const struct xt_tgdtor_param *par)
+{
+}
+
+
+
+static struct proc_dir_entry *trigger_proc = NULL;
+static int proc_trigger_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int i = 0;
+ char *out = page;
+ int len = 0;
+ struct list_head *cur_item, *tmp_item;
+
+ out += sprintf(out, "timeout: %d\n", trigger_ip_timeout/60);
+ out += sprintf(out, "trigger list:\n");
+
+ /* Empty the 'trigger_list' */
+ list_for_each_safe(cur_item, tmp_item, &trigger_list) {
+ struct ipt_trigger *trig = (void *)cur_item;
+ out += sprintf(out, "#%d srcip= %pI4, dstip= %pI4, mproto= %d, mport[0..1]=%d-%d, rproto= %d, rport[0..1]=%d-%d.\n",
+ ++i, &(trig->srcip), &(trig->dstip),
+ trig->mproto, trig->ports.mport[0], trig->ports.mport[1],
+ trig->rproto, trig->ports.rport[0], trig->ports.rport[1]);
+ }
+ 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_trigger_write( struct file *filp, const char __user *buf,unsigned long len, void *data )
+{
+ int ret;
+ char str_buf[256];
+ char action[20] = {0};
+ int val = 0;
+ struct list_head *cur_item, *tmp_item;
+
+ if(len > 255)
+ {
+ printk("Error: the value must be between 10-3600\n");
+ return len;
+ }
+
+ copy_from_user(str_buf, buf, len);
+ str_buf[len] = '\0';
+
+ ret = sscanf(str_buf, "%s %d", action, (int*)&val);
+ if(ret != 2 || val < 0 )
+ {
+ printk("Error.Sample: echo debug 1 > /proc/debug \n");
+ return len;
+ }
+ if (strcmp(action, "timeout") == 0)
+ {
+ if(val < 1 || val > 9999)
+ {
+ printk("Error: the value must be between 1-9999 minutes\n");
+ return len;
+ }
+ trigger_ip_timeout = val * 60;
+ }
+ else if (strcmp(action, "flush") == 0)
+ {
+ pr_debug("Flush trigger list.\n");
+
+ /* Empty the 'trigger_list' */
+ list_for_each_safe(cur_item, tmp_item, &trigger_list) {
+ struct ipt_trigger *trig = (void *)cur_item;
+ del_timer(&trig->timeout);
+ __del_trigger(trig);
+ }
+ }
+ else
+ {
+ printk("Error: Unkown command.\n");
+ }
+
+ return len;
+}
+
+
+static struct xt_target trigger_tg_reg __read_mostly = {
+ .name = "TRIGGER",
+ .family = NFPROTO_IPV4,
+ .target = trigger_tg,
+ .targetsize = sizeof(struct ipt_trigger_info),
+ //.table = "nat",
+ //.hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT),
+ .checkentry = trigger_tg_check,
+ .destroy = trigger_tg_destory,
+ .me = THIS_MODULE,
+};
+
+static int __init trigger_tg_init(void)
+{
+ trigger_proc = create_proc_entry("porttrigger", 0, NULL);
+ if (trigger_proc) {
+ trigger_proc->read_proc = proc_trigger_read;
+ trigger_proc->write_proc = proc_trigger_write;
+ }
+
+ return xt_register_target(&trigger_tg_reg);
+}
+
+static void __exit trigger_tg_exit(void)
+{
+ if (trigger_proc) {
+ remove_proc_entry("porttrigger", NULL);
+ trigger_proc = NULL;
+ }
+
+ xt_unregister_target(&trigger_tg_reg);
+}
+
+module_init(trigger_tg_init);
+module_exit(trigger_tg_exit);
+