Skip to content

Commit cc6eb43

Browse files
bazsikaber
authored andcommitted
tproxy: use the interface primary IP address as a default value for --on-ip
The REDIRECT target and the older TProxy versions used the primary address of the incoming interface as the default value of the --on-ip parameter. This was unintentionally changed during the initial TProxy submission and caused confusion among users. Since IPv6 has no notion of primary address, we just select the first address on the list: this way the socket lookup finds wildcard bound sockets properly and we cannot really do better without the user telling us the IPv6 address of the proxy. This is implemented for both IPv4 and IPv6. Signed-off-by: Balazs Scheidler <[email protected]> Signed-off-by: KOVACS Krisztian <[email protected]> Signed-off-by: Patrick McHardy <[email protected]>
1 parent b64c925 commit cc6eb43

File tree

1 file changed

+132
-70
lines changed

1 file changed

+132
-70
lines changed

net/netfilter/xt_TPROXY.c

Lines changed: 132 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,41 @@
1616
#include <net/checksum.h>
1717
#include <net/udp.h>
1818
#include <net/inet_sock.h>
19-
19+
#include <linux/inetdevice.h>
2020
#include <linux/netfilter/x_tables.h>
2121
#include <linux/netfilter_ipv4/ip_tables.h>
22-
#include <linux/netfilter_ipv6/ip6_tables.h>
23-
#include <linux/netfilter/xt_TPROXY.h>
2422

2523
#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
24+
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
25+
#include <net/if_inet6.h>
26+
#include <net/addrconf.h>
27+
#include <linux/netfilter_ipv6/ip6_tables.h>
2628
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
29+
#endif
30+
2731
#include <net/netfilter/nf_tproxy_core.h>
32+
#include <linux/netfilter/xt_TPROXY.h>
33+
34+
static inline __be32
35+
tproxy_laddr4(struct sk_buff *skb, __be32 user_laddr, __be32 daddr)
36+
{
37+
struct in_device *indev;
38+
__be32 laddr;
39+
40+
if (user_laddr)
41+
return user_laddr;
42+
43+
laddr = 0;
44+
rcu_read_lock();
45+
indev = __in_dev_get_rcu(skb->dev);
46+
for_primary_ifa(indev) {
47+
laddr = ifa->ifa_local;
48+
break;
49+
} endfor_ifa(indev);
50+
rcu_read_unlock();
51+
52+
return laddr ? laddr : daddr;
53+
}
2854

2955
/**
3056
* tproxy_handle_time_wait4() - handle IPv4 TCP TIME_WAIT reopen redirections
@@ -75,60 +101,6 @@ tproxy_handle_time_wait4(struct sk_buff *skb, __be32 laddr, __be16 lport,
75101
return sk;
76102
}
77103

78-
/**
79-
* tproxy_handle_time_wait6() - handle IPv6 TCP TIME_WAIT reopen redirections
80-
* @skb: The skb being processed.
81-
* @tproto: Transport protocol.
82-
* @thoff: Transport protocol header offset.
83-
* @par: Iptables target parameters.
84-
* @sk: The TIME_WAIT TCP socket found by the lookup.
85-
*
86-
* We have to handle SYN packets arriving to TIME_WAIT sockets
87-
* differently: instead of reopening the connection we should rather
88-
* redirect the new connection to the proxy if there's a listener
89-
* socket present.
90-
*
91-
* tproxy_handle_time_wait6() consumes the socket reference passed in.
92-
*
93-
* Returns the listener socket if there's one, the TIME_WAIT socket if
94-
* no such listener is found, or NULL if the TCP header is incomplete.
95-
*/
96-
static struct sock *
97-
tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff,
98-
const struct xt_action_param *par,
99-
struct sock *sk)
100-
{
101-
const struct ipv6hdr *iph = ipv6_hdr(skb);
102-
struct tcphdr _hdr, *hp;
103-
const struct xt_tproxy_target_info_v1 *tgi = par->targinfo;
104-
105-
hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
106-
if (hp == NULL) {
107-
inet_twsk_put(inet_twsk(sk));
108-
return NULL;
109-
}
110-
111-
if (hp->syn && !hp->rst && !hp->ack && !hp->fin) {
112-
/* SYN to a TIME_WAIT socket, we'd rather redirect it
113-
* to a listener socket if there's one */
114-
struct sock *sk2;
115-
116-
sk2 = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto,
117-
&iph->saddr,
118-
!ipv6_addr_any(&tgi->laddr.in6) ? &tgi->laddr.in6 : &iph->daddr,
119-
hp->source,
120-
tgi->lport ? tgi->lport : hp->dest,
121-
skb->dev, NFT_LOOKUP_LISTENER);
122-
if (sk2) {
123-
inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
124-
inet_twsk_put(inet_twsk(sk));
125-
sk = sk2;
126-
}
127-
}
128-
129-
return sk;
130-
}
131-
132104
static unsigned int
133105
tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport,
134106
u_int32_t mark_mask, u_int32_t mark_value)
@@ -150,6 +122,10 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport,
150122
hp->source, hp->dest,
151123
skb->dev, NFT_LOOKUP_ESTABLISHED);
152124

125+
laddr = tproxy_laddr4(skb, laddr, iph->daddr);
126+
if (!lport)
127+
lport = hp->dest;
128+
153129
/* UDP has no TCP_TIME_WAIT state, so we never enter here */
154130
if (sk && sk->sk_state == TCP_TIME_WAIT)
155131
/* reopening a TIME_WAIT connection needs special handling */
@@ -158,8 +134,8 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport,
158134
/* no, there's no established connection, check if
159135
* there's a listener on the redirected addr/port */
160136
sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
161-
iph->saddr, laddr ? laddr : iph->daddr,
162-
hp->source, lport ? lport : hp->dest,
137+
iph->saddr, laddr,
138+
hp->source, lport,
163139
skb->dev, NFT_LOOKUP_LISTENER);
164140

165141
/* NOTE: assign_sock consumes our sk reference */
@@ -174,9 +150,9 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport,
174150
return NF_ACCEPT;
175151
}
176152

177-
pr_debug("no socket, dropping: proto %hhu %08x:%hu -> %08x:%hu, mark: %x\n",
178-
iph->protocol, ntohl(iph->daddr), ntohs(hp->dest),
179-
ntohl(laddr), ntohs(lport), skb->mark);
153+
pr_debug("no socket, dropping: proto %hhu %pI4:%hu -> %pI4:%hu, mark: %x\n",
154+
iph->protocol, &iph->saddr, ntohs(hp->source),
155+
&iph->daddr, ntohs(hp->dest), skb->mark);
180156
return NF_DROP;
181157
}
182158

@@ -197,13 +173,97 @@ tproxy_tg4_v1(struct sk_buff *skb, const struct xt_action_param *par)
197173
}
198174

