6.0.5 Teams Direct Routing SBC: inbound 400 cause=95 + outbound 2xx ACK not completing (dual-transport TLS/UDP record_route_preset)

George Hanousek via sr-users <[email protected]> Thu, 18 Jun 2026 11:35:12 +0200
Newsgroups gmane.comp.voip.ser
Message-ID <CAPM21qTySF22eqgpwV_g6f-PYSYPWh74TKXw=+ZJgLuejM1JKA@mail.gmail.com>
Hello,

Kamailio 6.0.5 (distro packages) on Ubuntu 26.04 as an SBC between
Microsoft Teams Direct Routing (TLS/5061) and Twilio Elastic SIP
Trunking (UDP/5060), RTPEngine 13.5.1 bridging SRTP<->RTP (userspace,
table=-1). Outbound (Teams->Twilio) negotiates fully and rings; two
symptoms remain that I suspect share a Record-Route / dual-transport
root.

ENVIRONMENT
- SBC FQDN sbc.ms-teams-dev.intiveo.com
- Public IP 108.132.63.71  (UDP/TCP 5060 to Twilio)
- Private IP 10.0.0.164    (TLS 5061 to Teams, advertised as FQDN)
- TLS 1.2, LetsEncrypt cert + custom CA bundle (outbound TLS to
  sip.pstnhub.microsoft.com validates fine; calls reach Teams)
- listen=udp:10.0.0.164:5060 advertise 108.132.63.71:5060
- listen=tcp:10.0.0.164:5060 advertise 108.132.63.71:5060
- listen=tls:10.0.0.164:5061 advertise sbc.ms-teams-dev.intiveo.com:5061

SYMPTOM 1 - OUTBOUND (Teams->Twilio): rings, cannot be answered.
On answer, Twilio's 200 OK retransmits every 4s and is never confirmed;
the ACK from Teams does not complete back to Twilio. The outbound INVITE
to Twilio leaves over UDP from the public IP, but the first inserted
Record-Route is transport=tls:

  Record-Route: <sip:sbc.ms-teams-dev.intiveo.com:5061
;transport=tls;ftag=...;lr=on>
  Record-Route: <sip:10.0.0.164:5060;ftag=...;lr=on>

sngrep of the outbound call (Teams answered ~09:19:48):

  INVITE (SDP)        10.0.0.164:5060 -> 54.172.60.3:5060
  100 trying          <-
  183 Session prog.   <-   (rings)
  200 OK (SDP)        <-   (answered)
  INVITE (SDP)        ->        (re-INVITE/ACK path)
  200 OK              <-
  100 trying          <-
  200 OK (SDP)        <-
  183 Session prog.   <-
  200 OK (SDP)        <-   } 200 OK repeats every ~4s, never ACKed
  200 OK (SDP)        <-   }
  CANCEL              ->
  200 canceling       <-
  487 Request Term.   <-
  ACK                 ->

SYMPTOM 2 - INBOUND (Twilio->Teams): the forwarded INVITE reaches Teams
over TLS, and Teams returns:

  SIP/2.0 400 Bad Request
  Reason: Q.850;cause=95;text="Invalid unknown header: Cannot add a
  parameter known to SipUri as a generic parameter"

The Teams client never rings. I have tried removing a hand-built Contact
header and setting uac restore_mode=none; neither changed the cause=95.

QUESTIONS
1. With one UDP listen (public IP) and one TLS listen (private IP,
   advertised FQDN), is record_route_preset() the correct approach, or
   does the dual-transport case need record_route() with per-leg
   handling so the 2xx ACK bridges TLS->UDP? Is the transport=tls on the
   first RR entry causing the ACK to come back on the wrong leg?
2. For the inbound cause=95, which header/parameter is Teams objecting
   to with this config?

FULL kamailio.cfg BELOW:

#!KAMAILIO
#!define WITH_TLS
#!substdef "!MY_PUB_IP!108.132.63.71!g"
#!substdef "!MY_PRIV_IP!10.0.0.164!g"
#!substdef "!MY_FQDN!sbc.ms-teams-dev.intiveo.com!g"
#!substdef "!TWILIO_TRUNK!intiveo-ms-teams-dev.pstn.twilio.com!g"

debug=2
log_stderror=no
fork=yes
children=2
auto_aliases=no

listen=udp:MY_PRIV_IP:5060 advertise MY_PUB_IP:5060
listen=tcp:MY_PRIV_IP:5060 advertise MY_PUB_IP:5060
listen=tls:MY_PRIV_IP:5061 advertise MY_FQDN:5061

enable_tls=yes
tcp_connection_lifetime=3605
tls_max_connections=2048

