Files
netbird/client/internal/engine_filedrop_dial_ios.go
T
Zoltán Papp 898539381c [client] Scope the iOS file drop dial to the tunnel interface
A Network Extension's own unscoped sockets bypass its tunnel and leave on
the physical interface, so the file drop dial to a peer address died on
the local network. Bind the socket to the overlay address and the tunnel
interface index, the same way the iOS DNS upstream client dials. Other
platforms keep the plain dialer: Android and the desktops route the
client's own traffic into the tunnel already.
2026-08-27 16:10:25 +02:00

58 lines
1.4 KiB
Go

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