From c8c0cdab91a98a75a01e58e3dd39e7dc5e69d1ec Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Sun, 29 Sep 2024 23:16:14 +0300 Subject: [PATCH] tun: split tun_{bsd -> freebsd,openbsd}.go The address ioctl code is just plain broken. I fixed it for OpenBSD, but have no resources or time for FreeBSD, so there the code will stay as-is. FreeBSD also supports stuff like netlink, so perhaps a future rewrite will look different from OpenBSD's ioctl code (the only approach) anyway. --- src/tun/{tun_bsd.go => tun_freebsd.go} | 4 +- src/tun/tun_openbsd.go | 140 +++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 2 deletions(-) rename src/tun/{tun_bsd.go => tun_freebsd.go} (98%) create mode 100644 src/tun/tun_openbsd.go diff --git a/src/tun/tun_bsd.go b/src/tun/tun_freebsd.go similarity index 98% rename from src/tun/tun_bsd.go rename to src/tun/tun_freebsd.go index 3064546b..7b8ab50c 100644 --- a/src/tun/tun_bsd.go +++ b/src/tun/tun_freebsd.go @@ -1,5 +1,5 @@ -//go:build openbsd || freebsd -// +build openbsd freebsd +//go:build freebsd +// +build freebsd package tun diff --git a/src/tun/tun_openbsd.go b/src/tun/tun_openbsd.go new file mode 100644 index 00000000..221fe44c --- /dev/null +++ b/src/tun/tun_openbsd.go @@ -0,0 +1,140 @@ +//go:build openbsd +// +build openbsd + +package tun + +import ( + "encoding/binary" + "fmt" + "os/exec" + "strconv" + "strings" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" + + wgtun "golang.zx2c4.com/wireguard/tun" +) + +const SIOCSIFADDR_IN6 = (0x80000000) | ((288 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 12 + +type in6_addrlifetime struct { + ia6t_expire float64 + ia6t_preferred float64 + ia6t_vltime uint32 + ia6t_pltime uint32 +} + +type sockaddr_in6 struct { + sin6_len uint8 + sin6_family uint8 + sin6_port uint8 + sin6_flowinfo uint32 + sin6_addr [8]uint16 + sin6_scope_id uint32 +} + +/* +from +struct in6_ifreq { + 277 char ifr_name[IFNAMSIZ]; + 278 union { + 279 struct sockaddr_in6 ifru_addr; + 280 struct sockaddr_in6 ifru_dstaddr; + 281 int ifru_flags; + 282 int ifru_flags6; + 283 int ifru_metric; + 284 caddr_t ifru_data; + 285 struct in6_addrlifetime ifru_lifetime; + 286 struct in6_ifstat ifru_stat; + 287 struct icmp6_ifstat ifru_icmp6stat; + 288 u_int32_t ifru_scope_id[16]; + 289 } ifr_ifru; + 290 }; +*/ + +type in6_ifreq_addr struct { + ifr_name [syscall.IFNAMSIZ]byte + ifru_addr sockaddr_in6 +} + +type in6_ifreq_flags struct { + ifr_name [syscall.IFNAMSIZ]byte + flags int +} + +type in6_ifreq_lifetime struct { + ifr_name [syscall.IFNAMSIZ]byte + ifru_addrlifetime in6_addrlifetime +} + +// Configures the TUN adapter with the correct IPv6 address and MTU. +func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error { + iface, err := wgtun.CreateTUN(ifname, int(mtu)) + if err != nil { + return fmt.Errorf("failed to create TUN: %w", err) + } + tun.iface = iface + if mtu, err := iface.MTU(); err == nil { + tun.mtu = getSupportedMTU(uint64(mtu)) + } else { + tun.mtu = 0 + } + if addr != "" { + return tun.setupAddress(addr) + } + return nil +} + +// Configures the "utun" adapter from an existing file descriptor. +func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error { + return fmt.Errorf("setup via FD not supported on this platform") +} + +func (tun *TunAdapter) setupAddress(addr string) error { + var sfd int + var err error + + // Create system socket + if sfd, err = unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0); err != nil { + tun.log.Printf("Create AF_INET socket failed: %v.", err) + return err + } + + // Friendly output + tun.log.Infof("Interface name: %s", tun.Name()) + tun.log.Infof("Interface IPv6: %s", addr) + tun.log.Infof("Interface MTU: %d", tun.mtu) + + // Create the address request + // FIXME: I don't work! + var ar in6_ifreq_addr + copy(ar.ifr_name[:], tun.Name()) + ar.ifru_addr.sin6_len = uint8(unsafe.Sizeof(ar.ifru_addr)) + ar.ifru_addr.sin6_family = unix.AF_INET6 + parts := strings.Split(strings.Split(addr, "/")[0], ":") + for i := 0; i < 8; i++ { + addr, _ := strconv.ParseUint(parts[i], 16, 16) + b := make([]byte, 16) + binary.LittleEndian.PutUint16(b, uint16(addr)) + ar.ifru_addr.sin6_addr[i] = uint16(binary.BigEndian.Uint16(b)) + } + + // Set the interface address + if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(SIOCSIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { + err = errno + tun.log.Errorf("Error in SIOCSIFADDR_IN6: %v", errno) + + // Fall back to ifconfig to set the address + cmd := exec.Command("ifconfig", tun.Name(), "inet6", addr) + tun.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) + output, err := cmd.CombinedOutput() + if err != nil { + tun.log.Errorf("SIOCSIFADDR_IN6 fallback failed: %v.", err) + tun.log.Traceln(string(output)) + } + } + + return nil +}