netfilter: nf_conntrack_sip: allow duplicate SDP expectations
authorFlorian Westphal <fw@strlen.de>
Tue, 3 Apr 2018 10:10:09 +0000 (12:10 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 9 Apr 2018 15:05:27 +0000 (17:05 +0200)
Callum Sinclair reported SIP IP Phone errors that he tracked down to
such phones sending session descriptions for different media types but
with same port numbers.

The expect core will only 'refresh' existing expectation if it is
from same master AND same expectation class (media type).
As expectation class is different, we get an error.

The SIP connection tracking code will then

1). drop the SDP packet
2). if an rtp expectation was already installed successfully,
    error on rtcp expectation will cancel the rtp one.

Make the expect core report back to caller when the conflict is due
to different expectation class and have SIP tracker ignore soft-error.

Reported-by: Callum Sinclair <Callum.Sinclair@alliedtelesis.co.nz>
Tested-by: Callum Sinclair <Callum.Sinclair@alliedtelesis.co.nz>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/netfilter/nf_conntrack_expect.c
net/netfilter/nf_conntrack_sip.c

index 8ef21d9f9a00d960ff3f26396b1736cfb1898d36..4b2b3d53acfcabfbe8fd4899f6ed825289387143 100644 (file)
@@ -252,7 +252,7 @@ static inline int expect_clash(const struct nf_conntrack_expect *a,
 static inline int expect_matches(const struct nf_conntrack_expect *a,
                                 const struct nf_conntrack_expect *b)
 {
-       return a->master == b->master && a->class == b->class &&
+       return a->master == b->master &&
               nf_ct_tuple_equal(&a->tuple, &b->tuple) &&
               nf_ct_tuple_mask_equal(&a->mask, &b->mask) &&
               net_eq(nf_ct_net(a->master), nf_ct_net(b->master)) &&
@@ -421,6 +421,9 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
        h = nf_ct_expect_dst_hash(net, &expect->tuple);
        hlist_for_each_entry_safe(i, next, &nf_ct_expect_hash[h], hnode) {
                if (expect_matches(i, expect)) {
+                       if (i->class != expect->class)
+                               return -EALREADY;
+
                        if (nf_ct_remove_expect(i))
                                break;
                } else if (expect_clash(i, expect)) {
index 4dbb5bad4363ba9f67fea15160bb7b0b88ae9c38..908e51e2dc2b35b1a74a374b00745b0323d21d90 100644 (file)
@@ -938,11 +938,19 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
                                       datalen, rtp_exp, rtcp_exp,
                                       mediaoff, medialen, daddr);
        else {
-               if (nf_ct_expect_related(rtp_exp) == 0) {
-                       if (nf_ct_expect_related(rtcp_exp) != 0)
-                               nf_ct_unexpect_related(rtp_exp);
-                       else
+               /* -EALREADY handling works around end-points that send
+                * SDP messages with identical port but different media type,
+                * we pretend expectation was set up.
+                */
+               int errp = nf_ct_expect_related(rtp_exp);
+
+               if (errp == 0 || errp == -EALREADY) {
+                       int errcp = nf_ct_expect_related(rtcp_exp);
+
+                       if (errcp == 0 || errcp == -EALREADY)
                                ret = NF_ACCEPT;
+                       else if (errp == 0)
+                               nf_ct_unexpect_related(rtp_exp);
                }
        }
        nf_ct_expect_put(rtcp_exp);