Hello Experts,
I'm not an expert in Kamailio, but thanks to some excellent online resources, I managed to set up a basic interconnection between Teams and Kamailio.
So far, outbound calls, as well as blind and attended call transfers, are working smoothly. However, I'm facing a challenge with call disconnection for outbound calls. Specifically, when an outbound call is made and the called party hangs up, the BYE message isn't being relayed properly towards the Teams side.
In my routing configurations, I remove the Record-Route header received from Teams and add a new Record-Route header with Kamailio's IP before forwarding it to my IP PBX (FreeSwitch). For the BYE message to be processed correctly, Microsoft Teams requires the original contact in the Request URI (RURI) and the Route header to match the initial Record-Route included in the INVITE. Currently, my BYE message appears as follows:
BYE sip:api-du-a-auea.pstnhub.microsoft.com:443;x-i=5e96d703-b1e7-449c-aebf-a87bfa628176;x-c=f61b674bc597513ab8d888dbe7c560fb/d/8/9a6c508a9cbd44ebb892b62c33ba9b67 SIP/2.0 Via: SIP/2.0/UDP x.x.x.x:5060;branch=z9hG4bKeab5.b657e3455b6d11ed6975c69882632a27.0 Via: SIP/2.0/UDP x.x.x.x;received=x.x.x.x;rport=5060;branch=z9hG4bKK3HKQBUvX42ya Max-Forwards: 69 From: sip:+61403225xxx@kamsbc.xyzdomain.com:5061;user=phone;tag=j5gptBSXXrXDc To: "XYZ Pty Ltd" sip:+61291455xxx@sip.pstnhub.microsoft.com:5061;user=phone;tag=3a5f0c9e32bc4eb1bffed29dc954118f Call-ID: f61b674bc597513ab8d888dbe7c560fb CSeq: 80555743 BYE User-Agent: Unknown/v1.0 Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY Supported: timer, path, replaces Reason: MVTSLocal;cause=10;text="BYE received" Content-Length: 0
Kamailio conducts a lookup for api-du-a-auea.pstnhub.microsoft.com but ends up sending the BYE message to an incorrect IP.
Below is a snippet of my routing block configurations:
####### Routing Logic ########
/* Main SIP request routing logic * - processing of any incoming SIP request starts with this route * - note: this is the same as route { ... } */ request_route {
# per request initial checks route(REQINIT);
#check who is the sender route(INITCHECK);
# CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) { route(RELAY); } exit; }
# handle retransmissions if (!is_method("ACK")) { if(t_precheck_trans()) { t_check_trans(); exit; } t_check_trans(); }
# handle requests within SIP dialogs route(WITHINDLG);
### only initial requests (no To tag)
# record routing for dialog forming requests (in case they are routed) # - remove preloaded route headers remove_hf("Route"); if (is_method("INVITE|SUBSCRIBE")) { record_route(); }
# account only INVITEs if (is_method("INVITE")) { setflag(FLT_ACC); # do accounting }
if ($rU==$null) { # request with no Username in RURI sl_send_reply("484","Address Incomplete"); exit; }
# update $du to set the destination address for proxying #$du = "sip:" + $rd + ":9";
route(RELAY); exit; }
route[INITCHECK] { if(from_uri =~ ".*microsoft.com") { setflag(FROM_TEAMS); $du = "sip:" + "MY_PBX_IP" + ":" + "5060";
//added by SHK
remove_hf("Record-Route"); // Remove existing Record-Route header append_hf("Record-Route: sip:KAM_IP:5060;transport=udp\r\n");
route(HANDLE_RTP_FROM_TEAMS); }
if(from_uri =~ ".*" + "MY_PBX_IP") { setflag(FROM_PBX); $du="sip:sip.pstnhub.microsoft.com;transport=tls"; route(HANDLE_RTP_FROM_PBX); } }
#Manage RTP & transcoding comming from Teams to PBX route[HANDLE_RTP_FROM_TEAMS] { t_on_reply("PBX_REPLY_TO_TEAMS");
if (has_body("application/sdp")) { rtpengine_manage("RTP codec-mask=all codec-transcode=PCMA replace-origin replace-session-connection ICE=remove media-address=HOST_IP"); // record_route(); t_relay_to_udp("MY_PBX_IP","5060"); } else { rtpengine_manage(); } }
#Manage RTP & transcoding comming from PBX to Teams route[HANDLE_RTP_FROM_PBX] { t_on_reply("TEAMS_REPLY_TO_PBX");
if (has_body("application/sdp")) { rtpengine_manage("SRTP codec-mask=all ICE=force codec-transcode=PCMA replace-origin replace-session-connection media-address=ADVERTISE_IP");
$rd = "sip.pstnhub.microsoft.com"; $td = "kamsbc.xyzdomain.com"; $fd = "kamsbc.xyzdomain.com";
// record_route();
#Set TLS SNI (server name & server id) $xavp(tls=>server_name) = "kamsbc.xyzdomain.com"; $xavp(tls=>server_id) = "kamsbc.xyzdomain.com";
t_relay(); } else { rtpengine_manage(); } }
# Wrapper for relaying requests route[RELAY] {
# enable additional event routes for forwarded requests # - serial forking, RTP relaying handling, a.s.o.
if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE")) { if(!t_is_set("branch_route")) t_on_branch("MANAGE_BRANCH"); } if (is_method("INVITE|SUBSCRIBE|UPDATE")) { if(!t_is_set("onreply_route")) t_on_reply("MANAGE_REPLY"); } if (is_method("INVITE")) { if(!t_is_set("failure_route")) t_on_failure("MANAGE_FAILURE"); }
if (!t_relay()) { sl_reply_error(); } exit; }
# Per SIP request initial checks route[REQINIT] { if($ua =~ "friendly-scanner|sipcli|VaxSIPUserAgent") { # silent drop for scanners - uncomment next line if want to reply # sl_send_reply("200", "OK"); exit; }
if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }
if(is_method("OPTIONS")) { sl_send_reply("200","Keepalive"); exit; }
if(!sanity_check("1511", "7")) { xlog("Malformed SIP message from $si:$sp\n"); exit; } }
# Handle requests within SIP dialogs route[WITHINDLG] { if (!has_totag()) return;
#Teams reINVITEs if(isflagset(FROM_TEAMS)) { loose_route(); t_relay(); exit; }
if(isflagset(FROM_PBX)) { #Set TLS SNI (server name & server id) $xavp(tls=>server_name) = "kamsbc.xyzdomain.com"; $xavp(tls=>server_id) = "kamsbc.xyzdomain.com";
loose_route(); t_relay(); exit; }
# sequential request withing a dialog should # take the path determined by record-routing if (loose_route()) { if (is_method("BYE")) { setflag(FLT_ACC); # do accounting ... setflag(FLT_ACCFAILED); # ... even if the transaction fails
#set coresponding cert on transactions if($fd == "kamsbc.xyzdomain.com") { $xavp(tls=>server_name) = "kamsbc.xyzdomain.com"; $xavp(tls=>server_id) = "kamsbc.xyzdomain.com"; }
} else if ( is_method("NOTIFY") ) { # Add Record-Route for in-dialog NOTIFY as per RFC 6665. record_route(); } route(RELAY); exit; }
if ( is_method("ACK") ) { if ( t_check_trans() ) { # no loose-route, but stateful ACK; # must be an ACK after a 487 # or e.g. 404 from upstream server route(RELAY); exit; } else { # ACK without matching transaction ... ignore and discard exit; } } sl_send_reply("404","Not here"); exit; }
# Manage outgoing branches branch_route[MANAGE_BRANCH] { xdbg("new branch [$T_branch_idx] to $ru\n"); }
# Manage incoming replies onreply_route[MANAGE_REPLY] { xdbg("incoming reply\n"); }
#PBX On Reply onreply_route[PBX_REPLY_TO_TEAMS] {
if (has_body("application/sdp")) { rtpengine_manage("SRTP codec-mask=all codec-transcode=PCMA replace-origin replace-session-connection media-address=ADVERTISE_IP"); } else { rtpengine_manage(); }
//added by SHK
if (status == "200" && method == "INVITE") { remove_hf("Contact"); # Remove existing Contact header append_hf("Contact: sip:kamsbc.xyzdomain.com:5061;transport=tls\r\n"); }
if (status=="200") { remove_hf("ALLOW:"); append_hf("ALLOW: INVITE,ACK,OPTIONS,CANCEL,BYE,NOTIFY \r\n","CONTENT-TYPE");
}
}
#From Teams On Reply onreply_route[TEAMS_REPLY_TO_PBX] { if (has_body("application/sdp")) { rtpengine_manage("RTP codec-mask=all codec-transcode=PCMA replace-origin replace-session-connection media-address=HOST_IP"); } else { rtpengine_manage(); }
}
# Manage failure routing cases failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; }
event_route[tm:local-request] { if(is_method("OPTIONS") && $ru =~ "pstnhub.microsoft.com") { append_hf("Contact: sip:kamsbc.xyzdomain.com:SBC_PORT;transport=tls\r\n"); } xlog("L_INFO", "Sent out tm request: $mb\n"); }
####### Routing Logic End ########
Given these details, I suspect the issue might be related to how I'm handling the Record-Route headers or potentially a misconfiguration in directing the BYE message. I would greatly appreciate any insights, suggestions, or guidance on how to correctly relay the BYE message back to Teams, or further refine my existing configuration.
Thank you in advance for your help and support!
Regards, Shah Hussain