Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Benchmark findcipher and minor optimization #7

Merged
merged 8 commits into from
Dec 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions shadowsocks/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 8 additions & 3 deletions shadowsocks/tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -40,18 +42,21 @@ 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)
// 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))
cipherReader := NewShadowsocksReader(tmpReader, cipher)
// 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 TCP cipher %v: %v", id, err)
}
continue
}
logger.Debugf("Selected key %v", id)
if logger.IsEnabledFor(logging.DEBUG) {
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.
ssr := NewShadowsocksReader(io.MultiReader(&replayBuffer, clientConn), cipher)
Expand Down
57 changes: 57 additions & 0 deletions shadowsocks/tcp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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 (
"net"
"testing"

sstest "github.com/Jigsaw-Code/outline-ss-server/shadowsocks/testing"
logging "github.com/op/go-logging"
)

func BenchmarkTCPFindCipher(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)
}

cipherList, err := sstest.MakeTestCiphers(100)
if err != nil {
b.Fatal(err)
}
testPayload := sstest.MakeTestPayload(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()
}
}
44 changes: 44 additions & 0 deletions shadowsocks/testing/ciphers.go
Original file line number Diff line number Diff line change
@@ -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
}
10 changes: 7 additions & 3 deletions shadowsocks/udp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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")
Expand Down
37 changes: 37 additions & 0 deletions shadowsocks/udp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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 BenchmarkUDPUnpack(b *testing.B) {
logging.SetLevel(logging.INFO, "")

cipherList, err := sstest.MakeTestCiphers(100)
if err != nil {
b.Fatal(err)
}
testPayload := sstest.MakeTestPayload(60)
textBuf := make([]byte, udpBufSize)
b.ResetTimer()
for n := 0; n < b.N; n++ {
unpack(textBuf, testPayload, cipherList)
}
}