Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

usrloc keepalive: with r2 enabled, registrar configured with usrloc keepalives will send OPTIONS to received param of first hop #2447

Closed
gedia opened this issue Aug 18, 2020 · 5 comments

Comments

@gedia
Copy link

gedia commented Aug 18, 2020

Description

Consider the following setup: an edge proxy is configured with the path module, double rr is enabled (because the proxy is multihomed), add_path_received() is used to cope with NATted endpoints and the registrar is a separate kamailio instance residing on a private network. Contacts are saved successfully by the registrar, along with path information. INVITEs are first sent to the registrar which performs location lookup, and forwards the request based on the contact's Path. This part works as expected.

However, things seem to fail when using the new keepalive functionality of the usrloc module on the registrar. The OPTIONS request is forwarded to the "received" parameter of the first Route header instead of the URI of the first Route header.

Troubleshooting

SIP Traffic

EXAMPLES (check the first line printed by sngrep for L3/L4 info, public IPs have been censored):

  1. INVITE is routed to $route_uri, ignoring the "received" parameter when doing a loose_route():
2020/08/18 19:58:29.918672 172.30.154.189:5060 -> 172.28.155.1:5060

INVITE sip:voip-test-user-04@2.2.2.2:5060 SIP/2.0
Via: SIP/2.0/UDP 172.30.154.189;branch=z9hG4bKa788.302fa172e9b1851973593c7f20d3586d.0
Route: <sip:172.28.155.1;lr;received=sip:2.2.2.2:5060;r2=on>,<sip:3.3.3.3;lr;received=sip:2.2.2.2:5060;r2=on>
Via: SIP/2.0/UDP 172.30.152.3:5060;branch=z9hG4bK104f503d
Max-Forwards: 69
From: "sbcpub-stage-test-01" <sip:1234567890@sip.domain>;tag=as77e25748
To: <sip:voip-test-user-04@sip.domain>
Contact: <sip:1234567890@172.30.152.3:5060>
Call-ID: 53d1089c6bc695875816e25a49da8a29@sip.domain
CSeq: 102 INVITE
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, MESSAGE
Supported: replaces, timer
X-CID: ac32e4ae-580e164d@172.17.173.14
Remote-Party-ID: "sbcpub-stage-test-01" <sip:1234567890@sip.domain>;party=calling;privacy=off;screen=no
Content-Type: application/sdp
Content-Length: 346
X-Called-Username: voip-test-user-04

v=0
o=root 18222507 18222507 IN IP4 172.30.152.3
c=IN IP4 172.30.152.3
t=0 0
m=audio 16106 RTP/AVP 9 8 0 18 101
a=rtpmap:9 G722/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:18 G729/8000
a=fmtp:18 annexb=no
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=ptime:20
a=maxptime:150
a=sendrecv
  1. OPTIONS is routed to "received" value of first Route header when doing keepalives using the usrloc module:
2020/08/18 19:58:02.450949 172.30.154.189:5060 -> 2.2.2.2:5064

OPTIONS sip:voip-test-user-05@172.17.173.36:5064 SIP/2.0
Via: SIP/2.0/UDP 172.30.154.189:5060;branch=z9hG4bKx.1.1.0
Route: <sip:172.30.155.1;lr;received=sip:2.2.2.2:5064;r2=on>,<sip:3.3.3.3:6050;lr;received=sip:2.2.2.2:5064;r2=on>
From: <sip:conn-revival@sip.domain>;tag=uloc-5f3be864-5d75-2-8617a458-5f3c089a-6e141-1.1
To: <sip:voip-test-user-05@sip.domain>
Call-ID: ksrulka-1.1
CSeq: 80 OPTIONS
Content-Length: 0

usrloc entries

The location entries for the examples above are as follows:

        AoR: voip-test-user-04
        Contacts: {
                Contact: {
                        Address: sip:voip-test-user-04@2.2.2.2:5060
                        Expires: 47
                        Q: -1.000000
                        Call-ID: 34b4a487df71412e8e93a7a1c358d723
                        CSeq: 54
                        User-Agent: PolycomVVX-VVX_250-UA/6.3.0.14929
                        Received: sip:2.2.2.2:5060
                        Path: <sip:172.28.155.1;lr;received=sip:2.2.2.2:5060;r2=on>,<sip:3.3.3.3;lr;received=sip:2.2.2.2:5060;r2=on>
                        State: CS_DIRTY
                        Flags: 0
                        CFlags: 128
                        Socket: udp:172.30.154.189:5060
                        Methods: 8159
                        Ruid: uloc-5f3bf3b8-65c3-b7
                        Instance: [not set]
                        Reg-Id: 0
                        Server-Id: 0
                        Tcpconn-Id: -1
                        Keepalive: 0
                        Last-Keepalive: 1597771086
                        KA-Roundtrip: 0
                        Last-Modified: 1597771086
                }
        }
        AoR: voip-test-user-05
        Contacts: {
                Contact: {
                        Address: sip:voip-test-user-05@172.17.173.36:5064
                        Expires: 88
                        Q: -1.000000
                        Call-ID: 3279de38-f6ae2c18@172.17.173.36
                        CSeq: 17174
                        User-Agent: Cisco/SPA525G2-7.6.2e
                        Received: sip:2.2.2.2:5064
                        Path: <sip:172.30.155.1;lr;received=sip:2.2.2.2:5064;r2=on>,<sip:3.3.3.3:6050;lr;received=sip:2.2.2.2:5064;r2=on>
                        State: CS_DIRTY
                        Flags: 0
                        CFlags: 128
                        Socket: udp:172.30.154.189:5060
                        Methods: 6815
                        Ruid: uloc-5f3be864-5d75-2
                        Instance: [not set]
                        Reg-Id: 0
                        Server-Id: 0
                        Tcpconn-Id: -1
                        Keepalive: 0
                        Last-Keepalive: 1597771100
                        KA-Roundtrip: 0
                        Last-Modified: 1597771100
                }
        }

