From eec2fc325a92ac0a924fe0688d5889aba7c10b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 12 Apr 2024 09:05:39 +0800 Subject: [PATCH] Improve interface finder --- common/control/bind_finder.go | 30 +++------ common/control/bind_finder_default.go | 97 +++++++++++++++++++++++++++ common/metadata/addr.go | 12 +++- common/network/addr.go | 2 +- protocol/socks/handshake.go | 2 +- 5 files changed, 119 insertions(+), 24 deletions(-) create mode 100644 common/control/bind_finder_default.go diff --git a/common/control/bind_finder.go b/common/control/bind_finder.go index 21820fb..47e6a8e 100644 --- a/common/control/bind_finder.go +++ b/common/control/bind_finder.go @@ -1,30 +1,18 @@ package control -import "net" +import ( + "net/netip" +) type InterfaceFinder interface { InterfaceIndexByName(name string) (int, error) InterfaceNameByIndex(index int) (string, error) + InterfaceByAddr(addr netip.Addr) (*Interface, error) } -func DefaultInterfaceFinder() InterfaceFinder { - return (*netInterfaceFinder)(nil) -} - -type netInterfaceFinder struct{} - -func (w *netInterfaceFinder) InterfaceIndexByName(name string) (int, error) { - netInterface, err := net.InterfaceByName(name) - if err != nil { - return 0, err - } - return netInterface.Index, nil -} - -func (w *netInterfaceFinder) InterfaceNameByIndex(index int) (string, error) { - netInterface, err := net.InterfaceByIndex(index) - if err != nil { - return "", err - } - return netInterface.Name, nil +type Interface struct { + Index int + MTU int + Name string + Addresses []netip.Prefix } diff --git a/common/control/bind_finder_default.go b/common/control/bind_finder_default.go new file mode 100644 index 0000000..d8c2eed --- /dev/null +++ b/common/control/bind_finder_default.go @@ -0,0 +1,97 @@ +package control + +import ( + "net" + "net/netip" + _ "unsafe" + + "github.com/sagernet/sing/common" + M "github.com/sagernet/sing/common/metadata" +) + +type DefaultInterfaceFinder struct { + interfaces []Interface +} + +func NewDefaultInterfaceFinder() *DefaultInterfaceFinder { + return &DefaultInterfaceFinder{} +} + +func (f *DefaultInterfaceFinder) Update() error { + netIfs, err := net.Interfaces() + if err != nil { + return err + } + interfaces := make([]Interface, 0, len(netIfs)) + for _, netIf := range netIfs { + ifAddrs, err := netIf.Addrs() + if err != nil { + return err + } + interfaces = append(interfaces, Interface{ + Index: netIf.Index, + MTU: netIf.MTU, + Name: netIf.Name, + Addresses: common.Map(ifAddrs, M.PrefixFromNet), + }) + } + f.interfaces = interfaces + return nil +} + +func (f *DefaultInterfaceFinder) UpdateInterfaces(interfaces []Interface) { + f.interfaces = interfaces +} + +func (f *DefaultInterfaceFinder) InterfaceIndexByName(name string) (int, error) { + for _, netInterface := range f.interfaces { + if netInterface.Name == name { + return netInterface.Index, nil + } + } + netInterface, err := net.InterfaceByName(name) + if err != nil { + return 0, err + } + f.Update() + return netInterface.Index, nil +} + +func (f *DefaultInterfaceFinder) InterfaceNameByIndex(index int) (string, error) { + for _, netInterface := range f.interfaces { + if netInterface.Index == index { + return netInterface.Name, nil + } + } + netInterface, err := net.InterfaceByIndex(index) + if err != nil { + return "", err + } + f.Update() + return netInterface.Name, nil +} + +//go:linkname errNoSuchInterface net.errNoSuchInterface +var errNoSuchInterface error + +func (f *DefaultInterfaceFinder) InterfaceByAddr(addr netip.Addr) (*Interface, error) { + for _, netInterface := range f.interfaces { + for _, prefix := range netInterface.Addresses { + if prefix.Contains(addr) { + return &netInterface, nil + } + } + } + err := f.Update() + if err != nil { + return nil, err + } + for _, netInterface := range f.interfaces { + for _, prefix := range netInterface.Addresses { + if prefix.Contains(addr) { + return &netInterface, nil + } + } + } + return nil, &net.OpError{Op: "route", Net: "ip+net", Source: nil, Addr: &net.IPAddr{IP: addr.AsSlice()}, Err: errNoSuchInterface} +} diff --git a/common/metadata/addr.go b/common/metadata/addr.go index e1b9eaf..6da2d22 100644 --- a/common/metadata/addr.go +++ b/common/metadata/addr.go @@ -141,7 +141,7 @@ func SocksaddrFromNet(ap net.Addr) Socksaddr { return ParseSocksaddr(ap.String()) } -func AddrFromNetAddr(netAddr net.Addr) netip.Addr { +func AddrFromNet(netAddr net.Addr) netip.Addr { if addr := AddrPortFromNet(netAddr); addr.Addr().IsValid() { return addr.Addr() } @@ -157,6 +157,16 @@ func AddrFromNetAddr(netAddr net.Addr) netip.Addr { } } +func PrefixFromNet(netAddr net.Addr) netip.Prefix { + switch addr := netAddr.(type) { + case *net.IPNet: + bits, _ := addr.Mask.Size() + return netip.PrefixFrom(AddrFromIP(addr.IP), bits) + default: + return netip.Prefix{} + } +} + func AddrPortFromNet(netAddr net.Addr) netip.AddrPort { var ip net.IP var port uint16 diff --git a/common/network/addr.go b/common/network/addr.go index 87f59d7..0b9ada8 100644 --- a/common/network/addr.go +++ b/common/network/addr.go @@ -13,7 +13,7 @@ func LocalAddrs() ([]netip.Addr, error) { if err != nil { return nil, err } - return common.Map(interfaceAddrs, M.AddrFromNetAddr), nil + return common.Map(interfaceAddrs, M.AddrFromNet), nil } func IsPublicAddr(addr netip.Addr) bool { diff --git a/protocol/socks/handshake.go b/protocol/socks/handshake.go index a8736ff..2a2cecf 100644 --- a/protocol/socks/handshake.go +++ b/protocol/socks/handshake.go @@ -203,7 +203,7 @@ func HandleConnection0(ctx context.Context, conn net.Conn, version byte, authent return handler.NewConnection(ctx, conn, metadata) case socks5.CommandUDPAssociate: var udpConn *net.UDPConn - udpConn, err = net.ListenUDP(M.NetworkFromNetAddr("udp", M.AddrFromNetAddr(conn.LocalAddr())), net.UDPAddrFromAddrPort(netip.AddrPortFrom(M.AddrFromNetAddr(conn.LocalAddr()), 0))) + udpConn, err = net.ListenUDP(M.NetworkFromNetAddr("udp", M.AddrFromNet(conn.LocalAddr())), net.UDPAddrFromAddrPort(netip.AddrPortFrom(M.AddrFromNet(conn.LocalAddr()), 0))) if err != nil { return err }