From 64ee9c089711b874e5f700b913e56487b93b2a13 Mon Sep 17 00:00:00 2001 From: fortuna Date: Wed, 12 Dec 2018 11:57:53 -0500 Subject: [PATCH 1/8] Add comment on cipher size --- shadowsocks/stream.go | 1 + 1 file changed, 1 insertion(+) diff --git a/shadowsocks/stream.go b/shadowsocks/stream.go index 3cce192d..8f126c8a 100644 --- a/shadowsocks/stream.go +++ b/shadowsocks/stream.go @@ -142,6 +142,7 @@ func NewShadowsocksReader(reader io.Reader, ssCipher shadowaead.Cipher) Shadowso // init reads the salt from the inner Reader and sets up the AEAD object func (sr *shadowsocksReader) init() (err error) { if sr.aead == nil { + // For chacha20-poly1305, SaltSize is 32, NonceSize is 12 and Overhead is 16. salt := make([]byte, sr.ssCipher.SaltSize()) if _, err := io.ReadFull(sr.reader, salt); err != nil { if err != io.EOF && err != io.ErrUnexpectedEOF { From e086a69475d7a8056eed6a60adc663fd5bc61b98 Mon Sep 17 00:00:00 2001 From: fortuna Date: Wed, 12 Dec 2018 12:01:12 -0500 Subject: [PATCH 2/8] Benchmark findCipher --- shadowsocks/tcp_test.go | 71 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 shadowsocks/tcp_test.go diff --git a/shadowsocks/tcp_test.go b/shadowsocks/tcp_test.go new file mode 100644 index 00000000..50480420 --- /dev/null +++ b/shadowsocks/tcp_test.go @@ -0,0 +1,71 @@ +// Copyright 2018 Jigsaw Operations LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shadowsocks + +import ( + "fmt" + "net" + "testing" + + logging "github.com/op/go-logging" + "github.com/shadowsocks/go-shadowsocks2/core" + "github.com/shadowsocks/go-shadowsocks2/shadowaead" +) + +func BenchmarkFindCipher(b *testing.B) { + b.StopTimer() + b.ResetTimer() + + logging.SetLevel(logging.INFO, "") + listener, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0}) + if err != nil { + b.Fatalf("ListenTCP failed: %v", err) + } + + const numCiphers = 100 + cipherList := make(map[string]shadowaead.Cipher) + for i := 0; i < numCiphers; i++ { + cipherID := fmt.Sprintf("id-%v", i) + secret := fmt.Sprintf("secret-%v", i) + cipher, err := core.PickCipher("chacha20-ietf-poly1305", nil, secret) + if err != nil { + b.Fatalf("Failed to create cipher %v: %v", i, err) + } + cipherList[cipherID] = cipher.(shadowaead.Cipher) + } + + testPayload := []byte{ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60} + + for n := 0; n < b.N; n++ { + go func() { + conn, err := net.Dial("tcp", listener.Addr().String()) + if err != nil { + b.Fatalf("Failed to dial %v: %v", listener.Addr(), err) + } + conn.Write(testPayload) + conn.Close() + }() + clientConn, err := listener.AcceptTCP() + if err != nil { + b.Fatalf("AcceptTCP failed: %v", err) + } + b.StartTimer() + findAccessKey(clientConn, cipherList) + b.StopTimer() + } +} From 97d31e4e8ec04f07c511a9fbd10ece56d4b42f8e Mon Sep 17 00:00:00 2001 From: fortuna Date: Wed, 12 Dec 2018 12:01:24 -0500 Subject: [PATCH 3/8] Optimize debug logging --- shadowsocks/tcp.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/shadowsocks/tcp.go b/shadowsocks/tcp.go index c4d784cf..bdf55109 100644 --- a/shadowsocks/tcp.go +++ b/shadowsocks/tcp.go @@ -22,6 +22,8 @@ import ( "net" "time" + logging "github.com/op/go-logging" + "github.com/Jigsaw-Code/outline-ss-server/metrics" onet "github.com/Jigsaw-Code/outline-ss-server/net" @@ -40,7 +42,9 @@ func findAccessKey(clientConn onet.DuplexConn, cipherList map[string]shadowaead. // TODO: Reorder list to try previously successful ciphers first for the client IP. // TODO: Ban and log client IPs with too many failures too quick to protect against DoS. for id, cipher := range cipherList { - logger.Debugf("Trying key %v", id) + if logger.IsEnabledFor(logging.DEBUG) { + logger.Debugf("Trying key %v", id) + } // tmpReader reads first from the replayBuffer and then from clientConn if it needs more // bytes. All bytes read from clientConn are saved in replayBuffer for future replays. tmpReader := io.MultiReader(bytes.NewReader(replayBuffer.Bytes()), io.TeeReader(clientConn, &replayBuffer)) @@ -48,10 +52,14 @@ func findAccessKey(clientConn onet.DuplexConn, cipherList map[string]shadowaead. // Read should read just enough data to authenticate the payload size. _, err := cipherReader.Read(make([]byte, 0)) if err != nil { - logger.Debugf("Failed key %v: %v", id, err) + if logger.IsEnabledFor(logging.DEBUG) { + logger.Debugf("Failed key %v: %v", id, err) + } continue } - logger.Debugf("Selected key %v", id) + if logger.IsEnabledFor(logging.DEBUG) { + logger.Debugf("Selected key %v", id) + } // We don't need to keep storing and replaying the bytes anymore, but we don't want to drop // those already read into the replayBuffer. ssr := NewShadowsocksReader(io.MultiReader(&replayBuffer, clientConn), cipher) From 5e7117302c06befb4c50e225299ea6ccab7a3e99 Mon Sep 17 00:00:00 2001 From: fortuna Date: Wed, 12 Dec 2018 12:33:31 -0500 Subject: [PATCH 4/8] Standardize logging --- shadowsocks/tcp.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/shadowsocks/tcp.go b/shadowsocks/tcp.go index bdf55109..61fdeaeb 100644 --- a/shadowsocks/tcp.go +++ b/shadowsocks/tcp.go @@ -42,9 +42,6 @@ func findAccessKey(clientConn onet.DuplexConn, cipherList map[string]shadowaead. // TODO: Reorder list to try previously successful ciphers first for the client IP. // TODO: Ban and log client IPs with too many failures too quick to protect against DoS. for id, cipher := range cipherList { - if logger.IsEnabledFor(logging.DEBUG) { - logger.Debugf("Trying key %v", id) - } // tmpReader reads first from the replayBuffer and then from clientConn if it needs more // bytes. All bytes read from clientConn are saved in replayBuffer for future replays. tmpReader := io.MultiReader(bytes.NewReader(replayBuffer.Bytes()), io.TeeReader(clientConn, &replayBuffer)) @@ -53,12 +50,12 @@ func findAccessKey(clientConn onet.DuplexConn, cipherList map[string]shadowaead. _, err := cipherReader.Read(make([]byte, 0)) if err != nil { if logger.IsEnabledFor(logging.DEBUG) { - logger.Debugf("Failed key %v: %v", id, err) + logger.Debugf("Failed TCP cipher %v: %v", id, err) } continue } if logger.IsEnabledFor(logging.DEBUG) { - logger.Debugf("Selected key %v", id) + logger.Debugf("Selected TCP cipher %v", id) } // We don't need to keep storing and replaying the bytes anymore, but we don't want to drop // those already read into the replayBuffer. From 16e1ccaab8d1468fd4e77c3a337816710fbda7de Mon Sep 17 00:00:00 2001 From: fortuna Date: Wed, 12 Dec 2018 12:33:57 -0500 Subject: [PATCH 5/8] Add UDP benchmark --- shadowsocks/tcp_test.go | 24 ++++--------------- shadowsocks/testing/ciphers.go | 44 ++++++++++++++++++++++++++++++++++ shadowsocks/udp_test.go | 42 ++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 19 deletions(-) create mode 100644 shadowsocks/testing/ciphers.go create mode 100644 shadowsocks/udp_test.go diff --git a/shadowsocks/tcp_test.go b/shadowsocks/tcp_test.go index 50480420..4e495134 100644 --- a/shadowsocks/tcp_test.go +++ b/shadowsocks/tcp_test.go @@ -15,13 +15,11 @@ package shadowsocks import ( - "fmt" "net" "testing" + sstest "github.com/Jigsaw-Code/outline-ss-server/shadowsocks/testing" logging "github.com/op/go-logging" - "github.com/shadowsocks/go-shadowsocks2/core" - "github.com/shadowsocks/go-shadowsocks2/shadowaead" ) func BenchmarkFindCipher(b *testing.B) { @@ -34,23 +32,11 @@ func BenchmarkFindCipher(b *testing.B) { b.Fatalf("ListenTCP failed: %v", err) } - const numCiphers = 100 - cipherList := make(map[string]shadowaead.Cipher) - for i := 0; i < numCiphers; i++ { - cipherID := fmt.Sprintf("id-%v", i) - secret := fmt.Sprintf("secret-%v", i) - cipher, err := core.PickCipher("chacha20-ietf-poly1305", nil, secret) - if err != nil { - b.Fatalf("Failed to create cipher %v: %v", i, err) - } - cipherList[cipherID] = cipher.(shadowaead.Cipher) + cipherList, err := sstest.MakeTestCiphers(100) + if err != nil { + b.Fatal(err) } - - testPayload := []byte{ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60} - + testPayload := sstest.MakeTestPayload(60) for n := 0; n < b.N; n++ { go func() { conn, err := net.Dial("tcp", listener.Addr().String()) diff --git a/shadowsocks/testing/ciphers.go b/shadowsocks/testing/ciphers.go new file mode 100644 index 00000000..c8cbda3c --- /dev/null +++ b/shadowsocks/testing/ciphers.go @@ -0,0 +1,44 @@ +// Copyright 2018 Jigsaw Operations LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testing + +import ( + "fmt" + + "github.com/shadowsocks/go-shadowsocks2/core" + "github.com/shadowsocks/go-shadowsocks2/shadowaead" +) + +func MakeTestCiphers(numCiphers int) (map[string]shadowaead.Cipher, error) { + cipherList := make(map[string]shadowaead.Cipher) + for i := 0; i < numCiphers; i++ { + cipherID := fmt.Sprintf("id-%v", i) + secret := fmt.Sprintf("secret-%v", i) + cipher, err := core.PickCipher("chacha20-ietf-poly1305", nil, secret) + if err != nil { + return nil, fmt.Errorf("Failed to create cipher %v: %v", i, err) + } + cipherList[cipherID] = cipher.(shadowaead.Cipher) + } + return cipherList, nil +} + +func MakeTestPayload(size int) []byte { + payload := make([]byte, size) + for i := 0; i < size; i++ { + payload[i] = byte(i) + } + return payload +} diff --git a/shadowsocks/udp_test.go b/shadowsocks/udp_test.go new file mode 100644 index 00000000..d1a9e9ef --- /dev/null +++ b/shadowsocks/udp_test.go @@ -0,0 +1,42 @@ +// Copyright 2018 Jigsaw Operations LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shadowsocks + +import ( + "testing" + + sstest "github.com/Jigsaw-Code/outline-ss-server/shadowsocks/testing" + logging "github.com/op/go-logging" +) + +func BenchmarkUnpack(b *testing.B) { + b.StopTimer() + b.ResetTimer() + + logging.SetLevel(logging.INFO, "") + + cipherList, err := sstest.MakeTestCiphers(100) + if err != nil { + b.Fatal(err) + } + + testPayload := sstest.MakeTestPayload(60) + textBuf := make([]byte, udpBufSize) + for n := 0; n < b.N; n++ { + b.StartTimer() + unpack(textBuf, testPayload, cipherList) + b.StopTimer() + } +} From 9337108e2913d391a826998ee7edf7e0dc1fb3df Mon Sep 17 00:00:00 2001 From: fortuna Date: Wed, 12 Dec 2018 12:34:07 -0500 Subject: [PATCH 6/8] Optimize UDP logging --- shadowsocks/udp.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/shadowsocks/udp.go b/shadowsocks/udp.go index 70281bc1..5d697abf 100644 --- a/shadowsocks/udp.go +++ b/shadowsocks/udp.go @@ -23,6 +23,7 @@ import ( "github.com/Jigsaw-Code/outline-ss-server/metrics" onet "github.com/Jigsaw-Code/outline-ss-server/net" + logging "github.com/op/go-logging" "sync" @@ -36,13 +37,16 @@ const udpBufSize = 64 * 1024 // correctly. dst and src must not overlap. func unpack(dst, src []byte, ciphers map[string]shadowaead.Cipher) ([]byte, string, shadowaead.Cipher, error) { for id, cipher := range ciphers { - logger.Debugf("Trying UDP cipher %v", id) buf, err := shadowaead.Unpack(dst, src, cipher) if err != nil { - logger.Debugf("Failed UDP cipher %v: %v", id, err) + if logger.IsEnabledFor(logging.DEBUG) { + logger.Debugf("Failed UDP cipher %v: %v", id, err) + } continue } - logger.Debugf("Selected UDP cipher %v", id) + if logger.IsEnabledFor(logging.DEBUG) { + logger.Debugf("Selected UDP cipher %v", id) + } return buf, id, cipher, nil } return nil, "", nil, errors.New("could not find valid cipher") From 51e4a3c443ab0f975dc8e4098f2aa57c1c85a0fe Mon Sep 17 00:00:00 2001 From: fortuna Date: Wed, 12 Dec 2018 12:37:43 -0500 Subject: [PATCH 7/8] Rename methods --- shadowsocks/tcp_test.go | 2 +- shadowsocks/udp_test.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/shadowsocks/tcp_test.go b/shadowsocks/tcp_test.go index 4e495134..244b2e4f 100644 --- a/shadowsocks/tcp_test.go +++ b/shadowsocks/tcp_test.go @@ -22,7 +22,7 @@ import ( logging "github.com/op/go-logging" ) -func BenchmarkFindCipher(b *testing.B) { +func BenchmarkTCPFindCipher(b *testing.B) { b.StopTimer() b.ResetTimer() diff --git a/shadowsocks/udp_test.go b/shadowsocks/udp_test.go index d1a9e9ef..ceb5de69 100644 --- a/shadowsocks/udp_test.go +++ b/shadowsocks/udp_test.go @@ -21,7 +21,7 @@ import ( logging "github.com/op/go-logging" ) -func BenchmarkUnpack(b *testing.B) { +func BenchmarkUDPUnpack(b *testing.B) { b.StopTimer() b.ResetTimer() @@ -31,7 +31,6 @@ func BenchmarkUnpack(b *testing.B) { if err != nil { b.Fatal(err) } - testPayload := sstest.MakeTestPayload(60) textBuf := make([]byte, udpBufSize) for n := 0; n < b.N; n++ { From abe929df7b6b1bd2e0cb6ad01f6fe8fcb957a64c Mon Sep 17 00:00:00 2001 From: fortuna Date: Wed, 12 Dec 2018 12:51:51 -0500 Subject: [PATCH 8/8] SImplify UDP test --- shadowsocks/udp_test.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/shadowsocks/udp_test.go b/shadowsocks/udp_test.go index ceb5de69..70b92ced 100644 --- a/shadowsocks/udp_test.go +++ b/shadowsocks/udp_test.go @@ -22,9 +22,6 @@ import ( ) func BenchmarkUDPUnpack(b *testing.B) { - b.StopTimer() - b.ResetTimer() - logging.SetLevel(logging.INFO, "") cipherList, err := sstest.MakeTestCiphers(100) @@ -33,9 +30,8 @@ func BenchmarkUDPUnpack(b *testing.B) { } testPayload := sstest.MakeTestPayload(60) textBuf := make([]byte, udpBufSize) + b.ResetTimer() for n := 0; n < b.N; n++ { - b.StartTimer() unpack(textBuf, testPayload, cipherList) - b.StopTimer() } }