diff --git a/Dockerfile b/Dockerfile index 1f51cd87499..2a2625a4ed3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,13 @@ FROM golang:1.12 +RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD \ + && echo "deb https://repo.iovisor.org/apt/bionic bionic main" | tee /etc/apt/sources.list.d/iovisor.list + RUN apt-get update && apt-get install -y \ apparmor \ autoconf \ automake \ + bcc-tools \ bison \ build-essential \ curl \ @@ -34,6 +38,7 @@ RUN apt-get update && apt-get install -y \ libdevmapper-dev \ libgpgme11-dev \ liblzma-dev \ + libtinfo5 \ netcat \ socat \ lsof \ diff --git a/Makefile b/Makefile index 6734ef1b0e0..d6287360370 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,9 @@ BINDIR ?= ${PREFIX}/bin LIBEXECDIR ?= ${PREFIX}/libexec MANDIR ?= ${PREFIX}/share/man SHAREDIR_CONTAINERS ?= ${PREFIX}/share/containers +HOOK_BIN_DIR ?= ${PREFIX}/libexec/oci/hooks.d/ ETCDIR ?= /etc +HOOK_DIR ?= ${ETCDIR}/containers/oci/hooks.d/ TMPFILESDIR ?= ${PREFIX}/lib/tmpfiles.d SYSTEMDDIR ?= ${PREFIX}/lib/systemd/system USERSYSTEMDDIR ?= ${PREFIX}/lib/systemd/user @@ -34,6 +36,7 @@ BUILDTAGS ?= \ seccomp \ varlink +BUILDTAG_TRACE_HOOK ?= $(shell hack/generate_seccomp_tag.sh) GO_BUILD=$(GO) build # Go module support: set `-mod=vendor` to use the vendored sources ifeq ($(shell go help mod >/dev/null 2>&1 && echo true), true) @@ -46,6 +49,12 @@ $(warning \ Install libsystemd for journald support) endif +ifeq (,$(BUILDTAG_TRACE_HOOK)) +$(warning \ + Podman is being compiled without the oci_trace_hook build tag.\ + Install libbcc to use the oci-trace-hook) +endif + BUILDTAGS_CROSS ?= containers_image_openpgp containers_image_ostree_stub exclude_graphdriver_btrfs exclude_graphdriver_devicemapper exclude_graphdriver_overlay ifneq (,$(findstring varlink,$(BUILDTAGS))) PODMAN_VARLINK_DEPENDENCIES = cmd/podman/varlink/iopodman.go @@ -306,7 +315,7 @@ run-perftest: perftest ## Build and run perf tests vagrant-check: BOX=$(BOX) sh ./vagrant.sh -binaries: varlink_generate podman podman-remote ## Build podman +binaries: varlink_generate podman podman-remote oci-trace-hook ## Build podman install.catatonit: ./hack/install_catatonit.sh @@ -394,7 +403,7 @@ changelog: ## Generate changelog $(shell cat $(TMPFILE) >> changelog.txt) $(shell rm $(TMPFILE)) -install: .gopathok install.bin install.remote install.man install.cni install.systemd ## Install binaries to system locations +install: .gopathok install.bin install.remote install.man install.cni install.systemd install.oci-trace-hook ## Install binaries to system locations install.remote: podman-remote install ${SELINUXOPT} -d -m 755 $(DESTDIR)$(BINDIR) @@ -498,6 +507,21 @@ endef make all install; \ fi + +install.oci-trace-hook: + if [ ! -z "$(BUILDTAG_TRACE_HOOK)" ]; then \ + install ${SELINUXOPT} -d -m 755 ${DESTDIR}$(HOOK_BIN_DIR); \ + install ${SELINUXOPT} -d -m 755 ${DESTDIR}$(HOOK_DIR) ; \ + install ${SELINUXOPT} -m 755 bin/oci-trace-hook ${DESTDIR}$(HOOK_BIN_DIR) ; \ + install ${SELINUXOPT} -m 644 cmd/oci-trace-hook/oci-trace-hook-run.json ${DESTDIR}$(HOOK_DIR) ; \ + install ${SELINUXOPT} -m 644 cmd/oci-trace-hook/oci-trace-hook-stop.json ${DESTDIR}$(HOOK_DIR) ; \ + fi + +oci-trace-hook: + if [ ! -z "$(BUILDTAG_TRACE_HOOK)" ] ; then \ + $(GO_BUILD) -tags $(BUILDTAG_TRACE_HOOK) -o bin/oci-trace-hook $(PROJECT)/cmd/oci-trace-hook; \ + fi + varlink_generate: .gopathok cmd/podman/varlink/iopodman.go ## Generate varlink varlink_api_generate: .gopathok API.md diff --git a/cmd/oci-trace-hook/oci-trace-hook-run.json b/cmd/oci-trace-hook/oci-trace-hook-run.json new file mode 100644 index 00000000000..8e773c03dda --- /dev/null +++ b/cmd/oci-trace-hook/oci-trace-hook-run.json @@ -0,0 +1,18 @@ +{ + "version": "1.0.0", + "hook": { + "path": "/usr/local/libexec/oci/hooks.d/oci-trace-hook", + "args": [ + "oci-trace-hook", + "-s" + ] + }, + "when": { + "annotations": { + "io.containers.trace-syscall": ".*" + } + }, + "stages": [ + "prestart" + ] +} \ No newline at end of file diff --git a/cmd/oci-trace-hook/oci-trace-hook-stop.json b/cmd/oci-trace-hook/oci-trace-hook-stop.json new file mode 100644 index 00000000000..e3c0d042202 --- /dev/null +++ b/cmd/oci-trace-hook/oci-trace-hook-stop.json @@ -0,0 +1,18 @@ +{ + "version": "1.0.0", + "hook": { + "path": "/usr/local/libexec/oci/hooks.d/oci-trace-hook", + "args": [ + "oci-trace-hook", + "-t" + ] + }, + "when": { + "annotations": { + "io.containers.trace-syscall": ".*" + } + }, + "stages": [ + "poststop" + ] +} \ No newline at end of file diff --git a/cmd/oci-trace-hook/trace.go b/cmd/oci-trace-hook/trace.go new file mode 100644 index 00000000000..7d5329b22b9 --- /dev/null +++ b/cmd/oci-trace-hook/trace.go @@ -0,0 +1,334 @@ +// +build oci_trace_hook + +package main + +import ( + "bufio" + "bytes" + "encoding/binary" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "os" + "os/signal" + "path/filepath" + "sort" + "strconv" + "strings" + "syscall" + + "github.com/docker/docker/api/types" + "github.com/iovisor/gobpf/bcc" //nolint + spec "github.com/opencontainers/runtime-spec/specs-go" + seccomp "github.com/seccomp/libseccomp-golang" + "github.com/sirupsen/logrus" +) + +// event struct used to read data from the perf ring buffer +type event struct { + // PID of the process making the syscall + Pid uint32 + // syscall number + ID uint32 + // Command which makes the syscall + Command [16]byte +} + +// the source is a bpf program compiled at runtime. Some macro's like +// BPF_HASH and BPF_PERF_OUTPUT are expanded during compilation +// by bcc. $PARENT_PID get's replaced before compilation with the PID of the container +// Complete documentation is available at +// https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md +const source string = ` +#include +#include +#include +#include +#include +#include + +// BPF_HASH used to store the PID namespace of the parent PID +// of the processes inside the container. +BPF_HASH(parent_namespace, u64, unsigned int); + +// Opens a custom BPF table to push data to user space via perf ring buffer +BPF_PERF_OUTPUT(events); + +// data_t used to store the data received from the event +struct syscall_data +{ + // PID of the process + u32 pid; + // the syscall number + u32 id; + // command which is making the syscall + char comm[16]; +}; + +// enter_trace : function is attached to the kernel tracepoint raw_syscalls:sys_enter it is +// called whenever a syscall is made. The function stores the pid_namespace (task->nsproxy->pid_ns_for_children->ns.inum) of the PID which +// starts the container in the BPF_HASH called parent_namespace. +// The data of the syscall made by the process with the same pid_namespace as the parent_namespace is pushed to +// userspace using perf ring buffer + +// specification of args from sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/format +int enter_trace(struct tracepoint__raw_syscalls__sys_enter *args) +{ + struct syscall_data data = {}; + u64 key = 0; + unsigned int zero = 0; + struct task_struct *task; + + data.pid = bpf_get_current_pid_tgid(); + data.id = (int)args->id; + bpf_get_current_comm(&data.comm, sizeof(data.comm)); + + task = (struct task_struct *)bpf_get_current_task(); + struct nsproxy *ns = task->nsproxy; + unsigned int inum = ns->pid_ns_for_children->ns.inum; + + if (data.pid == $PARENT_PID) + { + parent_namespace.update(&key, &inum); + } + unsigned int *parent_inum = parent_namespace.lookup_or_init(&key, &zero); + + if (*parent_inum != inum) + { + return 0; + } + + events.perf_submit(args, &data, sizeof(data)); + return 0; +} +` + +func main() { + + terminate := flag.Bool("t", false, "send SIGINT to floating process") + runBPF := flag.Int("r", 0, "-r [PID] run the BPF function and attach to the pid") + fileName := flag.String("f", "", "path of the file to save the seccomp profile") + + start := flag.Bool("s", false, "Start the hook which would execute a process to trace syscalls made by the container") + flag.Parse() + + profilePath, err := filepath.Abs(*fileName) + if err != nil { + logrus.Error(err) + } + + logfilePath, err := filepath.Abs("trace-log") + if err != nil { + logrus.Error(err) + } + logfile, err := os.OpenFile(logfilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + logrus.Errorf("error opening file: %v", err) + } + + defer logfile.Close() + formatter := new(logrus.TextFormatter) + formatter.FullTimestamp = true + logrus.SetFormatter(formatter) + logrus.SetOutput(logfile) + if *runBPF > 0 { + logrus.Println("Filepath : ", profilePath) + if err := runBPFSource(*runBPF, profilePath); err != nil { + logrus.Error(err) + } + } else if *terminate { + if err := sendSIGINT(); err != nil { + logrus.Error(err) + } + } else if *start { + if err := startFloatingProcess(); err != nil { + logrus.Error(err) + } + } +} + +// Start a process which runs the BPF source and detach the process +func startFloatingProcess() error { + var s spec.State + reader := bufio.NewReader(os.Stdin) + decoder := json.NewDecoder(reader) + err := decoder.Decode(&s) + if err != nil { + return err + } + pid := s.Pid + fileName := s.Annotations["io.containers.trace-syscall"] + attr := &os.ProcAttr{ + Dir: ".", + Env: os.Environ(), + Files: []*os.File{ + os.Stdin, + nil, + nil, + }, + } + if pid > 0 { + sig := make(chan os.Signal, 1) + signal.Notify(sig, syscall.SIGUSR1) + + process, err := os.StartProcess("/usr/local/libexec/oci/hooks.d/oci-trace-hook", []string{"oci-trace-hook", "-r", strconv.Itoa(pid), "-f", fileName}, attr) + if err != nil { + return fmt.Errorf("cannot launch process err: %q", err.Error()) + } + + <-sig + + processPID := process.Pid + f, err := os.Create("pidfile") + if err != nil { + return fmt.Errorf("cannot write pid to file err:%q", err.Error()) + } + defer f.Close() + _, err = f.WriteString(strconv.Itoa(processPID)) + if err != nil { + return fmt.Errorf("cannot write pid to the file") + } + err = process.Release() + if err != nil { + return fmt.Errorf("cannot detach process err:%q", err.Error()) + } + + } else { + return fmt.Errorf("container not running") + } + return nil +} + +// run the BPF source and attach it to raw_syscalls:sys_enter tracepoint +func runBPFSource(pid int, profilePath string) error { + + ppid := os.Getppid() + parentProcess, err := os.FindProcess(ppid) + + if err != nil { + return fmt.Errorf("cannot find the parent process pid %d : %q", ppid, err) + } + + logrus.Println("Running floating process PID to attach:", pid) + syscalls := make(map[string]int, 303) + src := strings.Replace(source, "$PARENT_PID", strconv.Itoa(pid), -1) + m := bcc.NewModule(src, []string{}) + defer m.Close() + + tracepoint, err := m.LoadTracepoint("enter_trace") + if err != nil { + return err + } + + logrus.Println("Loaded tracepoint") + + if err := m.AttachTracepoint("raw_syscalls:sys_enter", tracepoint); err != nil { + return fmt.Errorf("unable to load tracepoint err:%q", err.Error()) + } + + // send a signal to the parent process to indicate the compilation has been completed + err = parentProcess.Signal(syscall.SIGUSR1) + if err != nil { + return err + } + + table := bcc.NewTable(m.TableId("events"), m) + channel := make(chan []byte) + perfMap, err := bcc.InitPerfMap(table, channel) + if err != nil { + return fmt.Errorf("unable to init perf map err:%q", err.Error()) + } + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt) + reachedPRCTL := false // Reached PRCTL syscall + go func() { + var e event + for { + data := <-channel + err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &e) + if err != nil { + logrus.Errorf("failed to decode received data '%s': %s\n", data, err) + continue + } + name, err := getName(e.ID) + if err != nil { + logrus.Errorf("failed to get name of syscall from id : %d received : %q", e.ID, name) + } + // syscalls are not recorded until prctl() is called + if name == "prctl" { + reachedPRCTL = true + } + if reachedPRCTL { + syscalls[name]++ + } + } + }() + logrus.Println("PerfMap Start") + perfMap.Start() + <-sig + logrus.Println("PerfMap Stop") + perfMap.Stop() + if err := generateProfile(syscalls, profilePath); err != nil { + return err + } + return nil +} + +// send SIGINT to the floating process reading the perfbuffer +func sendSIGINT() error { + f, err := ioutil.ReadFile("pidfile") + + if err != nil { + return err + } + + Spid := string(f) + + pid, _ := strconv.Atoi(Spid) + p, err := os.FindProcess(pid) + if err != nil { + return fmt.Errorf("cannot find process with PID %d \nerror msg: %q", pid, err.Error()) + } + err = p.Signal(os.Interrupt) + if err != nil { + return fmt.Errorf("cannot send signal to child process err: %q", err.Error()) + } + return nil +} + +// generate the seccomp profile from the syscalls provided +func generateProfile(c map[string]int, fileName string) error { + s := types.Seccomp{} + var names []string + for s, t := range c { + if t > 0 { + names = append(names, s) + } + } + sort.Strings(names) + s.DefaultAction = types.ActErrno + + s.Syscalls = []*types.Syscall{ + { + Action: types.ActAllow, + Names: names, + Args: []*types.Arg{}, + }, + } + sJSON, err := json.Marshal(s) + if err != nil { + return err + } + if err := ioutil.WriteFile(fileName, sJSON, 0644); err != nil { + return err + } + return nil +} + +// get the name of the syscall from it's ID +func getName(id uint32) (string, error) { + name, err := seccomp.ScmpSyscall(id).GetName() + return name, err +} diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index fc8197721d1..41cd6433f61 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -586,6 +586,16 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. annotations[splitAnnotation[0]] = splitAnnotation[1] } + if annotations["io.containers.trace-syscall"] != "" { + if !filepath.IsAbs(annotations["io.containers.trace-syscall"]) { + return nil, errors.Errorf("Invalid Path, Please enter an absolute path") + } + + if c.IsSet("rm") { + return nil, errors.Errorf("cannot use --rm while generating a SECCOMP profile") + } + } + // WORKING DIRECTORY workDir := "/" if c.IsSet("workdir") { diff --git a/contrib/cirrus/packer/fedora_setup.sh b/contrib/cirrus/packer/fedora_setup.sh index 6709eecaf36..1ed3f81786a 100644 --- a/contrib/cirrus/packer/fedora_setup.sh +++ b/contrib/cirrus/packer/fedora_setup.sh @@ -87,7 +87,6 @@ ooe.sh sudo dnf install -y \ xz \ zip - # Ensure there are no disruptive periodic services enabled by default in image systemd_banish @@ -95,11 +94,17 @@ sudo /tmp/libpod/hack/install_catatonit.sh # Same script is used for several related contexts case "$PACKER_BUILDER_NAME" in + xfedora*) echo "Configuring CGroups v2 enabled on next boot" sudo grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=1" sudo dnf install -y crun ;& # continue to next matching item + + *fedora-3*) + ooe.sh sudo dnf install -y bcc-devel + ;& + *) echo "Finalizing $PACKER_BUILDER_NAME VM image" ;; diff --git a/contrib/cirrus/packer/ubuntu_setup.sh b/contrib/cirrus/packer/ubuntu_setup.sh index da7d457a506..58463fa72c1 100644 --- a/contrib/cirrus/packer/ubuntu_setup.sh +++ b/contrib/cirrus/packer/ubuntu_setup.sh @@ -32,6 +32,9 @@ if [[ "$OS_RELEASE_VER" -eq "18" ]] then $LILTO $SUDOAPTADD ppa:longsleep/golang-backports fi +sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD +# The repository isn't available for ubnutu 19 but the binaries work perfectly +sudo echo "deb https://repo.iovisor.org/apt/bionic bionic main" | sudo tee /etc/apt/sources.list.d/iovisor.list $LILTO $SUDOAPTGET update @@ -42,6 +45,7 @@ $BIGTO $SUDOAPTGET install \ autoconf \ automake \ bats \ + bcc-tools \ bison \ btrfs-tools \ build-essential \ diff --git a/go.mod b/go.mod index 117f85b3cb4..b45dc34c7b7 100644 --- a/go.mod +++ b/go.mod @@ -55,6 +55,7 @@ require ( github.com/hpcloud/tail v1.0.0 github.com/imdario/mergo v0.3.7 // indirect github.com/json-iterator/go v1.1.7 + github.com/iovisor/gobpf v0.0.0-20190329163444-e0d8d785d368 github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/magiconair/properties v1.8.1 // indirect github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481 // indirect @@ -78,7 +79,7 @@ require ( github.com/rogpeppe/fastuuid v1.1.0 // indirect github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 // indirect github.com/seccomp/containers-golang v0.0.0-20190312124753-8ca8945ccf5f // indirect - github.com/seccomp/libseccomp-golang v0.9.1 // indirect + github.com/seccomp/libseccomp-golang v0.9.1 github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/go.sum b/go.sum index 63c2ddd309d..00a0bbe9fd7 100644 --- a/go.sum +++ b/go.sum @@ -303,6 +303,8 @@ github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/iovisor/gobpf v0.0.0-20190329163444-e0d8d785d368 h1:y3708HMZuw+OpIdFPPzwSMC12f/LncanSCpVAaR8jCY= +github.com/iovisor/gobpf v0.0.0-20190329163444-e0d8d785d368/go.mod h1:+5U5qu5UOu8YJ5oHVLvWKH7/Dr5QNHU7mZ2RfPEeXg8= github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed h1:3MJOWnAfq3T9eoCQTarEY2DMlUWYcBkBLf03dAMvEb8= github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8= github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111 h1:NAAiV9ass6VReWFjuxqrMIq12WKlSULI6Gs3PxQghLA= diff --git a/hack/generate_seccomp_tag.sh b/hack/generate_seccomp_tag.sh new file mode 100755 index 00000000000..0cb54cdf014 --- /dev/null +++ b/hack/generate_seccomp_tag.sh @@ -0,0 +1,4 @@ +#!/bin/bash +if pkg-config libbcc 2> /dev/null; then + echo oci_trace_hook +fi \ No newline at end of file diff --git a/install.md b/install.md index eb4ecfa683f..1ac431193d7 100644 --- a/install.md +++ b/install.md @@ -117,7 +117,9 @@ sudo yum install -y \ ostree-devel \ pkgconfig \ runc \ - containers-common + containers-common \ + bcc-devel \ + kernel-headers ``` Debian, Ubuntu, and related distributions: @@ -143,7 +145,9 @@ sudo apt-get install \ libsystemd-dev \ pkg-config \ runc \ - uidmap + uidmap \ + bcc \ + linux-headers-$(uname -r) ``` On Manjaro (and maybe other Linux distributions): @@ -318,7 +322,7 @@ make BUILDTAGS='seccomp apparmor' ``` | Build Tag | Feature | Dependency | -|----------------------------------|------------------------------------|----------------------| +| -------------------------------- | ---------------------------------- | -------------------- | | apparmor | apparmor support | libapparmor | | exclude_graphdriver_btrfs | exclude btrfs | libbtrfs | | exclude_graphdriver_devicemapper | exclude device-mapper | libdm | diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index 47db5c0dc51..050f443db4d 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -426,7 +426,6 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode logrus.Errorf("Error removing container %s: %v", ctr.ID(), err) } } - return exitCode, nil } diff --git a/test/e2e/run_generate_seccomp_test.go b/test/e2e/run_generate_seccomp_test.go new file mode 100644 index 00000000000..62b677e00a7 --- /dev/null +++ b/test/e2e/run_generate_seccomp_test.go @@ -0,0 +1,117 @@ +package integration + +import ( + "encoding/json" + "io/ioutil" + "os" + "reflect" + "time" + + "github.com/docker/docker/api/types" + + . "github.com/containers/libpod/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman generate profile", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + SkipIfRootless() + if _, err := os.Stat("/etc/containers/oci/hooks.d/oci-trace-hook-run.json"); err != nil { + Skip("oci-trace-hook prestart hook is not installed, please install the hook") + } + if _, err := os.Stat("/etc/containers/oci/hooks.d/oci-trace-hook-stop.json"); err != nil { + Skip("oci-trace-hook poststop hook is not installed, please install the hook") + } + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + podmanTest.SeedImages() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + }) + It("podman generate-seccomp generates profile that works ", func() { + tmpfile, _ := ioutil.TempFile("", "generate-seccomp.*.json") + fileName := tmpfile.Name() + + defer tmpfile.Close() + defer os.Remove(fileName) + + session := podmanTest.Podman([]string{"run", "--annotation", "io.containers.trace-syscall=" + fileName, ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // FIXME: The test doesn't wait for the file to be created so we have to pause for 2 seconds + time.Sleep(time.Second * 2) + + result := podmanTest.Podman([]string{"run", "--security-opt", "seccomp=" + fileName, ALPINE, "ls"}) + result.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman generate-seccomp should fail to run syscalls not present in the profile", func() { + tmpfile, _ := ioutil.TempFile("", "generate-seccomp.*.json") + fileName := tmpfile.Name() + + defer tmpfile.Close() + defer os.Remove(fileName) + + session := podmanTest.Podman([]string{"run", "--annotation", "io.containers.trace-syscall=" + fileName, ALPINE, "true"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // FIXME: The test doesn't wait for the file to be created so we have to pause for 2 seconds + time.Sleep(time.Second * 2) + + result := podmanTest.Podman([]string{"run", "--security-opt", "seccomp=" + fileName, ALPINE, "ls"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).ToNot(Equal(0)) + }) + It("podman generate-seccomp should generate the same seccomp profile for identical containers", func() { + tmpfile, _ := ioutil.TempFile("", "generate-seccomp.*.json") + profilePath1 := tmpfile.Name() + + tmpfile.Close() + defer os.Remove(profilePath1) + + session := podmanTest.Podman([]string{"run", "--annotation", "io.containers.trace-syscall=" + profilePath1, ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + tmpfile, _ = ioutil.TempFile("", "generate-seccomp.*.json") + profilePath2 := tmpfile.Name() + + tmpfile.Close() + defer os.Remove(profilePath2) + + result := podmanTest.Podman([]string{"run", "--annotation", "io.containers.trace-syscall=" + profilePath2, ALPINE, "ls"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + + // FIXME: The test doesn't wait for the file to be created so we have to pause for 2 seconds + time.Sleep(time.Second * 2) + + profileJSON1, _ := ioutil.ReadFile(profilePath1) + profile1 := types.Seccomp{} + err := json.Unmarshal(profileJSON1, &profile1) + Expect(err).To(BeNil()) + + profileJSON2, _ := ioutil.ReadFile(profilePath2) + profile2 := types.Seccomp{} + err = json.Unmarshal(profileJSON2, &profile2) + Expect(err).To(BeNil()) + Expect(reflect.DeepEqual(profile1.Syscalls, profile1.Syscalls)).To(Equal(true)) + }) +}) diff --git a/vendor/github.com/iovisor/gobpf/COPYRIGHT.txt b/vendor/github.com/iovisor/gobpf/COPYRIGHT.txt new file mode 100644 index 00000000000..1eae73fb260 --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/COPYRIGHT.txt @@ -0,0 +1,14 @@ +Copyright 2016 PLUMgrid +Copyright 2016 Kinvolk + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/github.com/iovisor/gobpf/LICENSE-bpf.txt b/vendor/github.com/iovisor/gobpf/LICENSE-bpf.txt new file mode 100644 index 00000000000..9d7d80e6130 --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/LICENSE-bpf.txt @@ -0,0 +1,374 @@ +The file at /elf/include/bpf.h is a copy of the Linux kernel file +/include/uapi/linux/bpf.h, retrieved from version 4.17, Git commit +36f9814, available at +https://raw.githubusercontent.com/torvalds/linux/36f9814a494a874d5a0f44843544b4b2539022db/include/uapi/linux/bpf.h. +It is provided here in unmodified source code form. + +As indicated in the file, it is licensed under the GNU General Public License, +version 2.0, with the Linux-syscall-note. Copies of the Linux-syscall-note and +GPL-2.0 license text are included below. gobpf has included this header file +in this repository with the intention of using it solely in the manner +described in the Linux-syscall-note. + += = = = = + +Linux-syscall-note: + + NOTE! This copyright does *not* cover user programs that use kernel + services by normal system calls - this is merely considered normal use + of the kernel, and does *not* fall under the heading of "derived work". + Also note that the GPL below is copyrighted by the Free Software + Foundation, but the instance of code that it refers to (the Linux + kernel) is copyrighted by me and others who actually wrote it. + + Also note that the only valid version of the GPL as far as the kernel + is concerned is _this_ particular version of the license (ie v2, not + v2.2 or v3.x or whatever), unless explicitly otherwise stated. + +Linus Torvalds + += = = = = + +GPL-2.0: + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/vendor/github.com/iovisor/gobpf/LICENSE.txt b/vendor/github.com/iovisor/gobpf/LICENSE.txt new file mode 100644 index 00000000000..8dada3edaf5 --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/iovisor/gobpf/bcc/module.go b/vendor/github.com/iovisor/gobpf/bcc/module.go new file mode 100644 index 00000000000..34a40e49368 --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/bcc/module.go @@ -0,0 +1,538 @@ +// Copyright 2016 PLUMgrid +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bcc + +import ( + "fmt" + "regexp" + "runtime" + "strings" + "sync" + "syscall" + "unsafe" + + "github.com/iovisor/gobpf/pkg/cpuonline" +) + +/* +#cgo CFLAGS: -I/usr/include/bcc/compat +#cgo LDFLAGS: -lbcc +#include +#include +*/ +import "C" + +// Module type +type Module struct { + p unsafe.Pointer + funcs map[string]int + kprobes map[string]int + uprobes map[string]int + tracepoints map[string]int + rawTracepoints map[string]int + perfEvents map[string][]int +} + +type compileRequest struct { + code string + cflags []string + rspCh chan *Module +} + +const ( + BPF_PROBE_ENTRY = iota + BPF_PROBE_RETURN +) + +const ( + XDP_FLAGS_UPDATE_IF_NOEXIST = uint32(1) << iota + XDP_FLAGS_SKB_MODE + XDP_FLAGS_DRV_MODE + XDP_FLAGS_HW_MODE + XDP_FLAGS_MODES = XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE + XDP_FLAGS_MASK = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_MODES +) + +var ( + defaultCflags []string + compileCh chan compileRequest + bpfInitOnce sync.Once +) + +func bpfInit() { + defaultCflags = []string{ + fmt.Sprintf("-DNUMCPUS=%d", runtime.NumCPU()), + } + compileCh = make(chan compileRequest) + go compile() +} + +// NewModule constructor +func newModule(code string, cflags []string) *Module { + cflagsC := make([]*C.char, len(defaultCflags)+len(cflags)) + defer func() { + for _, cflag := range cflagsC { + C.free(unsafe.Pointer(cflag)) + } + }() + for i, cflag := range cflags { + cflagsC[i] = C.CString(cflag) + } + for i, cflag := range defaultCflags { + cflagsC[len(cflags)+i] = C.CString(cflag) + } + cs := C.CString(code) + defer C.free(unsafe.Pointer(cs)) + c := C.bpf_module_create_c_from_string(cs, 2, (**C.char)(&cflagsC[0]), C.int(len(cflagsC)), (C.bool)(true)) + if c == nil { + return nil + } + return &Module{ + p: c, + funcs: make(map[string]int), + kprobes: make(map[string]int), + uprobes: make(map[string]int), + tracepoints: make(map[string]int), + rawTracepoints: make(map[string]int), + perfEvents: make(map[string][]int), + } +} + +// NewModule asynchronously compiles the code, generates a new BPF +// module and returns it. +func NewModule(code string, cflags []string) *Module { + bpfInitOnce.Do(bpfInit) + ch := make(chan *Module) + compileCh <- compileRequest{code, cflags, ch} + return <-ch +} + +func compile() { + for { + req := <-compileCh + req.rspCh <- newModule(req.code, req.cflags) + } +} + +// Close takes care of closing all kprobes opened by this modules and +// destroys the underlying libbpf module. +func (bpf *Module) Close() { + C.bpf_module_destroy(bpf.p) + for k, v := range bpf.kprobes { + C.bpf_close_perf_event_fd((C.int)(v)) + evNameCS := C.CString(k) + C.bpf_detach_kprobe(evNameCS) + C.free(unsafe.Pointer(evNameCS)) + } + for k, v := range bpf.uprobes { + C.bpf_close_perf_event_fd((C.int)(v)) + evNameCS := C.CString(k) + C.bpf_detach_uprobe(evNameCS) + C.free(unsafe.Pointer(evNameCS)) + } + for k, v := range bpf.tracepoints { + C.bpf_close_perf_event_fd((C.int)(v)) + parts := strings.SplitN(k, ":", 2) + tpCategoryCS := C.CString(parts[0]) + tpNameCS := C.CString(parts[1]) + C.bpf_detach_tracepoint(tpCategoryCS, tpNameCS) + C.free(unsafe.Pointer(tpCategoryCS)) + C.free(unsafe.Pointer(tpNameCS)) + } + for _, vs := range bpf.perfEvents { + for _, v := range vs { + C.bpf_close_perf_event_fd((C.int)(v)) + } + } + for _, fd := range bpf.funcs { + syscall.Close(fd) + } +} + +// GetProgramTag returns a tag for ebpf program under passed fd +func (bpf *Module) GetProgramTag(fd int) (tag uint64, err error) { + _, err = C.bpf_prog_get_tag(C.int(fd), (*C.ulonglong)(unsafe.Pointer(&tag))) + return tag, err +} + +// LoadNet loads a program of type BPF_PROG_TYPE_SCHED_ACT. +func (bpf *Module) LoadNet(name string) (int, error) { + return bpf.Load(name, C.BPF_PROG_TYPE_SCHED_ACT, 0, 0) +} + +// LoadKprobe loads a program of type BPF_PROG_TYPE_KPROBE. +func (bpf *Module) LoadKprobe(name string) (int, error) { + return bpf.Load(name, C.BPF_PROG_TYPE_KPROBE, 0, 0) +} + +// LoadTracepoint loads a program of type BPF_PROG_TYPE_TRACEPOINT +func (bpf *Module) LoadTracepoint(name string) (int, error) { + return bpf.Load(name, C.BPF_PROG_TYPE_TRACEPOINT, 0, 0) +} + +// LoadRawTracepoint loads a program of type BPF_PROG_TYPE_RAW_TRACEPOINT +func (bpf *Module) LoadRawTracepoint(name string) (int, error) { + return bpf.Load(name, C.BPF_PROG_TYPE_RAW_TRACEPOINT, 0, 0) +} + +// LoadPerfEvent loads a program of type BPF_PROG_TYPE_PERF_EVENT +func (bpf *Module) LoadPerfEvent(name string) (int, error) { + return bpf.Load(name, C.BPF_PROG_TYPE_PERF_EVENT, 0, 0) +} + +// LoadUprobe loads a program of type BPF_PROG_TYPE_KPROBE. +func (bpf *Module) LoadUprobe(name string) (int, error) { + return bpf.Load(name, C.BPF_PROG_TYPE_KPROBE, 0, 0) +} + +// Load a program. +func (bpf *Module) Load(name string, progType int, logLevel, logSize uint) (int, error) { + fd, ok := bpf.funcs[name] + if ok { + return fd, nil + } + fd, err := bpf.load(name, progType, logLevel, logSize) + if err != nil { + return -1, err + } + bpf.funcs[name] = fd + return fd, nil +} + +func (bpf *Module) load(name string, progType int, logLevel, logSize uint) (int, error) { + nameCS := C.CString(name) + defer C.free(unsafe.Pointer(nameCS)) + start := (*C.struct_bpf_insn)(C.bpf_function_start(bpf.p, nameCS)) + size := C.int(C.bpf_function_size(bpf.p, nameCS)) + license := C.bpf_module_license(bpf.p) + version := C.bpf_module_kern_version(bpf.p) + if start == nil { + return -1, fmt.Errorf("Module: unable to find %s", name) + } + var logBuf []byte + var logBufP *C.char + if logSize > 0 { + logBuf = make([]byte, logSize) + logBufP = (*C.char)(unsafe.Pointer(&logBuf[0])) + } + fd, err := C.bcc_prog_load(uint32(progType), nameCS, start, size, license, version, C.int(logLevel), logBufP, C.uint(len(logBuf))) + if fd < 0 { + return -1, fmt.Errorf("error loading BPF program: %v", err) + } + return int(fd), nil +} + +var kprobeRegexp = regexp.MustCompile("[+.]") +var uprobeRegexp = regexp.MustCompile("[^a-zA-Z0-9_]") + +func (bpf *Module) attachProbe(evName string, attachType uint32, fnName string, fd int, maxActive int) error { + if _, ok := bpf.kprobes[evName]; ok { + return nil + } + + evNameCS := C.CString(evName) + fnNameCS := C.CString(fnName) + res, err := C.bpf_attach_kprobe(C.int(fd), attachType, evNameCS, fnNameCS, (C.uint64_t)(0), C.int(maxActive)) + C.free(unsafe.Pointer(evNameCS)) + C.free(unsafe.Pointer(fnNameCS)) + + if res < 0 { + return fmt.Errorf("failed to attach BPF kprobe: %v", err) + } + bpf.kprobes[evName] = int(res) + return nil +} + +func (bpf *Module) attachUProbe(evName string, attachType uint32, path string, addr uint64, fd, pid int) error { + evNameCS := C.CString(evName) + binaryPathCS := C.CString(path) + res, err := C.bpf_attach_uprobe(C.int(fd), attachType, evNameCS, binaryPathCS, (C.uint64_t)(addr), (C.pid_t)(pid)) + C.free(unsafe.Pointer(evNameCS)) + C.free(unsafe.Pointer(binaryPathCS)) + + if res < 0 { + return fmt.Errorf("failed to attach BPF uprobe: %v", err) + } + bpf.uprobes[evName] = int(res) + return nil +} + +// AttachKprobe attaches a kprobe fd to a function. +func (bpf *Module) AttachKprobe(fnName string, fd int, maxActive int) error { + evName := "p_" + kprobeRegexp.ReplaceAllString(fnName, "_") + + return bpf.attachProbe(evName, BPF_PROBE_ENTRY, fnName, fd, maxActive) +} + +// AttachKretprobe attaches a kretprobe fd to a function. +func (bpf *Module) AttachKretprobe(fnName string, fd int, maxActive int) error { + evName := "r_" + kprobeRegexp.ReplaceAllString(fnName, "_") + + return bpf.attachProbe(evName, BPF_PROBE_RETURN, fnName, fd, maxActive) +} + +// AttachTracepoint attaches a tracepoint fd to a function +// The 'name' argument is in the format 'category:name' +func (bpf *Module) AttachTracepoint(name string, fd int) error { + if _, ok := bpf.tracepoints[name]; ok { + return nil + } + + parts := strings.SplitN(name, ":", 2) + if len(parts) < 2 { + return fmt.Errorf("failed to parse tracepoint name, expected %q, got %q", "category:name", name) + } + + tpCategoryCS := C.CString(parts[0]) + tpNameCS := C.CString(parts[1]) + + res, err := C.bpf_attach_tracepoint(C.int(fd), tpCategoryCS, tpNameCS) + + C.free(unsafe.Pointer(tpCategoryCS)) + C.free(unsafe.Pointer(tpNameCS)) + + if res < 0 { + return fmt.Errorf("failed to attach BPF tracepoint: %v", err) + } + bpf.tracepoints[name] = int(res) + return nil +} + +// AttachRawTracepoint attaches a raw tracepoint fd to a function +// The 'name' argument is in the format 'name', there is no category +func (bpf *Module) AttachRawTracepoint(name string, fd int) error { + if _, ok := bpf.rawTracepoints[name]; ok { + return nil + } + + tpNameCS := C.CString(name) + + res, err := C.bpf_attach_raw_tracepoint(C.int(fd), tpNameCS) + + C.free(unsafe.Pointer(tpNameCS)) + + if res < 0 { + return fmt.Errorf("failed to attach BPF tracepoint: %v", err) + } + bpf.rawTracepoints[name] = int(res) + return nil +} + +// AttachPerfEvent attaches a perf event fd to a function +// Argument 'evType' is a member of 'perf_type_id' enum in the kernel +// header 'include/uapi/linux/perf_event.h'. Argument 'evConfig' +// is one of PERF_COUNT_* constants in the same file. +func (bpf *Module) AttachPerfEvent(evType, evConfig int, samplePeriod int, sampleFreq int, pid, cpu, groupFd, fd int) error { + key := fmt.Sprintf("%d:%d", evType, evConfig) + if _, ok := bpf.perfEvents[key]; ok { + return nil + } + + res := []int{} + + if cpu > 0 { + r, err := C.bpf_attach_perf_event(C.int(fd), C.uint32_t(evType), C.uint32_t(evConfig), C.uint64_t(samplePeriod), C.uint64_t(sampleFreq), C.pid_t(pid), C.int(cpu), C.int(groupFd)) + if r < 0 { + return fmt.Errorf("failed to attach BPF perf event: %v", err) + } + + res = append(res, int(r)) + } else { + cpus, err := cpuonline.Get() + if err != nil { + return fmt.Errorf("failed to determine online cpus: %v", err) + } + + for _, i := range cpus { + r, err := C.bpf_attach_perf_event(C.int(fd), C.uint32_t(evType), C.uint32_t(evConfig), C.uint64_t(samplePeriod), C.uint64_t(sampleFreq), C.pid_t(pid), C.int(i), C.int(groupFd)) + if r < 0 { + return fmt.Errorf("failed to attach BPF perf event: %v", err) + } + + res = append(res, int(r)) + } + } + + bpf.perfEvents[key] = res + + return nil +} + +// AttachUprobe attaches a uprobe fd to the symbol in the library or binary 'name' +// The 'name' argument can be given as either a full library path (/usr/lib/..), +// a library without the lib prefix, or as a binary with full path (/bin/bash) +// A pid can be given to attach to, or -1 to attach to all processes +// +// Presently attempts to trace processes running in a different namespace +// to the tracer will fail due to limitations around namespace-switching +// in multi-threaded programs (such as Go programs) +func (bpf *Module) AttachUprobe(name, symbol string, fd, pid int) error { + path, addr, err := resolveSymbolPath(name, symbol, 0x0, pid) + if err != nil { + return err + } + evName := fmt.Sprintf("p_%s_0x%x", uprobeRegexp.ReplaceAllString(path, "_"), addr) + return bpf.attachUProbe(evName, BPF_PROBE_ENTRY, path, addr, fd, pid) +} + +// AttachMatchingUprobes attaches a uprobe fd to all symbols in the library or binary +// 'name' that match a given pattern. +// The 'name' argument can be given as either a full library path (/usr/lib/..), +// a library without the lib prefix, or as a binary with full path (/bin/bash) +// A pid can be given, or -1 to attach to all processes +// +// Presently attempts to trace processes running in a different namespace +// to the tracer will fail due to limitations around namespace-switching +// in multi-threaded programs (such as Go programs) +func (bpf *Module) AttachMatchingUprobes(name, match string, fd, pid int) error { + symbols, err := matchUserSymbols(name, match) + if err != nil { + return fmt.Errorf("unable to match symbols: %s", err) + } + if len(symbols) == 0 { + return fmt.Errorf("no symbols matching %s for %s found", match, name) + } + for _, symbol := range symbols { + if err := bpf.AttachUprobe(name, symbol.name, fd, pid); err != nil { + return err + } + } + return nil +} + +// AttachUretprobe attaches a uretprobe fd to the symbol in the library or binary 'name' +// The 'name' argument can be given as either a full library path (/usr/lib/..), +// a library without the lib prefix, or as a binary with full path (/bin/bash) +// A pid can be given to attach to, or -1 to attach to all processes +// +// Presently attempts to trace processes running in a different namespace +// to the tracer will fail due to limitations around namespace-switching +// in multi-threaded programs (such as Go programs) +func (bpf *Module) AttachUretprobe(name, symbol string, fd, pid int) error { + path, addr, err := resolveSymbolPath(name, symbol, 0x0, pid) + if err != nil { + return err + } + evName := fmt.Sprintf("r_%s_0x%x", uprobeRegexp.ReplaceAllString(path, "_"), addr) + return bpf.attachUProbe(evName, BPF_PROBE_RETURN, path, addr, fd, pid) +} + +// AttachMatchingUretprobes attaches a uretprobe fd to all symbols in the library or binary +// 'name' that match a given pattern. +// The 'name' argument can be given as either a full library path (/usr/lib/..), +// a library without the lib prefix, or as a binary with full path (/bin/bash) +// A pid can be given, or -1 to attach to all processes +// +// Presently attempts to trace processes running in a different namespace +// to the tracer will fail due to limitations around namespace-switching +// in multi-threaded programs (such as Go programs) +func (bpf *Module) AttachMatchingUretprobes(name, match string, fd, pid int) error { + symbols, err := matchUserSymbols(name, match) + if err != nil { + return fmt.Errorf("unable to match symbols: %s", err) + } + if len(symbols) == 0 { + return fmt.Errorf("no symbols matching %s for %s found", match, name) + } + for _, symbol := range symbols { + if err := bpf.AttachUretprobe(name, symbol.name, fd, pid); err != nil { + return err + } + } + return nil +} + +// TableSize returns the number of tables in the module. +func (bpf *Module) TableSize() uint64 { + size := C.bpf_num_tables(bpf.p) + return uint64(size) +} + +// TableId returns the id of a table. +func (bpf *Module) TableId(name string) C.size_t { + cs := C.CString(name) + defer C.free(unsafe.Pointer(cs)) + return C.bpf_table_id(bpf.p, cs) +} + +// TableDesc returns a map with table properties (name, fd, ...). +func (bpf *Module) TableDesc(id uint64) map[string]interface{} { + i := C.size_t(id) + return map[string]interface{}{ + "name": C.GoString(C.bpf_table_name(bpf.p, i)), + "fd": int(C.bpf_table_fd_id(bpf.p, i)), + "key_size": uint64(C.bpf_table_key_size_id(bpf.p, i)), + "leaf_size": uint64(C.bpf_table_leaf_size_id(bpf.p, i)), + "key_desc": C.GoString(C.bpf_table_key_desc_id(bpf.p, i)), + "leaf_desc": C.GoString(C.bpf_table_leaf_desc_id(bpf.p, i)), + } +} + +// TableIter returns a receveier channel to iterate over entries. +func (bpf *Module) TableIter() <-chan map[string]interface{} { + ch := make(chan map[string]interface{}) + go func() { + size := C.bpf_num_tables(bpf.p) + for i := C.size_t(0); i < size; i++ { + ch <- bpf.TableDesc(uint64(i)) + } + close(ch) + }() + return ch +} + +func (bpf *Module) attachXDP(devName string, fd int, flags uint32) error { + devNameCS := C.CString(devName) + res, err := C.bpf_attach_xdp(devNameCS, C.int(fd), C.uint32_t(flags)) + defer C.free(unsafe.Pointer(devNameCS)) + + if res != 0 || err != nil { + return fmt.Errorf("failed to attach BPF xdp to device %v: %v", devName, err) + } + return nil +} + +// AttachXDP attaches a xdp fd to a device. +func (bpf *Module) AttachXDP(devName string, fd int) error { + return bpf.attachXDP(devName, fd, 0) +} + +// AttachXDPWithFlags attaches a xdp fd to a device with flags. +func (bpf *Module) AttachXDPWithFlags(devName string, fd int, flags uint32) error { + return bpf.attachXDP(devName, fd, flags) +} + +// RemoveXDP removes any xdp from this device. +func (bpf *Module) RemoveXDP(devName string) error { + return bpf.attachXDP(devName, -1, 0) +} + +func GetSyscallFnName(name string) string { + return GetSyscallPrefix() + name +} + +func GetSyscallPrefix() string { + _, err := bccResolveName("", "sys_bpf", -1) + if err == nil { + return "sys_" + } + + _, err = bccResolveName("", "__x64_sys_bpf", -1) + if err == nil { + return "__x64_sys_" + } + + return "sys_" +} diff --git a/vendor/github.com/iovisor/gobpf/bcc/perf.go b/vendor/github.com/iovisor/gobpf/bcc/perf.go new file mode 100644 index 00000000000..a8317150322 --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/bcc/perf.go @@ -0,0 +1,200 @@ +// Copyright 2016 Kinvolk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bcc + +import ( + "encoding/binary" + "fmt" + "sync" + "unsafe" + + "github.com/iovisor/gobpf/pkg/cpuonline" +) + +/* +#cgo CFLAGS: -I/usr/include/bcc/compat +#cgo LDFLAGS: -lbcc +#include +#include +#include + +// perf_reader_raw_cb as defined in bcc libbpf.h +// typedef void (*perf_reader_raw_cb)(void *cb_cookie, void *raw, int raw_size); +extern void callback_to_go(void*, void*, int); +*/ +import "C" + +type PerfMap struct { + table *Table + readers []*C.struct_perf_reader + stop chan bool +} + +type callbackData struct { + receiverChan chan []byte +} + +const BPF_PERF_READER_PAGE_CNT = 8 + +var byteOrder binary.ByteOrder +var callbackRegister = make(map[uint64]*callbackData) +var callbackIndex uint64 +var mu sync.Mutex + +// In lack of binary.HostEndian ... +func init() { + byteOrder = determineHostByteOrder() +} + +func registerCallback(data *callbackData) uint64 { + mu.Lock() + defer mu.Unlock() + callbackIndex++ + for callbackRegister[callbackIndex] != nil { + callbackIndex++ + } + callbackRegister[callbackIndex] = data + return callbackIndex +} + +func unregisterCallback(i uint64) { + mu.Lock() + defer mu.Unlock() + delete(callbackRegister, i) +} + +func lookupCallback(i uint64) *callbackData { + return callbackRegister[i] +} + +// Gateway function as required with CGO Go >= 1.6 +// "If a C-program wants a function pointer, a gateway function has to +// be written. This is because we can't take the address of a Go +// function and give that to C-code since the cgo tool will generate a +// stub in C that should be called." +//export callback_to_go +func callback_to_go(cbCookie unsafe.Pointer, raw unsafe.Pointer, rawSize C.int) { + callbackData := lookupCallback(uint64(uintptr(cbCookie))) + callbackData.receiverChan <- C.GoBytes(raw, rawSize) +} + +// GetHostByteOrder returns the current byte-order. +func GetHostByteOrder() binary.ByteOrder { + return byteOrder +} + +func determineHostByteOrder() binary.ByteOrder { + var i int32 = 0x01020304 + u := unsafe.Pointer(&i) + pb := (*byte)(u) + b := *pb + if b == 0x04 { + return binary.LittleEndian + } + + return binary.BigEndian +} + +// InitPerfMap initializes a perf map with a receiver channel. +func InitPerfMap(table *Table, receiverChan chan []byte) (*PerfMap, error) { + fd := table.Config()["fd"].(int) + keySize := table.Config()["key_size"].(uint64) + leafSize := table.Config()["leaf_size"].(uint64) + + if keySize != 4 || leafSize != 4 { + return nil, fmt.Errorf("passed table has wrong size") + } + + callbackDataIndex := registerCallback(&callbackData{ + receiverChan, + }) + + key := make([]byte, keySize) + leaf := make([]byte, leafSize) + keyP := unsafe.Pointer(&key[0]) + leafP := unsafe.Pointer(&leaf[0]) + + readers := []*C.struct_perf_reader{} + + cpus, err := cpuonline.Get() + if err != nil { + return nil, fmt.Errorf("failed to determine online cpus: %v", err) + } + + for _, cpu := range cpus { + reader, err := bpfOpenPerfBuffer(cpu, callbackDataIndex) + if err != nil { + return nil, fmt.Errorf("failed to open perf buffer: %v", err) + } + + perfFd := C.perf_reader_fd((*C.struct_perf_reader)(reader)) + + readers = append(readers, (*C.struct_perf_reader)(reader)) + + byteOrder.PutUint32(leaf, uint32(perfFd)) + + r, err := C.bpf_update_elem(C.int(fd), keyP, leafP, 0) + if r != 0 { + return nil, fmt.Errorf("unable to initialize perf map: %v", err) + } + r = C.bpf_get_next_key(C.int(fd), keyP, keyP) + if r != 0 { + break + } + } + return &PerfMap{ + table, + readers, + make(chan bool), + }, nil +} + +// Start to poll the perf map reader and send back event data +// over the connected channel. +func (pm *PerfMap) Start() { + go pm.poll(500) +} + +// Stop to poll the perf map readers after a maximum of 500ms +// (the timeout we use for perf_reader_poll). Ideally we would +// have a way to cancel the poll, but perf_reader_poll doesn't +// support that yet. +func (pm *PerfMap) Stop() { + pm.stop <- true +} + +func (pm *PerfMap) poll(timeout int) { + for { + select { + case <-pm.stop: + return + default: + C.perf_reader_poll(C.int(len(pm.readers)), &pm.readers[0], C.int(timeout)) + } + } +} + +func bpfOpenPerfBuffer(cpu uint, callbackDataIndex uint64) (unsafe.Pointer, error) { + cpuC := C.int(cpu) + reader, err := C.bpf_open_perf_buffer( + (C.perf_reader_raw_cb)(unsafe.Pointer(C.callback_to_go)), + nil, + unsafe.Pointer(uintptr(callbackDataIndex)), + -1, cpuC, BPF_PERF_READER_PAGE_CNT) + if reader == nil { + return nil, fmt.Errorf("failed to open perf buffer: %v", err) + } + return reader, nil +} diff --git a/vendor/github.com/iovisor/gobpf/bcc/symbol.go b/vendor/github.com/iovisor/gobpf/bcc/symbol.go new file mode 100644 index 00000000000..e1fe1761413 --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/bcc/symbol.go @@ -0,0 +1,171 @@ +// Copyright 2017 Louis McCormack +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bcc + +import ( + "fmt" + "regexp" + "sync" + "unsafe" +) + +/* +#cgo CFLAGS: -I/usr/include/bcc/compat +#cgo LDFLAGS: -lbcc +#include +#include +#include +extern void foreach_symbol_callback(char*, uint64_t); +*/ +import "C" + +type symbolAddress struct { + name string + addr uint64 +} + +//symbolCache will cache module lookups +var symbolCache = struct { + cache map[string][]*symbolAddress + currentModule string + lock *sync.Mutex +}{ + cache: map[string][]*symbolAddress{}, + currentModule: "", + lock: &sync.Mutex{}, +} + +type bccSymbol struct { + name *C.char + demangleName *C.char + module *C.char + offset C.ulonglong +} + +type bccSymbolOption struct { + useDebugFile int + checkDebugFileCrc int + useSymbolType uint32 +} + +// resolveSymbolPath returns the file and offset to locate symname in module +func resolveSymbolPath(module string, symname string, addr uint64, pid int) (string, uint64, error) { + if pid == -1 { + pid = 0 + } + + modname, offset, err := bccResolveSymname(module, symname, addr, pid) + if err != nil { + return "", 0, fmt.Errorf("unable to locate symbol %s in module %s: %v", symname, module, err) + } + + return modname, offset, nil +} + +func bccResolveSymname(module string, symname string, addr uint64, pid int) (string, uint64, error) { + symbol := &bccSymbol{} + symbolC := (*C.struct_bcc_symbol)(unsafe.Pointer(symbol)) + moduleCS := C.CString(module) + defer C.free(unsafe.Pointer(moduleCS)) + symnameCS := C.CString(symname) + defer C.free(unsafe.Pointer(symnameCS)) + + res, err := C.bcc_resolve_symname(moduleCS, symnameCS, (C.uint64_t)(addr), C.int(pid), nil, symbolC) + if res < 0 { + return "", 0, fmt.Errorf("unable to locate symbol %s in module %s: %v", symname, module, err) + } + + return C.GoString(symbolC.module), (uint64)(symbolC.offset), nil +} + +func bccResolveName(module, symname string, pid int) (uint64, error) { + symbol := &bccSymbolOption{} + symbolC := (*C.struct_bcc_symbol_option)(unsafe.Pointer(symbol)) + + pidC := C.int(pid) + cache := C.bcc_symcache_new(pidC, symbolC) + + moduleCS := C.CString(module) + defer C.free(unsafe.Pointer(moduleCS)) + + nameCS := C.CString(symname) + defer C.free(unsafe.Pointer(nameCS)) + + var addr uint64 + addrC := C.uint64_t(addr) + res := C.bcc_symcache_resolve_name(cache, moduleCS, nameCS, &addrC) + if res < 0 { + return 0, fmt.Errorf("unable to locate symbol %s in module %s", symname, module) + } + + return addr, nil +} + +// getUserSymbolsAndAddresses finds a list of symbols associated with a module, +// along with their addresses. The results are cached in the symbolCache and +// returned +func getUserSymbolsAndAddresses(module string) ([]*symbolAddress, error) { + symbolCache.lock.Lock() + defer symbolCache.lock.Unlock() + // return previously cached list if it exists + if _, ok := symbolCache.cache[module]; ok { + return symbolCache.cache[module], nil + } + + symbolCache.cache[module] = []*symbolAddress{} + symbolCache.currentModule = module + + if err := bccForeachSymbol(module); err != nil { + return nil, err + } + + return symbolCache.cache[module], nil +} + +func matchUserSymbols(module, match string) ([]*symbolAddress, error) { + r, err := regexp.Compile(match) + if err != nil { + return nil, fmt.Errorf("invalid regex %s : %s", match, err) + } + matchedSymbols := []*symbolAddress{} + symbols, err := getUserSymbolsAndAddresses(module) + if err != nil { + return nil, err + } + for _, sym := range symbols { + if r.MatchString(sym.name) { + matchedSymbols = append(matchedSymbols, sym) + } + } + return matchedSymbols, nil +} + +// foreach_symbol_callback is a gateway function that will be exported to C +// so that it can be referenced as a function pointer +//export foreach_symbol_callback +func foreach_symbol_callback(symname *C.char, addr C.uint64_t) { + symbolCache.cache[symbolCache.currentModule] = + append(symbolCache.cache[symbolCache.currentModule], &symbolAddress{C.GoString(symname), (uint64)(addr)}) +} + +func bccForeachSymbol(module string) error { + moduleCS := C.CString(module) + defer C.free(unsafe.Pointer(moduleCS)) + res := C.bcc_foreach_function_symbol(moduleCS, (C.SYM_CB)(unsafe.Pointer(C.foreach_symbol_callback))) + if res < 0 { + return fmt.Errorf("unable to list symbols for %s", module) + } + return nil +} diff --git a/vendor/github.com/iovisor/gobpf/bcc/table.go b/vendor/github.com/iovisor/gobpf/bcc/table.go new file mode 100644 index 00000000000..d745b80049f --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/bcc/table.go @@ -0,0 +1,334 @@ +// Copyright 2016 PLUMgrid +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bcc + +import ( + "bytes" + "errors" + "fmt" + "os" + "unsafe" +) + +/* +#cgo CFLAGS: -I/usr/include/bcc/compat +#cgo LDFLAGS: -lbcc +#include +#include +*/ +import "C" + +var errIterationFailed = errors.New("table.Iter: leaf for next key not found") + +// Table references a BPF table. The zero value cannot be used. +type Table struct { + id C.size_t + module *Module +} + +// New tables returns a refernce to a BPF table. +func NewTable(id C.size_t, module *Module) *Table { + return &Table{ + id: id, + module: module, + } +} + +// ID returns the table id. +func (table *Table) ID() string { + return C.GoString(C.bpf_table_name(table.module.p, table.id)) +} + +// Name returns the table name. +func (table *Table) Name() string { + return C.GoString(C.bpf_table_name(table.module.p, table.id)) +} + +// Config returns the table properties (name, fd, ...). +func (table *Table) Config() map[string]interface{} { + mod := table.module.p + return map[string]interface{}{ + "name": C.GoString(C.bpf_table_name(mod, table.id)), + "fd": int(C.bpf_table_fd_id(mod, table.id)), + "key_size": uint64(C.bpf_table_key_size_id(mod, table.id)), + "leaf_size": uint64(C.bpf_table_leaf_size_id(mod, table.id)), + "key_desc": C.GoString(C.bpf_table_key_desc_id(mod, table.id)), + "leaf_desc": C.GoString(C.bpf_table_leaf_desc_id(mod, table.id)), + } +} + +func (table *Table) LeafStrToBytes(leafStr string) ([]byte, error) { + mod := table.module.p + + leafSize := C.bpf_table_leaf_size_id(mod, table.id) + leaf := make([]byte, leafSize) + leafP := unsafe.Pointer(&leaf[0]) + + leafCS := C.CString(leafStr) + defer C.free(unsafe.Pointer(leafCS)) + + r := C.bpf_table_leaf_sscanf(mod, table.id, leafCS, leafP) + if r != 0 { + return nil, fmt.Errorf("error scanning leaf (%v) from string", leafStr) + } + return leaf, nil +} + +func (table *Table) KeyStrToBytes(keyStr string) ([]byte, error) { + mod := table.module.p + + keySize := C.bpf_table_key_size_id(mod, table.id) + key := make([]byte, keySize) + keyP := unsafe.Pointer(&key[0]) + + keyCS := C.CString(keyStr) + defer C.free(unsafe.Pointer(keyCS)) + + r := C.bpf_table_key_sscanf(mod, table.id, keyCS, keyP) + if r != 0 { + return nil, fmt.Errorf("error scanning key (%v) from string", keyStr) + } + return key, nil +} + +// KeyBytesToStr returns the given key value formatted using the bcc-table's key string printer. +func (table *Table) KeyBytesToStr(key []byte) (string, error) { + keySize := len(key) + keyP := unsafe.Pointer(&key[0]) + + keyStr := make([]byte, keySize*8) + keyStrP := (*C.char)(unsafe.Pointer(&keyStr[0])) + + if res := C.bpf_table_key_snprintf(table.module.p, table.id, keyStrP, C.size_t(len(keyStr)), keyP); res != 0 { + return "", fmt.Errorf("formatting table-key: %d", res) + } + + return string(keyStr[:bytes.IndexByte(keyStr, 0)]), nil +} + +// LeafBytesToStr returns the given leaf value formatted using the bcc-table's leaf string printer. +func (table *Table) LeafBytesToStr(leaf []byte) (string, error) { + leafSize := len(leaf) + leafP := unsafe.Pointer(&leaf[0]) + + leafStr := make([]byte, leafSize*8) + leafStrP := (*C.char)(unsafe.Pointer(&leafStr[0])) + + if res := C.bpf_table_leaf_snprintf(table.module.p, table.id, leafStrP, C.size_t(len(leafStr)), leafP); res != 0 { + return "", fmt.Errorf("formatting table-leaf: %d", res) + } + + return string(leafStr[:bytes.IndexByte(leafStr, 0)]), nil +} + +// Get takes a key and returns the value or nil, and an 'ok' style indicator. +func (table *Table) Get(key []byte) ([]byte, error) { + mod := table.module.p + fd := C.bpf_table_fd_id(mod, table.id) + + keyP := unsafe.Pointer(&key[0]) + + leafSize := C.bpf_table_leaf_size_id(mod, table.id) + leaf := make([]byte, leafSize) + leafP := unsafe.Pointer(&leaf[0]) + + r, err := C.bpf_lookup_elem(fd, keyP, leafP) + if r != 0 { + keyStr, errK := table.KeyBytesToStr(key) + if errK != nil { + keyStr = fmt.Sprintf("%v", key) + } + return nil, fmt.Errorf("Table.Get: key %v: %v", keyStr, err) + } + + return leaf, nil +} + +// GetP takes a key and returns the value or nil. +func (table *Table) GetP(key unsafe.Pointer) (unsafe.Pointer, error) { + fd := C.bpf_table_fd_id(table.module.p, table.id) + + leafSize := C.bpf_table_leaf_size_id(table.module.p, table.id) + leaf := make([]byte, leafSize) + leafP := unsafe.Pointer(&leaf[0]) + + _, err := C.bpf_lookup_elem(fd, key, leafP) + if err != nil { + return nil, err + } + return leafP, nil +} + +// Set a key to a value. +func (table *Table) Set(key, leaf []byte) error { + fd := C.bpf_table_fd_id(table.module.p, table.id) + + keyP := unsafe.Pointer(&key[0]) + leafP := unsafe.Pointer(&leaf[0]) + + r, err := C.bpf_update_elem(fd, keyP, leafP, 0) + if r != 0 { + keyStr, errK := table.KeyBytesToStr(key) + if errK != nil { + keyStr = fmt.Sprintf("%v", key) + } + leafStr, errL := table.LeafBytesToStr(leaf) + if errL != nil { + leafStr = fmt.Sprintf("%v", leaf) + } + + return fmt.Errorf("Table.Set: update %v to %v: %v", keyStr, leafStr, err) + } + + return nil +} + +// SetP a key to a value as unsafe.Pointer. +func (table *Table) SetP(key, leaf unsafe.Pointer) error { + fd := C.bpf_table_fd_id(table.module.p, table.id) + + _, err := C.bpf_update_elem(fd, key, leaf, 0) + if err != nil { + return err + } + + return nil +} + +// Delete a key. +func (table *Table) Delete(key []byte) error { + fd := C.bpf_table_fd_id(table.module.p, table.id) + keyP := unsafe.Pointer(&key[0]) + r, err := C.bpf_delete_elem(fd, keyP) + if r != 0 { + keyStr, errK := table.KeyBytesToStr(key) + if errK != nil { + keyStr = fmt.Sprintf("%v", key) + } + return fmt.Errorf("Table.Delete: key %v: %v", keyStr, err) + } + return nil +} + +// DeleteP a key. +func (table *Table) DeleteP(key unsafe.Pointer) error { + fd := C.bpf_table_fd_id(table.module.p, table.id) + _, err := C.bpf_delete_elem(fd, key) + if err != nil { + return err + } + return nil +} + +// DeleteAll deletes all entries from the table +func (table *Table) DeleteAll() error { + mod := table.module.p + fd := C.bpf_table_fd_id(mod, table.id) + + keySize := C.bpf_table_key_size_id(mod, table.id) + key := make([]byte, keySize) + keyP := unsafe.Pointer(&key[0]) + for res := C.bpf_get_first_key(fd, keyP, keySize); res == 0; res = C.bpf_get_next_key(fd, keyP, keyP) { + r, err := C.bpf_delete_elem(fd, keyP) + if r != 0 { + return fmt.Errorf("Table.DeleteAll: unable to delete element: %v", err) + } + } + return nil +} + +// TableIterator contains the current position for iteration over a *bcc.Table and provides methods for iteration. +type TableIterator struct { + table *Table + fd C.int + + err error + + key []byte + leaf []byte +} + +// Iter returns an iterator to list all table entries available as raw bytes. +func (table *Table) Iter() *TableIterator { + fd := C.bpf_table_fd_id(table.module.p, table.id) + + return &TableIterator{ + table: table, + fd: fd, + } +} + +// Next looks up the next element and return true if one is available. +func (it *TableIterator) Next() bool { + if it.err != nil { + return false + } + + if it.key == nil { + keySize := C.bpf_table_key_size_id(it.table.module.p, it.table.id) + + key := make([]byte, keySize) + keyP := unsafe.Pointer(&key[0]) + if res, err := C.bpf_get_first_key(it.fd, keyP, keySize); res != 0 { + if !os.IsNotExist(err) { + it.err = err + } + return false + } + + leafSize := C.bpf_table_leaf_size_id(it.table.module.p, it.table.id) + leaf := make([]byte, leafSize) + + it.key = key + it.leaf = leaf + } else { + keyP := unsafe.Pointer(&it.key[0]) + if res, err := C.bpf_get_next_key(it.fd, keyP, keyP); res != 0 { + if !os.IsNotExist(err) { + it.err = err + } + return false + } + } + + keyP := unsafe.Pointer(&it.key[0]) + leafP := unsafe.Pointer(&it.leaf[0]) + if res, err := C.bpf_lookup_elem(it.fd, keyP, leafP); res != 0 { + it.err = errIterationFailed + if !os.IsNotExist(err) { + it.err = err + } + return false + } + + return true +} + +// Key returns the current key value of the iterator, if the most recent call to Next returned true. +// The slice is valid only until the next call to Next. +func (it *TableIterator) Key() []byte { + return it.key +} + +// Leaf returns the current leaf value of the iterator, if the most recent call to Next returned true. +// The slice is valid only until the next call to Next. +func (it *TableIterator) Leaf() []byte { + return it.leaf +} + +// Err returns the last error that ocurred while table.Iter oder iter.Next +func (it *TableIterator) Err() error { + return it.err +} diff --git a/vendor/github.com/iovisor/gobpf/pkg/cpuonline/cpu_range.go b/vendor/github.com/iovisor/gobpf/pkg/cpuonline/cpu_range.go new file mode 100644 index 00000000000..aa87830b2d4 --- /dev/null +++ b/vendor/github.com/iovisor/gobpf/pkg/cpuonline/cpu_range.go @@ -0,0 +1,43 @@ +package cpuonline + +import ( + "io/ioutil" + "strconv" + "strings" +) + +const cpuOnline = "/sys/devices/system/cpu/online" + +// loosely based on https://github.com/iovisor/bcc/blob/v0.3.0/src/python/bcc/utils.py#L15 +func readCPURange(cpuRangeStr string) ([]uint, error) { + var cpus []uint + cpuRangeStr = strings.Trim(cpuRangeStr, "\n ") + for _, cpuRange := range strings.Split(cpuRangeStr, ",") { + rangeOp := strings.SplitN(cpuRange, "-", 2) + first, err := strconv.ParseUint(rangeOp[0], 10, 32) + if err != nil { + return nil, err + } + if len(rangeOp) == 1 { + cpus = append(cpus, uint(first)) + continue + } + last, err := strconv.ParseUint(rangeOp[1], 10, 32) + if err != nil { + return nil, err + } + for n := first; n <= last; n++ { + cpus = append(cpus, uint(n)) + } + } + return cpus, nil +} + +// Get returns a slice with the online CPUs, for example `[0, 2, 3]` +func Get() ([]uint, error) { + buf, err := ioutil.ReadFile(cpuOnline) + if err != nil { + return nil, err + } + return readCPURange(string(buf)) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 3a27eea12b5..ed84851ed75 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -178,6 +178,7 @@ github.com/docker/distribution/registry/storage/cache github.com/docker/distribution/registry/storage/cache/memory github.com/docker/distribution/metrics # github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16 +github.com/docker/docker/api/types github.com/docker/docker/pkg/signal github.com/docker/docker/pkg/homedir github.com/docker/docker/oci/caps @@ -186,24 +187,25 @@ github.com/docker/docker/pkg/term github.com/docker/docker/pkg/ioutils github.com/docker/docker/profiles/seccomp github.com/docker/docker/pkg/parsers +github.com/docker/docker/api/types/container +github.com/docker/docker/api/types/filters +github.com/docker/docker/api/types/mount +github.com/docker/docker/api/types/network +github.com/docker/docker/api/types/registry +github.com/docker/docker/api/types/swarm github.com/docker/docker/api/types/versions github.com/docker/docker/pkg/idtools github.com/docker/docker/errdefs github.com/docker/docker/pkg/term/windows github.com/docker/docker/pkg/longpath -github.com/docker/docker/api/types github.com/docker/docker/pkg/parsers/kernel -github.com/docker/docker/api/types/registry -github.com/docker/docker/api/types/swarm +github.com/docker/docker/api/types/blkiodev +github.com/docker/docker/api/types/strslice +github.com/docker/docker/api/types/swarm/runtime github.com/docker/docker/pkg/fileutils github.com/docker/docker/pkg/stdcopy github.com/docker/docker/pkg/system github.com/docker/docker/client -github.com/docker/docker/api/types/container -github.com/docker/docker/api/types/filters -github.com/docker/docker/api/types/mount -github.com/docker/docker/api/types/network -github.com/docker/docker/api/types/swarm/runtime github.com/docker/docker/pkg/pools github.com/docker/docker/pkg/mount github.com/docker/docker/api @@ -211,8 +213,6 @@ github.com/docker/docker/api/types/events github.com/docker/docker/api/types/image github.com/docker/docker/api/types/time github.com/docker/docker/api/types/volume -github.com/docker/docker/api/types/blkiodev -github.com/docker/docker/api/types/strslice # github.com/docker/docker-credential-helpers v0.6.2 github.com/docker/docker-credential-helpers/credentials github.com/docker/docker-credential-helpers/client @@ -275,6 +275,9 @@ github.com/ijc/Gotty github.com/imdario/mergo # github.com/inconshreveable/mousetrap v1.0.0 github.com/inconshreveable/mousetrap +# github.com/iovisor/gobpf v0.0.0-20190329163444-e0d8d785d368 +github.com/iovisor/gobpf/bcc +github.com/iovisor/gobpf/pkg/cpuonline # github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111 github.com/ishidawataru/sctp # github.com/json-iterator/go v1.1.7