Summary: | ASTERISK-24889: Possible bug: PJSIP: stateless behavior when transport=tcp | ||||
Reporter: | Anatoli (anatoli) | Labels: | |||
Date Opened: | 2015-03-17 02:09:42 | Date Closed: | 2015-03-17 05:41:14 | ||
Priority: | Major | Regression? | |||
Status: | Closed/Complete | Components: | pjproject/pjsip | ||
Versions: | 13.2.0 | Frequency of Occurrence | Constant | ||
Related Issues: |
| ||||
Environment: | OS: Ubuntu 14.04, with all updates Kernel: 3.13.0-44-generic pjsip: 2.3 from http://www.pjsip.org/release/2.3/pjproject-2.3.tar.bz2 (./configure --prefix=/usr --enable-shared --enable-epoll; make; sudo make install) Asterisk: 13.2.0 from http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-13.2.0.tar.gz (./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var; make; sudo make install) | Attachments: | |||
Description: | When an endpoint registers via TCP, PJSIP processes everything as corresponds: the communication is via TCP, REGISTER/ACK pass OK, the endpoint can even establish calls. But, when another endpoint calls to the first one, PJSIP sends INVITE message in a new TCP session to the already opened port.
So if the registration was performed from TCP 55880 to 5060 and everything except INVITE flows in the same TCP session, INVITE is sent via a new TCP session from a random port on asterisk server (e.g. 32926) to the already opened 55880 port. With tcpdump I see a new SYN packet (and the already established TCP session was not closed/reset yet): {noformat} From: 10.101.10.1 (asterisk IP) To: 10.101.100.100 (endpoint IP) Source port: 32926 Destination port: 55880 Protocol: TCP TCP Data: [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=9227246 TSecr=0 WS=128 {noformat} Naturally, the endpoint responds with a RST,ACK. The CLI shows the following: {noformat} ERROR[19352]: pjsip:0 <?>: tcpc0x7fcdb404 TCP connect() error: Connection refused [code=120111] WARNING[19352]: pjsip:0 <?>: tsx0x7fcdb402e Failed to send Request msg INVITE/cseq=31054 (tdta0x7fcdb4034260)! err=120111 (Connection refused) {noformat} Investigating the code, I've found that to send the INVITE message PJSIP calls pjsip_endpt_send_request_stateless, which, as the name indicates, behaves statelessly, though using TCP. Call trace: {noformat} sip_dialog.c@1164 pjsip_dlg_send_request(INVITE) => @1214 call to pjsip_tsx_create_uac sip_transaction.c@1266 pjsip_tsx_create_uac => call to pjsip_tsx_create_uac2 sip_transaction.c@1273 pjsip_tsx_create_uac2 => @1367 tsx_on_state_null callback assignment sip_transaction.c@2311 tsx_on_state_null => @2351 call to tsx_send_msg sip_transaction.c@2006 tsx_send_msg => @2120 call to pjsip_endpt_send_request_stateless {noformat} Asterisk 13.2.0, PJSIP 2.3, everything compiled from sources. Config: same as in the examples except for one endpoint the transport=<tcp_transport> option is specified. Client: a number of softphones, tested with latest Linphone on iPhone, SessionChat (freeware from AppStore), Jitsi on Windows. Please let me know if additional information is required. Regards, Anatoli | ||||
Comments: | By: Olle Johansson (oej) 2015-03-17 05:12:08.950-0500 Theory lesson: The endpoint should register a listen port and not use that port when opening a session to Asterisk. Connection reuse happens if the endpoint signals support of the outbound extension to SIP - in that case Asterisk is allowed to reuse the existing TCP connection for the outbound request (the INVITE). --- End theory lesson --- Implementations may wary - some servers reuse connections as long as their open. They should NOT do this for TLS without outbound though. Can you add the contact used at registration to this bug report? It's interesting to see how the client suggest that the server should connect to it. By: Joshua C. Colp (jcolp) 2015-03-17 05:41:06.192-0500 This is a duplicate of ASTERISK-22658. If a "transport" is specified PJSIP will currently never reuse TCP connections. As well in order to have the existing connection reused the "rewrite_contact" option needs to be set to yes so the target address is the source. By: Anatoli (anatoli) 2015-03-21 18:16:21.871-0500 Olle, It looks like there are 2 problems involved in this issue. First one is what Joshua described, that res_pjsip won't reuse TCP connections if for the endpoint in question there is a {{transport=<transport_name>}} option specified in pjsip.conf. But there is also another problem (as you suspected): the Contact header filed that the softphones send is (in my opinion) incorrectly formatted. From SIP RFP: _"While the Via header field tells other elements where to send the response, the Contact header field tells other elements where to send future requests."_ And this is what Jitsi on Windows 7 sends: {code}REGISTER sip:server_ip;transport=tcp SIP/2.0 Call-ID: dd753e83b597b5ca2a2e4f162775375e@0:0:0:0:0:0:0:0 CSeq: 1 REGISTER From: "Name1" <sip:555@server_ip>;tag=1dde9f02 To: "Name1" <sip:555@server_ip> Via: SIP/2.0/TCP host_ip:3409;branch=z9hG4bK-313537-071be23f01c7c2ee583675273d44e27d Max-Forwards: 70 User-Agent: Jitsi2.6.5390Windows 7 Expires: 600 Contact: "Name1" <sip:555@host_ip:3409;transport=tcp;registering_acc=10_101_10_1>;expires=600 Content-Length: 0{code} Here Via says _"send the responses to host_ip:3409"_ and Contact says _"send new requests (INVITE, BYE), establishing a new TCP session, to.. the same host_ip:3409"_. If I understand it correctly, {{Contact}} should say {{host_ip:5060}} or omit the port entirely for the default value to be applied. Nevertheless Jitsi listens on all 3 standard SIP ports: tcp/5060, tcp/5061 & udp/5060 on 0.0.0.0 (they are specified in the global SIP configuration section) BUT the 3409 (random) port appears in netstat as ESTABLISHED, _not_ LISTENING and it doesn't accept new incoming connections. So, Jitsi expects incoming connections on 5060, but specifies the incoming port as 3409 in {{Contact}} field. Same behavior is observed in Linphone and SessionChat/SessionTalk both on iPhone: {code}REGISTER sip:server_ip SIP/2.0 Via: SIP/2.0/TCP host2_ip:59911;alias;branch=z9hG4bK.3kRQgBAbb;rport From: <sip:777@server_ip>;tag=4Yzj~FqLz To: sip:777@server_ip CSeq: 20 REGISTER Call-ID: 47V~RpZbNs Max-Forwards: 70 Supported: outbound Contact: <sip:777@host2_ip:59911;transport=tcp>;+sip.instance="<urn:uuid:1e603d84-0b11-42a7-83cb-7c34f71e0089>" Expires: 600 User-Agent: LinphoneIphone/2.2.4.1-27-ge8ab36a (belle-sip/1.3.3) Content-Length: 0{code} {code}REGISTER sip:server_ip SIP/2.0 Via: SIP/2.0/TCP host2_ip:35210;branch=z9hG4bK-524287-1---0a74526f8a4d825c;rport;alias Max-Forwards: 70 Contact: <sip:777@host2_ip:35210;rinstance=2a2202a7c0b108dd;transport=tcp> To: "Name1"<sip:777@server_ip> From: "Name1"<sip:777@server_ip>;tag=02dd3253 Call-ID: QFlg48EH71b0n3hklDy1IQ.. CSeq: 1 REGISTER Expires: 600 Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, SUBSCRIBE Supported: replaces, norefersub User-Agent: SessionTalk Version 5.11 Content-Length: 0{code} A similar discussion is held in the pjsip dev mailing list: http://comments.gmane.org/gmane.comp.voip.pjsip/19202. |