xref: /openbmc/linux/net/netfilter/xt_REDIRECT.c (revision 8b13eddf)
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 #include <net/netfilter/ipv4/nf_nat_redirect.h>
30 
31 static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
32 
33 static unsigned int
34 redirect_tg6(struct sk_buff *skb, const struct xt_action_param *par)
35 {
36 	const struct nf_nat_range *range = par->targinfo;
37 	struct nf_nat_range newrange;
38 	struct in6_addr newdst;
39 	enum ip_conntrack_info ctinfo;
40 	struct nf_conn *ct;
41 
42 	ct = nf_ct_get(skb, &ctinfo);
43 	if (par->hooknum == NF_INET_LOCAL_OUT)
44 		newdst = loopback_addr;
45 	else {
46 		struct inet6_dev *idev;
47 		struct inet6_ifaddr *ifa;
48 		bool addr = false;
49 
50 		rcu_read_lock();
51 		idev = __in6_dev_get(skb->dev);
52 		if (idev != NULL) {
53 			list_for_each_entry(ifa, &idev->addr_list, if_list) {
54 				newdst = ifa->addr;
55 				addr = true;
56 				break;
57 			}
58 		}
59 		rcu_read_unlock();
60 
61 		if (!addr)
62 			return NF_DROP;
63 	}
64 
65 	newrange.flags		= range->flags | NF_NAT_RANGE_MAP_IPS;
66 	newrange.min_addr.in6	= newdst;
67 	newrange.max_addr.in6	= newdst;
68 	newrange.min_proto	= range->min_proto;
69 	newrange.max_proto	= range->max_proto;
70 
71 	return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
72 }
73 
74 static int redirect_tg6_checkentry(const struct xt_tgchk_param *par)
75 {
76 	const struct nf_nat_range *range = par->targinfo;
77 
78 	if (range->flags & NF_NAT_RANGE_MAP_IPS)
79 		return -EINVAL;
80 	return 0;
81 }
82 
83 /* FIXME: Take multiple ranges --RR */
84 static int redirect_tg4_check(const struct xt_tgchk_param *par)
85 {
86 	const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
87 
88 	if (mr->range[0].flags & NF_NAT_RANGE_MAP_IPS) {
89 		pr_debug("bad MAP_IPS.\n");
90 		return -EINVAL;
91 	}
92 	if (mr->rangesize != 1) {
93 		pr_debug("bad rangesize %u.\n", mr->rangesize);
94 		return -EINVAL;
95 	}
96 	return 0;
97 }
98 
99 static unsigned int
100 redirect_tg4(struct sk_buff *skb, const struct xt_action_param *par)
101 {
102 	return nf_nat_redirect_ipv4(skb, par->targinfo, par->hooknum);
103 }
104 
105 static struct xt_target redirect_tg_reg[] __read_mostly = {
106 	{
107 		.name       = "REDIRECT",
108 		.family     = NFPROTO_IPV6,
109 		.revision   = 0,
110 		.table      = "nat",
111 		.checkentry = redirect_tg6_checkentry,
112 		.target     = redirect_tg6,
113 		.targetsize = sizeof(struct nf_nat_range),
114 		.hooks      = (1 << NF_INET_PRE_ROUTING) |
115 		              (1 << NF_INET_LOCAL_OUT),
116 		.me         = THIS_MODULE,
117 	},
118 	{
119 		.name       = "REDIRECT",
120 		.family     = NFPROTO_IPV4,
121 		.revision   = 0,
122 		.table      = "nat",
123 		.target     = redirect_tg4,
124 		.checkentry = redirect_tg4_check,
125 		.targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat),
126 		.hooks      = (1 << NF_INET_PRE_ROUTING) |
127 		              (1 << NF_INET_LOCAL_OUT),
128 		.me         = THIS_MODULE,
129 	},
130 };
131 
132 static int __init redirect_tg_init(void)
133 {
134 	return xt_register_targets(redirect_tg_reg,
135 				   ARRAY_SIZE(redirect_tg_reg));
136 }
137 
138 static void __exit redirect_tg_exit(void)
139 {
140 	xt_unregister_targets(redirect_tg_reg, ARRAY_SIZE(redirect_tg_reg));
141 }
142 
143 module_init(redirect_tg_init);
144 module_exit(redirect_tg_exit);
145 
146 MODULE_LICENSE("GPL");
147 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
148 MODULE_DESCRIPTION("Xtables: Connection redirection to localhost");
149 MODULE_ALIAS("ip6t_REDIRECT");
150 MODULE_ALIAS("ipt_REDIRECT");
151