From 4e820ed06cd7f61c4bff4d39ba32a48a55d01a64 Mon Sep 17 00:00:00 2001 From: hechen-eng Date: Tue, 31 Mar 2026 15:04:23 -0700 Subject: [PATCH 1/7] TEL-459: route outbound sip traffic through a proxy --- pkg/config/config.go | 5 +++-- pkg/sip/outbound.go | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index e59c9d29..37a06045 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -80,6 +80,7 @@ type Config struct { SIPPort int `yaml:"sip_port"` // announced SIP signaling port SIPPortListen int `yaml:"sip_port_listen"` // SIP signaling port to listen on SIPHostname string `yaml:"sip_hostname"` + SIPProxy string `yaml:"sip_proxy"` // optional SIP proxy address for outbound calls, e.g. "sip-proxy:5060" SIPRingingInterval time.Duration `yaml:"sip_ringing_interval"` // from 1 sec up to 60 (default '1s') TCP *TCPConfig `yaml:"tcp"` TLS *TLSConfig `yaml:"tls"` @@ -87,8 +88,8 @@ type Config struct { Logging logger.Config `yaml:"logging"` ClusterID string `yaml:"cluster_id"` // cluster this instance belongs to MaxCpuUtilization float64 `yaml:"max_cpu_utilization"` - MaxActiveCalls int `yaml:"max_active_calls"` // if set, used for affinity-based routing - SIPTrunkIds []string `yaml:"sip_trunk_ids"` // if set, only accept calls for these trunk IDs + MaxActiveCalls int `yaml:"max_active_calls"` // if set, used for affinity-based routing + SIPTrunkIds []string `yaml:"sip_trunk_ids"` // if set, only accept calls for these trunk IDs UseExternalIP bool `yaml:"use_external_ip"` LocalNet string `yaml:"local_net"` // local IP net to use, e.g. 192.168.0.0/24 diff --git a/pkg/sip/outbound.go b/pkg/sip/outbound.go index e3f65a5c..2dfdae47 100644 --- a/pkg/sip/outbound.go +++ b/pkg/sip/outbound.go @@ -1038,6 +1038,11 @@ func (c *sipOutbound) attemptInvite(ctx context.Context, callID sip.CallIDHeader req.AppendHeader(h) } + if proxy := c.c.conf.SIPProxy; proxy != "" { + req.SetDestination(proxy) + req.SetTransport("TCP") + } + tx, err := c.c.sipCli.TransactionRequest(req) if err != nil { return nil, nil, err From ea4572f868a7932429078f85ac611b762f777782 Mon Sep 17 00:00:00 2001 From: hechen-eng Date: Wed, 1 Apr 2026 12:59:37 -0700 Subject: [PATCH 2/7] depend route header for outbound call route Made-with: Cursor --- pkg/config/config.go | 2 +- pkg/sip/outbound.go | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 37a06045..741de18e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -80,7 +80,7 @@ type Config struct { SIPPort int `yaml:"sip_port"` // announced SIP signaling port SIPPortListen int `yaml:"sip_port_listen"` // SIP signaling port to listen on SIPHostname string `yaml:"sip_hostname"` - SIPProxy string `yaml:"sip_proxy"` // optional SIP proxy address for outbound calls, e.g. "sip-proxy:5060" + RouteHeaders []string `yaml:"route_headers"` // Route headers prepended to outbound requests, e.g. "" SIPRingingInterval time.Duration `yaml:"sip_ringing_interval"` // from 1 sec up to 60 (default '1s') TCP *TCPConfig `yaml:"tcp"` TLS *TLSConfig `yaml:"tls"` diff --git a/pkg/sip/outbound.go b/pkg/sip/outbound.go index 2dfdae47..a6e9af11 100644 --- a/pkg/sip/outbound.go +++ b/pkg/sip/outbound.go @@ -1038,9 +1038,8 @@ func (c *sipOutbound) attemptInvite(ctx context.Context, callID sip.CallIDHeader req.AppendHeader(h) } - if proxy := c.c.conf.SIPProxy; proxy != "" { - req.SetDestination(proxy) - req.SetTransport("TCP") + for _, route := range c.c.conf.RouteHeaders { + req.PrependHeader(sip.NewHeader("Route", route)) } tx, err := c.c.sipCli.TransactionRequest(req) From 5e7a3fa700cb34505d27cd535780a44e01a08008 Mon Sep 17 00:00:00 2001 From: hechen-eng Date: Wed, 1 Apr 2026 14:43:59 -0700 Subject: [PATCH 3/7] add feature flag for route headers --- pkg/sip/features.go | 5 ++++- pkg/sip/outbound.go | 16 ++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/pkg/sip/features.go b/pkg/sip/features.go index 7b3bf4d3..008402c8 100644 --- a/pkg/sip/features.go +++ b/pkg/sip/features.go @@ -1,3 +1,6 @@ package sip -const signalLoggingFeatureFlag = "sip.signal_logging" +const ( + signalLoggingFeatureFlag = "sip.signal_logging" + routeHeadersFeatureFlag = "sip.route_headers" +) diff --git a/pkg/sip/outbound.go b/pkg/sip/outbound.go index a6e9af11..4dd54552 100644 --- a/pkg/sip/outbound.go +++ b/pkg/sip/outbound.go @@ -135,6 +135,9 @@ func (c *Client) newCall(ctx context.Context, tid traceid.ID, conf *config.Confi } return AttrsToHeaders(r.LocalParticipant.Attributes(), c.sipConf.attrsToHeaders, headers) }) + if sipConf.featureFlags[routeHeadersFeatureFlag] == "true" { + call.cc.routeHeaders = conf.RouteHeaders + } call.mon = c.mon.NewCall(stats.Outbound, sipConf.host, sipConf.address) var err error @@ -769,11 +772,12 @@ func (c *Client) newOutbound(log logger.Logger, id LocalTag, from, contact URI, } type sipOutbound struct { - log logger.Logger - c *Client - id LocalTag - from *sip.FromHeader - contact *sip.ContactHeader + log logger.Logger + c *Client + id LocalTag + from *sip.FromHeader + contact *sip.ContactHeader + routeHeaders []string mu sync.RWMutex tag RemoteTag @@ -1038,7 +1042,7 @@ func (c *sipOutbound) attemptInvite(ctx context.Context, callID sip.CallIDHeader req.AppendHeader(h) } - for _, route := range c.c.conf.RouteHeaders { + for _, route := range c.routeHeaders { req.PrependHeader(sip.NewHeader("Route", route)) } From e079e44961252b30c18de044ece53ca9231b39db Mon Sep 17 00:00:00 2001 From: hechen-eng Date: Wed, 1 Apr 2026 16:20:15 -0700 Subject: [PATCH 4/7] naming --- pkg/config/config.go | 32 ++++++++++++++++---------------- pkg/sip/features.go | 4 ++-- pkg/sip/outbound.go | 4 ++-- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 741de18e..e1dd76fd 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -74,22 +74,22 @@ type Config struct { ApiSecret string `yaml:"api_secret"` // required (env LIVEKIT_API_SECRET) WsUrl string `yaml:"ws_url"` // required (env LIVEKIT_WS_URL) - HealthPort int `yaml:"health_port"` - PrometheusPort int `yaml:"prometheus_port"` - PProfPort int `yaml:"pprof_port"` - SIPPort int `yaml:"sip_port"` // announced SIP signaling port - SIPPortListen int `yaml:"sip_port_listen"` // SIP signaling port to listen on - SIPHostname string `yaml:"sip_hostname"` - RouteHeaders []string `yaml:"route_headers"` // Route headers prepended to outbound requests, e.g. "" - SIPRingingInterval time.Duration `yaml:"sip_ringing_interval"` // from 1 sec up to 60 (default '1s') - TCP *TCPConfig `yaml:"tcp"` - TLS *TLSConfig `yaml:"tls"` - RTPPort rtcconfig.PortRange `yaml:"rtp_port"` - Logging logger.Config `yaml:"logging"` - ClusterID string `yaml:"cluster_id"` // cluster this instance belongs to - MaxCpuUtilization float64 `yaml:"max_cpu_utilization"` - MaxActiveCalls int `yaml:"max_active_calls"` // if set, used for affinity-based routing - SIPTrunkIds []string `yaml:"sip_trunk_ids"` // if set, only accept calls for these trunk IDs + HealthPort int `yaml:"health_port"` + PrometheusPort int `yaml:"prometheus_port"` + PProfPort int `yaml:"pprof_port"` + SIPPort int `yaml:"sip_port"` // announced SIP signaling port + SIPPortListen int `yaml:"sip_port_listen"` // SIP signaling port to listen on + SIPHostname string `yaml:"sip_hostname"` + OutboundRouteHeaders []string `yaml:"outbound_route_headers"` // Route headers prepended to outbound requests, e.g. "" + SIPRingingInterval time.Duration `yaml:"sip_ringing_interval"` // from 1 sec up to 60 (default '1s') + TCP *TCPConfig `yaml:"tcp"` + TLS *TLSConfig `yaml:"tls"` + RTPPort rtcconfig.PortRange `yaml:"rtp_port"` + Logging logger.Config `yaml:"logging"` + ClusterID string `yaml:"cluster_id"` // cluster this instance belongs to + MaxCpuUtilization float64 `yaml:"max_cpu_utilization"` + MaxActiveCalls int `yaml:"max_active_calls"` // if set, used for affinity-based routing + SIPTrunkIds []string `yaml:"sip_trunk_ids"` // if set, only accept calls for these trunk IDs UseExternalIP bool `yaml:"use_external_ip"` LocalNet string `yaml:"local_net"` // local IP net to use, e.g. 192.168.0.0/24 diff --git a/pkg/sip/features.go b/pkg/sip/features.go index 008402c8..e27299c9 100644 --- a/pkg/sip/features.go +++ b/pkg/sip/features.go @@ -1,6 +1,6 @@ package sip const ( - signalLoggingFeatureFlag = "sip.signal_logging" - routeHeadersFeatureFlag = "sip.route_headers" + signalLoggingFeatureFlag = "sip.signal_logging" + outboundRouteHeadersFeatureFlag = "sip.outbound_route_headers" ) diff --git a/pkg/sip/outbound.go b/pkg/sip/outbound.go index 4dd54552..25847d63 100644 --- a/pkg/sip/outbound.go +++ b/pkg/sip/outbound.go @@ -135,8 +135,8 @@ func (c *Client) newCall(ctx context.Context, tid traceid.ID, conf *config.Confi } return AttrsToHeaders(r.LocalParticipant.Attributes(), c.sipConf.attrsToHeaders, headers) }) - if sipConf.featureFlags[routeHeadersFeatureFlag] == "true" { - call.cc.routeHeaders = conf.RouteHeaders + if sipConf.featureFlags[outboundRouteHeadersFeatureFlag] == "true" { + call.cc.routeHeaders = conf.OutboundRouteHeaders } call.mon = c.mon.NewCall(stats.Outbound, sipConf.host, sipConf.address) From 6075ebc6345aa6c2769f6e039162962c80b94f13 Mon Sep 17 00:00:00 2001 From: hechen-eng Date: Wed, 1 Apr 2026 22:31:05 -0700 Subject: [PATCH 5/7] fix --- pkg/sip/inbound.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/sip/inbound.go b/pkg/sip/inbound.go index 2409ba32..51ea02c9 100644 --- a/pkg/sip/inbound.go +++ b/pkg/sip/inbound.go @@ -1008,7 +1008,7 @@ func (c *inboundCall) runMediaConn(tid traceid.ID, offerData []byte, enc livekit c.mon.SDPSize(len(answerData), false) c.log().Debugw("SDP answer", "sdp", string(answerData)) - mconf.Processor = c.s.handler.GetMediaProcessor(features, featureFlags, c.call.LkCallId) + mconf.Processor = c.s.handler.GetMediaProcessor(features, featureFlags, string(c.cc.ID())) if err = c.media.SetConfig(mconf); err != nil { return nil, err } From 0bf61a24aa3e9b9aef4f474acc0026a5e600ac0b Mon Sep 17 00:00:00 2001 From: hechen-eng Date: Sat, 4 Apr 2026 19:42:35 -0700 Subject: [PATCH 6/7] bump sipgo --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8dcb6e37..9cf2d11c 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/livekit/protocol v1.45.1 github.com/livekit/psrpc v0.7.1 github.com/livekit/server-sdk-go/v2 v2.16.1 - github.com/livekit/sipgo v0.13.2-0.20260325054441-8dee0d1c8190 + github.com/livekit/sipgo v0.13.2-0.20260405084244-b5e26b6774b4 github.com/mjibson/go-dsp v0.0.0-20180508042940-11479a337f12 github.com/ory/dockertest/v3 v3.12.0 github.com/pion/rtp v1.10.1 diff --git a/go.sum b/go.sum index af7bd89d..a4a935c9 100644 --- a/go.sum +++ b/go.sum @@ -142,8 +142,8 @@ github.com/livekit/psrpc v0.7.1 h1:ms37az0QTD3UXIWuUC5D/SkmKOlRMVRsI261eBWu/Vw= github.com/livekit/psrpc v0.7.1/go.mod h1:bZ4iHFQptTkbPnB0LasvRNu/OBYXEu1NA6O5BMFo9kk= github.com/livekit/server-sdk-go/v2 v2.16.1 h1:ZkIA9OdVvQ6Up1uW/RtQ0YJUgYMJ6+ywOmDg0jX7bTg= github.com/livekit/server-sdk-go/v2 v2.16.1/go.mod h1:oQbYijcbPzfjBAOzoq7tz9Ktqur8JNRCd923VP8xOQQ= -github.com/livekit/sipgo v0.13.2-0.20260325054441-8dee0d1c8190 h1:faSHy5wbIRo9RXYtc4cV7M89GsXbbv69TIdkyVnMH2E= -github.com/livekit/sipgo v0.13.2-0.20260325054441-8dee0d1c8190/go.mod h1:aDa6mbFktNzA1D917RhFlIB5IOfNBTmrwt+/lX960j0= +github.com/livekit/sipgo v0.13.2-0.20260405084244-b5e26b6774b4 h1:a0EzJ/nWtC8LoizDzXYj3eu73/KnxdRGp1wRFCwc4RI= +github.com/livekit/sipgo v0.13.2-0.20260405084244-b5e26b6774b4/go.mod h1:aDa6mbFktNzA1D917RhFlIB5IOfNBTmrwt+/lX960j0= github.com/mackerelio/go-osstat v0.2.5 h1:+MqTbZUhoIt4m8qzkVoXUJg1EuifwlAJSk4Yl2GXh+o= github.com/mackerelio/go-osstat v0.2.5/go.mod h1:atxwWF+POUZcdtR1wnsUcQxTytoHG4uhl2AKKzrOajY= github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= From 8467365aa1c0bda4412ce85efabe64126625ce32 Mon Sep 17 00:00:00 2001 From: hechen-eng Date: Sun, 5 Apr 2026 11:19:02 -0700 Subject: [PATCH 7/7] update sipgo --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9cf2d11c..dda109d8 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/livekit/protocol v1.45.1 github.com/livekit/psrpc v0.7.1 github.com/livekit/server-sdk-go/v2 v2.16.1 - github.com/livekit/sipgo v0.13.2-0.20260405084244-b5e26b6774b4 + github.com/livekit/sipgo v0.13.2-0.20260405181755-35e98b69cf07 github.com/mjibson/go-dsp v0.0.0-20180508042940-11479a337f12 github.com/ory/dockertest/v3 v3.12.0 github.com/pion/rtp v1.10.1 diff --git a/go.sum b/go.sum index a4a935c9..6ef8e985 100644 --- a/go.sum +++ b/go.sum @@ -142,8 +142,8 @@ github.com/livekit/psrpc v0.7.1 h1:ms37az0QTD3UXIWuUC5D/SkmKOlRMVRsI261eBWu/Vw= github.com/livekit/psrpc v0.7.1/go.mod h1:bZ4iHFQptTkbPnB0LasvRNu/OBYXEu1NA6O5BMFo9kk= github.com/livekit/server-sdk-go/v2 v2.16.1 h1:ZkIA9OdVvQ6Up1uW/RtQ0YJUgYMJ6+ywOmDg0jX7bTg= github.com/livekit/server-sdk-go/v2 v2.16.1/go.mod h1:oQbYijcbPzfjBAOzoq7tz9Ktqur8JNRCd923VP8xOQQ= -github.com/livekit/sipgo v0.13.2-0.20260405084244-b5e26b6774b4 h1:a0EzJ/nWtC8LoizDzXYj3eu73/KnxdRGp1wRFCwc4RI= -github.com/livekit/sipgo v0.13.2-0.20260405084244-b5e26b6774b4/go.mod h1:aDa6mbFktNzA1D917RhFlIB5IOfNBTmrwt+/lX960j0= +github.com/livekit/sipgo v0.13.2-0.20260405181755-35e98b69cf07 h1:BW7XseN+9RhH14gQm7EFxqal61XRUvg6xlE/jROwiD0= +github.com/livekit/sipgo v0.13.2-0.20260405181755-35e98b69cf07/go.mod h1:aDa6mbFktNzA1D917RhFlIB5IOfNBTmrwt+/lX960j0= github.com/mackerelio/go-osstat v0.2.5 h1:+MqTbZUhoIt4m8qzkVoXUJg1EuifwlAJSk4Yl2GXh+o= github.com/mackerelio/go-osstat v0.2.5/go.mod h1:atxwWF+POUZcdtR1wnsUcQxTytoHG4uhl2AKKzrOajY= github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=