/* Drop the outgoing TCP packets with the RST flag set,
 * just in case you want re-implement some part of TCP in userspace.
 * NOTE: You can do it even using iptables */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/socket.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>

static struct nf_hook_ops output_filter;

static unsigned int output_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;
	struct tcphdr *tcp;
	int len = (*skb)->len;

	/* Don't process truncated packets */
	if (len < sizeof(struct iphdr) ||
	    len < (ip->ihl * 2) + sizeof(struct tcphdr))
		goto accept;

	if (ip->protocol != 6) /* not TCP */
		goto accept;

	/* drop TCP packets with RST flag set */
	tcp = (struct tcphdr*)((__u32 *)ip+ip->ihl);
	if (tcp->rst)
		return NF_DROP;

accept:
	return NF_ACCEPT;
}

int init_module(void)
{
	int result;

	output_filter.list.next = NULL;
	output_filter.list.prev = NULL;
	output_filter.hook = output_handler;
	output_filter.pf = PF_INET; /* IPv4 */
	output_filter.hooknum = NF_IP_LOCAL_OUT;

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

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

void cleanup_module(void)
{
	nf_unregister_hook(&output_filter);
	printk(KERN_INFO "drop-resets: module removed\n");
}
