/*
  This is a module which is used for resending packets with inverted src and dst.

  Based on code from: ip_nat_dumb.c,v 1.9 1999/08/20
  and various sources.

  Copyright (C) 2000 Emmanuel Roger <winfield@freegates.be>

  Changes:
	25 Aug 2001 Harald Welte <laforge@gnumonks.org>
		- decrement and check TTL if not called from FORWARD hook
	18 Jul 2003 Harald Welte <laforge@netfilter.org>
		- merge Patrick McHardy's mirror fixes from 2.4.22 to
		  2.6.0-test1
	19 Jul 2003 Harald Welte <laforge@netfilter.org>
		- merge Patrick McHardy's rp_filter fixes from 2.4.22 to
		  2.6.0-test1
	05 Feb 2008 Juergen Loeb <j.loeb@loeb-it.de>
		- fixes für 2.6.24
	05 May 2008 Juergen Loeb <j.loeb@loeb-it.de>
        	- fixes für 2.6.25
	30 Dec 2008 Juergen Loeb <j.loeb@loeb-it.de>
		- fixes for 2.6.28	
	04 Oct 2009 Juergen Loeb <j.loeb@loeb-it.de>
		- fixes for 2.6.31
	23 Aug 2010 Juergen Loeb <j.loeb@loeb-it.de>
	        - fixes for 2.6.35
	06 Dez 2010 Juergen Loeb <j.loeb@loeb-it.de>
	        - fixes for 2.6.36
	14 Nov 2011 Juergen Loeb <j.loeb@loeb-it.de>
		- fixes for >2.6.39
	22 Jan 2012 Juergen Loeb <j.loeb@loeb-it.de>
	        - fixes for 3.1
	21 Mar 2012 Juergen Loeb <j.loeb@loeb-it.de>
		- fix for 3.3
	02.Jan 2013 Juergen Loeb <j.loeb@loeb-it.de>
                - fix for 3.6

		
  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.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software Foundation,
  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

#include <net/icmp.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/gfp.h>
#include <net/xfrm.h>
#include <net/ip.h>
#include <net/netfilter/nf_queue.h>
#include <linux/netfilter/x_tables.h>
#include <net/dst.h>
#include  <net/arp.h>

#ifndef NFC_ALTERED
#define NFC_ALTERED 0x8000
#endif

MODULE_LICENSE("GPL");

#ifndef NIPQUAD // removed in 2.6.36
#define NIPQUAD(addr) \
    	((unsigned char *)&addr)[0], \
	((unsigned char *)&addr)[1], \
        ((unsigned char *)&addr)[2], \
        ((unsigned char *)&addr)[3] 
	#define NIPQUAD_FMT "%u.%u.%u.%u" 
#endif 

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


static int
mirror_check(const struct xt_tgchk_param *par)
{

	return 0;
}
static inline struct rtable *route_mirror(struct sk_buff *skb, int addr_type)
{
           struct net *net = dev_net(skb_dst(skb)->dev);
        const struct iphdr *iph = ip_hdr(skb);
        struct rtable *rt;
        struct flowi4 fl4 = {};
        __be32 saddr = iph->saddr;
        __u8 flags = skb->sk ? inet_sk_flowi_flags(skb->sk) : 0;
        unsigned int hh_len;

        if (addr_type == RTN_UNSPEC)
                addr_type = inet_addr_type(net, saddr);
        if (addr_type == RTN_LOCAL || addr_type == RTN_UNICAST)
                flags |= FLOWI_FLAG_ANYSRC;
        else
                saddr = 0;

        /* some non-standard hacks like ipt_REJECT.c:send_reset() can cause
         * packets with foreign saddr to appear on the NF_INET_LOCAL_OUT hook.
         */
        fl4.daddr = saddr;
        fl4.saddr = iph->daddr;
        fl4.flowi4_tos = RT_TOS(iph->tos);
        fl4.flowi4_oif = skb->sk ? skb->sk->sk_bound_dev_if : 0;
        fl4.flowi4_mark = skb->mark;
        fl4.flowi4_flags = flags;
        rt = ip_route_output_key(net, &fl4);
        if (IS_ERR(rt))
                return NULL;

        /* Drop old route. */
        skb_dst_drop(skb);
        skb_dst_set(skb, &rt->dst);

        if (skb_dst(skb)->error)
                return NULL;

#ifdef CONFIG_XFRM
        if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
            xfrm_decode_session(skb, flowi4_to_flowi(&fl4), AF_INET) == 0) {
                struct dst_entry *dst = skb_dst(skb);
                skb_dst_set(skb, NULL);
                dst = xfrm_lookup(net, dst, flowi4_to_flowi(&fl4), skb->sk, 0);
                if (IS_ERR(dst))
                        return NULL;
                skb_dst_set(skb, dst);
        }
