/* 
  Kernel module to match the childlevel of a connection.
  i.e. The ftp control stream is childlevel 0.
       The ftp data stream is childlevel 1.
  
  By Matthew Strait <quadong@users.sf.net>, Dec 2003.
  http://l7-filter.sf.net

  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.
  http://www.gnu.org/licenses/gpl.txt
*/

#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netfilter_ipv4/ip_conntrack.h>

#include <linux/netfilter_ipv4/ipt_childlevel.h>
#include <linux/netfilter_ipv4/ip_tables.h>

MODULE_AUTHOR("Matthew Strait <quadong@users.sf.net>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Childlevel match module");

static int match(const struct sk_buff *skb,    const struct net_device *in,
                 const struct net_device *out, const void *matchinfo,
                 int offset,                   int *hotdrop)
{
	struct ipt_childlevel_info * info = (struct ipt_childlevel_info *)matchinfo;
        enum ip_conntrack_info ctinfo;
        struct ip_conntrack * conntrack;
	int childlevel = 0;

        if(!(conntrack = ip_conntrack_get((struct sk_buff *)skb, &ctinfo))){
                printk(KERN_ERR "Netfilter: childlevel: error getting conntrack, giving up.\n");
		return 0;
        }
	
        while (master_ct(conntrack) != NULL){
		childlevel++;
                conntrack = master_ct(conntrack);
	}

    	return ( (childlevel == info->childlevel) ^ info->invert);
}

static int
checkentry(const char *tablename, const struct ipt_ip *ip,
           void *matchinfo,       unsigned int matchsize,   unsigned int hook_mask)
{
       if (matchsize != IPT_ALIGN(sizeof(struct ipt_childlevel_info))) 
               return 0;
       return 1;
}

static struct ipt_match childlevel_match = { 
	.name = "childlevel", 
	.match = &match, 
	.checkentry = &checkentry, 
	.me = THIS_MODULE 
};

static int __init init(void)
{
	return ipt_register_match(&childlevel_match);
}

static void __exit fini(void)
{
	ipt_unregister_match(&childlevel_match);
}

module_init(init);
module_exit(fini);
