Merge pull request #22 from fakeboboliu/main

add source ip support to macOS rootless solution
This commit is contained in:
tsosunchia
2023-11-12 00:20:18 +08:00
committed by GitHub
4 changed files with 56 additions and 6 deletions

View File

@@ -15,6 +15,8 @@ import (
"golang.org/x/net/context"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"github.com/nxtrace/NTrace-core/trace/internal"
)
type ICMPTracer struct {
@@ -67,7 +69,7 @@ func (t *ICMPTracer) Execute() (*Result, error) {
var err error
t.icmpListen, err = net.ListenPacket("ip4:1", t.SrcAddr)
t.icmpListen, err = internal.ListenICMP("ip4:1", t.SrcAddr)
if err != nil {
return &t.res, err
}

View File

@@ -13,6 +13,8 @@ import (
"golang.org/x/net/context"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv6"
"github.com/nxtrace/NTrace-core/trace/internal"
)
type ICMPTracerv6 struct {
@@ -65,7 +67,7 @@ func (t *ICMPTracerv6) Execute() (*Result, error) {
var err error
t.icmpListen, err = net.ListenPacket("ip6:58", t.SrcAddr)
t.icmpListen, err = internal.ListenICMP("ip6:58", t.SrcAddr)
if err != nil {
return &t.res, err
}

View File

@@ -12,13 +12,14 @@ import (
)
//go:linkname internetSocket net.internetSocket
func internetSocket(ctx context.Context, net string, laddr, raddr interface{}, sotype, proto int, mode string, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (fd unsafe.Pointer, err error)
func internetSocket(ctx context.Context, net string, laddr, raddr any, sotype, proto int, mode string, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (fd unsafe.Pointer, err error)
//go:linkname newIPConn net.newIPConn
func newIPConn(fd unsafe.Pointer) *net.IPConn
var (
errUnknownNetwork = errors.New("unknown network type")
errUnknownIface = errors.New("unknown network interface")
networkMap = map[string]string{
"ip4:icmp": "udp4",
@@ -28,7 +29,6 @@ var (
}
)
// ListenICMP 会造成指定出口IP功能不可使用
func ListenICMP(network string, laddr string) (net.PacketConn, error) {
if os.Getuid() == 0 { // root
return net.ListenPacket(network, laddr)
@@ -38,7 +38,48 @@ func ListenICMP(network string, laddr string) (net.PacketConn, error) {
if nw == "udp6" {
proto = syscall.IPPROTO_ICMPV6
}
isock, err := internetSocket(context.Background(), nw, nil, nil, syscall.SOCK_DGRAM, proto, "listen", nil)
var ifIndex int = -1
if laddr != "" {
la := net.ParseIP(laddr)
if ifaces, err := net.Interfaces(); err == nil {
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok {
if ipnet.IP.Equal(la) {
ifIndex = iface.Index
break
}
}
}
}
if ifIndex == -1 {
return nil, errUnknownIface
}
} else {
return nil, err
}
}
isock, err := internetSocket(context.Background(), nw, nil, nil, syscall.SOCK_DGRAM, proto, "listen",
func(ctx context.Context, network, address string, c syscall.RawConn) error {
if ifIndex != -1 {
if proto == syscall.IPPROTO_ICMP {
return c.Control(func(fd uintptr) {
syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BOUND_IF, ifIndex)
})
} else {
return c.Control(func(fd uintptr) {
syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BOUND_IF, ifIndex)
})
}
}
return nil
})
if err != nil {
panic(err)
}

View File

@@ -7,6 +7,7 @@ import (
"strconv"
"strings"
"sync"
"syscall"
"time"
"github.com/nxtrace/NTrace-core/ipgeo"
@@ -92,7 +93,11 @@ func Traceroute(method Method, config Config) (*Result, error) {
default:
return &Result{}, ErrInvalidMethod
}
return tracer.Execute()
result, err := tracer.Execute()
if err != nil && errors.Is(err, syscall.EPERM) {
err = fmt.Errorf("%w, please run as root", err)
}
return result, err
}
type Result struct {