diff --git a/go.mod b/go.mod index a86c654..b89b392 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,12 @@ toolchain go1.24.2 require ( github.com/gammazero/deque v1.0.0 github.com/livekit/protocol v1.38.1-0.20250511053429-f8ea8179871e - github.com/pion/ice/v4 v4.2.0 + github.com/pion/ice/v4 v4.2.6 github.com/pion/interceptor v0.1.43 github.com/pion/logging v0.2.4 github.com/pion/rtcp v1.2.16 github.com/pion/rtp v1.10.0 - github.com/pion/stun/v3 v3.1.1 + github.com/pion/stun/v3 v3.1.2 github.com/pion/transport/v4 v4.0.1 github.com/pion/webrtc/v4 v4.2.3 github.com/pkg/errors v0.9.1 @@ -28,13 +28,13 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/pion/datachannel v1.6.0 // indirect - github.com/pion/dtls/v3 v3.0.10 // indirect + github.com/pion/dtls/v3 v3.1.2 // indirect github.com/pion/mdns/v2 v2.1.0 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/sctp v1.9.2 // indirect github.com/pion/sdp/v3 v3.0.17 // indirect github.com/pion/srtp/v3 v3.0.10 // indirect - github.com/pion/turn/v4 v4.1.4 // indirect + github.com/pion/turn/v5 v5.0.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/puzpuzpuz/xsync/v3 v3.5.0 // indirect github.com/wlynxg/anet v0.0.5 // indirect @@ -44,7 +44,7 @@ require ( go.uber.org/zap/exp v0.3.0 // indirect golang.org/x/crypto v0.47.0 // indirect golang.org/x/net v0.49.0 // indirect - golang.org/x/sys v0.40.0 // indirect + golang.org/x/sys v0.41.0 // indirect golang.org/x/time v0.14.0 // indirect google.golang.org/protobuf v1.36.5 // indirect ) diff --git a/go.sum b/go.sum index c7663c8..9602542 100644 --- a/go.sum +++ b/go.sum @@ -18,10 +18,10 @@ github.com/livekit/protocol v1.38.1-0.20250511053429-f8ea8179871e h1:qa0NFwLRJy0 github.com/livekit/protocol v1.38.1-0.20250511053429-f8ea8179871e/go.mod h1:JpubNKJFmZuTksypbvFI0qmYxzTgR5+3sw3GM0JyYAA= github.com/pion/datachannel v1.6.0 h1:XecBlj+cvsxhAMZWFfFcPyUaDZtd7IJvrXqlXD/53i0= github.com/pion/datachannel v1.6.0/go.mod h1:ur+wzYF8mWdC+Mkis5Thosk+u/VOL287apDNEbFpsIk= -github.com/pion/dtls/v3 v3.0.10 h1:k9ekkq1kaZoxnNEbyLKI8DI37j/Nbk1HWmMuywpQJgg= -github.com/pion/dtls/v3 v3.0.10/go.mod h1:YEmmBYIoBsY3jmG56dsziTv/Lca9y4Om83370CXfqJ8= -github.com/pion/ice/v4 v4.2.0 h1:jJC8S+CvXCCvIQUgx+oNZnoUpt6zwc34FhjWwCU4nlw= -github.com/pion/ice/v4 v4.2.0/go.mod h1:EgjBGxDgmd8xB0OkYEVFlzQuEI7kWSCFu+mULqaisy4= +github.com/pion/dtls/v3 v3.1.2 h1:gqEdOUXLtCGW+afsBLO0LtDD8GnuBBjEy6HRtyofZTc= +github.com/pion/dtls/v3 v3.1.2/go.mod h1:Hw/igcX4pdY69z1Hgv5x7wJFrUkdgHwAn/Q/uo7YHRo= +github.com/pion/ice/v4 v4.2.6 h1:17SKPTupOmumCMAQ4ntf8rv6q6XEfVzuz1gSFrnhzPE= +github.com/pion/ice/v4 v4.2.6/go.mod h1:tmp90fBKpZhQDkHkp/QJb+Gn8vhMxuAcMq7PfhQWQHE= github.com/pion/interceptor v0.1.43 h1:6hmRfnmjogSs300xfkR0JxYFZ9k5blTEvCD7wxEDuNQ= github.com/pion/interceptor v0.1.43/go.mod h1:BSiC1qKIJt1XVr3l3xQ2GEmCFStk9tx8fwtCZxxgR7M= github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= @@ -40,14 +40,16 @@ github.com/pion/sdp/v3 v3.0.17 h1:9SfLAW/fF1XC8yRqQ3iWGzxkySxup4k4V7yN8Fs8nuo= github.com/pion/sdp/v3 v3.0.17/go.mod h1:9tyKzznud3qiweZcD86kS0ff1pGYB3VX+Bcsmkx6IXo= github.com/pion/srtp/v3 v3.0.10 h1:tFirkpBb3XccP5VEXLi50GqXhv5SKPxqrdlhDCJlZrQ= github.com/pion/srtp/v3 v3.0.10/go.mod h1:3mOTIB0cq9qlbn59V4ozvv9ClW/BSEbRp4cY0VtaR7M= -github.com/pion/stun/v3 v3.1.1 h1:CkQxveJ4xGQjulGSROXbXq94TAWu8gIX2dT+ePhUkqw= -github.com/pion/stun/v3 v3.1.1/go.mod h1:qC1DfmcCTQjl9PBaMa5wSn3x9IPmKxSdcCsxBcDBndM= +github.com/pion/stun/v3 v3.1.2 h1:86IhD8wFn6IDW4b1/0QzoQS+f5PeA8OHHRn8UZW5ErY= +github.com/pion/stun/v3 v3.1.2/go.mod h1:H7gDic7nNwlUL05pbs6T1dtaBehh/KjupxfWw3ZI7cA= github.com/pion/transport/v3 v3.1.1 h1:Tr684+fnnKlhPceU+ICdrw6KKkTms+5qHMgw6bIkYOM= github.com/pion/transport/v3 v3.1.1/go.mod h1:+c2eewC5WJQHiAA46fkMMzoYZSuGzA/7E2FPrOYHctQ= github.com/pion/transport/v4 v4.0.1 h1:sdROELU6BZ63Ab7FrOLn13M6YdJLY20wldXW2Cu2k8o= github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/YqmEx3ovP4jM= github.com/pion/turn/v4 v4.1.4 h1:EU11yMXKIsK43FhcUnjLlrhE4nboHZq+TXBIi3QpcxQ= github.com/pion/turn/v4 v4.1.4/go.mod h1:ES1DXVFKnOhuDkqn9hn5VJlSWmZPaRJLyBXoOeO/BmQ= +github.com/pion/turn/v5 v5.0.4 h1:xKAnP1b5eCnjFPd55OgxkqIVoyzbHKZa06SxZ3fopXQ= +github.com/pion/turn/v5 v5.0.4/go.mod h1:zbPsMp+fIVhKt5uWu2jcjk88FcoRbGMBSzVsaTuhcmM= github.com/pion/webrtc/v4 v4.2.3 h1:RtdWDnkenNQGxUrZqWa5gSkTm5ncsLg5d+zu0M4cXt4= github.com/pion/webrtc/v4 v4.2.3/go.mod h1:7vsyFzRzaKP5IELUnj8zLcglPyIT6wWwqTppBZ1k6Kc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -81,8 +83,8 @@ golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbD golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= diff --git a/pkg/rtcconfig/config.go b/pkg/rtcconfig/config.go index 9502428..2aaf22b 100644 --- a/pkg/rtcconfig/config.go +++ b/pkg/rtcconfig/config.go @@ -42,20 +42,22 @@ var DefaultStunServers = []string{ } type RTCConfig struct { - UDPPort PortRange `yaml:"udp_port,omitempty"` - TCPPort uint32 `yaml:"tcp_port,omitempty"` - ICEPortRangeStart uint32 `yaml:"port_range_start,omitempty"` - ICEPortRangeEnd uint32 `yaml:"port_range_end,omitempty"` - UseStunPortAsICE bool `yaml:"use_stun_port_as_ice,omitempty"` - NodeIP NodeIP `yaml:"node_ip,omitempty"` - NodeIPAutoGenerated bool `yaml:"-"` - STUNServers []string `yaml:"stun_servers,omitempty"` - UseExternalIP bool `yaml:"use_external_ip"` - UseICELite bool `yaml:"use_ice_lite,omitempty"` - Interfaces InterfacesConfig `yaml:"interfaces,omitempty"` - IPs IPsConfig `yaml:"ips,omitempty"` - EnableLoopbackCandidate bool `yaml:"enable_loopback_candidate"` - UseMDNS bool `yaml:"use_mdns,omitempty"` + UDPPort PortRange `yaml:"udp_port,omitempty"` + TCPPort uint32 `yaml:"tcp_port,omitempty"` + ICEPortRangeStart uint32 `yaml:"port_range_start,omitempty"` + ICEPortRangeEnd uint32 `yaml:"port_range_end,omitempty"` + UseStunPortAsICE bool `yaml:"use_stun_port_as_ice,omitempty"` + NodeIP NodeIP `yaml:"node_ip,omitempty"` + NodeIPAutoGenerated bool `yaml:"-"` + STUNServers []string `yaml:"stun_servers,omitempty"` + UseExternalIP bool `yaml:"use_external_ip"` + SkipExternalIPValidation bool `yaml:"skip_external_ip_validation,omitempty"` + AdvertiseInternalIP bool `yaml:"advertise_internal_ip,omitempty"` + UseICELite bool `yaml:"use_ice_lite,omitempty"` + Interfaces InterfacesConfig `yaml:"interfaces,omitempty"` + IPs IPsConfig `yaml:"ips,omitempty"` + EnableLoopbackCandidate bool `yaml:"enable_loopback_candidate"` + UseMDNS bool `yaml:"use_mdns,omitempty"` // when UseExternalIP is true, only advertise the external IP to client ExternalIPOnly bool `yaml:"external_ip_only,omitempty"` BatchIO BatchIOConfig `yaml:"batch_io,omitempty"` diff --git a/pkg/rtcconfig/ip.go b/pkg/rtcconfig/ip.go index 2cc5d28..28c58f1 100644 --- a/pkg/rtcconfig/ip.go +++ b/pkg/rtcconfig/ip.go @@ -130,7 +130,7 @@ func GetLocalIPAddresses(includeLoopback bool, includeV6 bool, ifFilter func(str return nil, fmt.Errorf("could not find local IP address") } -func findExternalIP(ctx context.Context, stunServer string, localAddr net.Addr) (string, error) { +func findExternalIP(ctx context.Context, stunServer string, localAddr net.Addr, validate bool) (string, error) { ctx1, cancel1 := context.WithTimeout(ctx, stunPingTimeout) defer cancel1() @@ -213,13 +213,21 @@ func findExternalIP(ctx context.Context, stunServer string, localAddr net.Addr) "localAddr", localAddr, "stunServer", stunServer, "externalIP", ipAddr, + "validateExternalIP", validate, ) + if !validate { + return ipAddr, nil + } return ipAddr, validateExternalIP(ctx, ipAddr, localAddr) } // GetExternalIP return external IP for localAddr from stun server. If localAddr is nil, a local address is chosen automatically, // else the address will be used to validate the external IP is accessible from the outside. func GetExternalIP(ctx context.Context, stunServers []string, localAddr net.Addr) (string, error) { + return getExternalIP(ctx, stunServers, localAddr, true) +} + +func getExternalIP(ctx context.Context, stunServers []string, localAddr net.Addr, validate bool) (string, error) { if len(stunServers) == 0 { return "", errors.New("STUN servers are required but not defined") } @@ -230,7 +238,7 @@ func GetExternalIP(ctx context.Context, stunServers []string, localAddr net.Addr var err error for _, ss := range stunServers { var ipAddr string - ipAddr, err = findExternalIP(ctx1, ss, localAddr) + ipAddr, err = findExternalIP(ctx1, ss, localAddr, validate) if err == nil { return ipAddr, nil } diff --git a/pkg/rtcconfig/webrtc_config.go b/pkg/rtcconfig/webrtc_config.go index 188e74c..55b1663 100644 --- a/pkg/rtcconfig/webrtc_config.go +++ b/pkg/rtcconfig/webrtc_config.go @@ -98,18 +98,18 @@ func NewWebRTCConfig(rtcConf *RTCConfig, development bool) (*WebRTCConfig, error s.SetIPFilter(ipFilter) if len(ips) == 0 { logger.Infow("no external IPs found, using node IP for NAT1To1Ips", "ip", rtcConf.NodeIP) - if err := SetNAT1To1AddressRewriteRules(&s, rtcConf.NodeIP.ToStringSlice(), webrtc.ICECandidateTypeHost); err != nil { + if err := SetNAT1To1AddressRewriteRules(&s, rtcConf.NodeIP.ToStringSlice(), false); err != nil { return nil, err } } else { - logger.Infow("using external IPs", "ips", ips) - if err := SetNAT1To1AddressRewriteRules(&s, ips, webrtc.ICECandidateTypeHost); err != nil { + logger.Infow("using external IPs", "ips", ips, "advertiseInternalIP", rtcConf.AdvertiseInternalIP) + if err := SetNAT1To1AddressRewriteRules(&s, ips, rtcConf.AdvertiseInternalIP); err != nil { return nil, err } } nat1to1IPs = ips } else { - if err := SetNAT1To1AddressRewriteRules(&s, rtcConf.NodeIP.ToStringSlice(), webrtc.ICECandidateTypeHost); err != nil { + if err := SetNAT1To1AddressRewriteRules(&s, rtcConf.NodeIP.ToStringSlice(), false); err != nil { return nil, err } } @@ -236,18 +236,22 @@ func NewWebRTCConfig(rtcConf *RTCConfig, development bool) (*WebRTCConfig, error }, nil } -func SetNAT1To1AddressRewriteRules(s *webrtc.SettingEngine, ips []string, candidateType webrtc.ICECandidateType) error { +func SetNAT1To1AddressRewriteRules(s *webrtc.SettingEngine, ips []string, includeInternal bool) error { rules := make([]webrtc.ICEAddressRewriteRule, 0, len(ips)+1) catchAll := make([]string, 0, len(ips)) + mode := webrtc.ICEAddressRewriteModeUnspecified + if includeInternal { + mode = webrtc.ICEAddressRewriteAppend + } for _, ip := range ips { if parts := strings.Split(ip, "/"); len(parts) == 2 { rules = append(rules, webrtc.ICEAddressRewriteRule{ External: []string{parts[0]}, Local: parts[1], - AsCandidateType: candidateType, + AsCandidateType: webrtc.ICECandidateTypeHost, + Mode: mode, }) - catchAll = append(catchAll, parts[0]) } else { catchAll = append(catchAll, ip) } @@ -255,7 +259,7 @@ func SetNAT1To1AddressRewriteRules(s *webrtc.SettingEngine, ips []string, candid if len(catchAll) > 0 { rules = append(rules, webrtc.ICEAddressRewriteRule{ External: catchAll, - AsCandidateType: candidateType, + AsCandidateType: webrtc.ICECandidateTypeHost, }) } @@ -304,7 +308,7 @@ func getNAT1to1IPsForConf(rtcConf *RTCConfig, ifFilter func(string) bool, ipFilt go func(localIP string) { defer wg.Done() for _, port := range udpPorts { - addr, err := GetExternalIP(ctx, stunServers, &net.UDPAddr{IP: net.ParseIP(localIP), Port: port}) + addr, err := getExternalIP(ctx, stunServers, &net.UDPAddr{IP: net.ParseIP(localIP), Port: port}, !rtcConf.SkipExternalIPValidation) if err != nil { if strings.Contains(err.Error(), "address already in use") { logger.Infow("failed to get external ip, address already in use", "local", localIP, "port", port)