|
@@ -146,12 +146,6 @@ type webRTCConfig struct {
|
|
|
ReliableTransport bool
|
|
ReliableTransport bool
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// webRTCSDPMetrics are network capability metrics values for an SDP.
|
|
|
|
|
-type webRTCSDPMetrics struct {
|
|
|
|
|
- iceCandidateTypes []ICECandidateType
|
|
|
|
|
- hasIPv6 bool
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
// newWebRTCConnWithOffer initiates a new WebRTC connection. An offer SDP is
|
|
// newWebRTCConnWithOffer initiates a new WebRTC connection. An offer SDP is
|
|
|
// returned, to be sent to the peer. After the offer SDP is forwarded and an
|
|
// returned, to be sent to the peer. After the offer SDP is forwarded and an
|
|
|
// answer SDP received in response, call SetRemoteSDP with the answer SDP and
|
|
// answer SDP received in response, call SetRemoteSDP with the answer SDP and
|
|
@@ -1528,31 +1522,37 @@ func prepareSDPAddresses(
|
|
|
encodedSDP,
|
|
encodedSDP,
|
|
|
portMappingExternalAddr,
|
|
portMappingExternalAddr,
|
|
|
disableIPv6Candidates,
|
|
disableIPv6Candidates,
|
|
|
- false, // bogons are expected, and stripped out
|
|
|
|
|
errorOnNoCandidates,
|
|
errorOnNoCandidates,
|
|
|
nil,
|
|
nil,
|
|
|
common.GeoIPData{})
|
|
common.GeoIPData{})
|
|
|
return modifiedSDP, metrics, errors.Trace(err)
|
|
return modifiedSDP, metrics, errors.Trace(err)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// validateSDPAddresses checks that the SDP does not contain an empty list of
|
|
|
|
|
|
|
+// filterSDPAddresses checks that the SDP does not contain an empty list of
|
|
|
// candidates, bogon candidates, or candidates outside of the country and ASN
|
|
// candidates, bogon candidates, or candidates outside of the country and ASN
|
|
|
-// for the specified expectedGeoIPData.
|
|
|
|
|
-func validateSDPAddresses(
|
|
|
|
|
|
|
+// for the specified expectedGeoIPData. Invalid candidates are stripped and a
|
|
|
|
|
+// filtered SDP is returned.
|
|
|
|
|
+func filterSDPAddresses(
|
|
|
encodedSDP []byte,
|
|
encodedSDP []byte,
|
|
|
errorOnNoCandidates bool,
|
|
errorOnNoCandidates bool,
|
|
|
lookupGeoIP LookupGeoIP,
|
|
lookupGeoIP LookupGeoIP,
|
|
|
- expectedGeoIPData common.GeoIPData) (*webRTCSDPMetrics, error) {
|
|
|
|
|
|
|
+ expectedGeoIPData common.GeoIPData) ([]byte, *webRTCSDPMetrics, error) {
|
|
|
|
|
|
|
|
- _, metrics, err := processSDPAddresses(
|
|
|
|
|
|
|
+ filteredSDP, metrics, err := processSDPAddresses(
|
|
|
encodedSDP,
|
|
encodedSDP,
|
|
|
"",
|
|
"",
|
|
|
false,
|
|
false,
|
|
|
- true, // bogons should already by stripped out
|
|
|
|
|
errorOnNoCandidates,
|
|
errorOnNoCandidates,
|
|
|
lookupGeoIP,
|
|
lookupGeoIP,
|
|
|
expectedGeoIPData)
|
|
expectedGeoIPData)
|
|
|
- return metrics, errors.Trace(err)
|
|
|
|
|
|
|
+ return filteredSDP, metrics, errors.Trace(err)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// webRTCSDPMetrics are network capability metrics values for an SDP.
|
|
|
|
|
+type webRTCSDPMetrics struct {
|
|
|
|
|
+ iceCandidateTypes []ICECandidateType
|
|
|
|
|
+ hasIPv6 bool
|
|
|
|
|
+ filteredICECandidates []string
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// processSDPAddresses is based on snowflake/common/util.StripLocalAddresses
|
|
// processSDPAddresses is based on snowflake/common/util.StripLocalAddresses
|
|
@@ -1592,12 +1592,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
================================================================================
|
|
================================================================================
|
|
|
|
|
|
|
|
*/
|
|
*/
|
|
|
-
|
|
|
|
|
func processSDPAddresses(
|
|
func processSDPAddresses(
|
|
|
encodedSDP []byte,
|
|
encodedSDP []byte,
|
|
|
portMappingExternalAddr string,
|
|
portMappingExternalAddr string,
|
|
|
disableIPv6Candidates bool,
|
|
disableIPv6Candidates bool,
|
|
|
- errorOnBogon bool,
|
|
|
|
|
errorOnNoCandidates bool,
|
|
errorOnNoCandidates bool,
|
|
|
lookupGeoIP LookupGeoIP,
|
|
lookupGeoIP LookupGeoIP,
|
|
|
expectedGeoIPData common.GeoIPData) ([]byte, *webRTCSDPMetrics, error) {
|
|
expectedGeoIPData common.GeoIPData) ([]byte, *webRTCSDPMetrics, error) {
|
|
@@ -1610,11 +1608,13 @@ func processSDPAddresses(
|
|
|
|
|
|
|
|
candidateTypes := map[ICECandidateType]bool{}
|
|
candidateTypes := map[ICECandidateType]bool{}
|
|
|
hasIPv6 := false
|
|
hasIPv6 := false
|
|
|
|
|
+ filteredCandidateReasons := make(map[string]int)
|
|
|
|
|
|
|
|
var portMappingICECandidates []sdp.Attribute
|
|
var portMappingICECandidates []sdp.Attribute
|
|
|
if portMappingExternalAddr != "" {
|
|
if portMappingExternalAddr != "" {
|
|
|
|
|
|
|
|
- // Prepare ICE candidate attibute pair for the port mapping, modeled after the definition of host candidates.
|
|
|
|
|
|
|
+ // Prepare ICE candidate attibute pair for the port mapping, modeled
|
|
|
|
|
+ // after the definition of host candidates.
|
|
|
|
|
|
|
|
host, portStr, err := net.SplitHostPort(portMappingExternalAddr)
|
|
host, portStr, err := net.SplitHostPort(portMappingExternalAddr)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -1631,7 +1631,7 @@ func processSDPAddresses(
|
|
|
|
|
|
|
|
for _, component := range []webrtc.ICEComponent{webrtc.ICEComponentRTP, webrtc.ICEComponentRTCP} {
|
|
for _, component := range []webrtc.ICEComponent{webrtc.ICEComponentRTP, webrtc.ICEComponentRTCP} {
|
|
|
|
|
|
|
|
- // The candidate ID is generated and the priorty and foundation
|
|
|
|
|
|
|
+ // The candidate ID is generated and the priority and foundation
|
|
|
// use the default for hosts.
|
|
// use the default for hosts.
|
|
|
//
|
|
//
|
|
|
// Limitation: NewCandidateHost initializes the networkType to
|
|
// Limitation: NewCandidateHost initializes the networkType to
|
|
@@ -1692,25 +1692,29 @@ func processSDPAddresses(
|
|
|
candidateIsIPv6 := false
|
|
candidateIsIPv6 := false
|
|
|
if candidateIP.To4() == nil {
|
|
if candidateIP.To4() == nil {
|
|
|
if disableIPv6Candidates {
|
|
if disableIPv6Candidates {
|
|
|
|
|
+ reason := "disabled IPv6"
|
|
|
|
|
+ filteredCandidateReasons[reason] += 1
|
|
|
continue
|
|
continue
|
|
|
}
|
|
}
|
|
|
candidateIsIPv6 = true
|
|
candidateIsIPv6 = true
|
|
|
- hasIPv6 = true
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Strip non-routable bogons, including LAN addresses.
|
|
// Strip non-routable bogons, including LAN addresses.
|
|
|
// Same-LAN client/proxy hops are not expected to be useful,
|
|
// Same-LAN client/proxy hops are not expected to be useful,
|
|
|
// and this also avoids unnecessary local network traffic.
|
|
// and this also avoids unnecessary local network traffic.
|
|
|
//
|
|
//
|
|
|
- // Well-behaved clients and proxies will strip these values;
|
|
|
|
|
- // the broker enforces this and uses errorOnBogon.
|
|
|
|
|
|
|
+ // Well-behaved clients and proxies should strip these values;
|
|
|
|
|
+ // the broker enforces this with filtering.
|
|
|
|
|
|
|
|
if !GetAllowBogonWebRTCConnections() &&
|
|
if !GetAllowBogonWebRTCConnections() &&
|
|
|
common.IsBogon(candidateIP) {
|
|
common.IsBogon(candidateIP) {
|
|
|
|
|
|
|
|
- if errorOnBogon {
|
|
|
|
|
- return nil, nil, errors.TraceNew("unexpected bogon")
|
|
|
|
|
|
|
+ version := "IPv4"
|
|
|
|
|
+ if candidateIsIPv6 {
|
|
|
|
|
+ version = "IPv6"
|
|
|
}
|
|
}
|
|
|
|
|
+ reason := fmt.Sprintf("bogon %s", version)
|
|
|
|
|
+ filteredCandidateReasons[reason] += 1
|
|
|
continue
|
|
continue
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1721,6 +1725,16 @@ func processSDPAddresses(
|
|
|
// Legitimate candidates will not all have the exact same IP
|
|
// Legitimate candidates will not all have the exact same IP
|
|
|
// address, as there could be a mix of IPv4 and IPv6, as well
|
|
// address, as there could be a mix of IPv4 and IPv6, as well
|
|
|
// as potentially different NAT paths.
|
|
// as potentially different NAT paths.
|
|
|
|
|
+ //
|
|
|
|
|
+ // In some cases, legitimate clients and proxies may
|
|
|
|
|
+ // unintentionally submit candidates with mismatching GeoIP.
|
|
|
|
|
+ // This can occur, for example, when a STUN candidate is only
|
|
|
|
|
+ // a partial hole punch through double NAT, and when internal
|
|
|
|
|
+ // network addresses misuse non-private IP ranges (so are
|
|
|
|
|
+ // technically not bogons). Instead of outright rejecting
|
|
|
|
|
+ // SDPs containing unexpected GeoIP candidates, they are
|
|
|
|
|
+ // instead stripped out and the the resulting filtered SDP is
|
|
|
|
|
+ // used.
|
|
|
|
|
|
|
|
if lookupGeoIP != nil {
|
|
if lookupGeoIP != nil {
|
|
|
candidateGeoIPData := lookupGeoIP(candidate.Address())
|
|
candidateGeoIPData := lookupGeoIP(candidate.Address())
|
|
@@ -1732,15 +1746,20 @@ func processSDPAddresses(
|
|
|
if candidateIsIPv6 {
|
|
if candidateIsIPv6 {
|
|
|
version = "IPv6"
|
|
version = "IPv6"
|
|
|
}
|
|
}
|
|
|
- errStr := fmt.Sprintf(
|
|
|
|
|
- "unexpected GeoIP for %s candidate: %s, %s",
|
|
|
|
|
|
|
+ reason := fmt.Sprintf(
|
|
|
|
|
+ "unexpected GeoIP for %s candidate: %s/%s",
|
|
|
version,
|
|
version,
|
|
|
candidateGeoIPData.Country,
|
|
candidateGeoIPData.Country,
|
|
|
candidateGeoIPData.ASN)
|
|
candidateGeoIPData.ASN)
|
|
|
- return nil, nil, errors.TraceNew(errStr)
|
|
|
|
|
|
|
+ filteredCandidateReasons[reason] += 1
|
|
|
|
|
+ continue
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if candidateIsIPv6 {
|
|
|
|
|
+ hasIPv6 = true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// These types are not reported:
|
|
// These types are not reported:
|
|
|
// - CandidateTypeRelay: TURN servers are not used.
|
|
// - CandidateTypeRelay: TURN servers are not used.
|
|
|
// - CandidateTypePeerReflexive: this candidate type only
|
|
// - CandidateTypePeerReflexive: this candidate type only
|
|
@@ -1777,6 +1796,10 @@ func processSDPAddresses(
|
|
|
for candidateType := range candidateTypes {
|
|
for candidateType := range candidateTypes {
|
|
|
metrics.iceCandidateTypes = append(metrics.iceCandidateTypes, candidateType)
|
|
metrics.iceCandidateTypes = append(metrics.iceCandidateTypes, candidateType)
|
|
|
}
|
|
}
|
|
|
|
|
+ for reason, count := range filteredCandidateReasons {
|
|
|
|
|
+ metrics.filteredICECandidates = append(metrics.filteredICECandidates,
|
|
|
|
|
+ fmt.Sprintf("%s: %d", reason, count))
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
return encodedSDP, metrics, nil
|
|
return encodedSDP, metrics, nil
|
|
|
}
|
|
}
|