support bring up net interface on darwin

This commit is contained in:
miloschwartz
2025-02-22 00:11:25 -05:00
parent 8d46ae3aa2
commit 02eab1ff52

109
main.go
View File

@@ -8,12 +8,13 @@ import (
"fmt"
"net"
"os"
"os/exec"
"os/signal"
"regexp"
"runtime"
"strconv"
"strings"
"syscall"
"unsafe"
"github.com/fosrl/newt/logger"
"github.com/fosrl/olm/websocket"
@@ -146,7 +147,10 @@ func resolveDomain(domain string) (string, error) {
}
// ConfigureInterface configures a network interface with an IP address and brings it up
func ConfigureInterface(interfaceName string, ipAddr string) error {
func ConfigureInterface(interfaceName string, wgData WgData) error {
var ipAddr string = wgData.TunnelIP
var destIP string = wgData.ServerIP
// Parse the IP address and network
ip, ipNet, err := net.ParseCIDR(ipAddr)
if err != nil {
@@ -157,69 +161,53 @@ func ConfigureInterface(interfaceName string, ipAddr string) error {
case "linux":
return configureLinux(interfaceName, ip, ipNet)
case "darwin":
return configureDarwin(interfaceName, ip, ipNet)
return configureDarwin(interfaceName, ip, destIP)
default:
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
}
}
func configureDarwin(interfaceName string, ip net.IP, ipNet *net.IPNet) error {
// Get interface by name
func findUnusedUTUN() (string, error) {
ifaces, err := net.Interfaces()
if err != nil {
return "", fmt.Errorf("failed to list interfaces: %v", err)
}
used := make(map[int]bool)
re := regexp.MustCompile(`^utun(\d+)$`)
for _, iface := range ifaces {
if matches := re.FindStringSubmatch(iface.Name); len(matches) == 2 {
if num, err := strconv.Atoi(matches[1]); err == nil {
used[num] = true
}
}
}
// Try utun0 up to utun255.
for i := 0; i < 256; i++ {
if !used[i] {
return fmt.Sprintf("utun%d", i), nil
}
}
return "", fmt.Errorf("no unused utun interface found")
}
func configureDarwin(interfaceName string, ip net.IP, destIp string) error {
logger.Info("Configuring darwin interface: %s", interfaceName)
iface, err := net.InterfaceByName(interfaceName)
if err != nil {
return fmt.Errorf("failed to get interface %s: %v", interfaceName, err)
}
// print something using the iface
logger.Info("Interface %s: %v", interfaceName, iface)
// Create socket
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)
ipStr := ip.String()
cmd := exec.Command("ifconfig", interfaceName, ipStr+"/24", destIp, "up")
// print the command used
logger.Info("Running command: %v", cmd)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("failed to create socket: %v", err)
}
defer syscall.Close(fd)
// Prepare interface request structure
ifr := struct {
Name [16]byte
Flags uint16
}{}
copy(ifr.Name[:], interfaceName)
// Get current flags
if err := ioctl(fd, syscall.SIOCGIFFLAGS, uintptr(unsafe.Pointer(&ifr))); err != nil {
return fmt.Errorf("failed to get interface flags: %v", err)
}
// Set interface up
ifr.Flags |= syscall.IFF_UP | syscall.IFF_RUNNING
if err := ioctl(fd, syscall.SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifr))); err != nil {
return fmt.Errorf("failed to set interface up: %v", err)
}
// Prepare address structure
var addr syscall.SockaddrInet4
copy(addr.Addr[:], ip.To4())
// Create interface address request
ifra := struct {
Name [16]byte
Addr syscall.RawSockaddrInet4
Mask syscall.RawSockaddrInet4
}{}
copy(ifra.Name[:], interfaceName)
copy(ifra.Addr.Addr[:], ip.To4())
copy(ifra.Mask.Addr[:], ipNet.Mask)
// Set IP address
if err := ioctl(fd, syscall.SIOCSIFADDR, uintptr(unsafe.Pointer(&ifra))); err != nil {
return fmt.Errorf("failed to set interface address: %v", err)
}
// Set netmask
if err := ioctl(fd, syscall.SIOCSIFNETMASK, uintptr(unsafe.Pointer(&ifra))); err != nil {
return fmt.Errorf("failed to set interface netmask: %v", err)
return fmt.Errorf("ifconfig command failed: %v, output: %s", err, out)
}
return nil
@@ -295,7 +283,7 @@ func main() {
reachableAt = os.Getenv("REACHABLE_AT")
if endpoint == "" {
flag.StringVar(&endpoint, "endpoint", "", "Endpoint of your pangolin server")
flag.StringVar(&endpoint, "endpoint", "", "Endpoint of your Pangolin server")
}
if id == "" {
flag.StringVar(&id, "id", "", "Olm ID")
@@ -383,8 +371,19 @@ func main() {
return
}
// NEED TO DETERMINE AVAILABLE TUN DEVICE HERE
tdev, err := func() (tun.Device, error) {
tunFdStr := os.Getenv(ENV_WG_TUN_FD)
// if on macOS, call findUnusedUTUN to get a new utun device
if runtime.GOOS == "darwin" {
interfaceName, err := findUnusedUTUN()
if err != nil {
return nil, err
}
return tun.CreateTUN(interfaceName, mtuInt)
}
if tunFdStr == "" {
return tun.CreateTUN(interfaceName, mtuInt)
}
@@ -490,7 +489,7 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub
}
// configure the interface
err = ConfigureInterface(realInterfaceName, wgData.TunnelIP)
err = ConfigureInterface(realInterfaceName, wgData)
if err != nil {
logger.Error("Failed to configure interface: %v", err)
}