Skip to content

Commit

Permalink
feat: add AS organization name to metrics (#204)
Browse files Browse the repository at this point in the history
* feat: add AS organization name to metrics

* Revert to pass `int` to `asnLabel()`.
  • Loading branch information
sbruens committed Aug 29, 2024
1 parent fa651d3 commit 55e8d0c
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 20 deletions.
26 changes: 13 additions & 13 deletions cmd/outline-ss-server/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func newProxyCollector(proto string) (*proxyCollector, error) {
prometheus.CounterOpts{
Name: "data_bytes_per_location",
Help: "Bytes transferred by the proxy, per location",
}, []string{"proto", "dir", "location", "asn"}).CurryWith(map[string]string{"proto": proto})
}, []string{"proto", "dir", "location", "asn", "asorg"}).CurryWith(map[string]string{"proto": proto})
if err != nil {
return nil, err
}
Expand All @@ -82,16 +82,16 @@ func (c *proxyCollector) Collect(ch chan<- prometheus.Metric) {

func (c *proxyCollector) addClientTarget(clientProxyBytes, proxyTargetBytes int64, accessKey string, clientInfo ipinfo.IPInfo) {
addIfNonZero(clientProxyBytes, c.dataBytesPerKey, "c>p", accessKey)
addIfNonZero(clientProxyBytes, c.dataBytesPerLocation, "c>p", clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN))
addIfNonZero(clientProxyBytes, c.dataBytesPerLocation, "c>p", clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN.Number), clientInfo.ASN.Organization)
addIfNonZero(proxyTargetBytes, c.dataBytesPerKey, "p>t", accessKey)
addIfNonZero(proxyTargetBytes, c.dataBytesPerLocation, "p>t", clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN))
addIfNonZero(proxyTargetBytes, c.dataBytesPerLocation, "p>t", clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN.Number), clientInfo.ASN.Organization)
}

func (c *proxyCollector) addTargetClient(targetProxyBytes, proxyClientBytes int64, accessKey string, clientInfo ipinfo.IPInfo) {
addIfNonZero(targetProxyBytes, c.dataBytesPerKey, "p<t", accessKey)
addIfNonZero(targetProxyBytes, c.dataBytesPerLocation, "p<t", clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN))
addIfNonZero(targetProxyBytes, c.dataBytesPerLocation, "p<t", clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN.Number), clientInfo.ASN.Organization)
addIfNonZero(proxyClientBytes, c.dataBytesPerKey, "c<p", accessKey)
addIfNonZero(proxyClientBytes, c.dataBytesPerLocation, "c<p", clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN))
addIfNonZero(proxyClientBytes, c.dataBytesPerLocation, "c<p", clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN.Number), clientInfo.ASN.Organization)
}

type tcpConnMetrics struct {
Expand Down Expand Up @@ -175,12 +175,12 @@ func newTCPCollector() (*tcpServiceMetrics, error) {
Namespace: namespace,
Name: "connections_opened",
Help: "Count of open TCP connections",
}, []string{"location", "asn"}),
}, []string{"location", "asn", "asorg"}),
closedConnections: prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Name: "connections_closed",
Help: "Count of closed TCP connections",
}, []string{"location", "asn", "status", "access_key"}),
}, []string{"location", "asn", "asorg", "status", "access_key"}),
connectionDurationMs: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: namespace,
Expand Down Expand Up @@ -217,11 +217,11 @@ func (c *tcpServiceMetrics) Collect(ch chan<- prometheus.Metric) {
}

func (c *tcpServiceMetrics) openConnection(clientInfo ipinfo.IPInfo) {
c.openConnections.WithLabelValues(clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN)).Inc()
c.openConnections.WithLabelValues(clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN.Number), clientInfo.ASN.Organization).Inc()
}

func (c *tcpServiceMetrics) closeConnection(status string, duration time.Duration, accessKey string, clientInfo ipinfo.IPInfo) {
c.closedConnections.WithLabelValues(clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN), status, accessKey).Inc()
c.closedConnections.WithLabelValues(clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN.Number), clientInfo.ASN.Organization, status, accessKey).Inc()
c.connectionDurationMs.WithLabelValues(status).Observe(duration.Seconds() * 1000)
}

Expand Down Expand Up @@ -310,7 +310,7 @@ func newUDPCollector() (*udpServiceMetrics, error) {
Namespace: namespace,
Name: "packets_from_client_per_location",
Help: "Packets received from the client, per location and status",
}, []string{"location", "asn", "status"}),
}, []string{"location", "asn", "asorg", "status"}),
addedNatEntries: prometheus.NewCounter(
prometheus.CounterOpts{
Namespace: namespace,
Expand Down Expand Up @@ -351,7 +351,7 @@ func (c *udpServiceMetrics) removeNatEntry() {
}

func (c *udpServiceMetrics) addPacketFromClient(status string, clientProxyBytes, proxyTargetBytes int64, accessKey string, clientInfo ipinfo.IPInfo) {
c.packetsFromClientPerLocation.WithLabelValues(clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN), status).Inc()
c.packetsFromClientPerLocation.WithLabelValues(clientInfo.CountryCode.String(), asnLabel(clientInfo.ASN.Number), clientInfo.ASN.Organization, status).Inc()
c.proxyCollector.addClientTarget(clientProxyBytes, proxyTargetBytes, accessKey, clientInfo)
}

Expand Down Expand Up @@ -408,7 +408,7 @@ func newTunnelTimeMetrics(ip2info ipinfo.IPInfoMap) *tunnelTimeMetrics {
Namespace: namespace,
Name: "seconds_per_location",
Help: "Tunnel time, per location.",
}, []string{"location", "asn"}),
}, []string{"location", "asn", "asorg"}),
}
}

