github.com/koomox/wireguard-go@v0.0.0-20230722134753-17a50b2f22a3/main.go (about)

     1  //go:build !windows
     2  
     3  /* SPDX-License-Identifier: MIT
     4   *
     5   * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
     6   */
     7  
     8  package main
     9  
    10  import (
    11  	"fmt"
    12  	"os"
    13  	"os/signal"
    14  	"runtime"
    15  	"strconv"
    16  
    17  	"golang.org/x/sys/unix"
    18  	"github.com/koomox/wireguard-go/conn"
    19  	"github.com/koomox/wireguard-go/device"
    20  	"github.com/koomox/wireguard-go/ipc"
    21  	"github.com/koomox/wireguard-go/tun"
    22  )
    23  
    24  const (
    25  	ExitSetupSuccess = 0
    26  	ExitSetupFailed  = 1
    27  )
    28  
    29  const (
    30  	ENV_WG_TUN_FD             = "WG_TUN_FD"
    31  	ENV_WG_UAPI_FD            = "WG_UAPI_FD"
    32  	ENV_WG_PROCESS_FOREGROUND = "WG_PROCESS_FOREGROUND"
    33  )
    34  
    35  func printUsage() {
    36  	fmt.Printf("Usage: %s [-f/--foreground] INTERFACE-NAME\n", os.Args[0])
    37  }
    38  
    39  func warning() {
    40  	switch runtime.GOOS {
    41  	case "linux", "freebsd", "openbsd":
    42  		if os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1" {
    43  			return
    44  		}
    45  	default:
    46  		return
    47  	}
    48  
    49  	fmt.Fprintln(os.Stderr, "┌──────────────────────────────────────────────────────┐")
    50  	fmt.Fprintln(os.Stderr, "│                                                      │")
    51  	fmt.Fprintln(os.Stderr, "│   Running wireguard-go is not required because this  │")
    52  	fmt.Fprintln(os.Stderr, "│   kernel has first class support for WireGuard. For  │")
    53  	fmt.Fprintln(os.Stderr, "│   information on installing the kernel module,       │")
    54  	fmt.Fprintln(os.Stderr, "│   please visit:                                      │")
    55  	fmt.Fprintln(os.Stderr, "│         https://www.wireguard.com/install/           │")
    56  	fmt.Fprintln(os.Stderr, "│                                                      │")
    57  	fmt.Fprintln(os.Stderr, "└──────────────────────────────────────────────────────┘")
    58  }
    59  
    60  func main() {
    61  	if len(os.Args) == 2 && os.Args[1] == "--version" {
    62  		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 <Jason@zx2c4.com>.\n", Version, runtime.GOOS, runtime.GOARCH)
    63  		return
    64  	}
    65  
    66  	warning()
    67  
    68  	var foreground bool
    69  	var interfaceName string
    70  	if len(os.Args) < 2 || len(os.Args) > 3 {
    71  		printUsage()
    72  		return
    73  	}
    74  
    75  	switch os.Args[1] {
    76  
    77  	case "-f", "--foreground":
    78  		foreground = true
    79  		if len(os.Args) != 3 {
    80  			printUsage()
    81  			return
    82  		}
    83  		interfaceName = os.Args[2]
    84  
    85  	default:
    86  		foreground = false
    87  		if len(os.Args) != 2 {
    88  			printUsage()
    89  			return
    90  		}
    91  		interfaceName = os.Args[1]
    92  	}
    93  
    94  	if !foreground {
    95  		foreground = os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1"
    96  	}
    97  
    98  	// get log level (default: info)
    99  
   100  	logLevel := func() int {
   101  		switch os.Getenv("LOG_LEVEL") {
   102  		case "verbose", "debug":
   103  			return device.LogLevelVerbose
   104  		case "error":
   105  			return device.LogLevelError
   106  		case "silent":
   107  			return device.LogLevelSilent
   108  		}
   109  		return device.LogLevelError
   110  	}()
   111  
   112  	// open TUN device (or use supplied fd)
   113  
   114  	tdev, err := func() (tun.Device, error) {
   115  		tunFdStr := os.Getenv(ENV_WG_TUN_FD)
   116  		if tunFdStr == "" {
   117  			return tun.CreateTUN(interfaceName, device.DefaultMTU)
   118  		}
   119  
   120  		// construct tun device from supplied fd
   121  
   122  		fd, err := strconv.ParseUint(tunFdStr, 10, 32)
   123  		if err != nil {
   124  			return nil, err
   125  		}
   126  
   127  		err = unix.SetNonblock(int(fd), true)
   128  		if err != nil {
   129  			return nil, err
   130  		}
   131  
   132  		file := os.NewFile(uintptr(fd), "")
   133  		return tun.CreateTUNFromFile(file, device.DefaultMTU)
   134  	}()
   135  
   136  	if err == nil {
   137  		realInterfaceName, err2 := tdev.Name()
   138  		if err2 == nil {
   139  			interfaceName = realInterfaceName
   140  		}
   141  	}
   142  
   143  	logger := device.NewLogger(
   144  		logLevel,
   145  		fmt.Sprintf("(%s) ", interfaceName),
   146  	)
   147  
   148  	logger.Verbosef("Starting wireguard-go version %s", Version)
   149  
   150  	if err != nil {
   151  		logger.Errorf("Failed to create TUN device: %v", err)
   152  		os.Exit(ExitSetupFailed)
   153  	}
   154  
   155  	// open UAPI file (or use supplied fd)
   156  
   157  	fileUAPI, err := func() (*os.File, error) {
   158  		uapiFdStr := os.Getenv(ENV_WG_UAPI_FD)
   159  		if uapiFdStr == "" {
   160  			return ipc.UAPIOpen(interfaceName)
   161  		}
   162  
   163  		// use supplied fd
   164  
   165  		fd, err := strconv.ParseUint(uapiFdStr, 10, 32)
   166  		if err != nil {
   167  			return nil, err
   168  		}
   169  
   170  		return os.NewFile(uintptr(fd), ""), nil
   171  	}()
   172  	if err != nil {
   173  		logger.Errorf("UAPI listen error: %v", err)
   174  		os.Exit(ExitSetupFailed)
   175  		return
   176  	}
   177  	// daemonize the process
   178  
   179  	if !foreground {
   180  		env := os.Environ()
   181  		env = append(env, fmt.Sprintf("%s=3", ENV_WG_TUN_FD))
   182  		env = append(env, fmt.Sprintf("%s=4", ENV_WG_UAPI_FD))
   183  		env = append(env, fmt.Sprintf("%s=1", ENV_WG_PROCESS_FOREGROUND))
   184  		files := [3]*os.File{}
   185  		if os.Getenv("LOG_LEVEL") != "" && logLevel != device.LogLevelSilent {
   186  			files[0], _ = os.Open(os.DevNull)
   187  			files[1] = os.Stdout
   188  			files[2] = os.Stderr
   189  		} else {
   190  			files[0], _ = os.Open(os.DevNull)
   191  			files[1], _ = os.Open(os.DevNull)
   192  			files[2], _ = os.Open(os.DevNull)
   193  		}
   194  		attr := &os.ProcAttr{
   195  			Files: []*os.File{
   196  				files[0], // stdin
   197  				files[1], // stdout
   198  				files[2], // stderr
   199  				tdev.File(),
   200  				fileUAPI,
   201  			},
   202  			Dir: ".",
   203  			Env: env,
   204  		}
   205  
   206  		path, err := os.Executable()
   207  		if err != nil {
   208  			logger.Errorf("Failed to determine executable: %v", err)
   209  			os.Exit(ExitSetupFailed)
   210  		}
   211  
   212  		process, err := os.StartProcess(
   213  			path,
   214  			os.Args,
   215  			attr,
   216  		)
   217  		if err != nil {
   218  			logger.Errorf("Failed to daemonize: %v", err)
   219  			os.Exit(ExitSetupFailed)
   220  		}
   221  		process.Release()
   222  		return
   223  	}
   224  
   225  	device := device.NewDevice(tdev, conn.NewDefaultBind(), logger)
   226  
   227  	logger.Verbosef("Device started")
   228  
   229  	errs := make(chan error)
   230  	term := make(chan os.Signal, 1)
   231  
   232  	uapi, err := ipc.UAPIListen(interfaceName, fileUAPI)
   233  	if err != nil {
   234  		logger.Errorf("Failed to listen on uapi socket: %v", err)
   235  		os.Exit(ExitSetupFailed)
   236  	}
   237  
   238  	go func() {
   239  		for {
   240  			conn, err := uapi.Accept()
   241  			if err != nil {
   242  				errs <- err
   243  				return
   244  			}
   245  			go device.IpcHandle(conn)
   246  		}
   247  	}()
   248  
   249  	logger.Verbosef("UAPI listener started")
   250  
   251  	// wait for program to terminate
   252  
   253  	signal.Notify(term, unix.SIGTERM)
   254  	signal.Notify(term, os.Interrupt)
   255  
   256  	select {
   257  	case <-term:
   258  	case <-errs:
   259  	case <-device.Wait():
   260  	}
   261  
   262  	// clean up
   263  
   264  	uapi.Close()
   265  	device.Close()
   266  
   267  	logger.Verbosef("Shutting down")
   268  }