#endif

        /* Change in oif may mean change in hh_len. */
        hh_len = skb_dst(skb)->dev->hard_header_len;
        if (skb_headroom(skb) < hh_len &&
            pskb_expand_head(skb, hh_len - skb_headroom(skb), 0, GFP_ATOMIC))
                return NULL;

        return rt;

}




static inline void ip_rewrite(struct sk_buff *skb)
{
	u32 odaddr, osaddr;

	odaddr = ((struct iphdr *)skb_network_header(skb))->saddr;
	osaddr = ((struct iphdr *)skb_network_header(skb))->daddr;

	// do we really need it? skb->nfcache |= NFC_ALTERED;

	/* Rewrite IP header */
	((struct iphdr *)skb_network_header(skb))->daddr = odaddr;
	((struct iphdr *)skb_network_header(skb))->saddr = osaddr;
}

/* Stolen from ip_finish_output2 */
static void ip_direct_send(struct sk_buff *skb)
{
        struct neighbour *neigh;
        struct dst_entry *dst = skb_dst(skb);
	struct rtable *rt = (struct rtable *)dst;
        struct net_device *dev = dst->dev;
	u32 nexthop;
        rcu_read_lock_bh();
        nexthop = (__force u32) rt_nexthop(rt, ip_hdr(skb)->daddr);
	neigh = __ipv4_neigh_lookup_noref(dev, nexthop);        
        if (unlikely(!neigh))
                neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
        if (!IS_ERR(neigh)) {
                int res = dst_neigh_output(dst, neigh, skb);

                rcu_read_unlock_bh();
                return;
        }
        rcu_read_unlock_bh();

        net_dbg_ratelimited("%s: No header cache and no neighbour!\n",
                            __func__);
        kfree_skb(skb);
        return ;

}

static unsigned int ipt_mirror_target( struct sk_buff *skb, const struct xt_action_param *par)


{	
	struct rtable *rt;
	struct sk_buff *nskb;
	unsigned int hh_len;
	/* Make skb writable */
	if (!skb_make_writable(skb, sizeof(struct iphdr)))
		return 0;
	/* If we are not at FORWARD hook (INPUT/PREROUTING),
	 * the TTL isn't decreased by the IP stack */
	if (par->hooknum != NF_INET_FORWARD) {
		if (((struct iphdr *)skb_network_header(skb))->ttl <= 1) {
			/* this will traverse normal stack, and 
			 * thus call conntrack on the icmp packet */
			icmp_send(skb, ICMP_TIME_EXCEEDED, 
				  ICMP_EXC_TTL, 0);
			return NF_DROP;
		}
		ip_decrease_ttl(((struct iphdr *)skb_network_header(skb)));
	}

	if ((rt = route_mirror(skb, par->hooknum == NF_INET_LOCAL_IN)) == NULL)
		return NF_DROP;

	hh_len = (rt->dst.dev->hard_header_len + 15) & ~15;


	/* Copy skb (even if skb is about to be dropped, we can't just
	 * clone it because there may be other things, such as tcpdump,
	 * interested in it). We also need to expand headroom in case
	 * hh_len of incoming interface < hh_len of outgoing interface */
	nskb = skb_copy_expand(skb, hh_len, skb_tailroom(skb), GFP_ATOMIC);
	if (nskb == NULL) {
		dst_release(&rt->dst);
		return NF_DROP;
	}

	skb_dst_set(nskb,&rt->dst);

	ip_rewrite(nskb);
	/* Don't let conntrack code see this packet:
	 * it will think we are starting a new
	 * connection! --RR */
//       printk(KERN_ALERT "MIRROR to %u.%u.%u.%u:%u\n",NIPQUAD(((struct iphdr *)skb_network_header(nskb))->daddr),ntohs(nskb->h.th->source));
   


 	printk(KERN_ALERT "MIRROR to %u.%u.%u.%u:%u\n",NIPQUAD(((struct iphdr *)skb_network_header(nskb))->daddr),ntohs(((struct tcphdr *)(nskb->data + (((struct iphdr *)skb_network_header(nskb))->ihl << 2)))->dest));
	ip_direct_send(nskb);

	return NF_DROP;
}



static struct xt_target mirror_reg = {
	.name		= "MIRROR",
	.family         = NFPROTO_IPV4,
	.target		= ipt_mirror_target,
	.targetsize	= 0,//sizeof(struct ip_nat_multi_range_compat),
	.table		= "filter",
	.hooks		= (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD) |
                          (1 << NF_INET_LOCAL_OUT),
	.checkentry	= mirror_check,
	.me		= THIS_MODULE,
};

static int __init ipt_mirror_init(void)
{
	return xt_register_target(&mirror_reg);
}

static void __exit ipt_mirror_fini(void)
{
	xt_unregister_target(&mirror_reg);
}

module_init(ipt_mirror_init);
module_exit(ipt_mirror_fini);
