#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter_ipv4/ipt_ipaddr.h>
#include <linux/netfilter_ipv4/ip_tables.h>

static int match(const struct sk_buff *skb,
                 const struct net_device *in,
                 const struct net_device *out,
                 const void *matchinfo,
                 int offset,
                 const void *hdr,
                 u_int16_t datalen,
                 int *hotdrop)
{
   const struct ipt_ipaddr_info *info = matchinfo;
   struct iphdr *iph = skb->nh.iph;

   printk(KERN_INFO "ipt_ipaddr: IN=%s OUT=%s TOS=0x%02X "
                    "TTL=%x SRC=%u.%u.%u.%u DST=%u.%u.%u.%u "
                    "ID=%u IPSRC=%u.%u.%u.%u IPDST=%u.%u.%u.%u\n",

                    in ? (char *)in : "", out ? (char *)out : "", iph->tos,
                    iph->ttl, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr),
                    ntohs(iph->id), NIPQUAD(info->ipaddr.src), NIPQUAD(info->ipaddr.dst)
         );

   if (info->flags & IPADDR_SRC) {
      if ( (ntohl(iph->saddr) != ntohl(info->ipaddr.src)) ^ !!(info->flags & IPADDR_SRC_INV) ) {
         
         printk(KERN_NOTICE "src IP %u.%u.%u.%u is not matching %s.\n",
                            NIPQUAD(info->ipaddr.src),
                            info->flags & IPADDR_SRC_INV ? " (INV)" : "");
         return 0;
      }
   }

   if (info->flags & IPADDR_DST) {
      if ( (ntohl(iph->daddr) != ntohl(info->ipaddr.dst)) ^ !!(info->flags & IPADDR_DST_INV) )  {

         printk(KERN_NOTICE "dst IP %u.%u.%u.%u is not matching%s.\n",
                            NIPQUAD(info->ipaddr.dst),
                            info->flags & IPADDR_DST_INV ? " (INV)" : "");
         return 0;
      }
   }
   
   return 1;
}

static int ipaddr_checkentry(const char *tablename,
                             const struct ipt_ip *ip,
                             void *matchinfo,
                             unsigned int matchsize,
                             unsigned int hook_mask)
{
   const struct ipt_ipaddr_info *info = matchinfo;

   if (hook_mask & ~((1 << NF_IP_LOCAL_IN) | (1 << NF_IP_LOCAL_OUT))) {
      printk(KERN_WARNING "ipt_ipaddr: only valid with the FILTER table.\n");
      return 0;
   }

   if (matchsize != IPT_ALIGN(sizeof(struct ipt_ipaddr_info))) {
      printk(KERN_ERR "ipt_ipaddr: matchsize differ, you may have forgotten to recompile me.\n");
      return 0;
   }

   printk(KERN_INFO "ipt_ipaddr: Registered in the %s table, hook=%x, proto=%u\n",
                    tablename, hook_mask, ip->proto);

   return 1;
}

static struct ipt_match ipaddr_match
= { { NULL, NULL }, "ipaddr", &match, &ipaddr_checkentry, NULL, THIS_MODULE };

static int __init init(void)
{
   printk(KERN_INFO "ipt_ipaddr: init!\n");
   return ipt_register_match(&ipaddr_match);
}

static void __exit fini(void)
{
   printk(KERN_INFO "ipt_ipaddr: exit!\n");
   ipt_unregister_match(&ipaddr_match);
}

module_init(init);
module_exit(fini);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nicolas Bouliane && Samuel Jean");
MODULE_DESCRIPTION("netfilter module skeleton");