Skip to content

Commit ee04805

Browse files
committed
netfilter: conntrack: make conntrack userspace helpers work again
Florian Westphal says: "Problem is that after the helper hook was merged back into the confirm one, the queueing itself occurs from the confirm hook, i.e. we queue from the last netfilter callback in the hook-list. Therefore, on return, the packet bypasses the confirm action and the connection is never committed to the main conntrack table. To fix this there are several ways: 1. revert the 'Fixes' commit and have a extra helper hook again. Works, but has the drawback of adding another indirect call for everyone. 2. Special case this: split the hooks only when userspace helper gets added, so queueing occurs at a lower priority again, and normal enqueue reinject would eventually call the last hook. 3. Extend the existing nf_queue ct update hook to allow a forced confirmation (plus run the seqadj code). This goes for 3)." Fixes: 827318f ("netfilter: conntrack: remove helper hook again") Reviewed-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 4c559f1 commit ee04805

File tree

1 file changed

+72
-6
lines changed

1 file changed

+72
-6
lines changed

net/netfilter/nf_conntrack_core.c

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2016,22 +2016,18 @@ static void nf_conntrack_attach(struct sk_buff *nskb, const struct sk_buff *skb)
20162016
nf_conntrack_get(skb_nfct(nskb));
20172017
}
20182018

2019-
static int nf_conntrack_update(struct net *net, struct sk_buff *skb)
2019+
static int __nf_conntrack_update(struct net *net, struct sk_buff *skb,
2020+
struct nf_conn *ct)
20202021
{
20212022
struct nf_conntrack_tuple_hash *h;
20222023
struct nf_conntrack_tuple tuple;
20232024
enum ip_conntrack_info ctinfo;
20242025
struct nf_nat_hook *nat_hook;
20252026
unsigned int status;
2026-
struct nf_conn *ct;
20272027
int dataoff;
20282028
u16 l3num;
20292029
u8 l4num;
20302030

2031-
ct = nf_ct_get(skb, &ctinfo);
2032-
if (!ct || nf_ct_is_confirmed(ct))
2033-
return 0;
2034-
20352031
l3num = nf_ct_l3num(ct);
20362032

20372033
dataoff = get_l4proto(skb, skb_network_offset(skb), l3num, &l4num);
@@ -2088,6 +2084,76 @@ static int nf_conntrack_update(struct net *net, struct sk_buff *skb)
20882084
return 0;
20892085
}
20902086

2087+
/* This packet is coming from userspace via nf_queue, complete the packet
2088+
* processing after the helper invocation in nf_confirm().
2089+
*/
2090+
static int nf_confirm_cthelper(struct sk_buff *skb, struct nf_conn *ct,
2091+
enum ip_conntrack_info ctinfo)
2092+
{
2093+
const struct nf_conntrack_helper *helper;
2094+
const struct nf_conn_help *help;
2095+
unsigned int protoff;
2096+
2097+
help = nfct_help(ct);
2098+
if (!help)
2099+
return 0;
2100+
2101+
helper = rcu_dereference(help->helper);
2102+
if (!(helper->flags & NF_CT_HELPER_F_USERSPACE))
2103+
return 0;
2104+
2105+
switch (nf_ct_l3num(ct)) {
2106+
case NFPROTO_IPV4:
2107+
protoff = skb_network_offset(skb) + ip_hdrlen(skb);
2108+
break;
2109+
#if IS_ENABLED(CONFIG_IPV6)
2110+
case NFPROTO_IPV6: {
2111+
__be16 frag_off;
2112+
u8 pnum;
2113+
2114+
pnum = ipv6_hdr(skb)->nexthdr;
2115+
protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum,
2116+
&frag_off);
2117+
if (protoff < 0 || (frag_off & htons(~0x7)) != 0)
2118+
return 0;
2119+
break;
2120+
}
2121+
#endif
2122+
default:
2123+
return 0;
2124+
}
2125+
2126+
if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
2127+
!nf_is_loopback_packet(skb)) {
2128+
if (!nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) {
2129+
NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop);
2130+
return -1;
2131+
}
2132+
}
2133+
2134+
/* We've seen it coming out the other side: confirm it */
2135+
return nf_conntrack_confirm(skb) == NF_DROP ? - 1 : 0;
2136+
}
2137+
2138+
static int nf_conntrack_update(struct net *net, struct sk_buff *skb)
2139+
{
2140+
enum ip_conntrack_info ctinfo;
2141+
struct nf_conn *ct;
2142+
int err;
2143+
2144+
ct = nf_ct_get(skb, &ctinfo);
2145+
if (!ct)
2146+
return 0;
2147+
2148+
if (!nf_ct_is_confirmed(ct)) {
2149+
err = __nf_conntrack_update(net, skb, ct);
2150+
if (err < 0)
2151+
return err;
2152+
}
2153+
2154+
return nf_confirm_cthelper(skb, ct, ctinfo);
2155+
}
2156+
20912157
static bool nf_conntrack_get_tuple_skb(struct nf_conntrack_tuple *dst_tuple,
20922158
const struct sk_buff *skb)
20932159
{

0 commit comments

Comments
 (0)