199175
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
176+
177+
static inline const struct in6_addr *
178+
tproxy_laddr6(struct sk_buff *skb, const struct in6_addr *user_laddr,
179+
const struct in6_addr *daddr)
180+
{
181+
struct inet6_dev *indev;
182+
struct inet6_ifaddr *ifa;
183+
struct in6_addr *laddr;
184+
185+
if (!ipv6_addr_any(user_laddr))
186+
return user_laddr;
187+
laddr = NULL;
188+
189+
rcu_read_lock();
190+
indev = __in6_dev_get(skb->dev);
191+
if (indev)
192+
list_for_each_entry(ifa, &indev->addr_list, if_list) {
193+
if (ifa->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED))
194+
continue;
195+
196+
laddr = &ifa->addr;
197+
break;
198+
}
199+
rcu_read_unlock();
200+
201+
return laddr ? laddr : daddr;
202+
}
203+
204+
/**
205+
* tproxy_handle_time_wait6() - handle IPv6 TCP TIME_WAIT reopen redirections
206+
* @skb: The skb being processed.
207+
* @tproto: Transport protocol.
208+
* @thoff: Transport protocol header offset.
209+
* @par: Iptables target parameters.
210+
* @sk: The TIME_WAIT TCP socket found by the lookup.
211+
*
212+
* We have to handle SYN packets arriving to TIME_WAIT sockets
213+
* differently: instead of reopening the connection we should rather
214+
* redirect the new connection to the proxy if there's a listener
215+
* socket present.
216+
*
217+
* tproxy_handle_time_wait6() consumes the socket reference passed in.
218+
*
219+
* Returns the listener socket if there's one, the TIME_WAIT socket if
220+
* no such listener is found, or NULL if the TCP header is incomplete.
221+
*/
222+
static struct sock *
223+
tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff,
224+
const struct xt_action_param *par,
225+
struct sock *sk)
226+
{
227+
const struct ipv6hdr *iph = ipv6_hdr(skb);
228+
struct tcphdr _hdr, *hp;
229+
const struct xt_tproxy_target_info_v1 *tgi = par->targinfo;
230+
231+
hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
232+
if (hp == NULL) {
233+
inet_twsk_put(inet_twsk(sk));
234+
return NULL;
235+
}
236+
237+
if (hp->syn && !hp->rst && !hp->ack && !hp->fin) {
238+
/* SYN to a TIME_WAIT socket, we'd rather redirect it
239+
* to a listener socket if there's one */
240+
struct sock *sk2;
241+
242+
sk2 = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto,
243+
&iph->saddr,
244+
tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr),
245+
hp->source,
246+
tgi->lport ? tgi->lport : hp->dest,
247+
skb->dev, NFT_LOOKUP_LISTENER);
248+
if (sk2) {
249+
inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
250+
inet_twsk_put(inet_twsk(sk));
251+
sk = sk2;
252+
}
253+
}
254+
255+
return sk;
256+
}
257+
200258
static unsigned int
201259
tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
202260
{
203261
const struct ipv6hdr *iph = ipv6_hdr(skb);
204262
const struct xt_tproxy_target_info_v1 *tgi = par->targinfo;
205263
struct udphdr _hdr, *hp;
206264
struct sock *sk;
265+
const struct in6_addr *laddr;
266+
__be16 lport;
207267
int thoff;
208268
int tproto;
209269

@@ -228,6 +288,9 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
228288
hp->source, hp->dest,
229289
par->in, NFT_LOOKUP_ESTABLISHED);
230290

291+
laddr = tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr);
292+
lport = tgi->lport ? tgi->lport : hp->dest;
293+
231294
/* UDP has no TCP_TIME_WAIT state, so we never enter here */
232295
if (sk && sk->sk_state == TCP_TIME_WAIT)
233296
/* reopening a TIME_WAIT connection needs special handling */
@@ -236,10 +299,8 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
236299
/* no there's no established connection, check if
237300
* there's a listener on the redirected addr/port */
238301
sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto,
239-
&iph->saddr,
240-
!ipv6_addr_any(&tgi->laddr.in6) ? &tgi->laddr.in6 : &iph->daddr,
241-
hp->source,
242-
tgi->lport ? tgi->lport : hp->dest,
302+
&iph->saddr, laddr,
303+
hp->source, lport,
243304
par->in, NFT_LOOKUP_LISTENER);
244305

245306
/* NOTE: assign_sock consumes our sk reference */
@@ -249,14 +310,15 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
249310
skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value;
250311

251312
pr_debug("redirecting: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n",
252-
tproto, &iph->saddr, ntohs(hp->dest),
253-
&tgi->laddr.in6, ntohs(tgi->lport), skb->mark);
313+
tproto, &iph->saddr, ntohs(hp->source),
314+
laddr, ntohs(lport), skb->mark);
254315
return NF_ACCEPT;
255316
}
256317

257318
pr_debug("no socket, dropping: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n",
258-
tproto, &iph->saddr, ntohs(hp->dest),
259-
&tgi->laddr.in6, ntohs(tgi->lport), skb->mark);
319+
tproto, &iph->saddr, ntohs(hp->source),
320+
&iph->daddr, ntohs(hp->dest), skb->mark);
321+
260322
return NF_DROP;
261323
}
262324

0 commit comments

Comments
 (0)