Skip to content

set dlg_req_swap_direction based on call direction#3072

Open
yutongqing wants to merge 1 commit into
signalwire:masterfrom
yutongqing:patch-3
Open

set dlg_req_swap_direction based on call direction#3072
yutongqing wants to merge 1 commit into
signalwire:masterfrom
yutongqing:patch-3

Conversation

@yutongqing

Copy link
Copy Markdown

Description

This PR fixes a call recovery issue during FreeSWITCH failover.

Background

FreeSWITCH uses the channel variable dlg_req_swap_direction to determine whether the From and To headers should be swapped when sending a re-INVITE for call recovery.

The variable is set to true when:

  • an outbound channel receives a nua_i_ack event, or

  • an inbound channel receives a nua_r_invite event.

Problem

Once dlg_req_swap_direction is set to true, there is no clear logic to reset or update it. As a result, subsequent SIP events may leave the variable in an incorrect state, eg: an outbound channel receives a nua_r_invite event later, causing invalid From/To header handling during call recovery after a FreeSWITCH failover.

Solution

This PR updates the handling of dlg_req_swap_direction so that its value correctly reflects the current SIP dialog state during call recovery.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Code cleanup / refactor

Related Issues

Testing

  • Added/updated unit tests
  • Tested manually
  • Tested with live SignalWire credentials (if applicable)

Checklist

  • I have read the CONTRIBUTING guidelines
  • My code follows the project's style guidelines
  • I have added tests for my changes (if applicable)
  • I have updated documentation (if applicable)
  • All existing tests pass

Additional Notes

@yutongqing

yutongqing commented Jul 19, 2026

Copy link
Copy Markdown
Author

We've encountered a FreeSWITCH call recovery issue. The reproduction steps are below.

@andywolk @morbit85 Could you please help to review and confirm whether this issue, Thanks!

1. FreeSwitch sends an outbound INVITE

INVITE sip:bob@example.com SIP/2.0
Max-Forwards: 70
From: Alice <sip:alice@example.com>;tag=1111
To: Bob <sip:bob@example.com>
Call-ID: call123@example.com
CSeq: 1 INVITE
Contact: <sip:alice@192.168.1.10>
Content-Type: application/sdp
Content-Length: ...
SIP/2.0 200 OK
From: Alice <sip:alice@example.com>;tag=1111
To: Bob <sip:bob@example.com>;tag=2222
Call-ID: call123@example.com
CSeq: 1 INVITE
Contact: <sip:bob@192.168.1.20>

2. FreeSwitch receives a re-INVITE from the remote party and responds with 200 OK

INVITE sip:alice@192.168.1.10 SIP/2.0
Max-Forwards: 70
From: Bob <sip:bob@example.com>;tag=2222
To: Alice <sip:alice@example.com>;tag=1111
Call-ID: call123@example.com
CSeq: 2 INVITE
Contact: <sip:bob@192.168.1.20>
Content-Type: application/sdp
SIP/2.0 200 OK
From: Bob <sip:bob@example.com>;tag=2222
To: Alice <sip:alice@example.com>;tag=1111
Call-ID: call123@example.com
CSeq: 2 INVITE
Contact: <sip:alice@192.168.1.10>
Content-Type: application/sdp

At this point, dlg_req_swap_direction is set to true

3. FreeSwitch failover and start to recover the call

INVITE sip:bob@example.com SIP/2.0
Max-Forwards: 70
From: Alice <sip:alice@example.com>;tag=1111
To: Bob <sip:bob@example.com>;tag=2222
Call-ID: call123@example.com
CSeq: 3 INVITE
Contact: <sip:alice@192.168.1.10>
Content-Type: application/sdp
Content-Length: ...
SIP/2.0 200 OK
From: Alice <sip:alice@example.com>;tag=1111
To: Bob <sip:bob@example.com>;tag=2222
Call-ID: call123@example.com
CSeq: 3 INVITE
Contact: <sip:bob@192.168.1.20>

The call is recovered successfully in this step.

4. FreeSWITCH fails over again but fails to recover the call

At this point, dlg_req_swap_direction is still true, According to the following logic (around line 2324 in sofia_glue.c):

if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
		tech_pvt->dest = switch_core_session_sprintf(session, "sip:%s", switch_channel_get_variable(channel, "sip_req_uri"));
		switch_channel_set_variable(channel, "sip_handle_full_from", switch_channel_get_variable(channel, swap ? "sip_full_to" : "sip_full_from"));
		switch_channel_set_variable(channel, "sip_handle_full_to", switch_channel_get_variable(channel, swap ? "sip_full_from" : "sip_full_to"));
	} 

Since swap is still true, FreeSWITCH sends the recovery INVITE with incorrect From and To headers:

INVITE sip:bob@example.com SIP/2.0
Max-Forwards: 70
From: Bob <sip:bob@example.com>;tag=2222
To: Alice <sip:alice@example.com>;tag=1111
Call-ID: call123@example.com
CSeq: 4 INVITE
Contact: <sip:alice@192.168.1.10>
Content-Type: application/sdp
Content-Length: ...

It looks like resetting dlg_req_swap_direction to false after the successful recovery in step 3 would allow the subsequent recovery in step 4 to generate the correct From and To headers.

@morbit85

Copy link
Copy Markdown
Collaborator

The fix looks correct, and it's not only subsequent recoveries. Because the flag was only ever set and never reset, a call re-INVITEd in both directions before the first recovery already stores a stale value and recovers wrong on the first attempt.

@morbit85
morbit85 requested review from andywolk and morbit85 July 19, 2026 12:15
@morbit85 morbit85 added the bug Something isn't working label Jul 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants