chan_sip: improved ip:port finding of peers for non-UDP transports.
authorJaco Kroon <jaco@uls.co.za>
Mon, 13 Aug 2018 13:12:21 +0000 (15:12 +0200)
committerJaco Kroon <jaco@uls.co.za>
Fri, 24 Aug 2018 06:23:46 +0000 (01:23 -0500)
commit9680790531e94c25ca8af6de9eeb0473b624aaad
tree8bff0e8737db944e9509316c517d01d5f7ec13d4
parent63ca367ab9ae32f2cae5f01101ef3e63ea53d570
chan_sip: improved ip:port finding of peers for non-UDP transports.

Also remove function peer_ipcmp_cb since it's not used (according to
rmudgett).

Prior to b2c4e8660a9c89d07041271371151779b7ec75f6 (ASTERISK_27457)
insecure=port was the defacto standard.  That commit also prevented
insecure=port from being applied for sip/tcp or sip/tls.

Into consideration there are three sets of behaviour:

1.  "previous" - before the above commit.
2.  "current" - post above commit, pre this one.
3.  "new" - post this commit.

The problem that the above commit tried to address was guests over TCP.
It succeeded in doing that but broke transport!=udp with host!=dynamic.

This commit attempts to restore sane behaviour with respect to
transport!=udp for host!=dynamic whilst still retaining the guest users
over tcp.

It should be noted that when looking for a peer, two passes are made, the
first pass doesn't have SIP_INSECURE_PORT set for the searched-for peer,
thus looking for full matches (IP + Port), the second pass sets
SIP_INSECURE_PORT, thus expecting matches on IP only where the matched
peer allows for that (in the author's opinion:  UDP with insecure=port,
or any TCP based, non-dynamic host).

In previous behaviour there was special handling for transport=tcp|tls
whereby a peer would match during the first pass if the utilized
transport was TCP|TLS (and the peer allowed that specific transport).

This behaviour was wrong, or dubious at best.  Consider two dynamic tcp
peers, both registering from the same IP (NAT), in this case either peer
could match for connections from an IP.  It's also this behaviour that
prevented SIP guests over tcp.

The above referenced commit removed this behaviour, but kept applying
the SIP_INSECURE_PORT only to WS|WSS|UDP.  Since WS and WSS is also TCP
based, the logic here should fall into the TCP category.

This patch updates things such that the previously non-explicit (TCP
behaviour) transport test gets performed explicitly (ie, matched peer
must allow for the used transport), as well as the indeterministic
source-port nature of the TCP protocol is taken into account.  The new
match algorithm now looks like:

1.  As per previous behaviour, IP address is matched first.

2.  Explicit filter with respect to transport protocol, previous
    behaviour was semi-implied in the test for TCP pure IP match - this now
    made explicit.

3.  During first pass (without SIP_INSECURE_PORT), always match on port.

4.  If doing UDP, match if matched against peer also has
    SIP_INSECURE_PORT, else don't match.

5.  Match if not a dynamic host (for non-UDP protocols)

6.  Don't match if this is WS|WSS, or we can't trust the Contact address
    (presumably due to NAT)

7.  Match (we have a valid Contact thus if the IP matches we have no
    choice, this will likely only apply to non-NAT).

To logic-test this we need a few different scenarios.  Towards this end,
I work with a set number of peers defined in sip.conf:

[peer1]
host=1.1.1.1
transport=tcp

[peer2]
host=1.1.1.1
transport=udp

[peer3]
host=1.1.1.1
port=5061
insecure=port
transport=udp

[peer4]
host=1.1.1.2
transport=udp,tcp

[peer5]
host=dynamic
transport=udp,tcp

Test cases for UDP:

1 - incoming UDP request from 1.1.1.1:
  - previous:
    - pass 1:
      * peer1 or peer2 if from port 5060 (indeterminate, depends on peer
        ordering)
      * peer3 if from port 5061
      * peer5 if registered from 1.1.1.1 and source port matches
    - pass 2:
      * peer3
  - current: as per previous.
  - new:
    - pass 1:
      * peer2 if from port 5060
      * peer3 if from port 5061
      * peer5 if registered from 1.1.1.1 and source port matches
    - pass 2:
      * peer3

2 - incoming UDP request from 1.1.1.2:
  - previous:
    - pass 1:
      * peer5 if registered from 1.1.1.2 and port matches
      * peer4 if source port is 5060
    - pass 2:
      * no match (guest)
  - current: as previous.
  - new as previous (with the variation that if peer5 didn't have udp as
          allowed transport it would not match peer5 whereas previous
          and current code could).

3 - incoming UDP request from anywhere else:
  - previous:
    - pass 1:
      * peer5 if registered from that address and source port matches.
    - pass 2:
      * peer5 if insecure=port is additionally set.
      * no match (guest)
  - current - as per previous
  - new - as per previous

Test cases for TCP based transports:

4 - incoming TCP request from 1.1.1.1
  - previous:
    - pass 1 (indeterministic, depends on ordering of peers in memory):
      * peer1; or
      * peer5 if peer5 registered from 1.1.1.1 (irrespective of source port); or
      * peer2 if the source port happens to be 5060; or
      * peer3 if the source port happens to be 5061.
    - pass 2: cannot happen since pass 1 will always find a peer.
  - current:
    - pass 1:
      * peer1 or peer2 if from source port 5060
      * peer3 if from source port 5060
      * peer5 if registered as 1.1.1.1 and source port matches
    - pass 2:
      * no match (guest)
  - new:
    - pass 1:
      * peer 1 if from port 5060
      * peer 5 if registered and source port matches
    - pass 2:
      * peer 1

5 - incoming TCP request from 1.1.1.2
  - previous (indeterminate, depends on ordering):
    - pass 1:
      * peer4; or
      * peer5 if peer5 registered from 1.1.1.2
    - pass 2: cannot happen since pass 1 will always find a peer.
  - current:
    - pass 1:
      * peer4 if source port is 5060
      * peer5 if peer5 registered as 1.1.1.2 and source port matches
    - pass 2:
      * no match (guest).
  - new:
    - pass 1:
      * peer4 if source port is 5060
      * peer5 if peer5 registered as 1.1.1.2 and source port matches
    - pass 2:
      * peer4

6 - incoming TCP request from anywhere else:
  - previous:
    - pass 1:
      * peer5 if registered from that address
    - pass 2: cannot happen since pass 1 will always find a peer.
  - current:
    - pass 1:
      * peer5 if registered from that address and port matches.
    - pass 2:
      * no match (guest)
  - new: as per current.

It should be noted the test cases don't make explicit mention of TLS, WS
or WSS.  WS and WSS previously followed UDP semantics, they will now
enforce source port matching.  TLS follow TCP semantics.

The previous commit specifically tried to address test-case 6, but broke
test-cases 4 and 5 in the process.

ASTERISK-27881 #close

Change-Id: I61a9804e4feba9c7224c481f7a10bf7eb7c7f2a2
channels/chan_sip.c