netfilter: tproxy: do not assign timewait sockets to skb->sk
Assigning a socket in timewait state to skb->sk can trigger kernel oops, e.g. in nfnetlink_log, which does: if (skb->sk) { read_lock_bh(&skb->sk->sk_callback_lock); if (skb->sk->sk_socket && skb->sk->sk_socket->file) ... in the timewait case, accessing sk->sk_callback_lock and sk->sk_socket is invalid. Either all of these spots will need to add a test for sk->sk_state != TCP_TIME_WAIT, or xt_TPROXY must not assign a timewait socket to skb->sk. This does the latter. If a TW socket is found, assign the tproxy nfmark, but skip the skb->sk assignment, thus mimicking behaviour of a '-m socket .. -j MARK/ACCEPT' re-routing rule. The 'SYN to TW socket' case is left unchanged -- we try to redirect to the listener socket. Cc: Balazs Scheidler <bazsi@balabit.hu> Cc: KOVACS Krisztian <hidden@balabit.hu> Signed-off-by: Florian Westphal <fwestphal@astaro.com> Signed-off-by: Patrick McHardy <kaber@trash.net>
This commit is contained in:
Родитель
de9963f0f2
Коммит
d503b30bd6
|
@ -201,18 +201,8 @@ nf_tproxy_get_sock_v6(struct net *net, const u8 protocol,
|
|||
}
|
||||
#endif
|
||||
|
||||
static inline void
|
||||
nf_tproxy_put_sock(struct sock *sk)
|
||||
{
|
||||
/* TIME_WAIT inet sockets have to be handled differently */
|
||||
if ((sk->sk_protocol == IPPROTO_TCP) && (sk->sk_state == TCP_TIME_WAIT))
|
||||
inet_twsk_put(inet_twsk(sk));
|
||||
else
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
/* assign a socket to the skb -- consumes sk */
|
||||
int
|
||||
void
|
||||
nf_tproxy_assign_sock(struct sk_buff *skb, struct sock *sk);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -28,26 +28,23 @@ nf_tproxy_destructor(struct sk_buff *skb)
|
|||
skb->destructor = NULL;
|
||||
|
||||
if (sk)
|
||||
nf_tproxy_put_sock(sk);
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
/* consumes sk */
|
||||
int
|
||||
void
|
||||
nf_tproxy_assign_sock(struct sk_buff *skb, struct sock *sk)
|
||||
{
|
||||
bool transparent = (sk->sk_state == TCP_TIME_WAIT) ?
|
||||
inet_twsk(sk)->tw_transparent :
|
||||
inet_sk(sk)->transparent;
|
||||
/* assigning tw sockets complicates things; most
|
||||
* skb->sk->X checks would have to test sk->sk_state first */
|
||||
if (sk->sk_state == TCP_TIME_WAIT) {
|
||||
inet_twsk_put(inet_twsk(sk));
|
||||
return;
|
||||
}
|
||||
|
||||
if (transparent) {
|
||||
skb_orphan(skb);
|
||||
skb->sk = sk;
|
||||
skb->destructor = nf_tproxy_destructor;
|
||||
return 1;
|
||||
} else
|
||||
nf_tproxy_put_sock(sk);
|
||||
|
||||
return 0;
|
||||
skb_orphan(skb);
|
||||
skb->sk = sk;
|
||||
skb->destructor = nf_tproxy_destructor;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_tproxy_assign_sock);
|
||||
|
||||
|
|
|
@ -33,6 +33,20 @@
|
|||
#include <net/netfilter/nf_tproxy_core.h>
|
||||
#include <linux/netfilter/xt_TPROXY.h>
|
||||
|
||||
static bool tproxy_sk_is_transparent(struct sock *sk)
|
||||
{
|
||||
if (sk->sk_state != TCP_TIME_WAIT) {
|
||||
if (inet_sk(sk)->transparent)
|
||||
return true;
|
||||
sock_put(sk);
|
||||
} else {
|
||||
if (inet_twsk(sk)->tw_transparent)
|
||||
return true;
|
||||
inet_twsk_put(inet_twsk(sk));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline __be32
|
||||
tproxy_laddr4(struct sk_buff *skb, __be32 user_laddr, __be32 daddr)
|
||||
{
|
||||
|
@ -141,7 +155,7 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport,
|
|||
skb->dev, NFT_LOOKUP_LISTENER);
|
||||
|
||||
/* NOTE: assign_sock consumes our sk reference */
|
||||
if (sk && nf_tproxy_assign_sock(skb, sk)) {
|
||||
if (sk && tproxy_sk_is_transparent(sk)) {
|
||||
/* This should be in a separate target, but we don't do multiple
|
||||
targets on the same rule yet */
|
||||
skb->mark = (skb->mark & ~mark_mask) ^ mark_value;
|
||||
|
@ -149,6 +163,8 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport,
|
|||
pr_debug("redirecting: proto %hhu %pI4:%hu -> %pI4:%hu, mark: %x\n",
|
||||
iph->protocol, &iph->daddr, ntohs(hp->dest),
|
||||
&laddr, ntohs(lport), skb->mark);
|
||||
|
||||
nf_tproxy_assign_sock(skb, sk);
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
|
@ -306,7 +322,7 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
|
|||
par->in, NFT_LOOKUP_LISTENER);
|
||||
|
||||
/* NOTE: assign_sock consumes our sk reference */
|
||||
if (sk && nf_tproxy_assign_sock(skb, sk)) {
|
||||
if (sk && tproxy_sk_is_transparent(sk)) {
|
||||
/* This should be in a separate target, but we don't do multiple
|
||||
targets on the same rule yet */
|
||||
skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value;
|
||||
|
@ -314,6 +330,8 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
|
|||
pr_debug("redirecting: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n",
|
||||
tproto, &iph->saddr, ntohs(hp->source),
|
||||
laddr, ntohs(lport), skb->mark);
|
||||
|
||||
nf_tproxy_assign_sock(skb, sk);
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,15 @@
|
|||
#include <net/netfilter/nf_conntrack.h>
|
||||
#endif
|
||||
|
||||
static void
|
||||
xt_socket_put_sk(struct sock *sk)
|
||||
{
|
||||
if (sk->sk_state == TCP_TIME_WAIT)
|
||||
inet_twsk_put(inet_twsk(sk));
|
||||
else
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
static int
|
||||
extract_icmp4_fields(const struct sk_buff *skb,
|
||||
u8 *protocol,
|
||||
|
@ -164,7 +173,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
|
|||
(sk->sk_state == TCP_TIME_WAIT &&
|
||||
inet_twsk(sk)->tw_transparent));
|
||||
|
||||
nf_tproxy_put_sock(sk);
|
||||
xt_socket_put_sk(sk);
|
||||
|
||||
if (wildcard || !transparent)
|
||||
sk = NULL;
|
||||
|
@ -298,7 +307,7 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par)
|
|||
(sk->sk_state == TCP_TIME_WAIT &&
|
||||
inet_twsk(sk)->tw_transparent));
|
||||
|
||||
nf_tproxy_put_sock(sk);
|
||||
xt_socket_put_sk(sk);
|
||||
|
||||
if (wildcard || !transparent)
|
||||
sk = NULL;
|
||||
|
|
Загрузка…
Ссылка в новой задаче