From 98db5b42a727d5a81786e113fb8c0f0477443561 Mon Sep 17 00:00:00 2001 From: Saber Haj Rabiee Date: Mon, 16 Dec 2024 13:29:41 -0800 Subject: [PATCH] feat: enable `fwmark` (`SO_MARK`) for outgoing sockets (#202) * feat: enable fwmark (SO_MARK) for outgoing sockets * fix: make fwmark linux-specific functionality * fix: minor improvements over handling fwmark * Use `transport.PacketListener` as interface. * Take the `syscall.RawConn` as input to `SetFwdmark()`. * Some cleanup. * Fix copyright dates for new files. * Fix the error types. * Revert changes to integration test. --------- Co-authored-by: sbruens --- cmd/outline-ss-server/config.go | 6 +++ cmd/outline-ss-server/config_example.yml | 23 +++++---- cmd/outline-ss-server/main.go | 30 +++++++++--- service/shadowsocks.go | 41 ++++++++++++---- service/socketopts_linux.go | 33 +++++++++++++ service/tcp.go | 17 ++----- service/tcp_linux.go | 40 ++++++++++++++++ service/tcp_other.go | 38 +++++++++++++++ service/tcp_test.go | 13 ++--- service/udp.go | 22 +++++++-- service/udp_linux.go | 61 ++++++++++++++++++++++++ service/udp_other.go | 30 ++++++++++++ service/udp_test.go | 8 +++- 13 files changed, 314 insertions(+), 48 deletions(-) create mode 100644 service/socketopts_linux.go create mode 100644 service/tcp_linux.go create mode 100644 service/tcp_other.go create mode 100644 service/udp_linux.go create mode 100644 service/udp_other.go diff --git a/cmd/outline-ss-server/config.go b/cmd/outline-ss-server/config.go index d85fd72a..0ed7295d 100644 --- a/cmd/outline-ss-server/config.go +++ b/cmd/outline-ss-server/config.go @@ -24,11 +24,13 @@ import ( type ServiceConfig struct { Listeners []ListenerConfig Keys []KeyConfig + Dialer DialerConfig } type ListenerType string const listenerTypeTCP ListenerType = "tcp" + const listenerTypeUDP ListenerType = "udp" type ListenerConfig struct { @@ -36,6 +38,10 @@ type ListenerConfig struct { Address string } +type DialerConfig struct { + Fwmark uint +} + type KeyConfig struct { ID string Cipher string diff --git a/cmd/outline-ss-server/config_example.yml b/cmd/outline-ss-server/config_example.yml index 7131c4cb..8dddbb6f 100644 --- a/cmd/outline-ss-server/config_example.yml +++ b/cmd/outline-ss-server/config_example.yml @@ -21,19 +21,22 @@ services: - type: udp address: "[::]:9000" keys: - - id: user-0 - cipher: chacha20-ietf-poly1305 - secret: Secret0 - - id: user-1 - cipher: chacha20-ietf-poly1305 - secret: Secret1 - + - id: user-0 + cipher: chacha20-ietf-poly1305 + secret: Secret0 + - id: user-1 + cipher: chacha20-ietf-poly1305 + secret: Secret1 + dialer: + # fwmark can be used in conjunction with other Linux networking features like cgroups, network namespaces, and TC (Traffic Control) for sophisticated network management. + # Value of 0 disables fwmark (SO_MARK) (Linux Only) + fwmark: 0 - listeners: - type: tcp address: "[::]:9001" - type: udp address: "[::]:9001" keys: - - id: user-2 - cipher: chacha20-ietf-poly1305 - secret: Secret2 + - id: user-2 + cipher: chacha20-ietf-poly1305 + secret: Secret2 diff --git a/cmd/outline-ss-server/main.go b/cmd/outline-ss-server/main.go index 3a04af0b..28a32425 100644 --- a/cmd/outline-ss-server/main.go +++ b/cmd/outline-ss-server/main.go @@ -28,17 +28,21 @@ import ( "time" "github.com/Jigsaw-Code/outline-sdk/transport/shadowsocks" - "github.com/Jigsaw-Code/outline-ss-server/ipinfo" - outline_prometheus "github.com/Jigsaw-Code/outline-ss-server/prometheus" - "github.com/Jigsaw-Code/outline-ss-server/service" "github.com/lmittmann/tint" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "golang.org/x/term" + + "github.com/Jigsaw-Code/outline-ss-server/ipinfo" + onet "github.com/Jigsaw-Code/outline-ss-server/net" + outline_prometheus "github.com/Jigsaw-Code/outline-ss-server/prometheus" + "github.com/Jigsaw-Code/outline-ss-server/service" ) -var logLevel = new(slog.LevelVar) // Info by default -var logHandler slog.Handler +var ( + logLevel = new(slog.LevelVar) // Info by default + logHandler slog.Handler +) // Set by goreleaser default ldflags. See https://goreleaser.com/customization/build/ var version = "dev" @@ -251,6 +255,8 @@ func (s *OutlineServer) runConfig(config Config) (func() error, error) { service.WithNatTimeout(s.natTimeout), service.WithMetrics(s.serviceMetrics), service.WithReplayCache(&s.replayCache), + service.WithStreamDialer(service.MakeValidatingTCPStreamDialer(onet.RequirePublicIP, serviceConfig.Dialer.Fwmark)), + service.WithPacketListener(service.MakeTargetUDPListener(serviceConfig.Dialer.Fwmark)), service.WithLogger(slog.Default()), ) if err != nil { @@ -263,14 +269,24 @@ func (s *OutlineServer) runConfig(config Config) (func() error, error) { if err != nil { return err } - slog.Info("TCP service started.", "address", ln.Addr().String()) + slog.Info("TCP service started.", "address", ln.Addr().String(), "fwmark", func() any { + if serviceConfig.Dialer.Fwmark == 0 { + return "disabled" + } + return serviceConfig.Dialer.Fwmark + }()) go service.StreamServe(ln.AcceptStream, ssService.HandleStream) case listenerTypeUDP: pc, err := lnSet.ListenPacket(lnConfig.Address) if err != nil { return err } - slog.Info("UDP service started.", "address", pc.LocalAddr().String()) + slog.Info("UDP service started.", "address", pc.LocalAddr().String(), "fwmark", func() any { + if serviceConfig.Dialer.Fwmark == 0 { + return "disabled" + } + return serviceConfig.Dialer.Fwmark + }()) go ssService.HandlePacket(pc) } } diff --git a/service/shadowsocks.go b/service/shadowsocks.go index 636fa94e..c194957c 100644 --- a/service/shadowsocks.go +++ b/service/shadowsocks.go @@ -21,6 +21,8 @@ import ( "time" "github.com/Jigsaw-Code/outline-sdk/transport" + + onet "github.com/Jigsaw-Code/outline-ss-server/net" ) const ( @@ -51,14 +53,17 @@ type Service interface { type Option func(s *ssService) type ssService struct { - logger *slog.Logger - metrics ServiceMetrics - ciphers CipherList - natTimeout time.Duration - replayCache *ReplayCache - - sh StreamHandler - ph PacketHandler + logger *slog.Logger + metrics ServiceMetrics + ciphers CipherList + natTimeout time.Duration + targetIPValidator onet.TargetIPValidator + replayCache *ReplayCache + + streamDialer transport.StreamDialer + sh StreamHandler + packetListener transport.PacketListener + ph PacketHandler } // NewShadowsocksService creates a new Shadowsocks service. @@ -83,9 +88,15 @@ func NewShadowsocksService(opts ...Option) (Service, error) { NewShadowsocksStreamAuthenticator(s.ciphers, s.replayCache, &ssConnMetrics{ServiceMetrics: s.metrics, proto: "tcp"}, s.logger), tcpReadTimeout, ) + if s.streamDialer != nil { + s.sh.SetTargetDialer(s.streamDialer) + } s.sh.SetLogger(s.logger) s.ph = NewPacketHandler(s.natTimeout, s.ciphers, s.metrics, &ssConnMetrics{ServiceMetrics: s.metrics, proto: "udp"}) + if s.packetListener != nil { + s.ph.SetTargetPacketListener(s.packetListener) + } s.ph.SetLogger(s.logger) return s, nil @@ -127,6 +138,20 @@ func WithNatTimeout(natTimeout time.Duration) Option { } } +// WithStreamDialer option function. +func WithStreamDialer(dialer transport.StreamDialer) Option { + return func(s *ssService) { + s.streamDialer = dialer + } +} + +// WithPacketListener option function. +func WithPacketListener(listener transport.PacketListener) Option { + return func(s *ssService) { + s.packetListener = listener + } +} + // HandleStream handles a Shadowsocks stream-based connection. func (s *ssService) HandleStream(ctx context.Context, conn transport.StreamConn) { var connMetrics TCPConnMetrics diff --git a/service/socketopts_linux.go b/service/socketopts_linux.go new file mode 100644 index 00000000..c6d807ca --- /dev/null +++ b/service/socketopts_linux.go @@ -0,0 +1,33 @@ +// Copyright 2024 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. + +//go:build linux + +package service + +import ( + "os" + "syscall" +) + +func SetFwmark(rc syscall.RawConn, fwmark uint) error { + var err error + rc.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, int(fwmark)) + }) + if err != nil { + return os.NewSyscallError("failed to set fwmark for socket", err) + } + return nil +} diff --git a/service/tcp.go b/service/tcp.go index 775509e9..23db7cd6 100644 --- a/service/tcp.go +++ b/service/tcp.go @@ -25,14 +25,14 @@ import ( "net" "net/netip" "sync" - "syscall" "time" "github.com/Jigsaw-Code/outline-sdk/transport" "github.com/Jigsaw-Code/outline-sdk/transport/shadowsocks" + "github.com/shadowsocks/go-shadowsocks2/socks" + onet "github.com/Jigsaw-Code/outline-ss-server/net" "github.com/Jigsaw-Code/outline-ss-server/service/metrics" - "github.com/shadowsocks/go-shadowsocks2/socks" ) // TCPConnMetrics is used to report metrics on TCP connections. @@ -170,19 +170,10 @@ func NewStreamHandler(authenticate StreamAuthenticateFunc, timeout time.Duration logger: noopLogger(), readTimeout: timeout, authenticate: authenticate, - dialer: defaultDialer, + dialer: MakeValidatingTCPStreamDialer(onet.RequirePublicIP, 0), } } -var defaultDialer = makeValidatingTCPStreamDialer(onet.RequirePublicIP) - -func makeValidatingTCPStreamDialer(targetIPValidator onet.TargetIPValidator) transport.StreamDialer { - return &transport.TCPDialer{Dialer: net.Dialer{Control: func(network, address string, c syscall.RawConn) error { - ip, _, _ := net.SplitHostPort(address) - return targetIPValidator(net.ParseIP(ip)) - }}} -} - // StreamHandler is a handler that handles stream connections. type StreamHandler interface { Handle(ctx context.Context, conn transport.StreamConn, connMetrics TCPConnMetrics) @@ -397,6 +388,8 @@ type NoOpTCPConnMetrics struct{} var _ TCPConnMetrics = (*NoOpTCPConnMetrics)(nil) func (m *NoOpTCPConnMetrics) AddAuthenticated(accessKey string) {} + func (m *NoOpTCPConnMetrics) AddClosed(status string, data metrics.ProxyMetrics, duration time.Duration) { } + func (m *NoOpTCPConnMetrics) AddProbe(status, drainResult string, clientProxyBytes int64) {} diff --git a/service/tcp_linux.go b/service/tcp_linux.go new file mode 100644 index 00000000..82c63870 --- /dev/null +++ b/service/tcp_linux.go @@ -0,0 +1,40 @@ +// Copyright 2024 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. + +//go:build linux + +package service + +import ( + "net" + "syscall" + + "github.com/Jigsaw-Code/outline-sdk/transport" + + onet "github.com/Jigsaw-Code/outline-ss-server/net" +) + +// fwmark can be used in conjunction with other Linux networking features like cgroups, network namespaces, and TC (Traffic Control) for sophisticated network management. +// Value of 0 disables fwmark (SO_MARK) (Linux Only) +func MakeValidatingTCPStreamDialer(targetIPValidator onet.TargetIPValidator, fwmark uint) transport.StreamDialer { + return &transport.TCPDialer{Dialer: net.Dialer{Control: func(network, address string, c syscall.RawConn) error { + if fwmark > 0 { + if err := SetFwmark(c, fwmark); err != nil { + return err + } + } + ip, _, _ := net.SplitHostPort(address) + return targetIPValidator(net.ParseIP(ip)) + }}} +} diff --git a/service/tcp_other.go b/service/tcp_other.go new file mode 100644 index 00000000..c86bb07c --- /dev/null +++ b/service/tcp_other.go @@ -0,0 +1,38 @@ +// Copyright 2024 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. + +//go:build !linux + +package service + +import ( + "net" + "syscall" + + "github.com/Jigsaw-Code/outline-sdk/transport" + + onet "github.com/Jigsaw-Code/outline-ss-server/net" +) + +// fwmark can be used in conjunction with other Linux networking features like cgroups, network namespaces, and TC (Traffic Control) for sophisticated network management. +// Value of 0 disables fwmark (SO_MARK) (Linux Only) +func MakeValidatingTCPStreamDialer(targetIPValidator onet.TargetIPValidator, fwmark uint) transport.StreamDialer { + if fwmark != 0 { + panic("fwmark is linux-specific feature and should be 0") + } + return &transport.TCPDialer{Dialer: net.Dialer{Control: func(network, address string, c syscall.RawConn) error { + ip, _, _ := net.SplitHostPort(address) + return targetIPValidator(net.ParseIP(ip)) + }}} +} diff --git a/service/tcp_test.go b/service/tcp_test.go index e69d1d1e..ab497f91 100644 --- a/service/tcp_test.go +++ b/service/tcp_test.go @@ -29,10 +29,11 @@ import ( "github.com/Jigsaw-Code/outline-sdk/transport" "github.com/Jigsaw-Code/outline-sdk/transport/shadowsocks" - "github.com/Jigsaw-Code/outline-ss-server/service/metrics" logging "github.com/op/go-logging" "github.com/shadowsocks/go-shadowsocks2/socks" "github.com/stretchr/testify/require" + + "github.com/Jigsaw-Code/outline-ss-server/service/metrics" ) func init() { @@ -215,8 +216,7 @@ func BenchmarkTCPFindCipherRepeat(b *testing.B) { } // Stub implementation for shadowsocks authentication metrics. -type fakeShadowsocksMetrics struct { -} +type fakeShadowsocksMetrics struct{} var _ ShadowsocksConnMetrics = (*fakeShadowsocksMetrics)(nil) @@ -232,6 +232,7 @@ type probeTestMetrics struct { } var _ TCPConnMetrics = (*probeTestMetrics)(nil) + var _ ShadowsocksConnMetrics = (*fakeShadowsocksMetrics)(nil) func (m *probeTestMetrics) AddClosed(status string, data metrics.ProxyMetrics, duration time.Duration) { @@ -367,7 +368,7 @@ func TestProbeClientBytesBasicTruncated(t *testing.T) { testMetrics := &probeTestMetrics{} authFunc := NewShadowsocksStreamAuthenticator(cipherList, nil, &fakeShadowsocksMetrics{}, nil) handler := NewStreamHandler(authFunc, 200*time.Millisecond) - handler.SetTargetDialer(makeValidatingTCPStreamDialer(allowAll)) + handler.SetTargetDialer(MakeValidatingTCPStreamDialer(allowAll, 0)) done := make(chan struct{}) go func() { StreamServe( @@ -405,7 +406,7 @@ func TestProbeClientBytesBasicModified(t *testing.T) { testMetrics := &probeTestMetrics{} authFunc := NewShadowsocksStreamAuthenticator(cipherList, nil, &fakeShadowsocksMetrics{}, nil) handler := NewStreamHandler(authFunc, 200*time.Millisecond) - handler.SetTargetDialer(makeValidatingTCPStreamDialer(allowAll)) + handler.SetTargetDialer(MakeValidatingTCPStreamDialer(allowAll, 0)) done := make(chan struct{}) go func() { StreamServe( @@ -444,7 +445,7 @@ func TestProbeClientBytesCoalescedModified(t *testing.T) { testMetrics := &probeTestMetrics{} authFunc := NewShadowsocksStreamAuthenticator(cipherList, nil, &fakeShadowsocksMetrics{}, nil) handler := NewStreamHandler(authFunc, 200*time.Millisecond) - handler.SetTargetDialer(makeValidatingTCPStreamDialer(allowAll)) + handler.SetTargetDialer(MakeValidatingTCPStreamDialer(allowAll, 0)) done := make(chan struct{}) go func() { StreamServe( diff --git a/service/udp.go b/service/udp.go index 94078029..52af2dc3 100644 --- a/service/udp.go +++ b/service/udp.go @@ -15,6 +15,7 @@ package service import ( + "context" "errors" "fmt" "log/slog" @@ -24,9 +25,11 @@ import ( "sync" "time" + "github.com/Jigsaw-Code/outline-sdk/transport" "github.com/Jigsaw-Code/outline-sdk/transport/shadowsocks" - onet "github.com/Jigsaw-Code/outline-ss-server/net" "github.com/shadowsocks/go-shadowsocks2/socks" + + onet "github.com/Jigsaw-Code/outline-ss-server/net" ) // UDPConnMetrics is used to report metrics on UDP connections. @@ -86,9 +89,10 @@ type packetHandler struct { m UDPMetrics ssm ShadowsocksConnMetrics targetIPValidator onet.TargetIPValidator + targetListener transport.PacketListener } -// NewPacketHandler creates a UDPService +// NewPacketHandler creates a PacketHandler func NewPacketHandler(natTimeout time.Duration, cipherList CipherList, m UDPMetrics, ssMetrics ShadowsocksConnMetrics) PacketHandler { if m == nil { m = &NoOpUDPMetrics{} @@ -103,6 +107,7 @@ func NewPacketHandler(natTimeout time.Duration, cipherList CipherList, m UDPMetr m: m, ssm: ssMetrics, targetIPValidator: onet.RequirePublicIP, + targetListener: MakeTargetUDPListener(0), } } @@ -112,6 +117,8 @@ type PacketHandler interface { SetLogger(l *slog.Logger) // SetTargetIPValidator sets the function to be used to validate the target IP addresses. SetTargetIPValidator(targetIPValidator onet.TargetIPValidator) + // SetTargetPacketListener sets the packet listener to use for target connections. + SetTargetPacketListener(targetListener transport.PacketListener) // Handle returns after clientConn closes and all the sub goroutines return. Handle(clientConn net.PacketConn) } @@ -127,6 +134,10 @@ func (h *packetHandler) SetTargetIPValidator(targetIPValidator onet.TargetIPVali h.targetIPValidator = targetIPValidator } +func (h *packetHandler) SetTargetPacketListener(targetListener transport.PacketListener) { + h.targetListener = targetListener +} + // Listen on addr for encrypted packets and basically do UDP NAT. // We take the ciphers as a pointer because it gets replaced on config updates. func (h *packetHandler) Handle(clientConn net.PacketConn) { @@ -181,10 +192,11 @@ func (h *packetHandler) Handle(clientConn net.PacketConn) { return onetErr } - udpConn, err := net.ListenPacket("udp", "") + udpConn, err := h.targetListener.ListenPacket(context.Background()) if err != nil { - return onet.NewConnectionError("ERR_CREATE_SOCKET", "Failed to create UDP socket", err) + return onet.NewConnectionError("ERR_CREATE_SOCKET", "Failed to create a `PacketConn`", err) } + targetConn = nm.Add(clientAddr, clientConn, cryptoKey, udpConn, keyID) } else { unpackStart := time.Now() @@ -468,8 +480,10 @@ var _ UDPConnMetrics = (*NoOpUDPConnMetrics)(nil) func (m *NoOpUDPConnMetrics) AddPacketFromClient(status string, clientProxyBytes, proxyTargetBytes int64) { } + func (m *NoOpUDPConnMetrics) AddPacketFromTarget(status string, targetProxyBytes, proxyClientBytes int64) { } + func (m *NoOpUDPConnMetrics) RemoveNatEntry() {} // NoOpUDPMetrics is a [UDPMetrics] that doesn't do anything. Useful in tests diff --git a/service/udp_linux.go b/service/udp_linux.go new file mode 100644 index 00000000..218727ad --- /dev/null +++ b/service/udp_linux.go @@ -0,0 +1,61 @@ +// Copyright 2024 Jigsaw Operations LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in comlniance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by aplnicable 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 imlnied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build linux + +package service + +import ( + "context" + "fmt" + "net" + + "github.com/Jigsaw-Code/outline-sdk/transport" +) + +type udpListener struct { + // fwmark can be used in conjunction with other Linux networking features like cgroups, network + // namespaces, and TC (Traffic Control) for sophisticated network management. + // Value of 0 disables fwmark (SO_MARK) (Linux only) + fwmark uint +} + +// NewPacketListener creates a new PacketListener that listens on UDP +// and optionally sets a firewall mark on the socket (Linux only). +func MakeTargetUDPListener(fwmark uint) transport.PacketListener { + return &udpListener{fwmark: fwmark} +} + +func (ln *udpListener) ListenPacket(ctx context.Context) (net.PacketConn, error) { + conn, err := net.ListenUDP("udp", nil) + if err != nil { + return nil, fmt.Errorf("Failed to create UDP socket: %w", err) + } + + if ln.fwmark > 0 { + rawConn, err := conn.SyscallConn() + if err != nil { + conn.Close() + return nil, fmt.Errorf("failed to get UDP raw connection: %w", err) + } + + err = SetFwmark(rawConn, ln.fwmark) + if err != nil { + conn.Close() + return nil, fmt.Errorf("Failed to set `fwmark`: %w", err) + + } + } + return conn, nil +} diff --git a/service/udp_other.go b/service/udp_other.go new file mode 100644 index 00000000..046cf910 --- /dev/null +++ b/service/udp_other.go @@ -0,0 +1,30 @@ +// Copyright 2024 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. + +//go:build !linux + +package service + +import ( + "github.com/Jigsaw-Code/outline-sdk/transport" +) + +// fwmark can be used in conjunction with other Linux networking features like cgroups, network namespaces, and TC (Traffic Control) for sophisticated network management. +// Value of 0 disables fwmark (SO_MARK) +func MakeTargetUDPListener(fwmark uint) transport.PacketListener { + if fwmark != 0 { + panic("fwmark is linux-specific feature and should be 0") + } + return &transport.UDPListener{Address: ""} +} diff --git a/service/udp_test.go b/service/udp_test.go index 6f620316..71aef184 100644 --- a/service/udp_test.go +++ b/service/udp_test.go @@ -23,18 +23,22 @@ import ( "time" "github.com/Jigsaw-Code/outline-sdk/transport/shadowsocks" - onet "github.com/Jigsaw-Code/outline-ss-server/net" logging "github.com/op/go-logging" "github.com/shadowsocks/go-shadowsocks2/socks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + onet "github.com/Jigsaw-Code/outline-ss-server/net" ) const timeout = 5 * time.Minute var clientAddr = net.UDPAddr{IP: []byte{192, 0, 2, 1}, Port: 12345} + var targetAddr = net.UDPAddr{IP: []byte{192, 0, 2, 2}, Port: 54321} + var dnsAddr = net.UDPAddr{IP: []byte{192, 0, 2, 3}, Port: 53} + var natCryptoKey *shadowsocks.EncryptionKey func init() { @@ -108,8 +112,10 @@ var _ UDPConnMetrics = (*fakeUDPConnMetrics)(nil) func (m *fakeUDPConnMetrics) AddPacketFromClient(status string, clientProxyBytes, proxyTargetBytes int64) { m.upstreamPackets = append(m.upstreamPackets, udpReport{m.clientAddr, m.accessKey, status, clientProxyBytes, proxyTargetBytes}) } + func (m *fakeUDPConnMetrics) AddPacketFromTarget(status string, targetProxyBytes, proxyClientBytes int64) { } + func (m *fakeUDPConnMetrics) RemoveNatEntry() { }