Merge pull request #338 from hellodword/bind_to_device

feat: bind socket to a particular device
This commit is contained in:
Toby 2022-06-08 20:49:36 -07:00 committed by GitHub
commit 595d1bda8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 96 additions and 1 deletions

View File

@ -63,6 +63,10 @@ type serverConfig struct {
User string `json:"user"`
Password string `json:"password"`
} `json:"socks5_outbound"`
BindOutbound struct {
Address string `json:"address"`
Device string `json:"device"`
} `json:"bind_outbound"`
}
func (c *serverConfig) Speed() (uint64, uint64, error) {

View File

@ -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,27 @@ func server(config *serverConfig) {
}
transport.DefaultServerTransport.SOCKS5Client = ob
}
// 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 find the interface")
}
transport.DefaultServerTransport.LocalUDPIntf = iface
sockopt.BindDialer(transport.DefaultServerTransport.Dialer, iface)
}
if config.BindOutbound.Address != "" {
ip := net.ParseIP(config.BindOutbound.Address)
if ip == nil {
logrus.WithFields(logrus.Fields{
"error": err,
}).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
if len(config.ACL) > 0 {

1
go.mod
View File

@ -22,6 +22,7 @@ require (
github.com/spf13/viper v1.12.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 (

23
pkg/sockopt/sockopt.go Normal file
View File

@ -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)
}

View File

@ -0,0 +1,22 @@
package sockopt
import (
"net"
"syscall"
"golang.org/x/sys/unix"
)
func bindRawConn(network string, c syscall.RawConn, bindIface *net.Interface) error {
var err1, err2 error
err1 = c.Control(func(fd uintptr) {
if bindIface != nil {
err2 = unix.BindToDevice(int(fd), bindIface.Name)
}
})
if err1 != nil {
return err1
} else {
return err2
}
}

View File

@ -0,0 +1,13 @@
//go:build !linux
package sockopt
import (
"errors"
"net"
"syscall"
)
func bindRawConn(network string, c syscall.RawConn, bindIface *net.Interface) error {
return errors.New("binding interface is not supported on the current system")
}

View File

@ -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
LocalUDPAddr *net.UDPAddr
LocalUDPIntf *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.LocalUDPAddr)
if err != nil {
return nil, err
}
if st.LocalUDPIntf != nil {
err = sockopt.BindUDPConn("udp", conn, st.LocalUDPIntf)
if err != nil {
conn.Close()
return nil, err
}
}
return &udpConnPUDPConn{
Conn: conn,
}, nil