mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-07 16:54:16 +01:00
145 lines
3.1 KiB
Go
145 lines
3.1 KiB
Go
// +build linux
|
|
|
|
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]),
|
|
)
|
|
}
|
|
|
|
// modName 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 modName(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 err != nil {
|
|
// skip broken files
|
|
return nil
|
|
}
|
|
|
|
if !info.Mode().IsRegular() {
|
|
return nil
|
|
}
|
|
fd, err := os.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer fd.Close()
|
|
name, err := modName(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")
|
|
return err == nil
|
|
}
|
|
|
|
// 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
|
|
}
|