Expand All @@ -433,7 +433,7 @@ func (c *tunnelTimeMetrics) reportTunnelTime(ipKey IPKey, client *activeClient,
tunnelTime := tNow.Sub(client.startTime)
slog.LogAttrs(nil, slog.LevelDebug, "Reporting tunnel time.", slog.String("key", ipKey.accessKey), slog.Duration("duration", tunnelTime))
c.tunnelTimePerKey.WithLabelValues(ipKey.accessKey).Add(tunnelTime.Seconds())
c.tunnelTimePerLocation.WithLabelValues(client.info.CountryCode.String(), asnLabel(client.info.ASN)).Add(tunnelTime.Seconds())
c.tunnelTimePerLocation.WithLabelValues(client.info.CountryCode.String(), asnLabel(client.info.ASN.Number), client.info.ASN.Organization).Add(tunnelTime.Seconds())
// Reset the start time now that the tunnel time has been reported.
client.startTime = tNow
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/outline-ss-server/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func TestTunnelTime(t *testing.T) {
expected := strings.NewReader(`
# HELP tunnel_time_seconds_per_location Tunnel time, per location.
# TYPE tunnel_time_seconds_per_location counter
tunnel_time_seconds_per_location{asn="",location="XL"} 5
tunnel_time_seconds_per_location{asn="",asorg="",location="XL"} 5
`)
err := promtest.GatherAndCompare(
reg,
Expand Down
7 changes: 6 additions & 1 deletion ipinfo/ipinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type IPInfoMap interface {

type IPInfo struct {
CountryCode CountryCode
ASN int
ASN ASN
}

type CountryCode string
Expand All @@ -35,6 +35,11 @@ func (cc CountryCode) String() string {
return string(cc)
}

type ASN struct {
Number int
Organization string
}

const (
// Codes in the X* range are reserved to be user-assigned.
// See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Decoding_table
Expand Down
5 changes: 4 additions & 1 deletion ipinfo/mmdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ func (ip2info *MMDBIPInfoMap) GetIPInfo(ip net.IP) (IPInfo, error) {
if asnErr != nil {
asnErr = fmt.Errorf("asn lookup failed: %w", asnErr)
} else if record != nil {
info.ASN = int(record.AutonomousSystemNumber)
info.ASN = ASN{
Number: int(record.AutonomousSystemNumber),
Organization: record.AutonomousSystemOrganization,
}
}
}
return info, errors.Join(countryErr, asnErr)
Expand Down
8 changes: 4 additions & 4 deletions ipinfo/mmdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ func TestIPInfoMapASNOnly(t *testing.T) {
// For examples, see https://github.com/maxmind/MaxMind-DB/blob/main/source-data/GeoLite2-ASN-Test.json
info, err := ip2info.GetIPInfo(net.ParseIP("38.108.80.24"))
require.NoError(t, err)
assert.Equal(t, IPInfo{ASN: 174}, info)
assert.Equal(t, IPInfo{ASN: ASN{Number: 174, Organization: "Cogent Communications"}}, info)

info, err = ip2info.GetIPInfo(net.ParseIP("2400::1"))
require.NoError(t, err)
assert.Equal(t, IPInfo{ASN: 4766}, info)
assert.Equal(t, IPInfo{ASN: ASN{Number: 4766, Organization: "Korea Telecom"}}, info)

info, err = ip2info.GetIPInfo(net.ParseIP("10.0.0.1"))
require.NoError(t, err)
Expand All @@ -104,15 +104,15 @@ func TestIPInfoMap(t *testing.T) {

info, err := ip2info.GetIPInfo(net.ParseIP("67.43.156.0"))
require.NoError(t, err)
assert.Equal(t, IPInfo{CountryCode: "BT", ASN: 35908}, info)
assert.Equal(t, IPInfo{CountryCode: "BT", ASN: ASN{Number: 35908, Organization: ""}}, info)

info, err = ip2info.GetIPInfo(net.ParseIP("2a02:d280::"))
require.NoError(t, err)
assert.Equal(t, IPInfo{CountryCode: "CZ"}, info)

info, err = ip2info.GetIPInfo(net.ParseIP("2400::1"))
require.NoError(t, err)
assert.Equal(t, IPInfo{ASN: 4766}, info)
assert.Equal(t, IPInfo{ASN: ASN{Number: 4766, Organization: "Korea Telecom"}}, info)

info, err = ip2info.GetIPInfo(net.ParseIP("10.0.0.1"))
require.NoError(t, err)
Expand Down

0 comments on commit 55e8d0c

Please sign in to comment.