Skip to content

Commit 7e51dbe

Browse files
authored
Merge pull request #12 from Jigsaw-Code/fortuna-opt
Keep active ciphers at the front and add metrics
2 parents 31d9b0e + d78829a commit 7e51dbe

File tree

10 files changed

+147
-176
lines changed

10 files changed

+147
-176
lines changed

metrics/metrics.go

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
"time"
2222

2323
onet "github.com/Jigsaw-Code/outline-ss-server/net"
24-
"github.com/oschwald/geoip2-golang"
24+
geoip2 "github.com/oschwald/geoip2-golang"
2525
"github.com/prometheus/client_golang/prometheus"
2626
)
2727

@@ -30,21 +30,25 @@ type ShadowsocksMetrics interface {
3030
GetLocation(net.Addr) (string, error)
3131

3232
SetNumAccessKeys(numKeys int, numPorts int)
33-
AddUDPPacketFromClient(clientLocation, accessKey, status string, clientProxyBytes, proxyTargetBytes int)
34-
AddUDPPacketFromTarget(clientLocation, accessKey, status string, targetProxyBytes, proxyClientBytes int)
33+
34+
// TCP metrics
3535
AddOpenTCPConnection(clientLocation string)
36-
AddClosedTCPConnection(clientLocation, accessKey, status string, data ProxyMetrics, duration time.Duration)
36+
AddClosedTCPConnection(clientLocation, accessKey, status string, data ProxyMetrics, timeToCipher, duration time.Duration)
3737

38-
AddUdpNatEntry()
39-
RemoveUdpNatEntry()
38+
// UDP metrics
39+
AddUDPPacketFromClient(clientLocation, accessKey, status string, clientProxyBytes, proxyTargetBytes int, timeToCipher time.Duration)
40+
AddUDPPacketFromTarget(clientLocation, accessKey, status string, targetProxyBytes, proxyClientBytes int)
41+
AddUDPNatEntry()
42+
RemoveUDPNatEntry()
4043
}
4144

4245
type shadowsocksMetrics struct {
4346
ipCountryDB *geoip2.Reader
4447

45-
accessKeys prometheus.Gauge
46-
ports prometheus.Gauge
47-
dataBytes *prometheus.CounterVec
48+
accessKeys prometheus.Gauge
49+
ports prometheus.Gauge
50+
dataBytes *prometheus.CounterVec
51+
timeToCipherMs *prometheus.SummaryVec
4852
// TODO: Add time to first byte.
4953

5054
tcpOpenConnections *prometheus.CounterVec
@@ -87,14 +91,21 @@ func NewShadowsocksMetrics(ipCountryDB *geoip2.Reader) ShadowsocksMetrics {
8791
Subsystem: "tcp",
8892
Name: "connection_duration_ms",
8993
Help: "TCP connection duration distributions.",
90-
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
94+
Objectives: map[float64]float64{0.5: 0.02, 0.9: 0.01, 0.99: 0.005},
9195
}, []string{"location", "status", "access_key"}),
9296
dataBytes: prometheus.NewCounterVec(
9397
prometheus.CounterOpts{
9498
Namespace: "shadowsocks",
9599
Name: "data_bytes",
96100
Help: "Bytes transferred by the proxy",
97101
}, []string{"dir", "proto", "location", "status", "access_key"}),
102+
timeToCipherMs: prometheus.NewSummaryVec(
103+
prometheus.SummaryOpts{
104+
Namespace: "shadowsocks",
105+
Name: "time_to_cipher_ms",
106+
Help: "Time needed to find the cipher",
107+
Objectives: map[float64]float64{0.5: 0.02, 0.9: 0.01, 0.99: 0.005},
108+
}, []string{"proto", "location", "status", "access_key"}),
98109
udpAddedNatEntries: prometheus.NewCounter(
99110
prometheus.CounterOpts{
100111
Namespace: "shadowsocks",
@@ -112,7 +123,7 @@ func NewShadowsocksMetrics(ipCountryDB *geoip2.Reader) ShadowsocksMetrics {
112123
}
113124
// TODO: Is it possible to pass where to register the collectors?
114125
prometheus.MustRegister(m.accessKeys, m.ports, m.tcpOpenConnections, m.tcpClosedConnections, m.tcpConnectionDurationMs,
115-
m.dataBytes, m.udpAddedNatEntries, m.udpRemovedNatEntries)
126+
m.dataBytes, m.timeToCipherMs, m.udpAddedNatEntries, m.udpRemovedNatEntries)
116127
return m
117128
}
118129

@@ -163,16 +174,18 @@ func (m *shadowsocksMetrics) AddOpenTCPConnection(clientLocation string) {
163174
m.tcpOpenConnections.WithLabelValues(clientLocation).Inc()
164175
}
165176

