From 32f35894cc750fb96b34e567d6bbda0807f71df9 Mon Sep 17 00:00:00 2001 From: hellodword <46193371+hellodword@users.noreply.github.com> Date: Mon, 23 May 2022 18:13:24 +0800 Subject: [PATCH 1/2] feat: bind socket to a particular device --- cmd/config.go | 4 ++++ cmd/server.go | 28 ++++++++++++++++++++++++++++ go.mod | 2 +- pkg/sockopt/sockopt.go | 23 +++++++++++++++++++++++ pkg/sockopt/sockopt_linux.go | 16 ++++++++++++++++ pkg/sockopt/sockopt_others.go | 10 ++++++++++ pkg/transport/server.go | 12 +++++++++++- 7 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 pkg/sockopt/sockopt.go create mode 100644 pkg/sockopt/sockopt_linux.go create mode 100644 pkg/sockopt/sockopt_others.go diff --git a/cmd/config.go b/cmd/config.go index ce5edb6..38ff2b4 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -63,6 +63,10 @@ type serverConfig struct { User string `json:"user"` Password string `json:"password"` } `json:"socks5_outbound"` + SocketConfig struct { + BindAddress string `json:"bind_address"` + BindToDevice string `json:"bind_to_device"` + } `json:"socket_config"` } func (c *serverConfig) Speed() (uint64, uint64, error) { diff --git a/cmd/server.go b/cmd/server.go index a738e1d..0c42297 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -15,6 +15,7 @@ import ( "github.com/tobyxdd/hysteria/pkg/core" "github.com/tobyxdd/hysteria/pkg/obfs" "github.com/tobyxdd/hysteria/pkg/pmtud_fix" + "github.com/tobyxdd/hysteria/pkg/sockopt" "github.com/tobyxdd/hysteria/pkg/transport" "github.com/yosuke-furukawa/json5/encoding/json5" "io" @@ -163,6 +164,33 @@ func server(config *serverConfig) { } transport.DefaultServerTransport.SOCKS5Client = ob } + // socket settings + if config.SocketConfig.BindToDevice != "" { + iface, err := net.InterfaceByName(config.SocketConfig.BindToDevice) + if err != nil { + logrus.WithFields(logrus.Fields{ + "error": err, + }).Fatal("Failed to get bind_to_device") + } + transport.DefaultServerTransport.Intf = iface + sockopt.BindDialer(transport.DefaultServerTransport.Dialer, iface) + } + if config.SocketConfig.BindAddress != "" { + ip := net.ParseIP(config.SocketConfig.BindAddress) + if ip == nil { + logrus.WithFields(logrus.Fields{ + "error": err, + }).Fatal("Failed to parse bind_address") + } + transport.DefaultServerTransport.Dialer.LocalAddr = &net.TCPAddr{ + IP: ip, + Port: 0, + } + transport.DefaultServerTransport.LocalAddrUDP = &net.UDPAddr{ + IP: ip, + Port: 0, + } + } // ACL var aclEngine *acl.Engine if len(config.ACL) > 0 { diff --git a/go.mod b/go.mod index 24a8a1c..61b94af 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/spf13/viper v1.11.0 github.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a github.com/yosuke-furukawa/json5 v0.1.1 + golang.org/x/sys v0.0.0-20220412211240-33da011f77ad ) require ( @@ -66,7 +67,6 @@ require ( golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect golang.org/x/mod v0.5.0 // indirect golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect - golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect diff --git a/pkg/sockopt/sockopt.go b/pkg/sockopt/sockopt.go new file mode 100644 index 0000000..d33826b --- /dev/null +++ b/pkg/sockopt/sockopt.go @@ -0,0 +1,23 @@ +package sockopt + +import ( + "net" + "syscall" +) + +//https://github.com/v2fly/v2ray-core/blob/4e247840821f3dd326722d4db02ee3c237074fc2/transport/internet/config.pb.go#L420-L426 + +func BindDialer(d *net.Dialer, intf *net.Interface) { + d.Control = func(network, address string, c syscall.RawConn) error { + return bindRawConn(network, c, intf) + } +} + +func BindUDPConn(network string, conn *net.UDPConn, intf *net.Interface) error { + c, err := conn.SyscallConn() + if err != nil { + return err + } + + return bindRawConn(network, c, intf) +} diff --git a/pkg/sockopt/sockopt_linux.go b/pkg/sockopt/sockopt_linux.go new file mode 100644 index 0000000..1fa795f --- /dev/null +++ b/pkg/sockopt/sockopt_linux.go @@ -0,0 +1,16 @@ +package sockopt + +import ( + "net" + "syscall" + + "golang.org/x/sys/unix" +) + +func bindRawConn(network string, c syscall.RawConn, bindIface *net.Interface) error { + return c.Control(func(fd uintptr) { + if bindIface != nil { + unix.BindToDevice(int(fd), bindIface.Name) + } + }) +} diff --git a/pkg/sockopt/sockopt_others.go b/pkg/sockopt/sockopt_others.go new file mode 100644 index 0000000..1694d53 --- /dev/null +++ b/pkg/sockopt/sockopt_others.go @@ -0,0 +1,10 @@ +//go:build !linux + +package sockopt + +import ( + "net" + "syscall" +) + +func bindRawConn(network string, c syscall.RawConn, bindIface *net.Interface) error { return nil } diff --git a/pkg/transport/server.go b/pkg/transport/server.go index c771dfc..8e03379 100644 --- a/pkg/transport/server.go +++ b/pkg/transport/server.go @@ -8,6 +8,7 @@ import ( "github.com/tobyxdd/hysteria/pkg/conns/udp" "github.com/tobyxdd/hysteria/pkg/conns/wechat" "github.com/tobyxdd/hysteria/pkg/obfs" + "github.com/tobyxdd/hysteria/pkg/sockopt" "github.com/tobyxdd/hysteria/pkg/utils" "net" "strconv" @@ -20,6 +21,8 @@ type ServerTransport struct { PrefEnabled bool PrefIPv6 bool PrefExclusive bool + LocalAddrUDP *net.UDPAddr + Intf *net.Interface } // AddrEx is like net.TCPAddr or net.UDPAddr, but with additional domain information for SOCKS5. @@ -164,10 +167,17 @@ func (st *ServerTransport) ListenUDP() (PUDPConn, error) { if st.SOCKS5Client != nil { return st.SOCKS5Client.ListenUDP() } else { - conn, err := net.ListenUDP("udp", nil) + conn, err := net.ListenUDP("udp", st.LocalAddrUDP) if err != nil { return nil, err } + if st.Intf != nil { + err = sockopt.BindUDPConn("udp", conn, st.Intf) + if err != nil { + conn.Close() + return nil, err + } + } return &udpConnPUDPConn{ Conn: conn, }, nil From 6d8e79170e083ff4093a1748b549fb0af2a1be2b Mon Sep 17 00:00:00 2001 From: Toby Date: Mon, 6 Jun 2022 16:06:30 -0700 Subject: [PATCH 2/2] chore: code optimizations --- cmd/config.go | 8 ++++---- cmd/server.go | 26 ++++++++++---------------- pkg/sockopt/sockopt.go | 2 +- pkg/sockopt/sockopt_linux.go | 10 ++++++++-- pkg/sockopt/sockopt_others.go | 5 ++++- pkg/transport/server.go | 10 +++++----- 6 files changed, 32 insertions(+), 29 deletions(-) diff --git a/cmd/config.go b/cmd/config.go index 38ff2b4..abb2339 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -63,10 +63,10 @@ type serverConfig struct { User string `json:"user"` Password string `json:"password"` } `json:"socks5_outbound"` - SocketConfig struct { - BindAddress string `json:"bind_address"` - BindToDevice string `json:"bind_to_device"` - } `json:"socket_config"` + BindOutbound struct { + Address string `json:"address"` + Device string `json:"device"` + } `json:"bind_outbound"` } func (c *serverConfig) Speed() (uint64, uint64, error) { diff --git a/cmd/server.go b/cmd/server.go index 0c42297..5334ac2 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -164,32 +164,26 @@ func server(config *serverConfig) { } transport.DefaultServerTransport.SOCKS5Client = ob } - // socket settings - if config.SocketConfig.BindToDevice != "" { - iface, err := net.InterfaceByName(config.SocketConfig.BindToDevice) + // Bind outbound + if config.BindOutbound.Device != "" { + iface, err := net.InterfaceByName(config.BindOutbound.Device) if err != nil { logrus.WithFields(logrus.Fields{ "error": err, - }).Fatal("Failed to get bind_to_device") + }).Fatal("Failed to find the interface") } - transport.DefaultServerTransport.Intf = iface + transport.DefaultServerTransport.LocalUDPIntf = iface sockopt.BindDialer(transport.DefaultServerTransport.Dialer, iface) } - if config.SocketConfig.BindAddress != "" { - ip := net.ParseIP(config.SocketConfig.BindAddress) + if config.BindOutbound.Address != "" { + ip := net.ParseIP(config.BindOutbound.Address) if ip == nil { logrus.WithFields(logrus.Fields{ "error": err, - }).Fatal("Failed to parse bind_address") - } - transport.DefaultServerTransport.Dialer.LocalAddr = &net.TCPAddr{ - IP: ip, - Port: 0, - } - transport.DefaultServerTransport.LocalAddrUDP = &net.UDPAddr{ - IP: ip, - Port: 0, + }).Fatal("Failed to parse the address") } + transport.DefaultServerTransport.Dialer.LocalAddr = &net.TCPAddr{IP: ip} + transport.DefaultServerTransport.LocalUDPAddr = &net.UDPAddr{IP: ip} } // ACL var aclEngine *acl.Engine diff --git a/pkg/sockopt/sockopt.go b/pkg/sockopt/sockopt.go index d33826b..db97810 100644 --- a/pkg/sockopt/sockopt.go +++ b/pkg/sockopt/sockopt.go @@ -5,7 +5,7 @@ import ( "syscall" ) -//https://github.com/v2fly/v2ray-core/blob/4e247840821f3dd326722d4db02ee3c237074fc2/transport/internet/config.pb.go#L420-L426 +// https://github.com/v2fly/v2ray-core/blob/4e247840821f3dd326722d4db02ee3c237074fc2/transport/internet/config.pb.go#L420-L426 func BindDialer(d *net.Dialer, intf *net.Interface) { d.Control = func(network, address string, c syscall.RawConn) error { diff --git a/pkg/sockopt/sockopt_linux.go b/pkg/sockopt/sockopt_linux.go index 1fa795f..e7df7a8 100644 --- a/pkg/sockopt/sockopt_linux.go +++ b/pkg/sockopt/sockopt_linux.go @@ -8,9 +8,15 @@ import ( ) func bindRawConn(network string, c syscall.RawConn, bindIface *net.Interface) error { - return c.Control(func(fd uintptr) { + var err1, err2 error + err1 = c.Control(func(fd uintptr) { if bindIface != nil { - unix.BindToDevice(int(fd), bindIface.Name) + err2 = unix.BindToDevice(int(fd), bindIface.Name) } }) + if err1 != nil { + return err1 + } else { + return err2 + } } diff --git a/pkg/sockopt/sockopt_others.go b/pkg/sockopt/sockopt_others.go index 1694d53..af0a107 100644 --- a/pkg/sockopt/sockopt_others.go +++ b/pkg/sockopt/sockopt_others.go @@ -3,8 +3,11 @@ package sockopt import ( + "errors" "net" "syscall" ) -func bindRawConn(network string, c syscall.RawConn, bindIface *net.Interface) error { return nil } +func bindRawConn(network string, c syscall.RawConn, bindIface *net.Interface) error { + return errors.New("binding interface is not supported on the current system") +} diff --git a/pkg/transport/server.go b/pkg/transport/server.go index 8e03379..0805f1a 100644 --- a/pkg/transport/server.go +++ b/pkg/transport/server.go @@ -21,8 +21,8 @@ type ServerTransport struct { PrefEnabled bool PrefIPv6 bool PrefExclusive bool - LocalAddrUDP *net.UDPAddr - Intf *net.Interface + LocalUDPAddr *net.UDPAddr + LocalUDPIntf *net.Interface } // AddrEx is like net.TCPAddr or net.UDPAddr, but with additional domain information for SOCKS5. @@ -167,12 +167,12 @@ func (st *ServerTransport) ListenUDP() (PUDPConn, error) { if st.SOCKS5Client != nil { return st.SOCKS5Client.ListenUDP() } else { - conn, err := net.ListenUDP("udp", st.LocalAddrUDP) + conn, err := net.ListenUDP("udp", st.LocalUDPAddr) if err != nil { return nil, err } - if st.Intf != nil { - err = sockopt.BindUDPConn("udp", conn, st.Intf) + if st.LocalUDPIntf != nil { + err = sockopt.BindUDPConn("udp", conn, st.LocalUDPIntf) if err != nil { conn.Close() return nil, err