mirror of https://github.com/tailscale/tailscale/
Revert "wgengine/router,util/kmod: load & log xt_mark"
This reverts commit 8d6793fd70
.
Reason: breaks Android build (cgo/pthreads addition)
We can try again next cycle.
Change-Id: I5e7e1730a8bf399a8acfce546a6d22e11fb835d5
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/4456/head
parent
df26c63793
commit
53588f632d
@ -1,31 +0,0 @@
|
|||||||
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux
|
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"tailscale.com/util/kmod"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if len(os.Args) != 2 {
|
|
||||||
fmt.Fprintln(os.Stderr, "error: a module name must be supplied")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
done, err := kmod.EnsureModule(os.Args[1])
|
|
||||||
if done {
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
|
||||||
}
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
@ -1,161 +0,0 @@
|
|||||||
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux
|
|
||||||
// +build linux
|
|
||||||
|
|
||||||
// Package kmod provides a simple API to attempt to ensure that a kernel
|
|
||||||
// module is loaded in a wide variety of environments, and otherwise
|
|
||||||
// report descriptive loggable error strings.
|
|
||||||
// This package does not have extensive unit testing, as the broader set
|
|
||||||
// of challenges associated with the package come from a wide variety of
|
|
||||||
// distribution and linux version differences that are problematic to
|
|
||||||
// mock/stub/emulate, including syscall boundary behaviors. The program
|
|
||||||
// `ensuremod` is kept nearby the source that provides a method for
|
|
||||||
// integration testing.
|
|
||||||
package kmod
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"go4.org/mem"
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
"kernel.org/pub/linux/libs/security/libcap/cap"
|
|
||||||
"pault.ag/go/modprobe"
|
|
||||||
"tailscale.com/util/lineread"
|
|
||||||
"tailscale.com/util/multierr"
|
|
||||||
)
|
|
||||||
|
|
||||||
// hasKernelModule attempts to find a kernel module by name using procfs and
|
|
||||||
// sysfs. If the module is found to be loaded, true is returned, in all other
|
|
||||||
// cases false is returned, regardless of errors or a missing module.
|
|
||||||
func hasKernelModule(name string) (bool, error) {
|
|
||||||
if _, err := os.Stat(filepath.Join("/sys/module", name)); err == nil {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
prefix := mem.S(name + " ")
|
|
||||||
stopFound := errors.New("")
|
|
||||||
|
|
||||||
err := lineread.File("/proc/modules", func(line []byte) error {
|
|
||||||
if mem.HasPrefix(mem.B(line), prefix) {
|
|
||||||
return stopFound
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err == stopFound {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("module %s not found in /sys/module or /proc/modules: %w", name, err)
|
|
||||||
}
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// canInstallModule attempts to determine if the current process has sufficient
|
|
||||||
// privilege to install modules. If the capabilities API can be queried without
|
|
||||||
// error, then the result depends on the SYS_MODULE effective capability,
|
|
||||||
// otherwise returns true only if the current process is running as root. A
|
|
||||||
// result of true implies that it may be worth trying to install a module, not
|
|
||||||
// that doing so will work.
|
|
||||||
func canInstallModule() (bool, error) {
|
|
||||||
caps, err := cap.GetPID(0) // 0 = current process
|
|
||||||
if err == nil {
|
|
||||||
// errors from GetFlag are either due to the receiver being
|
|
||||||
// uninitialized, or the kernel gave junk results, both of which aren't
|
|
||||||
// very meaningful out of context to a user, so this error is mostly
|
|
||||||
// ignored.
|
|
||||||
b, err := caps.GetFlag(cap.Effective, cap.SYS_MODULE)
|
|
||||||
if err == nil {
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// could not determine a well known result from capabilities, make an
|
|
||||||
// assumption based on uid.
|
|
||||||
if os.Getuid() == 0 {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return false, fmt.Errorf("not running as root, and unable to check kernel module capabilities")
|
|
||||||
}
|
|
||||||
|
|
||||||
// firstExecutable checks paths for a path that exists and is executable by the current user.
|
|
||||||
func firstExecutable(paths ...string) string {
|
|
||||||
for _, path := range paths {
|
|
||||||
if unix.Access(path, unix.X_OK) == nil {
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// runModprobe runs `modprobePath name` and reports summary error output on error.
|
|
||||||
func runModprobe(name, modprobePath string) error {
|
|
||||||
cmd := exec.Command(modprobePath, name)
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("%q failed: %w; %s", fmt.Sprintf("%s %s", modprobePath, name), err, bytes.TrimSpace(out))
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// tryInstallModule attempts to find a modprobe binary to run either in
|
|
||||||
// well-known paths, or in $PATH, and runs it. If it can not find a modprobe to
|
|
||||||
// run, it instead falls back to a syscall interface to attempt to install a
|
|
||||||
// module.
|
|
||||||
func tryInstallModule(name string) error {
|
|
||||||
path := firstExecutable("/usr/sbin/modprobe", "/sbin/modprobe")
|
|
||||||
if path != "" {
|
|
||||||
return runModprobe(name, path)
|
|
||||||
}
|
|
||||||
path, err := exec.LookPath("modprobe")
|
|
||||||
if err == nil {
|
|
||||||
return runModprobe(name, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = modprobe.Load(name, "")
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("unable to find modprobe(1), and load of module %s failed with: %w", name, err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnsureModule attempts to ensure that the given module is installed, returning
|
|
||||||
// true only if it has been found or successfully installed, otherwise false is
|
|
||||||
// returned along with a list of informational errors about probe attempts.
|
|
||||||
func EnsureModule(name string) (bool, error) {
|
|
||||||
has, hasErr := hasKernelModule(name)
|
|
||||||
if has {
|
|
||||||
return has, nil
|
|
||||||
}
|
|
||||||
var errors []error
|
|
||||||
if hasErr != nil {
|
|
||||||
errors = append(errors, hasErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
can, canErr := canInstallModule()
|
|
||||||
if can && canErr != nil {
|
|
||||||
errors = append(errors, canErr)
|
|
||||||
}
|
|
||||||
if !can {
|
|
||||||
if canErr == nil {
|
|
||||||
errors = append(errors, fmt.Errorf("module %q not found, and current user can not install modules", name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if can {
|
|
||||||
if err := tryInstallModule(name); err == nil {
|
|
||||||
return true, nil
|
|
||||||
} else {
|
|
||||||
errors = append(errors, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, multierr.New(errors...)
|
|
||||||
}
|
|
Loading…
Reference in New Issue