166-
func (m *shadowsocksMetrics) AddClosedTCPConnection(clientLocation, accessKey, status string, data ProxyMetrics, duration time.Duration) {
177+
func (m *shadowsocksMetrics) AddClosedTCPConnection(clientLocation, accessKey, status string, data ProxyMetrics, timeToCipher, duration time.Duration) {
167178
m.tcpClosedConnections.WithLabelValues(clientLocation, status, accessKey).Inc()
168179
m.tcpConnectionDurationMs.WithLabelValues(clientLocation, status, accessKey).Observe(duration.Seconds() * 1000)
180+
m.timeToCipherMs.WithLabelValues("tcp", clientLocation, status, accessKey).Observe(timeToCipher.Seconds() * 1000)
169181
m.dataBytes.WithLabelValues("c>p", "tcp", clientLocation, status, accessKey).Add(float64(data.ClientProxy))
170182
m.dataBytes.WithLabelValues("p>t", "tcp", clientLocation, status, accessKey).Add(float64(data.ProxyTarget))
171183
m.dataBytes.WithLabelValues("p<t", "tcp", clientLocation, status, accessKey).Add(float64(data.TargetProxy))
172184
m.dataBytes.WithLabelValues("c<p", "tcp", clientLocation, status, accessKey).Add(float64(data.ProxyClient))
173185
}
174186

175-
func (m *shadowsocksMetrics) AddUDPPacketFromClient(clientLocation, accessKey, status string, clientProxyBytes, proxyTargetBytes int) {
187+
func (m *shadowsocksMetrics) AddUDPPacketFromClient(clientLocation, accessKey, status string, clientProxyBytes, proxyTargetBytes int, timeToCipher time.Duration) {
188+
m.timeToCipherMs.WithLabelValues("udp", clientLocation, status, accessKey).Observe(timeToCipher.Seconds() * 1000)
176189
m.dataBytes.WithLabelValues("c>p", "udp", clientLocation, status, accessKey).Add(float64(clientProxyBytes))
177190
m.dataBytes.WithLabelValues("p>t", "udp", clientLocation, status, accessKey).Add(float64(proxyTargetBytes))
178191
}
@@ -182,11 +195,11 @@ func (m *shadowsocksMetrics) AddUDPPacketFromTarget(clientLocation, accessKey, s
182195
m.dataBytes.WithLabelValues("c<p", "udp", clientLocation, status, accessKey).Add(float64(proxyClientBytes))
183196
}
184197

185-
func (m *shadowsocksMetrics) AddUdpNatEntry() {
198+
func (m *shadowsocksMetrics) AddUDPNatEntry() {
186199
m.udpAddedNatEntries.Inc()
187200
}
188201

189-
func (m *shadowsocksMetrics) RemoveUdpNatEntry() {
202+
func (m *shadowsocksMetrics) RemoveUDPNatEntry() {
190203
m.udpRemovedNatEntries.Inc()
191204
}
192205

server.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func init() {
5454
type SSPort struct {
5555
tcpService shadowsocks.TCPService
5656
udpService shadowsocks.UDPService
57-
keys map[string]shadowaead.Cipher
57+
cipherList shadowsocks.CipherList
5858
}
5959

6060
type SSServer struct {
@@ -73,10 +73,10 @@ func (s *SSServer) startPort(portNum int) error {
7373
return fmt.Errorf("Failed to start UDP on port %v: %v", portNum, err)
7474
}
7575
logger.Infof("Listening TCP and UDP on port %v", portNum)
76-
port := &SSPort{keys: make(map[string]shadowaead.Cipher)}
76+
port := &SSPort{cipherList: shadowsocks.NewCipherList()}
7777
// TODO: Register initial data metrics at zero.
78-
port.tcpService = shadowsocks.NewTCPService(listener, &port.keys, s.m)
79-
port.udpService = shadowsocks.NewUDPService(packetConn, s.natTimeout, &port.keys, s.m)
78+
port.tcpService = shadowsocks.NewTCPService(listener, &port.cipherList, s.m)
79+
port.udpService = shadowsocks.NewUDPService(packetConn, s.natTimeout, &port.cipherList, s.m)
8080
s.ports[portNum] = port
8181
go port.udpService.Start()
8282
go port.tcpService.Start()
@@ -108,13 +108,13 @@ func (s *SSServer) loadConfig(filename string) error {
108108
}
109109

110110
portChanges := make(map[int]int)
111-
portKeys := make(map[int]map[string]shadowaead.Cipher)
111+
portCiphers := make(map[int]shadowsocks.CipherList)
112112
for _, keyConfig := range config.Keys {
113113
portChanges[keyConfig.Port] = 1
114-
keys, ok := portKeys[keyConfig.Port]
114+
cipherList, ok := portCiphers[keyConfig.Port]
115115
if !ok {
116-
keys = make(map[string]shadowaead.Cipher)
117-
portKeys[keyConfig.Port] = keys
116+
cipherList = shadowsocks.NewCipherList()
117+
portCiphers[keyConfig.Port] = cipherList
118118
}
119119
cipher, err := core.PickCipher(keyConfig.Cipher, nil, keyConfig.Secret)
120120
if err != nil {
@@ -127,7 +127,7 @@ func (s *SSServer) loadConfig(filename string) error {
127127
if !ok {
128128
return fmt.Errorf("Only AEAD ciphers are supported. Found %v", keyConfig.Cipher)
129129
}
130-
keys[keyConfig.ID] = aead
130+
cipherList.PushBack(keyConfig.ID, aead)
131131
}
132132
for port := range s.ports {
133133
portChanges[port] = portChanges[port] - 1
@@ -143,11 +143,11 @@ func (s *SSServer) loadConfig(filename string) error {
143143
}
144144
}
145145
}
146-
for portNum, keys := range portKeys {
147-
s.ports[portNum].keys = keys
146+
for portNum, cipherList := range portCiphers {
147+
s.ports[portNum].cipherList = cipherList
148148
}
149149
logger.Infof("Loaded %v access keys", len(config.Keys))
150-
s.m.SetNumAccessKeys(len(config.Keys), len(portKeys))
150+
s.m.SetNumAccessKeys(len(config.Keys), len(portCiphers))
151151
return nil
152152
}
153153

shadowsocks/cipher_cache.go

Lines changed: 0 additions & 81 deletions
This file was deleted.

shadowsocks/cipher_list.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2018 Jigsaw Operations LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package shadowsocks
16+
17+
import (
18+
"container/list"
19+
"sync"
20+
21+
"github.com/shadowsocks/go-shadowsocks2/shadowaead"
22+
)
23+
24+
// CipherEntry holds a Cipher with an identifier.
25+
type CipherEntry struct {
26+
ID string
27+
Cipher shadowaead.Cipher
28+
}
29+
30+
// CipherList is a list of CipherEntry elements that allows for thread-safe snapshotting and
31+
// moving to front.
32+
type CipherList interface {
33+
PushBack(id string, cipher shadowaead.Cipher) *list.Element
34+
SafeSnapshot() []*list.Element
35+
SafeMoveToFront(e *list.Element)
36+
}
37+
38+
type cipherList struct {
39+
CipherList
40+
list *list.List
41+
mu sync.RWMutex
42+
}
43+
44+
// NewCipherList creates an empty CipherList
45+
func NewCipherList() CipherList {
46+
return &cipherList{list: list.New()}
47+
}
48+
49+
func (cl *cipherList) PushBack(id string, cipher shadowaead.Cipher) *list.Element {
50+
return cl.list.PushBack(&CipherEntry{ID: id, Cipher: cipher})
51+
}
52+
53+
func (cl *cipherList) SafeSnapshot() []*list.Element {
54+
cl.mu.RLock()
55+
defer cl.mu.RUnlock()
56+
cipherArray := make([]*list.Element, cl.list.Len())
57+
i := 0
58+
for e := cl.list.Front(); e != nil; e = e.Next() {
59+
cipherArray[i] = e
60+
i++
61+
}
62+
return cipherArray
63+
}
64+
65+
func (cl *cipherList) SafeMoveToFront(e *list.Element) {
66+
cl.mu.Lock()
67+
defer cl.mu.Unlock()
68+
cl.list.MoveToFront(e)
69+
}

shadowsocks/cipher_map.go

Lines changed: 0 additions & 37 deletions
This file was deleted.

shadowsocks/testing/ciphers.go renamed to shadowsocks/cipher_testing.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package testing
15+
package shadowsocks
1616

1717
import (
1818
"fmt"
@@ -21,16 +21,16 @@ import (
2121
"github.com/shadowsocks/go-shadowsocks2/shadowaead"
2222
)
2323

24-
func MakeTestCiphers(numCiphers int) (map[string]shadowaead.Cipher, error) {
25-
cipherList := make(map[string]shadowaead.Cipher)
24+
func MakeTestCiphers(numCiphers int) (CipherList, error) {
25+
cipherList := NewCipherList()
2626
for i := 0; i < numCiphers; i++ {
2727
cipherID := fmt.Sprintf("id-%v", i)
2828
secret := fmt.Sprintf("secret-%v", i)
2929
cipher, err := core.PickCipher("chacha20-ietf-poly1305", nil, secret)
3030
if err != nil {
3131
return nil, fmt.Errorf("Failed to create cipher %v: %v", i, err)
3232
}
33-
cipherList[cipherID] = cipher.(shadowaead.Cipher)
33+
cipherList.PushBack(cipherID, cipher.(shadowaead.Cipher))
3434
}
3535
return cipherList, nil
3636
}

0 commit comments

Comments
 (0)