Merge tag 'v3.10.61' into update
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / netfilter / xt_REDIRECT.c
CommitLineData
2cbc78a2
JE
1/*
2 * (C) 1999-2001 Paul `Rusty' Russell
3 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
4 * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * Based on Rusty Russell's IPv4 REDIRECT target. Development of IPv6
11 * NAT funded by Astaro.
12 */
13
14#include <linux/if.h>
15#include <linux/inetdevice.h>
16#include <linux/ip.h>
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/netdevice.h>
20#include <linux/netfilter.h>
21#include <linux/types.h>
22#include <linux/netfilter_ipv4.h>
23#include <linux/netfilter_ipv6.h>
24#include <linux/netfilter/x_tables.h>
25#include <net/addrconf.h>
26#include <net/checksum.h>
27#include <net/protocol.h>
28#include <net/netfilter/nf_nat.h>
29
30static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
31
32static unsigned int
33redirect_tg6(struct sk_buff *skb, const struct xt_action_param *par)
34{
35 const struct nf_nat_range *range = par->targinfo;
36 struct nf_nat_range newrange;
37 struct in6_addr newdst;
38 enum ip_conntrack_info ctinfo;
39 struct nf_conn *ct;
40
41 ct = nf_ct_get(skb, &ctinfo);
42 if (par->hooknum == NF_INET_LOCAL_OUT)
43 newdst = loopback_addr;
44 else {
45 struct inet6_dev *idev;
46 struct inet6_ifaddr *ifa;
47 bool addr = false;
48
49 rcu_read_lock();
50 idev = __in6_dev_get(skb->dev);
51 if (idev != NULL) {
52 list_for_each_entry(ifa, &idev->addr_list, if_list) {
53 newdst = ifa->addr;
54 addr = true;
55 break;
56 }
57 }
58 rcu_read_unlock();
59
60 if (!addr)
61 return NF_DROP;
62 }
63
64 newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS;
65 newrange.min_addr.in6 = newdst;
66 newrange.max_addr.in6 = newdst;
67 newrange.min_proto = range->min_proto;
68 newrange.max_proto = range->max_proto;
69
70 return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
71}
72
73static int redirect_tg6_checkentry(const struct xt_tgchk_param *par)
74{
75 const struct nf_nat_range *range = par->targinfo;
76
77 if (range->flags & NF_NAT_RANGE_MAP_IPS)
78 return -EINVAL;
79 return 0;
80}
81
82/* FIXME: Take multiple ranges --RR */
83static int redirect_tg4_check(const struct xt_tgchk_param *par)
84{
85 const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
86
87 if (mr->range[0].flags & NF_NAT_RANGE_MAP_IPS) {
88 pr_debug("bad MAP_IPS.\n");
89 return -EINVAL;
90 }
91 if (mr->rangesize != 1) {
92 pr_debug("bad rangesize %u.\n", mr->rangesize);
93 return -EINVAL;
94 }
95 return 0;
96}
97
98static unsigned int
99redirect_tg4(struct sk_buff *skb, const struct xt_action_param *par)
100{
101 struct nf_conn *ct;
102 enum ip_conntrack_info ctinfo;
103 __be32 newdst;
104 const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
105 struct nf_nat_range newrange;
106
107 NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING ||
108 par->hooknum == NF_INET_LOCAL_OUT);
109
110 ct = nf_ct_get(skb, &ctinfo);
111 NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
112
113 /* Local packets: make them go to loopback */
114 if (par->hooknum == NF_INET_LOCAL_OUT)
115 newdst = htonl(0x7F000001);
116 else {
117 struct in_device *indev;
118 struct in_ifaddr *ifa;
119
120 newdst = 0;
121
122 rcu_read_lock();
123 indev = __in_dev_get_rcu(skb->dev);
124 if (indev && (ifa = indev->ifa_list))
125 newdst = ifa->ifa_local;
126 rcu_read_unlock();
127
128 if (!newdst)
129 return NF_DROP;
130 }
131
132 /* Transfer from original range. */
133 memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
134 memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
135 newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
136 newrange.min_addr.ip = newdst;
137 newrange.max_addr.ip = newdst;
138 newrange.min_proto = mr->range[0].min;
139 newrange.max_proto = mr->range[0].max;
140
141 /* Hand modified range to generic setup. */
142 return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
143}
144
145static struct xt_target redirect_tg_reg[] __read_mostly = {
146 {
147 .name = "REDIRECT",
148 .family = NFPROTO_IPV6,
149 .revision = 0,
150 .table = "nat",
151 .checkentry = redirect_tg6_checkentry,
152 .target = redirect_tg6,
153 .targetsize = sizeof(struct nf_nat_range),
154 .hooks = (1 << NF_INET_PRE_ROUTING) |
155 (1 << NF_INET_LOCAL_OUT),
156 .me = THIS_MODULE,
157 },
158 {
159 .name = "REDIRECT",
160 .family = NFPROTO_IPV4,
161 .revision = 0,
162 .table = "nat",
163 .target = redirect_tg4,
164 .checkentry = redirect_tg4_check,
165 .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat),
166 .hooks = (1 << NF_INET_PRE_ROUTING) |
167 (1 << NF_INET_LOCAL_OUT),
168 .me = THIS_MODULE,
169 },
170};
171
172static int __init redirect_tg_init(void)
173{
174 return xt_register_targets(redirect_tg_reg,
175 ARRAY_SIZE(redirect_tg_reg));
176}
177
178static void __exit redirect_tg_exit(void)
179{
180 xt_unregister_targets(redirect_tg_reg, ARRAY_SIZE(redirect_tg_reg));
181}
182
183module_init(redirect_tg_init);
184module_exit(redirect_tg_exit);
185
186MODULE_LICENSE("GPL");
187MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
188MODULE_DESCRIPTION("Xtables: Connection redirection to localhost");
189MODULE_ALIAS("ip6t_REDIRECT");
190MODULE_ALIAS("ipt_REDIRECT");