loadmodule "tm.so"
loadmodule "sl.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "textops.so"
loadmodule "textopsx.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "ctl.so"
loadmodule "kex.so"
loadmodule "corex.so"
loadmodule "tls.so"
loadmodule "rtpengine.so"
loadmodule "uac.so"
loadmodule "dispatcher.so"

modparam("tls", "config", "/etc/kamailio/tls.cfg")
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1)
modparam("uac", "restore_mode", "none")

modparam("dispatcher", "list_file", "/etc/kamailio/dispatcher.list")
modparam("dispatcher", "ds_ping_interval", 30)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_threshold", 3)
modparam("dispatcher", "ds_inactive_threshold", 3)
modparam("dispatcher", "ds_probing_mode", 1)

request_route {
    if (!($si=="54.172.60.0" || $si=="54.172.60.1" || $si=="54.172.60.2" ||
$si=="54.172.60.3"
          || $si =~ "^52\.11[2-5]\." || $si =~ "^52\.12[0-3]\.")) {
        xlog("L_WARN","Drop SIP from untrusted $si\n");
        sl_send_reply("403","Forbidden");
        exit;
    }
    if (!mf_process_maxfwd_header("10")) {
        sl_send_reply("483","Too Many Hops");
        exit;
    }
    if (has_totag()) {
        if (loose_route()) {
            route(RELAY);
            exit;
        }
        sl_send_reply("404","Not Here");
        exit;
    }
    if (is_method("ACK")) {
        if (t_check_trans()) { route(RELAY); }
        exit;
    }
    if (is_method("CANCEL")) {
        if (t_check_trans()) { route(RELAY); }
        exit;
    }
    if (is_method("OPTIONS")) {
        sl_send_reply("200","OK");
        exit;
    }
    if (!is_method("INVITE")) {
        sl_send_reply("405","Method Not Allowed");
        exit;
    }

    record_route_preset("sbc.ms-teams-dev.intiveo.com:5061;transport=tls", "
10.0.0.164:5060");

    if ($si =~ "^52\.11[2-5]\." || $si =~ "^52\.12[0-3]\.") {
        route(TO_TWILIO);
    } else {
        route(TO_TEAMS);
    }
    exit;
}

route[TO_TWILIO] {
    rtpengine_manage("replace-origin replace-session-connection RTP");
    sethostport("intiveo-ms-teams-dev.pstn.twilio.com:5060");
    $du = "sip:TWILIO_TRUNK:5060;transport=udp";
    route(RELAY);
}

route[TO_TEAMS] {
    rtpengine_manage("replace-origin replace-session-connection SRTP");
    sethostport("sip.pstnhub.microsoft.com:5061");
    uac_replace_from("sip:$fU@MY_FQDN");
    $du = "sip:sip.pstnhub.microsoft.com:5061;transport=tls";
    t_on_failure("TEAMS_FAILOVER");
    route(RELAY);
}

route[RELAY] {
    if (is_method("INVITE")) {
        t_on_reply("MANAGE_REPLY");
    }
    if (!t_relay()) {
        sl_reply_error();
    }
    exit;
}

failure_route[TEAMS_FAILOVER] {
    if (t_is_canceled()) { exit; }
    if (t_check_status("503|408") || t_branch_timeout()) {
        if ($avp(tdc) == $null) {
            $avp(tdc) = 1;
            $du = "sip:sip2.pstnhub.microsoft.com:5061;transport=tls";
            t_on_failure("TEAMS_FAILOVER");
            t_relay();
            exit;
        } else if ($avp(tdc) == 1) {
            $avp(tdc) = 2;
            $du = "sip:sip3.pstnhub.microsoft.com:5061;transport=tls";
            t_relay();
            exit;
        }
    }
}

onreply_route[MANAGE_REPLY] {
    if (status =~ "[12][0-9][0-9]") {
        rtpengine_manage();
    }
}

event_route[tm:local-request] {
    if (is_method("OPTIONS") && $ru =~ "pstnhub.microsoft.com") {
        append_hf("Contact: <sip:sbc.ms-teams-dev.intiveo.com:5061
;transport=tls>\r\n");
    }
}

(tls.cfg uses method=TLSv1.2, server+client sections both with the
LetsEncrypt cert/key and a custom CA bundle including DigiCert Global
Root CA. dispatcher.list has the three pstnhub proxies, setid 1, with
socket=tls:10.0.0.164:5061;ping_from=sip:sbc.ms-teams-dev.intiveo.com.)

Thanks for any pointers.

George

__________________________________________________________
Kamailio - Users Mailing List - Non Commercial Discussions -- [email protected]
To unsubscribe send an email to [email protected]
Important: keep the mailing list in the recipients, do not reply only to the sender!