#include <linux/config.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <net/ip.h>
#include <net/checksum.h>
struct in_device;
#include <net/route.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include "ipt_SIGN.h"

void shaSignPacket(const struct iphdr *ih, const char *key, int offset, int len, char *staging, char *signptr);

#if 1
#define DEBUGP printk
#else
#define DEBUGP(format, args...)
#endif

/* $Id: ipt_SIGN1.c,v 1.3 2001/08/29 21:15:07 ahu Exp $ */

static char *staging;

static unsigned int sign(struct sk_buff **pskb,
			   unsigned int hooknum,
			   const struct net_device *in,
			   const struct net_device *out,
			   const void *targinfo,
			   void *userinfo)
{
	const struct ipt_sign_info *sign = targinfo;
	struct iphdr *ih=(*pskb)->nh.iph;
	struct spshdr sh;
	int spslen;

	char *signptr;

	if(ih->protocol==128) {
	  printk("Not resigning\n");
	  return IPT_CONTINUE;;
	}

	if(skb_tailroom(*pskb)<sizeof(sh)) {
	  printk("Not enough room to sign, dropping\n");
	  return NF_DROP; /* FIXME: see ip_queue.c for help */
	}

	skb_put((*pskb),sizeof(sh)+20);
	
	if(sign->secret && *sign->secret)
	  printk("About to sign a packet with secret: %s\n", sign->secret);
	else
	  printk("Would have signed a packet\n");
	
	printk("Size of header: %d, total length: %d\n",ih->ihl*4, ntohs(ih->tot_len));


	/* build SPS header */
	sh.orig_proto=ih->protocol;
	sh.algorithm=1;
	sh.sig_bytes=20;
	spslen=sizeof(sh)+sh.sig_bytes;

	ih->protocol=128; /* now an SPS packet */

	/* push down payload */
	memmove((char *)ih+ih->ihl*4+spslen, (char *)ih+ih->ihl*4, ntohs(ih->tot_len)-ih->ihl*4);

	/* insert header */
	memcpy((char*)ih+ih->ihl*4,&sh,sizeof(sh));

	signptr=(char*)ih+ih->ihl*4+sizeof(sh);

	shaSignPacket(ih, sign->secret, spslen, ntohs(ih->tot_len)-ih->ihl*4, staging, signptr); /* SIGN! */
	
	/* fix up packet length in ip header */
	ih->tot_len=htons(ntohs(ih->tot_len)+spslen);

	/* recalculate checksum */
	ih->check=0;
	ih->check=ip_fast_csum((char *)ih, ih->ihl); /* FIXME: this should do incremental */
	
	(*pskb)->nfcache |= NFC_ALTERED; // tell netfilter we changed
	
	return IPT_CONTINUE;
}

static int check(const char *tablename,
		 const struct ipt_entry *e,
		 void *targinfo,
		 unsigned int targinfosize,
		 unsigned int hook_mask)
{
 	const struct ipt_sign_info *signinfo = targinfo;

 	if (targinfosize != IPT_ALIGN(sizeof(struct ipt_sign_info))) {
  		DEBUGP("SIGN: targinfosize %u != %u\n", targinfosize,
		       IPT_ALIGN(sizeof(struct ipt_sign_info)));
  		return 0;
  	}

	/* Only allow these for packet mangling. */
	if (strcmp(tablename, "mangle") != 0) {
		DEBUGP("SIGN: bad table `%s'.\n", tablename);
		return 0;
	}
	if ((hook_mask & ~((1 << NF_IP_LOCAL_OUT)
			   | (1 << NF_IP_FORWARD)
			   )) != 0) {
		DEBUGP("SIGN: bad hook mask %X\n", hook_mask);
		return 0;
	}

	if(*signinfo->key) {
		DEBUGP("ipt_SIGN: Can't yet sign with a key, must have a secret\n");
		return 0;
	}
	if(!*signinfo->secret) {
		DEBUGP("ipt_SIGN: No secret specified\n");
		return 0;
	}

	return 1;
}

static struct ipt_target ipt_sign_reg
= { { NULL, NULL }, "SIGN", sign, check, NULL, THIS_MODULE };

static int __init init(void)
{
	if(!(staging=kmalloc(2000, GFP_KERNEL)))
		return -ENOMEM;

	if (ipt_register_target(&ipt_sign_reg))
		return -EINVAL;
	return 0;
}

static void __exit fini(void)
{
	ipt_unregister_target(&ipt_sign_reg);
}

module_init(init);
module_exit(fini);

