netfilter: nf_conntrack_expect: add and use nf_ct_expect_related_pair()

Add a new function to insert a pair of expectations, this is required by
the SIP and H323 NAT helpers. The spinlock is held to check if there is
a slot for both expectations, in such case, insert them.

This removes the need for nf_ct_unexpect_related() inside the loop to
find a pair of consecutive ports, otherwise inserting expectations whose
dead flag is already set on can happen.

Bump master_help->expecting for the expectation class after checking if
the expectation fits in the master expectation list, which is needed for
this new _pair() function variant to run the eviction routine including
the preallocated slot for the first expectation in the pair.

Fixes: b8b09dc2bf ("netfilter: nf_conntrack_expect: use conntrack GC to reap expectations")
Reported-by: Jaeyeong Lee <iostreampy@proton.me>
Link: https://patch.msgid.link/178377968720.33756.12204817361601593230@proton.me/
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Pablo Neira Ayuso
2026-07-13 00:26:04 +02:00
parent 1d6123f87e
commit 4aa63842fc
4 changed files with 50 additions and 30 deletions

View File

@@ -161,6 +161,9 @@ static inline int nf_ct_expect_related(struct nf_conntrack_expect *expect,
return nf_ct_expect_related_report(expect, 0, 0, flags);
}
int nf_ct_expect_related_pair(struct nf_conntrack_expect *expect[],
unsigned int flag);
struct nf_conn_help;
void nf_ct_expectation_gc(struct nf_conn_help *master_help);

View File

@@ -182,6 +182,7 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
struct nf_conntrack_expect *rtp_exp,
struct nf_conntrack_expect *rtcp_exp)
{
struct nf_conntrack_expect *rtp_pair[2] = { rtp_exp, rtcp_exp };
struct nf_ct_h323_master *info = nfct_help_data(ct);
int dir = CTINFO2DIR(ctinfo);
int i;
@@ -227,22 +228,13 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
int ret;
rtp_exp->tuple.dst.u.udp.port = htons(nated_port);
ret = nf_ct_expect_related(rtp_exp, 0);
rtcp_exp->tuple.dst.u.udp.port = htons(nated_port + 1);
ret = nf_ct_expect_related_pair(rtp_pair, 0);
if (ret == 0) {
rtcp_exp->tuple.dst.u.udp.port =
htons(nated_port + 1);
ret = nf_ct_expect_related(rtcp_exp, 0);
if (ret == 0)
break;
else if (ret == -EBUSY) {
nf_ct_unexpect_related(rtp_exp);
continue;
} else if (ret < 0) {
nf_ct_unexpect_related(rtp_exp);
nated_port = 0;
break;
}
} else if (ret != -EBUSY) {
break;
} else if (ret == -EBUSY) {
continue;
} else if (ret < 0) {
nated_port = 0;
break;
}

View File

@@ -427,7 +427,6 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp,
exp->timeout += helper->expect_policy[exp->class].timeout * HZ;
hlist_add_head_rcu(&exp->lnode, &master_help->expectations);
master_help->expecting[exp->class]++;
hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]);
cnet = nf_ct_pernet(net);
@@ -534,6 +533,7 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
if (ret < 0)
goto out;
master_help->expecting[expect->class]++;
nf_ct_expect_insert(expect, master_help);
nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
@@ -546,6 +546,39 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
}
EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
int nf_ct_expect_related_pair(struct nf_conntrack_expect *expect[],
unsigned int flags)
{
struct nf_conn_help *master_help;
int i, ret;
spin_lock_bh(&nf_conntrack_expect_lock);
master_help = nfct_help(expect[0]->master);
if (!master_help || master_help != nfct_help(expect[1]->master)) {
ret = -EINVAL;
goto out;
}
for (i = 0; i < 2; i++) {
ret = __nf_ct_expect_check(expect[i], master_help, flags);
if (ret < 0) {
if (i == 1)
master_help->expecting[expect[0]->class]--;
goto out;
}
master_help->expecting[expect[i]->class]++;
}
for (i = 0; i < 2; i++) {
nf_ct_expect_insert(expect[i], master_help);
nf_ct_expect_event_report(IPEXP_NEW, expect[i], 0, 0);
}
out:
spin_unlock_bh(&nf_conntrack_expect_lock);
return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_expect_related_pair);
void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, void *data),
void *data)
{

View File

@@ -592,6 +592,7 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff,
unsigned int medialen,
union nf_inet_addr *rtp_addr)
{
struct nf_conntrack_expect *rtp_pair[2] = { rtp_exp, rtcp_exp };
enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
@@ -622,24 +623,15 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff,
int ret;
rtp_exp->tuple.dst.u.udp.port = htons(port);
ret = nf_ct_expect_related(rtp_exp,
NF_CT_EXP_F_SKIP_MASTER);
if (ret == -EBUSY)
continue;
else if (ret < 0) {
port = 0;
break;
}
rtcp_exp->tuple.dst.u.udp.port = htons(port + 1);
ret = nf_ct_expect_related(rtcp_exp,
NF_CT_EXP_F_SKIP_MASTER);
ret = nf_ct_expect_related_pair(rtp_pair,
NF_CT_EXP_F_SKIP_MASTER);
if (ret == 0)
break;
else if (ret == -EBUSY) {
nf_ct_unexpect_related(rtp_exp);
else if (ret == -EBUSY)
continue;
} else if (ret < 0) {
nf_ct_unexpect_related(rtp_exp);
else if (ret < 0) {
port = 0;
break;
}