mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-22 12:58:37 +00:00
net/portmapper: add clientmetric for UPnP error codes
This should allow us to gather a bit more information about errors that we encounter when creating UPnP mappings. Since we don't have a "LabelMap" construction for clientmetrics, do what sockstats does and lazily register a new metric when we see a new code. Updates #9343 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: Ibb5aadd6138beb58721f98123debcc7273b611ba
This commit is contained in:
parent
19a9d9037f
commit
d9ae7d670e
@ -28,6 +28,7 @@ import (
|
|||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/types/nettype"
|
"tailscale.com/types/nettype"
|
||||||
"tailscale.com/util/clientmetric"
|
"tailscale.com/util/clientmetric"
|
||||||
|
"tailscale.com/util/mak"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DebugKnobs contains debug configuration that can be provided when creating a
|
// DebugKnobs contains debug configuration that can be provided when creating a
|
||||||
@ -1024,3 +1025,22 @@ var (
|
|||||||
// we received a UPnP response with a new meta.
|
// we received a UPnP response with a new meta.
|
||||||
metricUPnPUpdatedMeta = clientmetric.NewCounter("portmap_upnp_updated_meta")
|
metricUPnPUpdatedMeta = clientmetric.NewCounter("portmap_upnp_updated_meta")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UPnP error metric that's keyed by code; lazily registered on first read
|
||||||
|
var (
|
||||||
|
metricUPnPErrorsByCodeMu sync.Mutex
|
||||||
|
metricUPnPErrorsByCode map[int]*clientmetric.Metric
|
||||||
|
)
|
||||||
|
|
||||||
|
func getUPnPErrorsMetric(code int) *clientmetric.Metric {
|
||||||
|
metricUPnPErrorsByCodeMu.Lock()
|
||||||
|
defer metricUPnPErrorsByCodeMu.Unlock()
|
||||||
|
mm := metricUPnPErrorsByCode[code]
|
||||||
|
if mm != nil {
|
||||||
|
return mm
|
||||||
|
}
|
||||||
|
|
||||||
|
mm = clientmetric.NewCounter(fmt.Sprintf("portmap_upnp_errors_with_code_%d", code))
|
||||||
|
mak.Set(&metricUPnPErrorsByCode, code, mm)
|
||||||
|
return mm
|
||||||
|
}
|
||||||
|
@ -337,9 +337,14 @@ func (c *Client) getUPnPPortMapping(
|
|||||||
// duration; see the following issue for details:
|
// duration; see the following issue for details:
|
||||||
// https://github.com/tailscale/tailscale/issues/9343
|
// https://github.com/tailscale/tailscale/issues/9343
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
code, ok := getUPnPErrorCode(err)
|
||||||
|
if ok {
|
||||||
|
getUPnPErrorsMetric(code).Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
// From the UPnP spec: http://upnp.org/specs/gw/UPnP-gw-WANIPConnection-v2-Service.pdf
|
// From the UPnP spec: http://upnp.org/specs/gw/UPnP-gw-WANIPConnection-v2-Service.pdf
|
||||||
// 725: OnlyPermanentLeasesSupported
|
// 725: OnlyPermanentLeasesSupported
|
||||||
if isUPnPError(err, 725) {
|
if ok && code == 725 {
|
||||||
newPort, err = addAnyPortMapping(
|
newPort, err = addAnyPortMapping(
|
||||||
ctx,
|
ctx,
|
||||||
client,
|
client,
|
||||||
@ -387,13 +392,13 @@ func (c *Client) getUPnPPortMapping(
|
|||||||
return upnp.external, true
|
return upnp.external, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// isUPnPError returns whether the provided error is a UPnP error response with
|
// getUPnPErrorCode returns the UPnP error code from the given response, if the
|
||||||
// the given error code. It returns false if the error is not a SOAP error, or
|
// error is a SOAP error in the proper format, and a boolean indicating whether
|
||||||
// the inner error details are not a UPnP error.
|
// the provided error was actually a UPnP error.
|
||||||
func isUPnPError(err error, errCode int) bool {
|
func getUPnPErrorCode(err error) (int, bool) {
|
||||||
soapErr, ok := err.(*soap.SOAPFaultError)
|
soapErr, ok := err.(*soap.SOAPFaultError)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
var upnpErr struct {
|
var upnpErr struct {
|
||||||
@ -402,13 +407,12 @@ func isUPnPError(err error, errCode int) bool {
|
|||||||
Description string `xml:"errorDescription"`
|
Description string `xml:"errorDescription"`
|
||||||
}
|
}
|
||||||
if err := xml.Unmarshal([]byte(soapErr.Detail.Raw), &upnpErr); err != nil {
|
if err := xml.Unmarshal([]byte(soapErr.Detail.Raw), &upnpErr); err != nil {
|
||||||
return false
|
return 0, false
|
||||||
}
|
}
|
||||||
if upnpErr.XMLName.Local != "UPnPError" {
|
if upnpErr.XMLName.Local != "UPnPError" {
|
||||||
return false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
return upnpErr.Code, true
|
||||||
return upnpErr.Code == errCode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type uPnPDiscoResponse struct {
|
type uPnPDiscoResponse struct {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user