From dd72a01ecfab62d94e108b4c1fc107c5a26e4373 Mon Sep 17 00:00:00 2001 From: braginini Date: Tue, 22 Jun 2021 14:38:28 +0200 Subject: [PATCH] feature: add check of Wireguard kernel module existence (Linux only) --- go.mod | 1 + iface/mod.go | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 iface/mod.go diff --git a/go.mod b/go.mod index 1b3a90f85..50e4a32ca 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/vishvananda/netlink v1.1.0 github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf + golang.org/x/sys v0.0.0-20210510120138-977fb7262007 golang.zx2c4.com/wireguard v0.0.0-20210604143328-f9b48a961cd2 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5 golang.zx2c4.com/wireguard/windows v0.3.14 diff --git a/iface/mod.go b/iface/mod.go new file mode 100644 index 000000000..0b59f3d9a --- /dev/null +++ b/iface/mod.go @@ -0,0 +1,140 @@ +package iface + +// Holds logic to check existence of Wireguard kernel module +// Copied from https://github.com/paultag/go-modprobe + +import ( + "debug/elf" + "fmt" + "golang.org/x/sys/unix" + "os" + "path/filepath" + "strings" +) + +var ( + // get the root directory for the kernel modules. If this line panics, + // it's because getModuleRoot has failed to get the uname of the running + // kernel (likely a non-POSIX system, but maybe a broken kernel?) + moduleRoot = getModuleRoot() +) + +// Get the module root (/lib/modules/$(uname -r)/) +func getModuleRoot() string { + uname := unix.Utsname{} + if err := unix.Uname(&uname); err != nil { + panic(err) + } + + i := 0 + for ; uname.Release[i] != 0; i++ { + } + + return filepath.Join( + "/lib/modules", + string(uname.Release[:i]), + ) +} + +// Name will, given a file descriptor to a Kernel Module (.ko file), parse the +// binary to get the module name. For instance, given a handle to the file at +// `kernel/drivers/usb/gadget/legacy/g_ether.ko`, return `g_ether`. +func Name(file *os.File) (string, error) { + f, err := elf.NewFile(file) + if err != nil { + return "", err + } + + syms, err := f.Symbols() + if err != nil { + return "", err + } + + for _, sym := range syms { + if strings.Compare(sym.Name, "__this_module") == 0 { + section := f.Sections[sym.Section] + data, err := section.Data() + if err != nil { + return "", err + } + + if len(data) < 25 { + return "", fmt.Errorf("modprobe: data is short, __this_module is '%s'", data) + } + + data = data[24:] + i := 0 + for ; data[i] != 0x00; i++ { + } + return string(data[:i]), nil + } + } + + return "", fmt.Errorf("No name found. Is this a .ko or just an ELF?") +} + +// Open every single kernel module under the root, and parse the ELF headers to +// extract the module name. +func elfMap(root string) (map[string]string, error) { + ret := map[string]string{} + + err := filepath.Walk( + root, + func(path string, info os.FileInfo, err error) error { + if !info.Mode().IsRegular() { + return nil + } + fd, err := os.Open(path) + if err != nil { + return err + } + defer fd.Close() + name, err := Name(fd) + if err != nil { + /* For now, let's just ignore that and avoid adding to it */ + return nil + } + + ret[name] = path + return nil + }) + + if err != nil { + return nil, err + } + + return ret, nil +} + +// Open every single kernel module under the kernel module directory +// (/lib/modules/$(uname -r)/), and parse the ELF headers to extract the +// module name. +func generateMap() (map[string]string, error) { + return elfMap(moduleRoot) +} + +// WireguardModExists returns true if Wireguard kernel module exists. +func WireguardModExists() bool { + _, err := resolveModName("wireguard") + if err != nil { + return false + } + + return true +} + +// resolveModName will, given a module name (such as `wireguard`) return an absolute +// path to the .ko that provides that module. +func resolveModName(name string) (string, error) { + paths, err := generateMap() + if err != nil { + return "", err + } + + fsPath := paths[name] + if !strings.HasPrefix(fsPath, moduleRoot) { + return "", fmt.Errorf("module isn't in the module directory") + } + + return fsPath, nil +}