]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
e8439270 KK |
2 | /* |
3 | * Transparent proxy support for Linux/iptables | |
4 | * | |
6ad78893 | 5 | * Copyright (c) 2006-2010 BalaBit IT Ltd. |
e8439270 | 6 | * Author: Balazs Scheidler, Krisztian Kovacs |
e8439270 | 7 | */ |
ff67e4e4 | 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
e8439270 KK |
9 | #include <linux/module.h> |
10 | #include <linux/skbuff.h> | |
11 | #include <linux/ip.h> | |
12 | #include <net/checksum.h> | |
13 | #include <net/udp.h> | |
93742cf8 | 14 | #include <net/tcp.h> |
e8439270 | 15 | #include <net/inet_sock.h> |
93742cf8 | 16 | #include <net/inet_hashtables.h> |
cc6eb433 | 17 | #include <linux/inetdevice.h> |
e8439270 KK |
18 | #include <linux/netfilter/x_tables.h> |
19 | #include <linux/netfilter_ipv4/ip_tables.h> | |
e8439270 KK |
20 | |
21 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> | |
f6318e55 | 22 | |
c0cd1156 | 23 | #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) |
f6318e55 | 24 | #define XT_TPROXY_HAVE_IPV6 1 |
cc6eb433 BS |
25 | #include <net/if_inet6.h> |
26 | #include <net/addrconf.h> | |
93742cf8 | 27 | #include <net/inet6_hashtables.h> |
cc6eb433 | 28 | #include <linux/netfilter_ipv6/ip6_tables.h> |
6ad78893 | 29 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> |
cc6eb433 BS |
30 | #endif |
31 | ||
45ca4e0c | 32 | #include <net/netfilter/nf_tproxy.h> |
cc6eb433 BS |
33 | #include <linux/netfilter/xt_TPROXY.h> |
34 | ||
e8439270 | 35 | static unsigned int |
686c9b50 | 36 | tproxy_tg4(struct net *net, struct sk_buff *skb, __be32 laddr, __be16 lport, |
6ad78893 | 37 | u_int32_t mark_mask, u_int32_t mark_value) |
e8439270 KK |
38 | { |
39 | const struct iphdr *iph = ip_hdr(skb); | |
e8439270 KK |
40 | struct udphdr _hdr, *hp; |
41 | struct sock *sk; | |
42 | ||
43 | hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr); | |
44 | if (hp == NULL) | |
45 | return NF_DROP; | |
46 | ||
6ad78893 BS |
47 | /* check if there's an ongoing connection on the packet |
48 | * addresses, this happens if the redirect already happened | |
49 | * and the current packet belongs to an already established | |
50 | * connection */ | |
5711b4e8 | 51 | sk = nf_tproxy_get_sock_v4(net, skb, iph->protocol, |
106e4c26 BS |
52 | iph->saddr, iph->daddr, |
53 | hp->source, hp->dest, | |
45ca4e0c | 54 | skb->dev, NF_TPROXY_LOOKUP_ESTABLISHED); |
106e4c26 | 55 | |
45ca4e0c | 56 | laddr = nf_tproxy_laddr4(skb, laddr, iph->daddr); |
cc6eb433 BS |
57 | if (!lport) |
58 | lport = hp->dest; | |
59 | ||
106e4c26 BS |
60 | /* UDP has no TCP_TIME_WAIT state, so we never enter here */ |
61 | if (sk && sk->sk_state == TCP_TIME_WAIT) | |
6ad78893 | 62 | /* reopening a TIME_WAIT connection needs special handling */ |
45ca4e0c | 63 | sk = nf_tproxy_handle_time_wait4(net, skb, laddr, lport, sk); |
106e4c26 | 64 | else if (!sk) |
6ad78893 BS |
65 | /* no, there's no established connection, check if |
66 | * there's a listener on the redirected addr/port */ | |
5711b4e8 | 67 | sk = nf_tproxy_get_sock_v4(net, skb, iph->protocol, |
cc6eb433 BS |
68 | iph->saddr, laddr, |
69 | hp->source, lport, | |
45ca4e0c | 70 | skb->dev, NF_TPROXY_LOOKUP_LISTENER); |
6ad78893 BS |
71 | |
72 | /* NOTE: assign_sock consumes our sk reference */ | |
45ca4e0c | 73 | if (sk && nf_tproxy_sk_is_transparent(sk)) { |
6ad78893 BS |
74 | /* This should be in a separate target, but we don't do multiple |
75 | targets on the same rule yet */ | |
76 | skb->mark = (skb->mark & ~mark_mask) ^ mark_value; | |
d503b30b | 77 | nf_tproxy_assign_sock(skb, sk); |
6ad78893 BS |
78 | return NF_ACCEPT; |
79 | } | |
80 | ||
6ad78893 BS |
81 | return NF_DROP; |
82 | } | |
83 | ||
84 | static unsigned int | |
85 | tproxy_tg4_v0(struct sk_buff *skb, const struct xt_action_param *par) | |
86 | { | |
87 | const struct xt_tproxy_target_info *tgi = par->targinfo; | |
88 | ||
613dbd95 PNA |
89 | return tproxy_tg4(xt_net(par), skb, tgi->laddr, tgi->lport, |
90 | tgi->mark_mask, tgi->mark_value); | |
6ad78893 BS |
91 | } |
92 | ||
93 | static unsigned int | |
94 | tproxy_tg4_v1(struct sk_buff *skb, const struct xt_action_param *par) | |
95 | { | |
96 | const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; | |
97 | ||
613dbd95 PNA |
98 | return tproxy_tg4(xt_net(par), skb, tgi->laddr.ip, tgi->lport, |
99 | tgi->mark_mask, tgi->mark_value); | |
6ad78893 BS |
100 | } |
101 | ||
f6318e55 | 102 | #ifdef XT_TPROXY_HAVE_IPV6 |
cc6eb433 | 103 | |
6ad78893 BS |
104 | static unsigned int |
105 | tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | |
106 | { | |
107 | const struct ipv6hdr *iph = ipv6_hdr(skb); | |
108 | const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; | |
109 | struct udphdr _hdr, *hp; | |
110 | struct sock *sk; | |
cc6eb433 BS |
111 | const struct in6_addr *laddr; |
112 | __be16 lport; | |
84018f55 | 113 | int thoff = 0; |
6ad78893 BS |
114 | int tproto; |
115 | ||
84018f55 | 116 | tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); |
aa8c7cdb | 117 | if (tproto < 0) |
6ad78893 | 118 | return NF_DROP; |
6ad78893 BS |
119 | |
120 | hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); | |
aa8c7cdb | 121 | if (!hp) |
6ad78893 | 122 | return NF_DROP; |
6ad78893 BS |
123 | |
124 | /* check if there's an ongoing connection on the packet | |
125 | * addresses, this happens if the redirect already happened | |
126 | * and the current packet belongs to an already established | |
127 | * connection */ | |
5711b4e8 | 128 | sk = nf_tproxy_get_sock_v6(xt_net(par), skb, thoff, tproto, |
6ad78893 BS |
129 | &iph->saddr, &iph->daddr, |
130 | hp->source, hp->dest, | |
45ca4e0c | 131 | xt_in(par), NF_TPROXY_LOOKUP_ESTABLISHED); |
6ad78893 | 132 | |
45ca4e0c | 133 | laddr = nf_tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr); |
cc6eb433 BS |
134 | lport = tgi->lport ? tgi->lport : hp->dest; |
135 | ||
6ad78893 | 136 | /* UDP has no TCP_TIME_WAIT state, so we never enter here */ |
45ca4e0c ME |
137 | if (sk && sk->sk_state == TCP_TIME_WAIT) { |
138 | const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; | |
6ad78893 | 139 | /* reopening a TIME_WAIT connection needs special handling */ |
45ca4e0c ME |
140 | sk = nf_tproxy_handle_time_wait6(skb, tproto, thoff, |
141 | xt_net(par), | |
142 | &tgi->laddr.in6, | |
143 | tgi->lport, | |
144 | sk); | |
145 | } | |
6ad78893 BS |
146 | else if (!sk) |
147 | /* no there's no established connection, check if | |
148 | * there's a listener on the redirected addr/port */ | |
5711b4e8 | 149 | sk = nf_tproxy_get_sock_v6(xt_net(par), skb, thoff, |
a583636a | 150 | tproto, &iph->saddr, laddr, |
cc6eb433 | 151 | hp->source, lport, |
45ca4e0c | 152 | xt_in(par), NF_TPROXY_LOOKUP_LISTENER); |
e8439270 KK |
153 | |
154 | /* NOTE: assign_sock consumes our sk reference */ | |
45ca4e0c | 155 | if (sk && nf_tproxy_sk_is_transparent(sk)) { |
e8439270 KK |
156 | /* This should be in a separate target, but we don't do multiple |
157 | targets on the same rule yet */ | |
158 | skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value; | |
d503b30b | 159 | nf_tproxy_assign_sock(skb, sk); |
e8439270 KK |
160 | return NF_ACCEPT; |
161 | } | |
162 | ||
e8439270 KK |
163 | return NF_DROP; |
164 | } | |
165 | ||
6ad78893 BS |
166 | static int tproxy_tg6_check(const struct xt_tgchk_param *par) |
167 | { | |
168 | const struct ip6t_ip6 *i = par->entryinfo; | |
834184b1 FW |
169 | int err; |
170 | ||
171 | err = nf_defrag_ipv6_enable(par->net); | |
172 | if (err) | |
173 | return err; | |
6ad78893 | 174 | |
3d8c6dce PNA |
175 | if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP) && |
176 | !(i->invflags & IP6T_INV_PROTO)) | |
6ad78893 BS |
177 | return 0; |
178 | ||
b2606644 | 179 | pr_info_ratelimited("Can be used only with -p tcp or -p udp\n"); |
6ad78893 BS |
180 | return -EINVAL; |
181 | } | |
de8c1211 FW |
182 | |
183 | static void tproxy_tg6_destroy(const struct xt_tgdtor_param *par) | |
184 | { | |
185 | nf_defrag_ipv6_disable(par->net); | |
186 | } | |
6ad78893 BS |
187 | #endif |
188 | ||
189 | static int tproxy_tg4_check(const struct xt_tgchk_param *par) | |
e8439270 | 190 | { |
af5d6dc2 | 191 | const struct ipt_ip *i = par->entryinfo; |
834184b1 FW |
192 | int err; |
193 | ||
194 | err = nf_defrag_ipv4_enable(par->net); | |
195 | if (err) | |
196 | return err; | |
e8439270 KK |
197 | |
198 | if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP) | |
199 | && !(i->invflags & IPT_INV_PROTO)) | |
d6b00a53 | 200 | return 0; |
e8439270 | 201 | |
b2606644 | 202 | pr_info_ratelimited("Can be used only with -p tcp or -p udp\n"); |
d6b00a53 | 203 | return -EINVAL; |
e8439270 KK |
204 | } |
205 | ||
de8c1211 FW |
206 | static void tproxy_tg4_destroy(const struct xt_tgdtor_param *par) |
207 | { | |
208 | nf_defrag_ipv4_disable(par->net); | |
209 | } | |
210 | ||
6ad78893 BS |
211 | static struct xt_target tproxy_tg_reg[] __read_mostly = { |
212 | { | |
213 | .name = "TPROXY", | |
214 | .family = NFPROTO_IPV4, | |
215 | .table = "mangle", | |
216 | .target = tproxy_tg4_v0, | |
217 | .revision = 0, | |
218 | .targetsize = sizeof(struct xt_tproxy_target_info), | |
219 | .checkentry = tproxy_tg4_check, | |
de8c1211 | 220 | .destroy = tproxy_tg4_destroy, |
6ad78893 BS |
221 | .hooks = 1 << NF_INET_PRE_ROUTING, |
222 | .me = THIS_MODULE, | |
223 | }, | |
224 | { | |
225 | .name = "TPROXY", | |
226 | .family = NFPROTO_IPV4, | |
227 | .table = "mangle", | |
228 | .target = tproxy_tg4_v1, | |
229 | .revision = 1, | |
230 | .targetsize = sizeof(struct xt_tproxy_target_info_v1), | |
231 | .checkentry = tproxy_tg4_check, | |
de8c1211 | 232 | .destroy = tproxy_tg4_destroy, |
6ad78893 BS |
233 | .hooks = 1 << NF_INET_PRE_ROUTING, |
234 | .me = THIS_MODULE, | |
235 | }, | |
f6318e55 | 236 | #ifdef XT_TPROXY_HAVE_IPV6 |
6ad78893 BS |
237 | { |
238 | .name = "TPROXY", | |
239 | .family = NFPROTO_IPV6, | |
240 | .table = "mangle", | |
241 | .target = tproxy_tg6_v1, | |
242 | .revision = 1, | |
243 | .targetsize = sizeof(struct xt_tproxy_target_info_v1), | |
244 | .checkentry = tproxy_tg6_check, | |
de8c1211 | 245 | .destroy = tproxy_tg6_destroy, |
6ad78893 BS |
246 | .hooks = 1 << NF_INET_PRE_ROUTING, |
247 | .me = THIS_MODULE, | |
248 | }, | |
249 | #endif | |
250 | ||
e8439270 KK |
251 | }; |
252 | ||
253 | static int __init tproxy_tg_init(void) | |
254 | { | |
6ad78893 | 255 | return xt_register_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg)); |
e8439270 KK |
256 | } |
257 | ||
258 | static void __exit tproxy_tg_exit(void) | |
259 | { | |
6ad78893 | 260 | xt_unregister_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg)); |
e8439270 KK |
261 | } |
262 | ||
263 | module_init(tproxy_tg_init); | |
264 | module_exit(tproxy_tg_exit); | |
265 | MODULE_LICENSE("GPL"); | |
6ad78893 | 266 | MODULE_AUTHOR("Balazs Scheidler, Krisztian Kovacs"); |
e8439270 KK |
267 | MODULE_DESCRIPTION("Netfilter transparent proxy (TPROXY) target module."); |
268 | MODULE_ALIAS("ipt_TPROXY"); | |
6ad78893 | 269 | MODULE_ALIAS("ip6t_TPROXY"); |