From a328c32b14a257eaf0997a48ce2297fbe6897092 Mon Sep 17 00:00:00 2001 From: hechen-eng Date: Wed, 6 May 2026 15:26:02 -0700 Subject: [PATCH 1/2] fix SIP trunk-level MediaEncryption being silently dropped --- .changeset/sip-fix-trunk-encryption.md | 6 ++++++ rpc/sip.go | 1 - rpc/sip_test.go | 25 +++++++++++++++++++++++ sip/sip.go | 1 - sip/sip_test.go | 28 ++++++++++++++++++++++++++ 5 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 .changeset/sip-fix-trunk-encryption.md diff --git a/.changeset/sip-fix-trunk-encryption.md b/.changeset/sip-fix-trunk-encryption.md new file mode 100644 index 000000000..f1838449f --- /dev/null +++ b/.changeset/sip-fix-trunk-encryption.md @@ -0,0 +1,6 @@ +--- +"@livekit/protocol": patch +"github.com/livekit/protocol": patch +--- + +Fix SIP trunk-level MediaEncryption being silently dropped on outbound and inbound calls. The early `req.Upgrade()` / `rule.Upgrade()` calls pinned `Media.Encryption` to the (legacy) request/rule field before the trunk's MediaEncryption was merged, causing INVITEs to omit SRTP when only the trunk had it configured. diff --git a/rpc/sip.go b/rpc/sip.go index 7fc8dbe57..1b1ae1c2b 100644 --- a/rpc/sip.go +++ b/rpc/sip.go @@ -73,7 +73,6 @@ func NewCreateSIPParticipantRequest( req *livekit.CreateSIPParticipantRequest, trunk *livekit.SIPOutboundTrunkInfo, ) (*InternalCreateSIPParticipantRequest, error) { - req.Upgrade() if err := req.Validate(); err != nil { return nil, err } diff --git a/rpc/sip_test.go b/rpc/sip_test.go index f7d6fa5a1..cb5a81078 100644 --- a/rpc/sip_test.go +++ b/rpc/sip_test.go @@ -150,3 +150,28 @@ func TestNewCreateSIPParticipantRequest(t *testing.T) { require.NoError(t, err) require.True(t, proto.Equal(exp, res), "%v\nvs\n%v", exp, res) } + +// Regression: trunk-level MediaEncryption must be honored when the request specifies +// neither MediaEncryption nor Media. A prior version called req.Upgrade() at the top of +// NewCreateSIPParticipantRequest, which pinned req.Media.Encryption to req.MediaEncryption (0) +// before the trunk was consulted, causing outbound INVITEs to omit SRTP and upstream +// providers (e.g. Twilio) to reject with 488 / 32208. +func TestNewCreateSIPParticipantRequest_TrunkOnlyEncryption(t *testing.T) { + r := &livekit.CreateSIPParticipantRequest{ + SipTrunkId: "trunk", + SipCallTo: "+3333", + RoomName: "room", + } + tr := &livekit.SIPOutboundTrunkInfo{ + SipTrunkId: "trunk", + Address: "sip.example.com", + Numbers: []string{"+1111"}, + MediaEncryption: livekit.SIPMediaEncryption_SIP_MEDIA_ENCRYPT_REQUIRE, + } + res, err := NewCreateSIPParticipantRequest("p_123", "call-id", "xyz.sip.livekit.cloud", "url", "token", r, tr) + require.NoError(t, err) + require.Equal(t, livekit.SIPMediaEncryption_SIP_MEDIA_ENCRYPT_REQUIRE, res.MediaEncryption) + require.NotNil(t, res.Media) + require.NotNil(t, res.Media.Encryption) + require.Equal(t, livekit.SIPMediaEncryption_SIP_MEDIA_ENCRYPT_REQUIRE, *res.Media.Encryption) +} diff --git a/sip/sip.go b/sip/sip.go index 5cc741e1a..2c97a521e 100644 --- a/sip/sip.go +++ b/sip/sip.go @@ -834,7 +834,6 @@ func MatchDispatchRuleIter(trunk *livekit.SIPInboundTrunkInfo, rules iters.Iter[ // EvaluateDispatchRule checks a selected Dispatch Rule against the provided request. func EvaluateDispatchRule(projectID string, trunk *livekit.SIPInboundTrunkInfo, rule *livekit.SIPDispatchRuleInfo, req *rpc.EvaluateSIPDispatchRulesRequest) (*rpc.EvaluateSIPDispatchRulesResponse, error) { - rule.Upgrade() call := req.SIPCall() sentPin := req.GetPin() diff --git a/sip/sip_test.go b/sip/sip_test.go index 37bc0f703..53adf41f6 100644 --- a/sip/sip_test.go +++ b/sip/sip_test.go @@ -933,6 +933,34 @@ func TestEvaluateDispatchRule(t *testing.T) { }) } +// Regression: trunk-level MediaEncryption must be honored when the dispatch rule specifies +// neither MediaEncryption nor Media. A prior version called rule.Upgrade() at the top of +// EvaluateDispatchRule, which pinned rule.Media.Encryption to rule.MediaEncryption (0) +// before the trunk was consulted, causing the inbound trunk's encryption setting to be +// silently dropped. +func TestEvaluateDispatchRule_TrunkOnlyEncryption(t *testing.T) { + d := &livekit.SIPDispatchRuleInfo{ + SipDispatchRuleId: "rule", + Rule: newDirectDispatch("room", ""), + } + r := &rpc.EvaluateSIPDispatchRulesRequest{ + SipCallId: "call-id", + CallingNumber: "+11112222", + CallingHost: "sip.example.com", + CalledNumber: "+3333", + } + tr := &livekit.SIPInboundTrunkInfo{ + SipTrunkId: "trunk", + MediaEncryption: livekit.SIPMediaEncryption_SIP_MEDIA_ENCRYPT_REQUIRE, + } + res, err := EvaluateDispatchRule("p_123", tr, d, r) + require.NoError(t, err) + require.Equal(t, livekit.SIPMediaEncryption_SIP_MEDIA_ENCRYPT_REQUIRE, res.MediaEncryption) + require.NotNil(t, res.Media) + require.NotNil(t, res.Media.Encryption) + require.Equal(t, livekit.SIPMediaEncryption_SIP_MEDIA_ENCRYPT_REQUIRE, *res.Media.Encryption) +} + func TestMatchIP(t *testing.T) { cases := []struct { addr string From 9906ff07c6bb4b1b8b4124ca2d9f388f806dc024 Mon Sep 17 00:00:00 2001 From: Denys Smirnov Date: Thu, 7 May 2026 09:55:47 +0200 Subject: [PATCH 2/2] Ignore zero value when upgrading to a new SIP media config. --- livekit/sip.go | 3 ++- rpc/sip.go | 1 + sip/sip.go | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/livekit/sip.go b/livekit/sip.go index e16074e1a..64e110644 100644 --- a/livekit/sip.go +++ b/livekit/sip.go @@ -1081,7 +1081,8 @@ func (p *SIPMediaConfig) UpgradeWith(enc SIPMediaEncryption) *SIPMediaConfig { if p == nil { p = new(SIPMediaConfig) } - if p.Encryption == nil { + // Ignore DISABLE as it's a zero value which means "unset". + if p.Encryption == nil && enc != SIPMediaEncryption_SIP_MEDIA_ENCRYPT_DISABLE { p.Encryption = &enc } return p diff --git a/rpc/sip.go b/rpc/sip.go index 1b1ae1c2b..7fc8dbe57 100644 --- a/rpc/sip.go +++ b/rpc/sip.go @@ -73,6 +73,7 @@ func NewCreateSIPParticipantRequest( req *livekit.CreateSIPParticipantRequest, trunk *livekit.SIPOutboundTrunkInfo, ) (*InternalCreateSIPParticipantRequest, error) { + req.Upgrade() if err := req.Validate(); err != nil { return nil, err } diff --git a/sip/sip.go b/sip/sip.go index 2c97a521e..5cc741e1a 100644 --- a/sip/sip.go +++ b/sip/sip.go @@ -834,6 +834,7 @@ func MatchDispatchRuleIter(trunk *livekit.SIPInboundTrunkInfo, rules iters.Iter[ // EvaluateDispatchRule checks a selected Dispatch Rule against the provided request. func EvaluateDispatchRule(projectID string, trunk *livekit.SIPInboundTrunkInfo, rule *livekit.SIPDispatchRuleInfo, req *rpc.EvaluateSIPDispatchRulesRequest) (*rpc.EvaluateSIPDispatchRulesResponse, error) { + rule.Upgrade() call := req.SIPCall() sentPin := req.GetPin()