summaryrefslogtreecommitdiff
path: root/release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_standalone.c
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2015-01-03 12:04:58 +0100
committerAndreas Baumann <mail@andreasbaumann.cc>2015-01-03 12:04:58 +0100
commit008d0be72b2f160382c6e880765e96b64a050c65 (patch)
tree36f48a98a3815a408e2ce1693dd182af90f80305 /release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_standalone.c
parent611becfb8726c60cb060368541ad98191d4532f5 (diff)
downloadtomato-008d0be72b2f160382c6e880765e96b64a050c65.tar.gz
tomato-008d0be72b2f160382c6e880765e96b64a050c65.tar.bz2
imported original firmware WRT54GL_v4.30.11_11_US
Diffstat (limited to 'release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_standalone.c')
-rw-r--r--release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_standalone.c900
1 files changed, 900 insertions, 0 deletions
diff --git a/release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_standalone.c b/release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_standalone.c
new file mode 100644
index 00000000..c7e31931
--- /dev/null
+++ b/release/src/linux/linux/net/ipv4/netfilter/ip_conntrack_standalone.c
@@ -0,0 +1,900 @@
+/* This file contains all the functions required for the standalone
+ ip_conntrack module.
+
+ These are not required by the compatibility layer.
+*/
+
+/* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General
+ Public Licence. */
+
+#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/version.h>
+#include <linux/brlock.h>
+#include <net/checksum.h>
+
+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock)
+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock)
+
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#define DEBUGP(format, args...)
+
+struct module *ip_conntrack_module = THIS_MODULE;
+MODULE_LICENSE("GPL");
+
+#define CLEAR_IP_CONNTRACK
+#define DEL_IP_CONNTRACK_ENTRY 1
+#ifdef DEL_IP_CONNTRACK_ENTRY
+/*
+ *
+ *This part of code add for delete an entry in ip_conntrack table.
+ *
+ */
+
+
+#define DEL_LIST_PATH "/tmp/.del_ip_conntrack"
+#define printkerrline() printk("del_ip_conntrack error : %s %s %d\n", __FILE__, __func__, __LINE__)
+
+struct del_list
+{
+ unsigned short proto;
+ unsigned int begin_port;
+ unsigned int end_port;
+ unsigned int ip;
+ struct del_list *next;
+};
+
+void free_del_list(struct del_list *head);
+void print_del_list(struct del_list *head);
+static struct del_list * malloc_new_node(const char *buf, struct del_list * head);
+struct del_list * init_del_list(const char *buf, size_t size);
+static int read_del_file(char * buf, unsigned int size, char *path);
+static int del_match_method(const struct ip_conntrack_tuple_hash *pConn, const struct del_list * pList);
+static int del_conntrack_check(const struct ip_conntrack_tuple_hash *pConn, const struct del_list * head);
+void pf_del_ip_conntrack(void);
+static int proc_read_del_ip_conntrack(char *page, char **start, off_t off, int count, int *eof, void *context);
+static int proc_write_del_ip_conntrack(struct file *file, const char *buffer, unsigned long count, void *data);
+static int end_proc_read(const char *p, char *page, off_t off, int count, char **start, int *eof);
+
+void pf_del_ip_conntrack(void)
+{
+#define MAX_BUF_SIZE 1024*2
+ int i;
+ char buf[MAX_BUF_SIZE];
+ struct del_list * del_head = NULL;
+ struct list_head *head, *temp_head;
+ struct ip_conntrack_tuple_hash *tuple_hash;
+
+ //printk("pf_del_ip_conntrack---------------------------------------1\n");
+ memset(buf, 0, MAX_BUF_SIZE);
+
+ if(read_del_file(buf, MAX_BUF_SIZE, DEL_LIST_PATH) == -1)
+ {
+ goto final_return;
+ }
+
+ buf[MAX_BUF_SIZE - 1] = '\0';
+ del_head = init_del_list(buf, MAX_BUF_SIZE - 1);
+ //print_del_list(del_head);
+ READ_LOCK(&ip_conntrack_lock);
+ for (i = 0; i < ip_conntrack_htable_size; i++)
+ {
+ head = &ip_conntrack_hash[i];
+ temp_head = head;
+ while(1)
+ {
+ temp_head = temp_head->next;
+ if(temp_head == head)
+ {
+ head = NULL;
+ temp_head = NULL;
+ break;
+ }
+ tuple_hash = (struct ip_conntrack_tuple_hash *)temp_head;
+ if(del_conntrack_check(tuple_hash, del_head) == 1)
+ {
+ del_selected_conntrack(tuple_hash);
+ }
+ }
+ }
+ READ_UNLOCK(&ip_conntrack_lock);
+ free_del_list(del_head);
+
+final_return:
+
+ //printk("pf_del_ip_conntrack---------------------------------------2\n");
+ return;
+#undef MAX_BUF_SIZE
+}
+
+static int del_conntrack_check(const struct ip_conntrack_tuple_hash *pConn, const struct del_list * head)
+{
+ int ret;
+ const struct del_list * p;
+
+ ret = 0;
+
+ if(pConn == NULL || head == NULL)
+ {
+ ret = -1;
+ goto final_return;
+ }
+
+ for(p = head; p; p = p->next)
+ {
+ if(del_match_method(pConn, p) == 1)
+ {
+ //Match,jump out
+ ret = 1;
+ break;
+ }
+ }
+
+final_return:
+ return ret;
+}
+
+static int del_match_method(const struct ip_conntrack_tuple_hash *pConn, const struct del_list * pList)
+{
+ int ret;
+ typedef enum
+ {
+ TCP_PROTO = 0x06,
+ UDP_PROTO = 0x11,
+ }proto_type;
+ proto_type pt[2] = {TCP_PROTO, UDP_PROTO};
+
+ ret = 0;
+ //Check tcp and udp only
+ if(pConn->tuple.dst.protonum == TCP_PROTO || pConn->tuple.dst.protonum == UDP_PROTO)
+ {
+ //Check proto match
+ if((pList->proto == 3) ||
+ ((pList->proto == 0 || pList->proto == 1) && (pConn->tuple.dst.protonum == pt[pList->proto])))
+ {
+ //Chcek ip address match
+ if(pConn->ctrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip == pList->ip)
+ {
+ //Check port match
+ unsigned int tport;
+ if(pConn->tuple.dst.protonum == TCP_PROTO)
+ {
+ //TCP
+ tport = pConn->ctrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.tcp.port;
+ }
+ else
+ {
+ //UDP
+ tport = pConn->ctrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.udp.port;
+ }
+ tport = htons(tport);
+ if(tport >= pList->begin_port && tport <= pList->end_port)
+ {
+ ret = 1;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+static int read_del_file(char * buf, unsigned int size, char *path)
+{
+ int retval, orgfsuid, orgfsgid;
+ mm_segment_t orgfs;
+ struct file *srcf;
+
+ // Save uid and gid used for filesystem access.
+ // Set user and group to 0 (root)
+ orgfsuid = current->fsuid;
+ orgfsgid = current->fsgid;
+ current->fsuid=current->fsgid = 0;
+ orgfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ if(path && *path)
+ {
+ srcf = filp_open(path, O_RDONLY, 0);
+ if(IS_ERR(srcf))
+ {
+ printkerrline();
+ retval = -1;
+ goto final_return;
+ }
+ else
+ {
+ if(srcf->f_op && srcf->f_op->read)
+ {
+ memset(buf, 0x00, size);
+ retval=srcf->f_op->read(srcf, buf, size, &srcf->f_pos);
+ if(retval < 0)
+ {
+ printkerrline();
+ retval = -1;
+ goto final_return;
+ }
+ else
+ {
+ //Success,go!
+ retval = 0;
+ goto final_return;
+ }
+ }
+ else
+ {
+ printkerrline();
+ retval = -1;
+ goto final_return;
+ }
+ }
+ }
+ else
+ {
+ printkerrline();
+ retval = -1;
+ goto final_return;
+ }
+
+final_return:
+ if(!IS_ERR(srcf))
+ {
+ retval=filp_close(srcf,NULL);
+ if(retval)
+ {
+ printkerrline();
+ retval = -1;
+ }
+ }
+ set_fs(orgfs);
+ current->fsuid = orgfsuid;
+ current->fsgid = orgfsgid;
+
+ return retval;
+}
+
+struct del_list * init_del_list(const char *buf, size_t size)
+{
+#define LINE_FEED "\n"
+#define TMP_BUF_SIZE 100
+ const char *begin, *end;
+ char tmpbuf[TMP_BUF_SIZE];
+ struct del_list * head = NULL, *tmp_p;
+
+ if(buf == NULL || size <= 0 || buf[size] != '\0')
+ {
+ head = NULL;
+ goto final_return;
+ }
+
+ for(begin = end = buf; begin && (begin - buf < size); begin = end + strlen(LINE_FEED))
+ {
+ end = strstr(begin, LINE_FEED);
+ if(end)
+ {
+ if((end - begin) > (TMP_BUF_SIZE - 1))
+ {
+ //Too large,go on
+ continue;
+ }
+ else
+ {
+ memcpy(tmpbuf, begin, end - begin);
+ tmpbuf[end - begin] = '\0';
+ //printk("obtain string : %s\n", tmpbuf);
+ if((tmp_p = malloc_new_node(tmpbuf, head)) == NULL)
+ {
+ //Invalid format or malloc fail,go on
+ continue;
+ }
+ else
+ {
+ head = tmp_p;
+ }
+ }
+ }
+ else
+ {
+ //printk("Last string : %s\n", begin);
+ if((tmp_p = malloc_new_node(begin, head)) == NULL)
+ {
+ //Invalid format or malloc fail,jump out
+ break;
+ }
+ else
+ {
+ head = tmp_p;
+ }
+ }
+ }
+
+final_return:
+ return head;
+
+#undef TMP_BUF_SIZE
+#undef LINE_FEED
+}
+
+static struct del_list * malloc_new_node(const char *buf, struct del_list * head)
+{
+#define SSCANF_MATCH_NUM 7
+ int i, j, k, c1, c2, c3, c4;
+ struct del_list *p = NULL;
+
+ if(sscanf(buf, "%d %d.%d.%d.%d %d-%d", &i, &c4, &c3, &c2, &c1, &j, &k) != SSCANF_MATCH_NUM)
+ {
+ p = NULL;
+ goto final_return;
+ }
+ else
+ {
+ if(p = (struct del_list *)kmalloc(sizeof(struct del_list), GFP_ATOMIC))
+ {
+ p->proto = i;
+ #if 0
+ //Big endian
+ ((char *)&(p->ip))[0] = (char)c1;
+ ((char *)&(p->ip))[1] = (char)c2;
+ ((char *)&(p->ip))[2] = (char)c3;
+ ((char *)&(p->ip))[3] = (char)c4;
+ #else
+ //Little endian
+ ((char *)&(p->ip))[3] = (char)c1;
+ ((char *)&(p->ip))[2] = (char)c2;
+ ((char *)&(p->ip))[1] = (char)c3;
+ ((char *)&(p->ip))[0] = (char)c4;
+ #endif
+ p->begin_port = j;
+ p->end_port = k;
+ p->next = head;
+ }
+ else
+ {
+ p = NULL;
+ goto final_return;
+ }
+ }
+
+final_return:
+ return p;
+#undef SSCANF_MATCH_NUM
+}
+
+void print_del_list(struct del_list *head)
+{
+ int i;
+ struct del_list *tmp_p;
+
+ for(i = 1, tmp_p = head; tmp_p; tmp_p = tmp_p->next, i++)
+ {
+ printk("Node(%d): proto=%d | ip=%0x | port=[%d-%d]\n", i, tmp_p->proto, tmp_p->ip, tmp_p->begin_port, tmp_p->end_port);
+ }
+}
+
+void free_del_list(struct del_list *head)
+{
+ int i;
+ struct del_list *tmp_p;
+
+ if(head == NULL)
+ {
+ goto final_return;
+ }
+ for(i = 1, tmp_p = head; head; head = tmp_p, i++)
+ {
+ tmp_p = head->next;
+ //printk("Free@Node(%d):proto=%d | ip=%0x | port=[%d-%d]\n", i, head->proto, head->ip, head->begin_port, head->end_port);
+ kfree(head);
+ }
+
+final_return:
+ return;
+}
+
+static int proc_read_del_ip_conntrack(char *page, char **start, off_t off, int count, int *eof, void *context)
+{
+ char *p;
+
+ p = page;
+ p += sprintf(page, "%s\n", "use echo \"1(0)\" to enable or disbable");
+ return end_proc_read(p, page, off, count, start, eof);
+}
+
+static int proc_write_del_ip_conntrack(struct file *file, const char *buffer, unsigned long count, void *data)
+{
+ unsigned char tmp[2];
+
+ if(buffer)
+ {
+ memset(tmp, 0, sizeof(tmp));
+ copy_from_user(tmp, buffer, count);
+ tmp[1] = 0x00;
+ switch(*tmp)
+ {
+ case '0':
+ //Do something here
+ break;
+
+ case '1':
+ pf_del_ip_conntrack();
+ break;
+
+ default:
+ printk("<1>invalid args\n");
+ }
+ return count;
+ }
+ return 0;
+}
+
+static int end_proc_read(const char *p, char *page, off_t off, int count, char **start, int *eof)
+{
+ int len = p - page;
+
+ if(len < off + count)
+ {
+ *eof = 1;
+ }
+
+ *start = page + off;
+ len -= off;
+ if(len > count)
+ {
+ len = count;
+ }
+
+ if(len < 0)
+ {
+ len = 0;
+ }
+
+ return len;
+}
+
+#endif
+
+#ifdef CLEAR_IP_CONNTRACK
+void clear_ip_conntrack(void)
+{
+ int i;
+ struct list_head *head, *temp_head;
+ struct ip_conntrack_tuple_hash *tuple_hash;
+
+ printk("warning : %s %d\n", __func__, __LINE__);
+
+ READ_LOCK(&ip_conntrack_lock);
+ for (i = 0; i < ip_conntrack_htable_size; i++)
+ {
+ head = &ip_conntrack_hash[i];
+ temp_head = head;
+ while(1)
+ {
+ temp_head = temp_head->next;
+ if(temp_head == head)
+ {
+ head = NULL;
+ temp_head = NULL;
+ break;
+ }
+ tuple_hash = (struct ip_conntrack_tuple_hash *)temp_head;
+ del_selected_conntrack(tuple_hash);
+ }
+ }
+ READ_UNLOCK(&ip_conntrack_lock);
+}
+
+static int proc_read_clear_ip_conntrack(char *page, char **start, off_t off, int count, int *eof, void *context)
+{
+ char *p;
+
+ p = page;
+ p += sprintf(page, "%s\n", "use echo \"1(0)\" to enable or disbable");
+ return end_proc_read(p, page, off, count, start, eof);
+}
+
+static int proc_write_clear_ip_conntrack(struct file *file, const char *buffer, unsigned long count, void *data)
+{
+ unsigned char tmp[2];
+
+ if(buffer)
+ {
+ memset(tmp, 0, sizeof(tmp));
+ copy_from_user(tmp, buffer, count);
+ tmp[1] = 0x00;
+ switch(*tmp)
+ {
+ case '0':
+ //Do something here
+ break;
+
+ case '1':
+ clear_ip_conntrack();
+ break;
+
+ default:
+ printk("<1>invalid args\n");
+ }
+ return count;
+ }
+ return 0;
+}
+#endif
+
+static int kill_proto(const struct ip_conntrack *i, void *data)
+{
+ return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum ==
+ *((u_int8_t *) data));
+}
+
+static unsigned int
+print_tuple(char *buffer, const struct ip_conntrack_tuple *tuple,
+ struct ip_conntrack_protocol *proto)
+{
+ int len;
+
+ len = sprintf(buffer, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
+ NIPQUAD(tuple->src.ip), NIPQUAD(tuple->dst.ip));
+
+ len += proto->print_tuple(buffer + len, tuple);
+
+ return len;
+}
+
+static unsigned int
+print_expect(char *buffer, const struct ip_conntrack_expect *expect)
+{
+ unsigned int len;
+
+ if (!expect || !expect->expectant || !expect->expectant->helper) {
+ DEBUGP("expect %x expect->expectant %x expect->expectant->helper %x\n",
+ expect, expect->expectant, expect->expectant->helper);
+ return 0;
+ }
+
+ if (expect->expectant->helper->timeout)
+ len = sprintf(buffer, "EXPECTING: %lu ",
+ timer_pending(&expect->timeout)
+ ? (expect->timeout.expires - jiffies)/HZ : 0);
+ else
+ len = sprintf(buffer, "EXPECTING: - ");
+ len += sprintf(buffer + len, "use=%u proto=%u ",
+ atomic_read(&expect->use), expect->tuple.dst.protonum);
+ len += print_tuple(buffer + len, &expect->tuple,
+ __ip_ct_find_proto(expect->tuple.dst.protonum));
+ len += sprintf(buffer + len, "\n");
+ return len;
+}
+
+static unsigned int
+print_conntrack(char *buffer, const struct ip_conntrack *conntrack)
+{
+ unsigned int len;
+ struct ip_conntrack_protocol *proto
+ = __ip_ct_find_proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
+ .tuple.dst.protonum);
+
+ len = sprintf(buffer, "%-8s %u %lu ",
+ proto->name,
+ conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
+ .tuple.dst.protonum,
+ timer_pending(&conntrack->timeout)
+ ? (conntrack->timeout.expires - jiffies)/HZ : 0);
+
+ len += proto->print_conntrack(buffer + len, conntrack);
+ len += print_tuple(buffer + len,
+ &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
+ proto);
+ if (!(conntrack->status & IPS_SEEN_REPLY))
+ len += sprintf(buffer + len, "[UNREPLIED] ");
+ len += print_tuple(buffer + len,
+ &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
+ proto);
+ if (conntrack->status & IPS_ASSURED)
+ len += sprintf(buffer + len, "[ASSURED] ");
+ len += sprintf(buffer + len, "use=%u ",
+ atomic_read(&conntrack->ct_general.use));
+ len += sprintf(buffer + len, "\n");
+
+ return len;
+}
+
+/* Returns true when finished. */
+static inline int
+conntrack_iterate(const struct ip_conntrack_tuple_hash *hash,
+ char *buffer, off_t offset, off_t *upto,
+ unsigned int *len, unsigned int maxlen)
+{
+ unsigned int newlen;
+ IP_NF_ASSERT(hash->ctrack);
+
+ MUST_BE_READ_LOCKED(&ip_conntrack_lock);
+
+ /* Only count originals */
+ if (DIRECTION(hash))
+ return 0;
+
+ if ((*upto)++ < offset)
+ return 0;
+
+ newlen = print_conntrack(buffer + *len, hash->ctrack);
+ if (*len + newlen > maxlen)
+ return 1;
+ else *len += newlen;
+
+ return 0;
+}
+
+static int
+list_conntracks(char *buffer, char **start, off_t offset, int length)
+{
+ unsigned int i;
+ unsigned int len = 0;
+ off_t upto = 0;
+ struct list_head *e;
+
+ READ_LOCK(&ip_conntrack_lock);
+ /* Traverse hash; print originals then reply. */
+ for (i = 0; i < ip_conntrack_htable_size; i++) {
+ if (LIST_FIND(&ip_conntrack_hash[i], conntrack_iterate,
+ struct ip_conntrack_tuple_hash *,
+ buffer, offset, &upto, &len, length))
+ goto finished;
+ }
+
+ /* Now iterate through expecteds. */
+ for (e = ip_conntrack_expect_list.next;
+ e != &ip_conntrack_expect_list; e = e->next) {
+ unsigned int last_len;
+ struct ip_conntrack_expect *expect
+ = (struct ip_conntrack_expect *)e;
+ if (upto++ < offset) continue;
+
+ last_len = len;
+ len += print_expect(buffer + len, expect);
+ if (len > length) {
+ len = last_len;
+ goto finished;
+ }
+ }
+
+ finished:
+ READ_UNLOCK(&ip_conntrack_lock);
+
+ /* `start' hack - see fs/proc/generic.c line ~165 */
+ *start = (char *)((unsigned int)upto - offset);
+ return len;
+}
+
+static unsigned int ip_confirm(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ /* We've seen it coming out the other side: confirm it */
+ return ip_conntrack_confirm(*pskb);
+}
+
+static unsigned int ip_refrag(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct rtable *rt = (struct rtable *)(*pskb)->dst;
+
+ /* We've seen it coming out the other side: confirm */
+ if (ip_confirm(hooknum, pskb, in, out, okfn) != NF_ACCEPT)
+ return NF_DROP;
+
+ /* Local packets are never produced too large for their
+ interface. We degfragment them at LOCAL_OUT, however,
+ so we have to refragment them here. */
+ if ((*pskb)->len > rt->u.dst.pmtu) {
+ /* No hook can be after us, so this should be OK. */
+ ip_fragment(*pskb, okfn);
+ return NF_STOLEN;
+ }
+ return NF_ACCEPT;
+}
+
+static unsigned int ip_conntrack_local(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ /* root is playing with raw sockets. */
+ if ((*pskb)->len < sizeof(struct iphdr)
+ || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
+ if (net_ratelimit())
+ printk("ipt_hook: happy cracking.\n");
+ return NF_ACCEPT;
+ }
+ return ip_conntrack_in(hooknum, pskb, in, out, okfn);
+}
+
+/* Connection tracking may drop packets, but never alters them, so
+ make it the first hook. */
+static struct nf_hook_ops ip_conntrack_in_ops
+= { { NULL, NULL }, ip_conntrack_in, PF_INET, NF_IP_PRE_ROUTING,
+ NF_IP_PRI_CONNTRACK };
+static struct nf_hook_ops ip_conntrack_local_out_ops
+= { { NULL, NULL }, ip_conntrack_local, PF_INET, NF_IP_LOCAL_OUT,
+ NF_IP_PRI_CONNTRACK };
+/* Refragmenter; last chance. */
+static struct nf_hook_ops ip_conntrack_out_ops
+= { { NULL, NULL }, ip_refrag, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_LAST };
+static struct nf_hook_ops ip_conntrack_local_in_ops
+= { { NULL, NULL }, ip_confirm, PF_INET, NF_IP_LOCAL_IN, NF_IP_PRI_LAST-1 };
+
+static int init_or_cleanup(int init)
+{
+ struct proc_dir_entry *proc;
+ int ret = 0;
+
+ if (!init) goto cleanup;
+
+ ret = ip_conntrack_init();
+ if (ret < 0)
+ goto cleanup_nothing;
+
+#ifdef DEL_IP_CONNTRACK_ENTRY
+ proc = proc_net_create("del_ip_conntrack", S_IFREG | S_IRUGO | S_IWUSR, proc_read_del_ip_conntrack);
+ if(proc)
+ {
+ proc->write_proc = proc_write_del_ip_conntrack;
+ proc->owner = THIS_MODULE;
+ }
+ else
+ {
+ //Maybe we can just let it go!
+ }
+#endif
+#ifdef CLEAR_IP_CONNTRACK
+ proc = proc_net_create("clear_ip_conntrack", S_IFREG | S_IRUGO | S_IWUSR, proc_read_clear_ip_conntrack);
+ if(proc)
+ {
+ proc->write_proc = proc_write_clear_ip_conntrack;
+ proc->owner = THIS_MODULE;
+ }
+ else
+ {
+ //Maybe we can just let it go!
+ }
+#endif
+ proc = proc_net_create("ip_conntrack",0,list_conntracks);
+ if (!proc) goto cleanup_init;
+ proc->owner = THIS_MODULE;
+
+ ret = nf_register_hook(&ip_conntrack_in_ops);
+ if (ret < 0) {
+ printk("ip_conntrack: can't register pre-routing hook.\n");
+ goto cleanup_proc;
+ }
+ ret = nf_register_hook(&ip_conntrack_local_out_ops);
+ if (ret < 0) {
+ printk("ip_conntrack: can't register local out hook.\n");
+ goto cleanup_inops;
+ }
+ ret = nf_register_hook(&ip_conntrack_out_ops);
+ if (ret < 0) {
+ printk("ip_conntrack: can't register post-routing hook.\n");
+ goto cleanup_inandlocalops;
+ }
+ ret = nf_register_hook(&ip_conntrack_local_in_ops);
+ if (ret < 0) {
+ printk("ip_conntrack: can't register local in hook.\n");
+ goto cleanup_inoutandlocalops;
+ }
+
+ return ret;
+
+ cleanup:
+ nf_unregister_hook(&ip_conntrack_local_in_ops);
+ cleanup_inoutandlocalops:
+ nf_unregister_hook(&ip_conntrack_out_ops);
+ cleanup_inandlocalops:
+ nf_unregister_hook(&ip_conntrack_local_out_ops);
+ cleanup_inops:
+ nf_unregister_hook(&ip_conntrack_in_ops);
+ cleanup_proc:
+ proc_net_remove("ip_conntrack");
+ cleanup_init:
+ ip_conntrack_cleanup();
+ cleanup_nothing:
+ return ret;
+}
+
+int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto)
+{
+ int ret = 0;
+ struct list_head *i;
+
+ WRITE_LOCK(&ip_conntrack_lock);
+ for (i = protocol_list.next; i != &protocol_list; i = i->next) {
+ if (((struct ip_conntrack_protocol *)i)->proto
+ == proto->proto) {
+ ret = -EBUSY;
+ goto out;
+ }
+ }
+
+ list_prepend(&protocol_list, proto);
+ MOD_INC_USE_COUNT;
+
+ out:
+ WRITE_UNLOCK(&ip_conntrack_lock);
+ return ret;
+}
+
+void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto)
+{
+ WRITE_LOCK(&ip_conntrack_lock);
+
+ /* ip_ct_find_proto() returns proto_generic in case there is no protocol
+ * helper. So this should be enough - HW */
+ LIST_DELETE(&protocol_list, proto);
+ WRITE_UNLOCK(&ip_conntrack_lock);
+
+ /* Somebody could be still looking at the proto in bh. */
+ br_write_lock_bh(BR_NETPROTO_LOCK);
+ br_write_unlock_bh(BR_NETPROTO_LOCK);
+
+ /* Remove all contrack entries for this protocol */
+ ip_ct_selective_cleanup(kill_proto, &proto->proto);
+
+ MOD_DEC_USE_COUNT;
+}
+
+static int __init init(void)
+{
+ return init_or_cleanup(1);
+}
+
+static void __exit fini(void)
+{
+ init_or_cleanup(0);
+}
+
+module_init(init);
+module_exit(fini);
+
+EXPORT_SYMBOL(ip_conntrack_protocol_register);
+EXPORT_SYMBOL(ip_conntrack_protocol_unregister);
+EXPORT_SYMBOL(invert_tuplepr);
+EXPORT_SYMBOL(ip_conntrack_alter_reply);
+EXPORT_SYMBOL(ip_conntrack_destroyed);
+EXPORT_SYMBOL(ip_conntrack_get);
+EXPORT_SYMBOL(ip_conntrack_module);
+EXPORT_SYMBOL(ip_conntrack_helper_register);
+EXPORT_SYMBOL(ip_conntrack_helper_unregister);
+EXPORT_SYMBOL(ip_ct_selective_cleanup);
+EXPORT_SYMBOL(ip_ct_refresh);
+EXPORT_SYMBOL(ip_ct_find_proto);
+EXPORT_SYMBOL(__ip_ct_find_proto);
+EXPORT_SYMBOL(ip_ct_find_helper);
+EXPORT_SYMBOL(sysctl_ip_conntrack_tcp_timeouts);
+EXPORT_SYMBOL(sysctl_ip_conntrack_udp_timeouts);
+EXPORT_SYMBOL(ip_conntrack_expect_related);
+EXPORT_SYMBOL(ip_conntrack_change_expect);
+EXPORT_SYMBOL(ip_conntrack_unexpect_related);
+EXPORT_SYMBOL_GPL(ip_conntrack_expect_find_get);
+EXPORT_SYMBOL_GPL(ip_conntrack_expect_put);
+EXPORT_SYMBOL(ip_conntrack_tuple_taken);
+EXPORT_SYMBOL(ip_ct_gather_frags);
+EXPORT_SYMBOL(ip_conntrack_htable_size);
+EXPORT_SYMBOL(ip_conntrack_expect_list);
+EXPORT_SYMBOL(ip_conntrack_lock);
+EXPORT_SYMBOL(ip_conntrack_hash);
+EXPORT_SYMBOL_GPL(ip_conntrack_find_get);
+EXPORT_SYMBOL_GPL(ip_conntrack_put);