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
hi,
i have kamailio acting as SBC
i need hide topology like this
ds_select_dst(DSP_GRP_TRUNK, "6");
$tu = $(tu{re.subst,/PRIVATE_IP/IP_OF_CURRENT_SELECTED_DISPATCHER/g});
what is best way for IP extraction from $du?
thanks
Marek
Hello,
I have an issue with SCSF which challenge again after a successful first registration.
I have an error after the second register saying that no matching auth vector found.
I attach the logs and pcap file.
I don't see where is the issue. Could you help me?
Thanks a lot.
Anthony
I'm trying to add something simple like the following:
append_hf("X-testheader: True\r\n", "From");
However, I don't see my X-testheader in a packet capture. Are there
any common pitfalls that would prevent append_hf from working as
expected?
Hi all
I've set up a kamailio server on a public IP address to serve public clients.
- The clients REGISTER over a mix of TCP and TLS, so kamailio has
listeners on the same IP address: port 5060 for UDP/TCP and port 5061
for TLS.
- Some clients REGISTER from the same public IP address, behind the same NAT.
- Sometimes, the client-side NAT can use the same client-side IP
address and port for two concurrent connections - one TCP and one TLS
- which is normal because the server-side port is different (they're
different 5-tuples).
- Now when a request comes to kamailio, to be routed to a client, the
the t_relay_to_tls() function sometimes can relay the request over a
TCP connection instead of a TLS connection.
I've enabled tcp_connection_match=1. This is helpful, but it doesn't
completely resolve the problem. It helps when both connections (TCP
and TLS) are open, because the TLS connection will always be used for
TLS requests. However, when there's no TLS connection open, kamailio
will erroneously use the TCP connection.
Note that this setup depends on TCP/TLS connections being made from
the client to the server, not the other way around.
I've made a cut-down version of this setup to demonstrate the problem.
- Client: 11.15.32.1
- Server: 11.15.32.11
- version: kamailio 5.9.0-dev0 (x86_64/linux)
#################################################################
#!KAMAILIO
enable_tls=yes
listen=tls:11.15.32.11:5061
listen=tcp:11.15.32.11:5060
listen=udp:11.15.32.11:5060
loadmodule "tm"
loadmodule "tls"
loadmodule "pv"
loadmodule "rr"
fork=yes
log_facility=LOG_LOCAL0
log_stderror=no
children=2
debug=3
tcp_connection_match=1
modparam("tls", "low_mem_threshold1", -1)
modparam("tls", "low_mem_threshold2", -1)
modparam("tls", "certificate", "/usr/local/etc/kamailio/certs/chain")
modparam("tls", "private_key", "/usr/local/etc/kamailio/certs/key")
request_route {
loose_route();
$fs="tls:11.15.32.11:5061";
t_relay_to_tls();
}
#################################################################
This is a BYE message to be sent from internally in the network.
###########################
BYE sip:user3@11.15.32.1:33333;transport=tls SIP/2.0
Via: SIP/2.0/UDP 0.0.0.0:11111;rport;branch=z9hG4bK-d8754z-cc2c63344f5218d3-1
Route: <sip:11.15.32.11:5060;transport=tcp;r2=on;lr;ftag=ZUrDU2m4Z9Zam>
Route: <sip:11.15.32.11:5061;transport=tls;r2=on;lr;ftag=ZUrDU2m4Z9Zam>
f: <sip:user1@server>;tag=ZUrDU2m4Z9Zam
t: <sip:user3@server>;tag=gK0ea97395
i:sdfg8we790t874ujk
CSeq: 102 BYE
Content-Length: 0
###########################
When I send that to kamailio over UDP, kamailio relays it over a TCP
connection, as seen with socat here (socat runs on the machine with
address 11.15.32.1).
###########################
$ socat - TCP:11.15.32.11:5060,bind=:33333
BYE sip:user3@11.15.32.1:33333;transport=tls SIP/2.0
Via: SIP/2.0/TLS
11.15.32.11:5061;branch=z9hG4bKd2bf.137932ff9a937cde941fd1790db040c9.0
Via: SIP/2.0/UDP
0.0.0.0:11111;received=11.15.32.11;rport=11111;branch=z9hG4bK-d8754z-cc2c63344f5218d3-1
f: <sip:user1@server>;tag=ZUrDU2m4Z9Zam
t: <sip:user3@server>;tag=gK0ea97395
i:sdfg8we790t874ujk
CSeq: 102 BYE
Content-Length: 0
###########################
Note that
- the RURI has transport=tls in it,
- kamailio is forcing the TLS socket,
- kamailio is using the t_relay_to_tls function,
- the Route header field tells that TLS should be used, and
- the Via header field shows that kamailio tried to send this over TLS, but
- the BYE was sent over unencrypted TCP.
If I use this socat command instead, then I can receive the BYE,
showing that kamailio is able to use TLS if there's a connection
available.
- socat - OPENSSL:11.15.32.11:5061,bind=:33333,verify=no
I can't find any setting to control this, so it looks to me like a
kamailio bug. At that, I see this as a security flaw, because the SIP
traffic should always be encrypted but now is leaked.
Can anyone help?
James
Hi,
I use KEMI/Lua. I have simple lua class:
myclass = {}
myclass.event = function()
KSR.xlog.xinfo("Event working...\n")
end
In kamailio.conf have:
modparam("xhttp", "event_callback", "myclass.event")
In debug log have:
Mar 12 13:21:30 kamailio DEBUG: app_lua [app_lua_api.c:549]: sr_lua_reload_script(): No need to reload [/etc/kamailio/donjon.lua] is version 0
Mar 12 13:21:30 kamailio DEBUG: app_lua [app_lua_api.c:733]: app_lua_run_ex(): executing Lua function: [[myclass.event]]
Mar 12 13:21:30 kamailio DEBUG: app_lua [app_lua_api.c:735]: app_lua_run_ex(): lua top index is: 40
Mar 12 13:21:30 kamailio DEBUG: app_lua [app_lua_mod.c:164]: sr_kemi_config_engine_lua(): execution of route type 513 with name [myclass.event] returned 1
The problem is that the event function in the myclasses class is not called.
Is there any other way or syntax to do this? Of course, if I define a function without a class, it works.
Thanks
Michal
My SIP proxy didn't start with Kamailio 5.8 using the same config that
starts OK with 5.7. The error is:
2024-03-11T06:56:06.457107+02:00 lohi /usr/bin/sip-proxy[2358954]: DEBUG: acc [acc_mod.c:362]: parse_failed_filter(): failed_filter 0 = 407
2024-03-11T06:56:06.457265+02:00 lohi /usr/bin/sip-proxy[2358954]: ERROR: acc [acc_mod.c:369]: parse_failed_filter(): response code is not followed by comma or end of string
2024-03-11T06:56:06.457436+02:00 lohi /usr/bin/sip-proxy[2358954]: ERROR: acc [acc_mod.c:439]: mod_init(): failed to parse failed_filter param
The relevant params are:
modparam("acc", "failed_transaction_flag", 8)
modparam("acc", "failed_filter", "407")
I didn't find any difference in acc_mod.c source code between 5.7 and
5.8 and parse_failed_filter function looked OK.
Then I added a debug statement in the beginning of parse_failed_filter
function:
LM_DBG("parsing failed_filter %s\n", s);
and got to syslog:
cc_mod.c:341]: parse_failed_filter(): parsing failed_filter 407rmissions|pua|rtpengine)$
That explains the parse_failed_filter error, but where does that bogus
param value come from?
Before acc params I have:
modparam("auth_db|dialplan|domain|htable|lcr|msilo|mtree|permissions|pua|rtpengine", "db_url", "mysql://xxxx/sip_proxy")
modparam("registrar|nathelper", "received_avp", "$avp(received_uri)")
So it from there, but why? Is there a bug somewhere or is my SIP proxy
running out of memory or something? Before the above error messages,
there are no other error messages.
-- Juha
Hello Kamailians!
Before I start going down this path - anyone that has written a Kamailio Yocto layer and want to share either layer or experience or both?
For those that doesn’t know: Yocto is a toolkit for building your own Linux distros for embedded systems.
/O
Hi everyone
I have a small question:
i want to set up a basic SIP Redirect Server
so I install Kamailio v5.3
and comment default request route and set simple
request_route {
rewritehostport("1.2.3.4");
sl_send_reply("302", "Moved Temporarily");
}
but for some reason, Kamailio does not care about the port and sends a
reply to 5060
[image: image.png]
i also tried to delete rewritehostport
but it only deletes Contact: from the reply
but reply itself is still sent to 5060 port
[image: image.png]
--
*Antony*
satskiy.a(a)gmail.com
+380669197533
+48727830247
Kamailio 5.8 build generates on my Debian 12 the following warning:
core/mem/q_malloc.c:996:14: warning: 'qm_strnstr' defined but not used [-Wunused-function]
996 | static void *qm_strnstr(const void *b1, int l1, const void *b2, int l2)
| ^~~~~~~~~~
Perhaps the definition of the function should be #ifdef'ed by
DBG_QM_MALLOC.
-- Juha