1/* Redirect.  Simple mapping which alters dst to a local IP address. */
2#include <linux/types.h>
3#include <linux/ip.h>
4#include <linux/timer.h>
5#include <linux/module.h>
6#include <linux/netfilter.h>
7#include <linux/netdevice.h>
8#include <linux/if.h>
9#include <linux/inetdevice.h>
10#include <net/protocol.h>
11#include <net/checksum.h>
12#include <linux/netfilter_ipv4.h>
13#include <linux/netfilter_ipv4/ip_nat_rule.h>
14
15#define DEBUGP(format, args...)
16
17static int
18redirect_check(const char *tablename,
19	       const struct ipt_entry *e,
20	       void *targinfo,
21	       unsigned int targinfosize,
22	       unsigned int hook_mask)
23{
24	const struct ip_nat_multi_range *mr = targinfo;
25
26	if (strcmp(tablename, "nat") != 0) {
27		DEBUGP("redirect_check: bad table `%s'.\n", table);
28		return 0;
29	}
30	if (targinfosize != IPT_ALIGN(sizeof(*mr))) {
31		DEBUGP("redirect_check: size %u.\n", targinfosize);
32		return 0;
33	}
34	if (hook_mask & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT))) {
35		DEBUGP("redirect_check: bad hooks %x.\n", hook_mask);
36		return 0;
37	}
38	if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) {
39		DEBUGP("redirect_check: bad MAP_IPS.\n");
40		return 0;
41	}
42	if (mr->rangesize != 1) {
43		DEBUGP("redirect_check: bad rangesize %u.\n", mr->rangesize);
44		return 0;
45	}
46	return 1;
47}
48
49static unsigned int
50redirect_target(struct sk_buff **pskb,
51		unsigned int hooknum,
52		const struct net_device *in,
53		const struct net_device *out,
54		const void *targinfo,
55		void *userinfo)
56{
57	struct ip_conntrack *ct;
58	enum ip_conntrack_info ctinfo;
59	u_int32_t newdst;
60	const struct ip_nat_multi_range *mr = targinfo;
61	struct ip_nat_multi_range newrange;
62
63	IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING
64		     || hooknum == NF_IP_LOCAL_OUT);
65
66	ct = ip_conntrack_get(*pskb, &ctinfo);
67	IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
68
69	/* Local packets: make them go to loopback */
70	if (hooknum == NF_IP_LOCAL_OUT)
71		newdst = htonl(0x7F000001);
72	else {
73		struct in_device *indev;
74
75		/* Device might not have an associated in_device. */
76		indev = (struct in_device *)(*pskb)->dev->ip_ptr;
77		if (indev == NULL)
78			return NF_DROP;
79
80		/* Grab first address on interface. */
81		newdst = indev->ifa_list->ifa_local;
82	}
83
84	/* Transfer from original range. */
85	newrange = ((struct ip_nat_multi_range)
86		{ 1, { { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS,
87			 newdst, newdst,
88			 mr->range[0].min, mr->range[0].max } } });
89
90	/* Hand modified range to generic setup. */
91	return ip_nat_setup_info(ct, &newrange, hooknum);
92}
93
94static struct ipt_target redirect_reg
95= { { NULL, NULL }, "REDIRECT", redirect_target, redirect_check, NULL,
96    THIS_MODULE };
97
98static int __init init(void)
99{
100	return ipt_register_target(&redirect_reg);
101}
102
103static void __exit fini(void)
104{
105	ipt_unregister_target(&redirect_reg);
106}
107
108module_init(init);
109module_exit(fini);
110MODULE_LICENSE("GPL");
111