diff --git a/client/internal/engine_filedrop_dial.go b/client/internal/engine_filedrop_dial.go new file mode 100644 index 000000000..3ace5d214 --- /dev/null +++ b/client/internal/engine_filedrop_dial.go @@ -0,0 +1,14 @@ +//go:build !ios + +package internal + +import ( + "net" + + "github.com/netbirdio/netbird/client/internal/filedrop" +) + +func fileDropOSDial(WGIface) filedrop.DialFunc { + dialer := &net.Dialer{} + return dialer.DialContext +} diff --git a/client/internal/engine_filedrop_dial_ios.go b/client/internal/engine_filedrop_dial_ios.go new file mode 100644 index 000000000..367fa5473 --- /dev/null +++ b/client/internal/engine_filedrop_dial_ios.go @@ -0,0 +1,57 @@ +//go:build ios + +package internal + +import ( + "context" + "fmt" + "net" + "net/netip" + "syscall" + + "golang.org/x/sys/unix" + + "github.com/netbirdio/netbird/client/internal/filedrop" +) + +// fileDropOSDial scopes the dial to the tunnel interface, since a Network +// Extension's own unscoped sockets bypass its tunnel and leave on the +// physical interface. +func fileDropOSDial(wgIface WGIface) filedrop.DialFunc { + return func(ctx context.Context, network, addr string) (net.Conn, error) { + addrPort, err := netip.ParseAddrPort(addr) + if err != nil { + return nil, err + } + + osIface, err := net.InterfaceByName(wgIface.Name()) + if err != nil { + return nil, fmt.Errorf("lookup interface %q: %w", wgIface.Name(), err) + } + + wgAddr := wgIface.Address() + bindIP := wgAddr.IP + proto, opt := unix.IPPROTO_IP, unix.IP_BOUND_IF + if addrPort.Addr().Is6() { + if !wgAddr.HasIPv6() { + return nil, fmt.Errorf("no IPv6 address on %s", wgIface.Name()) + } + bindIP = wgAddr.IPv6 + proto, opt = unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF + } + + dialer := &net.Dialer{ + LocalAddr: net.TCPAddrFromAddrPort(netip.AddrPortFrom(bindIP, 0)), + Control: func(_, _ string, c syscall.RawConn) error { + var operr error + if err := c.Control(func(s uintptr) { + operr = unix.SetsockoptInt(int(s), proto, opt, osIface.Index) + }); err != nil { + return err + } + return operr + }, + } + return dialer.DialContext(ctx, network, addr) + } +}