//go:build !windows /* SPDX-License-Identifier: MIT * * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. */ package main import ( "fmt" "os" "os/signal" "runtime" "strconv" "syscall" "golang.zx2c4.com/wireguard/conn" "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/ipc" "golang.zx2c4.com/wireguard/tun" ) const ( ExitSetupSuccess = 0 ExitSetupFailed = 1 ) const ( ENV_WG_TUN_FD = "WG_TUN_FD" ENV_WG_UAPI_FD = "WG_UAPI_FD" ENV_WG_PROCESS_FOREGROUND = "WG_PROCESS_FOREGROUND" ) func printUsage() { fmt.Printf("Usage: %s [-f/--foreground] INTERFACE-NAME\n", os.Args[0]) } func warning() { switch runtime.GOOS { case "linux", "freebsd", "openbsd": if os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1" { return } default: return } fmt.Fprintln(os.Stderr, "┌──────────────────────────────────────────────────────┐") fmt.Fprintln(os.Stderr, "│ │") fmt.Fprintln(os.Stderr, "│ Running wireguard-go is not required because this │") fmt.Fprintln(os.Stderr, "│ kernel has first class support for WireGuard. For │") fmt.Fprintln(os.Stderr, "│ information on installing the kernel module, │") fmt.Fprintln(os.Stderr, "│ please visit: │") fmt.Fprintln(os.Stderr, "│ https://www.wireguard.com/install/ │") fmt.Fprintln(os.Stderr, "│ │") fmt.Fprintln(os.Stderr, "└──────────────────────────────────────────────────────┘") } func main() { if len(os.Args) == 2 && os.Args[1] == "--version" { fmt.Printf("wireguard-go v%s\n\nUserspace WireGuard daemon for %s-%s.\nInformation available at https://www.wireguard.com.\nCopyright (C) Jason A. Donenfeld .\n", Version, runtime.GOOS, runtime.GOARCH) return } warning() var foreground bool var interfaceName string if len(os.Args) < 2 || len(os.Args) > 3 { printUsage() return } switch os.Args[1] { case "-f", "--foreground": foreground = true if len(os.Args) != 3 { printUsage() return } interfaceName = os.Args[2] default: foreground = false if len(os.Args) != 2 { printUsage() return } interfaceName = os.Args[1] } if !foreground { foreground = os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1" } // get log level (default: info) logLevel := func() int { switch os.Getenv("LOG_LEVEL") { case "verbose", "debug": return device.LogLevelVerbose case "error": return device.LogLevelError case "silent": return device.LogLevelSilent } return device.LogLevelError }() // open TUN device (or use supplied fd) tun, err := func() (tun.Device, error) { tunFdStr := os.Getenv(ENV_WG_TUN_FD) if tunFdStr == "" { return tun.CreateTUN(interfaceName, device.DefaultMTU) } // construct tun device from supplied fd fd, err := strconv.ParseUint(tunFdStr, 10, 32) if err != nil { return nil, err } err = syscall.SetNonblock(int(fd), true) if err != nil { return nil, err } file := os.NewFile(uintptr(fd), "") return tun.CreateTUNFromFile(file, device.DefaultMTU) }() if err == nil { realInterfaceName, err2 := tun.Name() if err2 == nil { interfaceName = realInterfaceName } } logger := device.NewLogger( logLevel, fmt.Sprintf("(%s) ", interfaceName), ) logger.Verbosef("Starting wireguard-go version %s", Version) if err != nil { logger.Errorf("Failed to create TUN device: %v", err) os.Exit(ExitSetupFailed) } // open UAPI file (or use supplied fd) fileUAPI, err := func() (*os.File, error) { uapiFdStr := os.Getenv(ENV_WG_UAPI_FD) if uapiFdStr == "" { return ipc.UAPIOpen(interfaceName) } // use supplied fd fd, err := strconv.ParseUint(uapiFdStr, 10, 32) if err != nil { return nil, err } return os.NewFile(uintptr(fd), ""), nil }() if err != nil { logger.Errorf("UAPI listen error: %v", err) os.Exit(ExitSetupFailed) return } // daemonize the process if !foreground { env := os.Environ() env = append(env, fmt.Sprintf("%s=3", ENV_WG_TUN_FD)) env = append(env, fmt.Sprintf("%s=4", ENV_WG_UAPI_FD)) env = append(env, fmt.Sprintf("%s=1", ENV_WG_PROCESS_FOREGROUND)) files := [3]*os.File{} if os.Getenv("LOG_LEVEL") != "" && logLevel != device.LogLevelSilent { files[0], _ = os.Open(os.DevNull) files[1] = os.Stdout files[2] = os.Stderr } else { files[0], _ = os.Open(os.DevNull) files[1], _ = os.Open(os.DevNull) files[2], _ = os.Open(os.DevNull) } attr := &os.ProcAttr{ Files: []*os.File{ files[0], // stdin files[1], // stdout files[2], // stderr tun.File(), fileUAPI, }, Dir: ".", Env: env, } path, err := os.Executable() if err != nil { logger.Errorf("Failed to determine executable: %v", err) os.Exit(ExitSetupFailed) } process, err := os.StartProcess( path, os.Args, attr, ) if err != nil { logger.Errorf("Failed to daemonize: %v", err) os.Exit(ExitSetupFailed) } process.Release() return } device := device.NewDevice(tun, conn.NewDefaultBind(), logger) logger.Verbosef("Device started") errs := make(chan error) term := make(chan os.Signal, 1) uapi, err := ipc.UAPIListen(interfaceName, fileUAPI) if err != nil { logger.Errorf("Failed to listen on uapi socket: %v", err) os.Exit(ExitSetupFailed) } go func() { for { conn, err := uapi.Accept() if err != nil { errs <- err return } go device.IpcHandle(conn) } }() logger.Verbosef("UAPI listener started") // wait for program to terminate signal.Notify(term, syscall.SIGTERM) signal.Notify(term, os.Interrupt) select { case <-term: case <-errs: case <-device.Wait(): } // clean up uapi.Close() device.Close() logger.Verbosef("Shutting down") }