1/* Redirect.  Simple mapping which alters dst to a local IP address. */
2/* (C) 1999-2001 Paul `Rusty' Russell
3 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9
10#include <linux/types.h>
11#include <linux/ip.h>
12#include <linux/timer.h>
13#include <linux/module.h>
14#include <linux/netfilter.h>
15#include <linux/netdevice.h>
16#include <linux/if.h>
17#include <linux/inetdevice.h>
18#include <net/protocol.h>
19#include <net/checksum.h>
20#include <linux/netfilter_ipv4.h>
21#include <linux/netfilter/x_tables.h>
22#include <net/netfilter/nf_nat_rule.h>
23
24MODULE_LICENSE("GPL");
25MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
26MODULE_DESCRIPTION("iptables REDIRECT target module");
27
28#define DEBUGP(format, args...)
29
30static int
31redirect_check(const char *tablename,
32	       const void *e,
33	       const struct xt_target *target,
34	       void *targinfo,
35	       unsigned int hook_mask)
36{
37	const struct nf_nat_multi_range_compat *mr = targinfo;
38
39	if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) {
40		DEBUGP("redirect_check: bad MAP_IPS.\n");
41		return 0;
42	}
43	if (mr->rangesize != 1) {
44		DEBUGP("redirect_check: bad rangesize %u.\n", mr->rangesize);
45		return 0;
46	}
47	return 1;
48}
49
50static unsigned int
51redirect_target(struct sk_buff **pskb,
52		const struct net_device *in,
53		const struct net_device *out,
54		unsigned int hooknum,
55		const struct xt_target *target,
56		const void *targinfo)
57{
58	struct nf_conn *ct;
59	enum ip_conntrack_info ctinfo;
60	__be32 newdst;
61	const struct nf_nat_multi_range_compat *mr = targinfo;
62	struct nf_nat_range newrange;
63
64	NF_CT_ASSERT(hooknum == NF_IP_PRE_ROUTING
65		     || hooknum == NF_IP_LOCAL_OUT);
66
67	ct = nf_ct_get(*pskb, &ctinfo);
68	NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
69
70	/* Local packets: make them go to loopback */
71	if (hooknum == NF_IP_LOCAL_OUT)
72		newdst = htonl(0x7F000001);
73	else {
74		struct in_device *indev;
75		struct in_ifaddr *ifa;
76
77		newdst = 0;
78
79		rcu_read_lock();
80		indev = __in_dev_get_rcu((*pskb)->dev);
81		if (indev && (ifa = indev->ifa_list))
82			newdst = ifa->ifa_local;
83		rcu_read_unlock();
84
85		if (!newdst)
86			return NF_DROP;
87	}
88
89	/* Transfer from original range. */
90	newrange = ((struct nf_nat_range)
91		{ mr->range[0].flags | IP_NAT_RANGE_MAP_IPS,
92		  newdst, newdst,
93		  mr->range[0].min, mr->range[0].max });
94
95	/* Hand modified range to generic setup. */
96	return nf_nat_setup_info(ct, &newrange, hooknum);
97}
98
99static struct xt_target redirect_reg = {
100	.name		= "REDIRECT",
101	.family		= AF_INET,
102	.target		= redirect_target,
103	.targetsize	= sizeof(struct nf_nat_multi_range_compat),
104	.table		= "nat",
105	.hooks		= (1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT),
106	.checkentry	= redirect_check,
107	.me		= THIS_MODULE,
108};
109
110static int __init ipt_redirect_init(void)
111{
112	return xt_register_target(&redirect_reg);
113}
114
115static void __exit ipt_redirect_fini(void)
116{
117	xt_unregister_target(&redirect_reg);
118}
119
120module_init(ipt_redirect_init);
121module_exit(ipt_redirect_fini);
122