Possible Solutions

There are two ways I can thing of to mitigate this:

  • Modify the path module so that the received parameter is only set for the first Path header, which corresponds to the interface the request was received on. I believe this is the preferred way to deal with this, as I don't see the point in adding ";received" to both path entries when performing double "r2" path injection.
  • Or, make usrloc keepalive behave the same way as loose_route(). It seems loose_route will only honour the ";received=" parameter in a Route header only if it's the last remaining one? I'm not sure this is how it works, but somehow I get the desired behaviour with loose_route()

On a sidenote, usrloc locally generated OPTIONS do not seem to be handled by the local-request event route. Is this intentional? AFAICT it would require engaging the tm module which will add some overhead...

Additional Information

  • Kamailio Version - output of kamailio -v
version: kamailio 5.4.0 (x86_64/linux)

built with this patch: 19128f2

  • Operating System:
Linux sbcpub0-stage-lhe0-cn1 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1 (2020-01-26) x86_64 GNU/Linux
@miconda
Copy link
Member

miconda commented Aug 18, 2020

@gedia
Copy link
Author

gedia commented Aug 18, 2020

Do you have this parameter set?

* https://www.kamailio.org/docs/modules/stable/modules/registrar.html#registrar.p.path_use_received

Yes:

# ----- registrar params -----
modparam("registrar", "method_filtering", 1)
/* uncomment the next line to disable parallel forking via location */
# modparam("registrar", "append_branches", 0)
/* uncomment the next line not to allow more than 3 contacts per AOR */
modparam("registrar", "max_contacts", 3)
/* max value for expires of registrations */
modparam("registrar", "max_expires", 300)
/* min value for expires of registrations */
modparam("registrar", "min_expires", 60)
/* default value for expires for registrations if not provided */
modparam("registrar", "default_expires", 120)
/* avoid having registrations arrive at the same time */
modparam("registrar", "default_expires_range", 30) # +- 30% from default_expires
modparam("registrar", "expires_range", 30) # expires within [0.7*expires .. expires]
/* set it to 1 to enable GRUU */
modparam("registrar", "gruu_enabled", 1)
/* set it to 0 to disable Path handling */
modparam("registrar", "use_path", 1)
/* save Path even if not listed in Supported header */
modparam("registrar", "path_mode", 1)
modparam("registrar", "path_use_received", 1)
modparam("registrar", "outbound_mode", 1)

@miconda
Copy link
Member

miconda commented Aug 19, 2020

You do not need that to be 1 if you want to use Path for routing, only if you want to bypass the intermediary proxy doing Path.

IIRC, the nathelper module has (or had) a mode to spoof source IP and then it could send UDP NAT keepalives pretending to be the intermediary proxy to received address set in this way.

@gedia
Copy link
Author

gedia commented Aug 19, 2020

You do not need that to be 1 if you want to use Path for routing, only if you want to bypass the intermediary proxy doing Path.

IIRC, the nathelper module has (or had) a mode to spoof source IP and then it could send UDP NAT keepalives pretending to be the intermediary proxy to received address set in this way.

Indeed the path_use_received was the culprit. Thank you.

Before I close this, is it normal that INVITEs do not go directly to the Received attr fetched by lookup()? I mean shouldn't this be consistent for locally generated requests by usrloc module and for forwarded requests via usrloc/lookup()?

@gedia
Copy link
Author

gedia commented Aug 19, 2020

Never mind, this is properly documented in the registrar module docs:

A call to lookup(...) always uses the path header if found, and inserts it as Route HF either in front of the first Route HF, or after the last Via HF if no Route is present. It also sets the destination uri to the first Path uri, thus overwriting the received-uri, because NAT has to be handled at the outbound-proxy of the UAC (the first hop after client's NAT).

Only question is whether path_use_received=1 should override this behaviour....

@gedia gedia closed this as completed Aug 19, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants