/* IPOPT-DROP
 * This filters out the IP options for the incoming IP packets.
 * Copyright(C) 2001 Salvatore Sanfilippo <antirez@invece.org>
 *
 * under the GPL version 2 license
 * USE IT AT YOUR RISK
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/socket.h>
#include <linux/ip.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/string.h>
#include <linux/net.h>
#include <linux/in.h>
#include <net/ip.h>

static struct nf_hook_ops input_filter;

static unsigned int input_handler(	unsigned int hooknum,
					struct sk_buff **skb,
					const struct net_device *in,
					const struct net_device *out,
					int (*okfn)(struct sk_buff *))
{
	struct iphdr *ip = (*skb)->nh.iph;
	int len = (*skb)->len;
	int iphdrlen;

	/* Don't process packets with bad IP header len, but
	 * don't drop it */
	if (len < sizeof(struct iphdr) || len < (ip->ihl * 4))
		goto accept;

	iphdrlen = ip->ihl * 4;

	/* No options */
	if (ip->ihl == sizeof(struct iphdr) / 4)
		goto accept;

	/* Get the IP data len */
	len -= iphdrlen;
	if (len != 0) {
		memmove((*skb)->data + sizeof(struct iphdr),
			(*skb)->data + iphdrlen, len);
	}

	/* Fix what isn't no longer OK */
	ip->ihl = sizeof(struct iphdr) / 4;
	/* speed-up for: ip->tot_len = htons(ntohs(ip->tot_len) - sizeof... */
	ip->tot_len -= htons(iphdrlen - sizeof(struct iphdr));
	(*skb)->h.raw = (*skb)->data + sizeof(struct iphdr);
	skb_trim(*skb, len + sizeof(struct iphdr));
	ip_send_check(ip);

accept:
	return NF_ACCEPT;
}

int init_module(void)
{
	int result;

	input_filter.list.next = NULL;
	input_filter.list.prev = NULL;
	input_filter.hook = input_handler;
	input_filter.pf = PF_INET; /* IPv4 */
	input_filter.hooknum = NF_IP_PRE_ROUTING;

	result = nf_register_hook(&input_filter);
	if (result)
		return result;

	printk(KERN_INFO "ipopt-drop module loaded\n");
	return 0;
}

void cleanup_module(void)
{
	nf_unregister_hook(&input_filter);
	printk(KERN_INFO "ipopt-drop module removed\n");
}
