Merge pull request #91366 from giuseppe/cgroupfs-cgroupv2

vendor: update google/cadvisor and opencontainers/runc
This commit is contained in:
Kubernetes Prow Robot
2020-06-26 04:17:31 -07:00
committed by GitHub
641 changed files with 36678 additions and 89396 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
test/test
test/piggie
test/phaul
image
rpc/rpc.proto

View File

@@ -0,0 +1,25 @@
language: go
sudo: required
os:
- linux
go:
- "1.14.x"
- "1.13.x"
- tip
env:
# Run the tests with CRIU master and criu-dev
- CRIU_BRANCH="master"
- CRIU_BRANCH="criu-dev"
install:
- sudo apt-get update
- sudo apt-get install -y libprotobuf-dev libprotobuf-c0-dev protobuf-c-compiler protobuf-compiler python-protobuf libnl-3-dev libnet-dev libcap-dev
- go get github.com/checkpoint-restore/go-criu
- git clone --single-branch -b ${CRIU_BRANCH} https://github.com/checkpoint-restore/criu.git
- cd criu; make
- sudo install -D -m 755 criu/criu /usr/sbin/
- cd ..
script:
# This builds the code without running the tests.
- make build phaul test/test test/phaul test/piggie
# Run actual test as root as it uses CRIU.
- sudo make test phaul-test

33
vendor/github.com/checkpoint-restore/go-criu/v4/BUILD generated vendored Normal file
View File

@@ -0,0 +1,33 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"main.go",
"notify.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/checkpoint-restore/go-criu/v4",
importpath = "github.com/checkpoint-restore/go-criu/v4",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/checkpoint-restore/go-criu/v4/rpc:go_default_library",
"//vendor/github.com/golang/protobuf/proto:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/checkpoint-restore/go-criu/v4/rpc:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,60 @@
GO ?= go
CC ?= gcc
ifeq ($(GOPATH),)
export GOPATH := $(shell $(GO) env GOPATH)
endif
FIRST_GOPATH := $(firstword $(subst :, ,$(GOPATH)))
GOBIN := $(shell $(GO) env GOBIN)
ifeq ($(GOBIN),)
GOBIN := $(FIRST_GOPATH)/bin
endif
all: build test phaul phaul-test
lint:
@golint . test phaul
build:
@$(GO) build -v
test/piggie: test/piggie.c
@$(CC) $^ -o $@
test/test: test/main.go
@$(GO) build -v -o test/test test/main.go
test: test/test test/piggie
mkdir -p image
test/piggie
test/test dump `pidof piggie` image
test/test restore image
pkill -9 piggie || :
phaul:
@cd phaul; go build -v
test/phaul: test/phaul-main.go
@$(GO) build -v -o test/phaul test/phaul-main.go
phaul-test: test/phaul test/piggie
rm -rf image
test/piggie
test/phaul `pidof piggie`
pkill -9 piggie || :
clean:
@rm -f test/test test/piggie test/phaul
@rm -rf image
@rm -f rpc/rpc.proto
install.tools:
if [ ! -x "$(GOBIN)/golint" ]; then \
$(GO) get -u golang.org/x/lint/golint; \
fi
rpc/rpc.proto:
curl -s https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/rpc.proto -o $@
rpc/rpc.pb.go: rpc/rpc.proto
protoc --go_out=. $^
.PHONY: build test clean lint phaul

View File

@@ -0,0 +1,75 @@
[![master](https://travis-ci.org/checkpoint-restore/go-criu.svg?branch=master)](https://travis-ci.org/checkpoint-restore/go-criu)
## go-criu -- Go bindings for [CRIU](https://criu.org/)
This repository provides Go bindings for CRIU. The code is based on the Go based PHaul
implementation from the CRIU repository. For easier inclusion into other Go projects the
CRIU Go bindings have been moved to this repository.
The Go bindings provide an easy way to use the CRIU RPC calls from Go without the need
to set up all the infrastructure to make the actual RPC connection to CRIU.
The following example would print the version of CRIU:
```
c := criu.MakeCriu()
version, err := c.GetCriuVersion()
fmt.Println(version)
```
or to just check if at least a certain CRIU version is installed:
```
c := criu.MakeCriu()
result, err := c.IsCriuAtLeast(31100)
```
## Releases
The first go-criu release was 3.11 based on CRIU 3.11. The initial plan
was to follow CRIU so that go-criu would carry the same version number as
CRIU.
As go-criu is imported in other projects and as Go modules are expected
to follow Semantic Versioning go-criu will also follow Semantic Versioning
starting with the 4.0.0 release.
4.0.0 is based on CRIU 3.14
## How to contribute
While bug fixes can first be identified via an "issue", that is not required.
It's ok to just open up a PR with the fix, but make sure you include the same
information you would have included in an issue - like how to reproduce it.
PRs for new features should include some background on what use cases the
new code is trying to address. When possible and when it makes sense, try to
break-up larger PRs into smaller ones - it's easier to review smaller
code changes. But only if those smaller ones make sense as stand-alone PRs.
Regardless of the type of PR, all PRs should include:
* well documented code changes
* additional testcases. Ideally, they should fail w/o your code change applied
* documentation changes
Squash your commits into logical pieces of work that might want to be reviewed
separate from the rest of the PRs. Ideally, each commit should implement a
single idea, and the PR branch should pass the tests at every commit. GitHub
makes it easy to review the cumulative effect of many commits; so, when in
doubt, use smaller commits.
PRs that fix issues should include a reference like `Closes #XXXX` in the
commit message so that github will automatically close the referenced issue
when the PR is merged.
Contributors must assert that they are in compliance with the [Developer
Certificate of Origin 1.1](http://developercertificate.org/). This is achieved
by adding a "Signed-off-by" line containing the contributor's name and e-mail
to every commit message. Your signature certifies that you wrote the patch or
otherwise have the right to pass it on as an open-source patch.
### License and copyright
Unless mentioned otherwise in a specific file's header, all code in
this project is released under the Apache 2.0 license.
The author of a change remains the copyright holder of their code
(no copyright assignment). The list of authors and contributors can be
retrieved from the git commit history and in some cases, the file headers.

View File

@@ -0,0 +1,5 @@
module github.com/checkpoint-restore/go-criu/v4
go 1.13
require github.com/golang/protobuf v1.3.5

20
vendor/github.com/checkpoint-restore/go-criu/v4/go.sum generated vendored Normal file
View File

@@ -0,0 +1,20 @@
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=

250
vendor/github.com/checkpoint-restore/go-criu/v4/main.go generated vendored Normal file
View File

@@ -0,0 +1,250 @@
package criu
import (
"errors"
"fmt"
"os"
"os/exec"
"strconv"
"syscall"
"github.com/checkpoint-restore/go-criu/v4/rpc"
"github.com/golang/protobuf/proto"
)
// Criu struct
type Criu struct {
swrkCmd *exec.Cmd
swrkSk *os.File
}
// MakeCriu returns the Criu object required for most operations
func MakeCriu() *Criu {
return &Criu{}
}
// Prepare sets up everything for the RPC communication to CRIU
func (c *Criu) Prepare() error {
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_SEQPACKET, 0)
if err != nil {
return err
}
cln := os.NewFile(uintptr(fds[0]), "criu-xprt-cln")
syscall.CloseOnExec(fds[0])
srv := os.NewFile(uintptr(fds[1]), "criu-xprt-srv")
defer srv.Close()
args := []string{"swrk", strconv.Itoa(fds[1])}
cmd := exec.Command("criu", args...)
err = cmd.Start()
if err != nil {
cln.Close()
return err
}
c.swrkCmd = cmd
c.swrkSk = cln
return nil
}
// Cleanup cleans up
func (c *Criu) Cleanup() {
if c.swrkCmd != nil {
c.swrkSk.Close()
c.swrkSk = nil
c.swrkCmd.Wait()
c.swrkCmd = nil
}
}
func (c *Criu) sendAndRecv(reqB []byte) ([]byte, int, error) {
cln := c.swrkSk
_, err := cln.Write(reqB)
if err != nil {
return nil, 0, err
}
respB := make([]byte, 2*4096)
n, err := cln.Read(respB)
if err != nil {
return nil, 0, err
}
return respB, n, nil
}
func (c *Criu) doSwrk(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) error {
resp, err := c.doSwrkWithResp(reqType, opts, nfy)
if err != nil {
return err
}
respType := resp.GetType()
if respType != reqType {
return errors.New("unexpected responce")
}
return nil
}
func (c *Criu) doSwrkWithResp(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) (*rpc.CriuResp, error) {
var resp *rpc.CriuResp
req := rpc.CriuReq{
Type: &reqType,
Opts: opts,
}
if nfy != nil {
opts.NotifyScripts = proto.Bool(true)
}
if c.swrkCmd == nil {
err := c.Prepare()
if err != nil {
return nil, err
}
defer c.Cleanup()
}
for {
reqB, err := proto.Marshal(&req)
if err != nil {
return nil, err
}
respB, respS, err := c.sendAndRecv(reqB)
if err != nil {
return nil, err
}
resp = &rpc.CriuResp{}
err = proto.Unmarshal(respB[:respS], resp)
if err != nil {
return nil, err
}
if !resp.GetSuccess() {
return resp, fmt.Errorf("operation failed (msg:%s err:%d)",
resp.GetCrErrmsg(), resp.GetCrErrno())
}
respType := resp.GetType()
if respType != rpc.CriuReqType_NOTIFY {
break
}
if nfy == nil {
return resp, errors.New("unexpected notify")
}
notify := resp.GetNotify()
switch notify.GetScript() {
case "pre-dump":
err = nfy.PreDump()
case "post-dump":
err = nfy.PostDump()
case "pre-restore":
err = nfy.PreRestore()
case "post-restore":
err = nfy.PostRestore(notify.GetPid())
case "network-lock":
err = nfy.NetworkLock()
case "network-unlock":
err = nfy.NetworkUnlock()
case "setup-namespaces":
err = nfy.SetupNamespaces(notify.GetPid())
case "post-setup-namespaces":
err = nfy.PostSetupNamespaces()
case "post-resume":
err = nfy.PostResume()
default:
err = nil
}
if err != nil {
return resp, err
}
req = rpc.CriuReq{
Type: &respType,
NotifySuccess: proto.Bool(true),
}
}
return resp, nil
}
// Dump dumps a process
func (c *Criu) Dump(opts rpc.CriuOpts, nfy Notify) error {
return c.doSwrk(rpc.CriuReqType_DUMP, &opts, nfy)
}
// Restore restores a process
func (c *Criu) Restore(opts rpc.CriuOpts, nfy Notify) error {
return c.doSwrk(rpc.CriuReqType_RESTORE, &opts, nfy)
}
// PreDump does a pre-dump
func (c *Criu) PreDump(opts rpc.CriuOpts, nfy Notify) error {
return c.doSwrk(rpc.CriuReqType_PRE_DUMP, &opts, nfy)
}
// StartPageServer starts the page server
func (c *Criu) StartPageServer(opts rpc.CriuOpts) error {
return c.doSwrk(rpc.CriuReqType_PAGE_SERVER, &opts, nil)
}
// StartPageServerChld starts the page server and returns PID and port
func (c *Criu) StartPageServerChld(opts rpc.CriuOpts) (int, int, error) {
resp, err := c.doSwrkWithResp(rpc.CriuReqType_PAGE_SERVER_CHLD, &opts, nil)
if err != nil {
return 0, 0, err
}
return int(resp.Ps.GetPid()), int(resp.Ps.GetPort()), nil
}
// GetCriuVersion executes the VERSION RPC call and returns the version
// as an integer. Major * 10000 + Minor * 100 + SubLevel
func (c *Criu) GetCriuVersion() (int, error) {
resp, err := c.doSwrkWithResp(rpc.CriuReqType_VERSION, nil, nil)
if err != nil {
return 0, err
}
if resp.GetType() != rpc.CriuReqType_VERSION {
return 0, fmt.Errorf("Unexpected CRIU RPC response")
}
version := int(*resp.GetVersion().MajorNumber) * 10000
version += int(*resp.GetVersion().MinorNumber) * 100
if resp.GetVersion().Sublevel != nil {
version += int(*resp.GetVersion().Sublevel)
}
if resp.GetVersion().Gitid != nil {
// taken from runc: if it is a git release -> increase minor by 1
version -= (version % 100)
version += 100
}
return version, nil
}
// IsCriuAtLeast checks if the version is at least the same
// as the parameter version
func (c *Criu) IsCriuAtLeast(version int) (bool, error) {
criuVersion, err := c.GetCriuVersion()
if err != nil {
return false, err
}
if criuVersion >= version {
return true, nil
}
return false, nil
}

View File

@@ -0,0 +1,63 @@
package criu
//Notify interface
type Notify interface {
PreDump() error
PostDump() error
PreRestore() error
PostRestore(pid int32) error
NetworkLock() error
NetworkUnlock() error
SetupNamespaces(pid int32) error
PostSetupNamespaces() error
PostResume() error
}
// NoNotify struct
type NoNotify struct {
}
// PreDump NoNotify
func (c NoNotify) PreDump() error {
return nil
}
// PostDump NoNotify
func (c NoNotify) PostDump() error {
return nil
}
// PreRestore NoNotify
func (c NoNotify) PreRestore() error {
return nil
}
// PostRestore NoNotify
func (c NoNotify) PostRestore(pid int32) error {
return nil
}
// NetworkLock NoNotify
func (c NoNotify) NetworkLock() error {
return nil
}
// NetworkUnlock NoNotify
func (c NoNotify) NetworkUnlock() error {
return nil
}
// SetupNamespaces NoNotify
func (c NoNotify) SetupNamespaces(pid int32) error {
return nil
}
// PostSetupNamespaces NoNotify
func (c NoNotify) PostSetupNamespaces() error {
return nil
}
// PostResume NoNotify
func (c NoNotify) PostResume() error {
return nil
}

View File

@@ -3,8 +3,8 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["rpc.pb.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/checkpoint-restore/go-criu/rpc",
importpath = "github.com/checkpoint-restore/go-criu/rpc",
importmap = "k8s.io/kubernetes/vendor/github.com/checkpoint-restore/go-criu/v4/rpc",
importpath = "github.com/checkpoint-restore/go-criu/v4/rpc",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/golang/protobuf/proto:go_default_library"],
)

File diff suppressed because it is too large Load Diff

View File

@@ -7,14 +7,10 @@ go_library(
"collection.go",
"doc.go",
"elf_reader.go",
"feature.go",
"linker.go",
"map.go",
"marshalers.go",
"prog.go",
"ptr_32_be.go",
"ptr_32_le.go",
"ptr_64.go",
"syscalls.go",
"types.go",
"types_string.go",
@@ -25,8 +21,9 @@ go_library(
deps = [
"//vendor/github.com/cilium/ebpf/asm:go_default_library",
"//vendor/github.com/cilium/ebpf/internal:go_default_library",
"//vendor/github.com/cilium/ebpf/internal/btf:go_default_library",
"//vendor/github.com/cilium/ebpf/internal/unix:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/golang.org/x/xerrors:go_default_library",
],
)

41
vendor/github.com/cilium/ebpf/abi.go generated vendored
View File

@@ -8,7 +8,9 @@ import (
"os"
"syscall"
"github.com/pkg/errors"
"github.com/cilium/ebpf/internal"
"golang.org/x/xerrors"
)
// MapABI are the attributes of a Map which are available across all supported kernels.
@@ -30,10 +32,10 @@ func newMapABIFromSpec(spec *MapSpec) *MapABI {
}
}
func newMapABIFromFd(fd *bpfFD) (string, *MapABI, error) {
func newMapABIFromFd(fd *internal.FD) (string, *MapABI, error) {
info, err := bpfGetMapInfoByFD(fd)
if err != nil {
if errors.Cause(err) == syscall.EINVAL {
if xerrors.Is(err, syscall.EINVAL) {
abi, err := newMapABIFromProc(fd)
return "", abi, err
}
@@ -49,7 +51,7 @@ func newMapABIFromFd(fd *bpfFD) (string, *MapABI, error) {
}, nil
}
func newMapABIFromProc(fd *bpfFD) (*MapABI, error) {
func newMapABIFromProc(fd *internal.FD) (*MapABI, error) {
var abi MapABI
err := scanFdInfo(fd, map[string]interface{}{
"map_type": &abi.Type,
@@ -93,10 +95,10 @@ func newProgramABIFromSpec(spec *ProgramSpec) *ProgramABI {
}
}
func newProgramABIFromFd(fd *bpfFD) (string, *ProgramABI, error) {
func newProgramABIFromFd(fd *internal.FD) (string, *ProgramABI, error) {
info, err := bpfGetProgInfoByFD(fd)
if err != nil {
if errors.Cause(err) == syscall.EINVAL {
if xerrors.Is(err, syscall.EINVAL) {
return newProgramABIFromProc(fd)
}
@@ -104,10 +106,10 @@ func newProgramABIFromFd(fd *bpfFD) (string, *ProgramABI, error) {
}
var name string
if bpfName := convertCString(info.name[:]); bpfName != "" {
if bpfName := internal.CString(info.name[:]); bpfName != "" {
name = bpfName
} else {
name = convertCString(info.tag[:])
name = internal.CString(info.tag[:])
}
return name, &ProgramABI{
@@ -115,7 +117,7 @@ func newProgramABIFromFd(fd *bpfFD) (string, *ProgramABI, error) {
}, nil
}
func newProgramABIFromProc(fd *bpfFD) (string, *ProgramABI, error) {
func newProgramABIFromProc(fd *internal.FD) (string, *ProgramABI, error) {
var (
abi ProgramABI
name string
@@ -125,6 +127,12 @@ func newProgramABIFromProc(fd *bpfFD) (string, *ProgramABI, error) {
"prog_type": &abi.Type,
"prog_tag": &name,
})
if xerrors.Is(err, errMissingFields) {
return "", nil, &internal.UnsupportedFeatureError{
Name: "reading ABI from /proc/self/fdinfo",
MinimumVersion: internal.Version{4, 11, 0},
}
}
if err != nil {
return "", nil, err
}
@@ -132,8 +140,8 @@ func newProgramABIFromProc(fd *bpfFD) (string, *ProgramABI, error) {
return name, &abi, nil
}
func scanFdInfo(fd *bpfFD, fields map[string]interface{}) error {
raw, err := fd.value()
func scanFdInfo(fd *internal.FD, fields map[string]interface{}) error {
raw, err := fd.Value()
if err != nil {
return err
}
@@ -144,9 +152,14 @@ func scanFdInfo(fd *bpfFD, fields map[string]interface{}) error {
}
defer fh.Close()
return errors.Wrap(scanFdInfoReader(fh, fields), fh.Name())
if err := scanFdInfoReader(fh, fields); err != nil {
return xerrors.Errorf("%s: %w", fh.Name(), err)
}
return nil
}
var errMissingFields = xerrors.New("missing fields")
func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error {
var (
scanner = bufio.NewScanner(r)
@@ -166,7 +179,7 @@ func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error {
}
if n, err := fmt.Fscanln(bytes.NewReader(parts[1]), field); err != nil || n != 1 {
return errors.Wrapf(err, "can't parse field %s", name)
return xerrors.Errorf("can't parse field %s: %v", name, err)
}
scanned++
@@ -177,7 +190,7 @@ func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error {
}
if scanned != len(fields) {
return errors.Errorf("parsed %d instead of %d fields", scanned, len(fields))
return errMissingFields
}
return nil

View File

@@ -20,7 +20,7 @@ go_library(
importmap = "k8s.io/kubernetes/vendor/github.com/cilium/ebpf/asm",
importpath = "github.com/cilium/ebpf/asm",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/pkg/errors:go_default_library"],
deps = ["//vendor/golang.org/x/xerrors:go_default_library"],
)
filegroup(

View File

@@ -7,7 +7,7 @@ import (
"math"
"strings"
"github.com/pkg/errors"
"golang.org/x/xerrors"
)
// InstructionSize is the size of a BPF instruction in bytes
@@ -39,10 +39,12 @@ func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, err
}
ins.OpCode = bi.OpCode
ins.Dst = bi.Registers.Dst()
ins.Src = bi.Registers.Src()
ins.Offset = bi.Offset
ins.Constant = int64(bi.Constant)
ins.Dst, ins.Src, err = bi.Registers.Unmarshal(bo)
if err != nil {
return 0, xerrors.Errorf("can't unmarshal registers: %s", err)
}
if !bi.OpCode.isDWordLoad() {
return InstructionSize, nil
@@ -51,10 +53,10 @@ func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, err
var bi2 bpfInstruction
if err := binary.Read(r, bo, &bi2); err != nil {
// No Wrap, to avoid io.EOF clash
return 0, errors.New("64bit immediate is missing second half")
return 0, xerrors.New("64bit immediate is missing second half")
}
if bi2.OpCode != 0 || bi2.Offset != 0 || bi2.Registers != 0 {
return 0, errors.New("64bit immediate has non-zero fields")
return 0, xerrors.New("64bit immediate has non-zero fields")
}
ins.Constant = int64(uint64(uint32(bi2.Constant))<<32 | uint64(uint32(bi.Constant)))
@@ -64,7 +66,7 @@ func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, err
// Marshal encodes a BPF instruction.
func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) {
if ins.OpCode == InvalidOpCode {
return 0, errors.New("invalid opcode")
return 0, xerrors.New("invalid opcode")
}
isDWordLoad := ins.OpCode.isDWordLoad()
@@ -75,9 +77,14 @@ func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error)
cons = int32(uint32(ins.Constant))
}
regs, err := newBPFRegisters(ins.Dst, ins.Src, bo)
if err != nil {
return 0, xerrors.Errorf("can't marshal registers: %s", err)
}
bpfi := bpfInstruction{
ins.OpCode,
newBPFRegisters(ins.Dst, ins.Src),
regs,
ins.Offset,
cons,
}
@@ -103,22 +110,52 @@ func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error)
// RewriteMapPtr changes an instruction to use a new map fd.
//
// Returns an error if the fd is invalid, or the instruction
// is incorrect.
// Returns an error if the instruction doesn't load a map.
func (ins *Instruction) RewriteMapPtr(fd int) error {
if !ins.OpCode.isDWordLoad() {
return errors.Errorf("%s is not a 64 bit load", ins.OpCode)
return xerrors.Errorf("%s is not a 64 bit load", ins.OpCode)
}
if fd < 0 {
return errors.New("invalid fd")
if ins.Src != PseudoMapFD && ins.Src != PseudoMapValue {
return xerrors.New("not a load from a map")
}
ins.Src = R1
ins.Constant = int64(fd)
// Preserve the offset value for direct map loads.
offset := uint64(ins.Constant) & (math.MaxUint32 << 32)
rawFd := uint64(uint32(fd))
ins.Constant = int64(offset | rawFd)
return nil
}
func (ins *Instruction) mapPtr() uint32 {
return uint32(uint64(ins.Constant) & math.MaxUint32)
}
// RewriteMapOffset changes the offset of a direct load from a map.
//
// Returns an error if the instruction is not a direct load.
func (ins *Instruction) RewriteMapOffset(offset uint32) error {
if !ins.OpCode.isDWordLoad() {
return xerrors.Errorf("%s is not a 64 bit load", ins.OpCode)
}
if ins.Src != PseudoMapValue {
return xerrors.New("not a direct load from a map")
}
fd := uint64(ins.Constant) & math.MaxUint32
ins.Constant = int64(uint64(offset)<<32 | fd)
return nil
}
func (ins *Instruction) mapOffset() uint32 {
return uint32(uint64(ins.Constant) >> 32)
}
func (ins *Instruction) isLoadFromMap() bool {
return ins.OpCode == LoadImmOp(DWord) && (ins.Src == PseudoMapFD || ins.Src == PseudoMapValue)
}
// Format implements fmt.Formatter.
func (ins Instruction) Format(f fmt.State, c rune) {
if c != 'v' {
@@ -139,6 +176,19 @@ func (ins Instruction) Format(f fmt.State, c rune) {
return
}
if ins.isLoadFromMap() {
fd := int32(ins.mapPtr())
switch ins.Src {
case PseudoMapFD:
fmt.Fprintf(f, "LoadMapPtr dst: %s fd: %d", ins.Dst, fd)
case PseudoMapValue:
fmt.Fprintf(f, "LoadMapValue dst: %s, fd: %d off: %d", ins.Dst, fd, ins.mapOffset())
}
goto ref
}
fmt.Fprintf(f, "%v ", op)
switch cls := op.Class(); cls {
case LdClass, LdXClass, StClass, StXClass:
@@ -166,7 +216,7 @@ func (ins Instruction) Format(f fmt.State, c rune) {
case JumpClass:
switch jop := op.JumpOp(); jop {
case Call:
if ins.Src == R1 {
if ins.Src == PseudoCall {
// bpf-to-bpf call
fmt.Fprint(f, ins.Constant)
} else {
@@ -183,6 +233,7 @@ func (ins Instruction) Format(f fmt.State, c rune) {
}
}
ref:
if ins.Reference != "" {
fmt.Fprintf(f, " <%s>", ins.Reference)
}
@@ -200,7 +251,7 @@ func (insns Instructions) String() string {
// Returns an error if the symbol isn't used, see IsUnreferencedSymbol.
func (insns Instructions) RewriteMapPtr(symbol string, fd int) error {
if symbol == "" {
return errors.New("empty symbol")
return xerrors.New("empty symbol")
}
found := false
@@ -235,7 +286,7 @@ func (insns Instructions) SymbolOffsets() (map[string]int, error) {
}
if _, ok := offsets[ins.Symbol]; ok {
return nil, errors.Errorf("duplicate symbol %s", ins.Symbol)
return nil, xerrors.Errorf("duplicate symbol %s", ins.Symbol)
}
offsets[ins.Symbol] = i
@@ -273,7 +324,7 @@ func (insns Instructions) marshalledOffsets() (map[string]int, error) {
}
if _, ok := symbols[ins.Symbol]; ok {
return nil, errors.Errorf("duplicate symbol %s", ins.Symbol)
return nil, xerrors.Errorf("duplicate symbol %s", ins.Symbol)
}
symbols[ins.Symbol] = currentPos
@@ -350,11 +401,11 @@ func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
num := 0
for i, ins := range insns {
switch {
case ins.OpCode.JumpOp() == Call && ins.Constant == -1:
case ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall && ins.Constant == -1:
// Rewrite bpf to bpf call
offset, ok := absoluteOffsets[ins.Reference]
if !ok {
return errors.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
return xerrors.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
}
ins.Constant = int64(offset - num - 1)
@@ -363,7 +414,7 @@ func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
// Rewrite jump to label
offset, ok := absoluteOffsets[ins.Reference]
if !ok {
return errors.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
return xerrors.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
}
ins.Offset = int16(offset - num - 1)
@@ -371,7 +422,7 @@ func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
n, err := ins.Marshal(w, bo)
if err != nil {
return errors.Wrapf(err, "instruction %d", i)
return xerrors.Errorf("instruction %d: %w", i, err)
}
num += int(n / InstructionSize)
@@ -388,16 +439,26 @@ type bpfInstruction struct {
type bpfRegisters uint8
func newBPFRegisters(dst, src Register) bpfRegisters {
return bpfRegisters((src << 4) | (dst & 0xF))
func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) {
switch bo {
case binary.LittleEndian:
return bpfRegisters((src << 4) | (dst & 0xF)), nil
case binary.BigEndian:
return bpfRegisters((dst << 4) | (src & 0xF)), nil
default:
return 0, xerrors.Errorf("unrecognized ByteOrder %T", bo)
}
}
func (r bpfRegisters) Dst() Register {
return Register(r & 0xF)
}
func (r bpfRegisters) Src() Register {
return Register(r >> 4)
func (r bpfRegisters) Unmarshal(bo binary.ByteOrder) (dst, src Register, err error) {
switch bo {
case binary.LittleEndian:
return Register(r & 0xF), Register(r >> 4), nil
case binary.BigEndian:
return Register(r >> 4), Register(r & 0xf), nil
default:
return 0, 0, xerrors.Errorf("unrecognized ByteOrder %T", bo)
}
}
type unreferencedSymbolError struct {

View File

@@ -95,7 +95,7 @@ func (op JumpOp) Label(label string) Instruction {
if op == Call {
return Instruction{
OpCode: OpCode(JumpClass).SetJumpOp(Call),
Src: R1,
Src: PseudoCall,
Constant: -1,
Reference: label,
}

View File

@@ -110,11 +110,26 @@ func LoadMapPtr(dst Register, fd int) Instruction {
return Instruction{
OpCode: LoadImmOp(DWord),
Dst: dst,
Src: R1,
Src: PseudoMapFD,
Constant: int64(fd),
}
}
// LoadMapValue stores a pointer to the value at a certain offset of a map.
func LoadMapValue(dst Register, fd int, offset uint32) Instruction {
if fd < 0 {
return Instruction{OpCode: InvalidOpCode}
}
fdAndOffset := (uint64(offset) << 32) | uint64(uint32(fd))
return Instruction{
OpCode: LoadImmOp(DWord),
Dst: dst,
Src: PseudoMapValue,
Constant: int64(fdAndOffset),
}
}
// LoadIndOp returns the OpCode for loading a value of given size from an sk_buff.
func LoadIndOp(size Size) OpCode {
return OpCode(LdClass).SetMode(IndMode).SetSize(size)

View File

@@ -225,7 +225,7 @@ func (op OpCode) String() string {
}
default:
fmt.Fprintf(&f, "%#x", op)
fmt.Fprintf(&f, "OpCode(%#x)", uint8(op))
}
return f.String()

View File

@@ -33,6 +33,13 @@ const (
RFP = R10
)
// Pseudo registers used by 64bit loads and jumps
const (
PseudoMapFD = R1 // BPF_PSEUDO_MAP_FD
PseudoMapValue = R2 // BPF_PSEUDO_MAP_VALUE
PseudoCall = R1 // BPF_PSEUDO_CALL
)
func (r Register) String() string {
v := uint8(r)
if v == 10 {

View File

@@ -1,8 +1,12 @@
package ebpf
import (
"math"
"github.com/cilium/ebpf/asm"
"github.com/pkg/errors"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf"
"golang.org/x/xerrors"
)
// CollectionOptions control loading a collection into the kernel.
@@ -38,6 +42,89 @@ func (cs *CollectionSpec) Copy() *CollectionSpec {
return &cpy
}
// RewriteMaps replaces all references to specific maps.
//
// Use this function to use pre-existing maps instead of creating new ones
// when calling NewCollection. Any named maps are removed from CollectionSpec.Maps.
//
// Returns an error if a named map isn't used in at least one program.
func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error {
for symbol, m := range maps {
// have we seen a program that uses this symbol / map
seen := false
fd := m.FD()
for progName, progSpec := range cs.Programs {
err := progSpec.Instructions.RewriteMapPtr(symbol, fd)
switch {
case err == nil:
seen = true
case asm.IsUnreferencedSymbol(err):
// Not all programs need to use the map
default:
return xerrors.Errorf("program %s: %w", progName, err)
}
}
if !seen {
return xerrors.Errorf("map %s not referenced by any programs", symbol)
}
// Prevent NewCollection from creating rewritten maps
delete(cs.Maps, symbol)
}
return nil
}
// RewriteConstants replaces the value of multiple constants.
//
// The constant must be defined like so in the C program:
//
// static volatile const type foobar;
// static volatile const type foobar = default;
//
// Replacement values must be of the same length as the C sizeof(type).
// If necessary, they are marshalled according to the same rules as
// map values.
//
// From Linux 5.5 the verifier will use constants to eliminate dead code.
//
// Returns an error if a constant doesn't exist.
func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error {
rodata := cs.Maps[".rodata"]
if rodata == nil {
return xerrors.New("missing .rodata section")
}
if rodata.BTF == nil {
return xerrors.New(".rodata section has no BTF")
}
if n := len(rodata.Contents); n != 1 {
return xerrors.Errorf("expected one key in .rodata, found %d", n)
}
kv := rodata.Contents[0]
value, ok := kv.Value.([]byte)
if !ok {
return xerrors.Errorf("first value in .rodata is %T not []byte", kv.Value)
}
buf := make([]byte, len(value))
copy(buf, value)
err := patchValue(buf, btf.MapValue(rodata.BTF), consts)
if err != nil {
return err
}
rodata.Contents[0] = MapKV{kv.Key, buf}
return nil
}
// Collection is a collection of Programs and Maps associated
// with their symbols
type Collection struct {
@@ -55,45 +142,103 @@ func NewCollection(spec *CollectionSpec) (*Collection, error) {
// NewCollectionWithOptions creates a Collection from a specification.
//
// Only maps referenced by at least one of the programs are initialized.
func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) {
maps := make(map[string]*Map)
for mapName, mapSpec := range spec.Maps {
m, err := NewMap(mapSpec)
func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (coll *Collection, err error) {
var (
maps = make(map[string]*Map)
progs = make(map[string]*Program)
btfs = make(map[*btf.Spec]*btf.Handle)
)
defer func() {
for _, btf := range btfs {
btf.Close()
}
if err == nil {
return
}
for _, m := range maps {
m.Close()
}
for _, p := range progs {
p.Close()
}
}()
loadBTF := func(spec *btf.Spec) (*btf.Handle, error) {
if btfs[spec] != nil {
return btfs[spec], nil
}
handle, err := btf.NewHandle(spec)
if err != nil {
return nil, errors.Wrapf(err, "map %s", mapName)
return nil, err
}
btfs[spec] = handle
return handle, nil
}
for mapName, mapSpec := range spec.Maps {
var handle *btf.Handle
if mapSpec.BTF != nil {
handle, err = loadBTF(btf.MapSpec(mapSpec.BTF))
if err != nil && !xerrors.Is(err, btf.ErrNotSupported) {
return nil, err
}
}
m, err := newMapWithBTF(mapSpec, handle)
if err != nil {
return nil, xerrors.Errorf("map %s: %w", mapName, err)
}
maps[mapName] = m
}
progs := make(map[string]*Program)
for progName, origProgSpec := range spec.Programs {
progSpec := origProgSpec.Copy()
// Rewrite any reference to a valid map.
for i := range progSpec.Instructions {
var (
ins = &progSpec.Instructions[i]
m = maps[ins.Reference]
)
ins := &progSpec.Instructions[i]
if ins.Reference == "" || m == nil {
if ins.OpCode != asm.LoadImmOp(asm.DWord) || ins.Reference == "" {
continue
}
if ins.Src == asm.R1 {
if uint32(ins.Constant) != math.MaxUint32 {
// Don't overwrite maps already rewritten, users can
// rewrite programs in the spec themselves
continue
}
m := maps[ins.Reference]
if m == nil {
return nil, xerrors.Errorf("program %s: missing map %s", progName, ins.Reference)
}
fd := m.FD()
if fd < 0 {
return nil, xerrors.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd)
}
if err := ins.RewriteMapPtr(m.FD()); err != nil {
return nil, errors.Wrapf(err, "progam %s: map %s", progName, ins.Reference)
return nil, xerrors.Errorf("progam %s: map %s: %w", progName, ins.Reference, err)
}
}
prog, err := NewProgramWithOptions(progSpec, opts.Programs)
var handle *btf.Handle
if progSpec.BTF != nil {
handle, err = loadBTF(btf.ProgramSpec(progSpec.BTF))
if err != nil && !xerrors.Is(err, btf.ErrNotSupported) {
return nil, err
}
}
prog, err := newProgramWithBTF(progSpec, handle, opts.Programs)
if err != nil {
return nil, errors.Wrapf(err, "program %s", progName)
return nil, xerrors.Errorf("program %s: %w", progName, err)
}
progs[progName] = prog
}

View File

@@ -4,20 +4,25 @@ import (
"bytes"
"debug/elf"
"encoding/binary"
"fmt"
"io"
"math"
"os"
"strings"
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf"
"github.com/cilium/ebpf/internal/unix"
"github.com/pkg/errors"
"golang.org/x/xerrors"
)
type elfCode struct {
*elf.File
symbols []elf.Symbol
symbolsPerSection map[elf.SectionIndex]map[uint64]string
symbolsPerSection map[elf.SectionIndex]map[uint64]elf.Symbol
license string
version uint32
}
// LoadCollectionSpec parses an ELF file into a CollectionSpec.
@@ -29,12 +34,15 @@ func LoadCollectionSpec(file string) (*CollectionSpec, error) {
defer f.Close()
spec, err := LoadCollectionSpecFromReader(f)
return spec, errors.Wrapf(err, "file %s", file)
if err != nil {
return nil, xerrors.Errorf("file %s: %w", file, err)
}
return spec, nil
}
// LoadCollectionSpecFromReader parses an ELF file into a CollectionSpec.
func LoadCollectionSpecFromReader(code io.ReaderAt) (*CollectionSpec, error) {
f, err := elf.NewFile(code)
func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
f, err := elf.NewFile(rd)
if err != nil {
return nil, err
}
@@ -42,15 +50,21 @@ func LoadCollectionSpecFromReader(code io.ReaderAt) (*CollectionSpec, error) {
symbols, err := f.Symbols()
if err != nil {
return nil, errors.Wrap(err, "load symbols")
return nil, xerrors.Errorf("load symbols: %v", err)
}
ec := &elfCode{f, symbols, symbolsPerSection(symbols)}
ec := &elfCode{f, symbols, symbolsPerSection(symbols), "", 0}
var (
licenseSection *elf.Section
versionSection *elf.Section
btfMaps = make(map[elf.SectionIndex]*elf.Section)
progSections = make(map[elf.SectionIndex]*elf.Section)
relSections = make(map[elf.SectionIndex]*elf.Section)
mapSections = make(map[elf.SectionIndex]*elf.Section)
dataSections = make(map[elf.SectionIndex]*elf.Section)
)
var licenseSection, versionSection *elf.Section
progSections := make(map[elf.SectionIndex]*elf.Section)
relSections := make(map[elf.SectionIndex]*elf.Section)
mapSections := make(map[elf.SectionIndex]*elf.Section)
for i, sec := range ec.Sections {
switch {
case strings.HasPrefix(sec.Name, "license"):
@@ -59,15 +73,19 @@ func LoadCollectionSpecFromReader(code io.ReaderAt) (*CollectionSpec, error) {
versionSection = sec
case strings.HasPrefix(sec.Name, "maps"):
mapSections[elf.SectionIndex(i)] = sec
case sec.Name == ".maps":
btfMaps[elf.SectionIndex(i)] = sec
case sec.Name == ".bss" || sec.Name == ".rodata" || sec.Name == ".data":
dataSections[elf.SectionIndex(i)] = sec
case sec.Type == elf.SHT_REL:
if int(sec.Info) >= len(ec.Sections) {
return nil, errors.Errorf("found relocation section %v for missing section %v", i, sec.Info)
return nil, xerrors.Errorf("found relocation section %v for missing section %v", i, sec.Info)
}
// Store relocations under the section index of the target
idx := elf.SectionIndex(sec.Info)
if relSections[idx] != nil {
return nil, errors.Errorf("section %d has multiple relocation sections", idx)
return nil, xerrors.Errorf("section %d has multiple relocation sections", sec.Info)
}
relSections[idx] = sec
case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0:
@@ -75,45 +93,58 @@ func LoadCollectionSpecFromReader(code io.ReaderAt) (*CollectionSpec, error) {
}
}
license, err := loadLicense(licenseSection)
ec.license, err = loadLicense(licenseSection)
if err != nil {
return nil, errors.Wrap(err, "load license")
return nil, xerrors.Errorf("load license: %w", err)
}
version, err := loadVersion(versionSection, ec.ByteOrder)
ec.version, err = loadVersion(versionSection, ec.ByteOrder)
if err != nil {
return nil, errors.Wrap(err, "load version")
return nil, xerrors.Errorf("load version: %w", err)
}
maps, err := ec.loadMaps(mapSections)
btfSpec, err := btf.LoadSpecFromReader(rd)
if err != nil {
return nil, errors.Wrap(err, "load maps")
return nil, xerrors.Errorf("load BTF: %w", err)
}
progs, libs, err := ec.loadPrograms(progSections, relSections, license, version)
if err != nil {
return nil, errors.Wrap(err, "load programs")
maps := make(map[string]*MapSpec)
if err := ec.loadMaps(maps, mapSections); err != nil {
return nil, xerrors.Errorf("load maps: %w", err)
}
if len(libs) > 0 {
for name, prog := range progs {
prog.Instructions, err = link(prog.Instructions, libs...)
if err != nil {
return nil, errors.Wrapf(err, "program %s", name)
}
if len(btfMaps) > 0 {
if err := ec.loadBTFMaps(maps, btfMaps, btfSpec); err != nil {
return nil, xerrors.Errorf("load BTF maps: %w", err)
}
}
if len(dataSections) > 0 {
if err := ec.loadDataSections(maps, dataSections, btfSpec); err != nil {
return nil, xerrors.Errorf("load data sections: %w", err)
}
}
relocations, err := ec.loadRelocations(relSections)
if err != nil {
return nil, xerrors.Errorf("load relocations: %w", err)
}
progs, err := ec.loadPrograms(progSections, relocations, btfSpec)
if err != nil {
return nil, xerrors.Errorf("load programs: %w", err)
}
return &CollectionSpec{maps, progs}, nil
}
func loadLicense(sec *elf.Section) (string, error) {
if sec == nil {
return "", errors.Errorf("missing license section")
return "", xerrors.New("missing license section")
}
data, err := sec.Data()
if err != nil {
return "", errors.Wrapf(err, "section %s", sec.Name)
return "", xerrors.Errorf("section %s: %v", sec.Name, err)
}
return string(bytes.TrimRight(data, "\000")), nil
}
@@ -124,92 +155,227 @@ func loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) {
}
var version uint32
err := binary.Read(sec.Open(), bo, &version)
return version, errors.Wrapf(err, "section %s", sec.Name)
if err := binary.Read(sec.Open(), bo, &version); err != nil {
return 0, xerrors.Errorf("section %s: %v", sec.Name, err)
}
return version, nil
}
func (ec *elfCode) loadPrograms(progSections, relSections map[elf.SectionIndex]*elf.Section, license string, version uint32) (map[string]*ProgramSpec, []asm.Instructions, error) {
func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section, relocations map[elf.SectionIndex]map[uint64]elf.Symbol, btf *btf.Spec) (map[string]*ProgramSpec, error) {
var (
progs = make(map[string]*ProgramSpec)
libs []asm.Instructions
progs []*ProgramSpec
libs []*ProgramSpec
)
for idx, prog := range progSections {
for idx, sec := range progSections {
syms := ec.symbolsPerSection[idx]
if len(syms) == 0 {
return nil, nil, errors.Errorf("section %v: missing symbols", prog.Name)
return nil, xerrors.Errorf("section %v: missing symbols", sec.Name)
}
funcSym := syms[0]
if funcSym == "" {
return nil, nil, errors.Errorf("section %v: no label at start", prog.Name)
funcSym, ok := syms[0]
if !ok {
return nil, xerrors.Errorf("section %v: no label at start", sec.Name)
}
rels, err := ec.loadRelocations(relSections[idx])
insns, length, err := ec.loadInstructions(sec, syms, relocations[idx])
if err != nil {
return nil, nil, errors.Wrapf(err, "program %s: can't load relocations", funcSym)
return nil, xerrors.Errorf("program %s: can't unmarshal instructions: %w", funcSym.Name, err)
}
insns, err := ec.loadInstructions(prog, syms, rels)
if err != nil {
return nil, nil, errors.Wrapf(err, "program %s: can't unmarshal instructions", funcSym)
progType, attachType := getProgType(sec.Name)
spec := &ProgramSpec{
Name: funcSym.Name,
Type: progType,
AttachType: attachType,
License: ec.license,
KernelVersion: ec.version,
Instructions: insns,
ByteOrder: ec.ByteOrder,
}
if progType, attachType := getProgType(prog.Name); progType == UnspecifiedProgram {
if btf != nil {
spec.BTF, err = btf.Program(sec.Name, length)
if err != nil {
return nil, xerrors.Errorf("BTF for section %s (program %s): %w", sec.Name, funcSym.Name, err)
}
}
if spec.Type == UnspecifiedProgram {
// There is no single name we can use for "library" sections,
// since they may contain multiple functions. We'll decode the
// labels they contain later on, and then link sections that way.
libs = append(libs, insns)
libs = append(libs, spec)
} else {
progs[funcSym] = &ProgramSpec{
Name: funcSym,
Type: progType,
AttachType: attachType,
License: license,
KernelVersion: version,
Instructions: insns,
}
progs = append(progs, spec)
}
}
return progs, libs, nil
res := make(map[string]*ProgramSpec, len(progs))
for _, prog := range progs {
err := link(prog, libs)
if err != nil {
return nil, xerrors.Errorf("program %s: %w", prog.Name, err)
}
res[prog.Name] = prog
}
return res, nil
}
func (ec *elfCode) loadInstructions(section *elf.Section, symbols, relocations map[uint64]string) (asm.Instructions, error) {
func (ec *elfCode) loadInstructions(section *elf.Section, symbols, relocations map[uint64]elf.Symbol) (asm.Instructions, uint64, error) {
var (
r = section.Open()
insns asm.Instructions
ins asm.Instruction
offset uint64
)
for {
var ins asm.Instruction
n, err := ins.Unmarshal(r, ec.ByteOrder)
if err == io.EOF {
return insns, nil
return insns, offset, nil
}
if err != nil {
return nil, errors.Wrapf(err, "offset %d", offset)
return nil, 0, xerrors.Errorf("offset %d: %w", offset, err)
}
ins.Symbol = symbols[offset]
ins.Reference = relocations[offset]
ins.Symbol = symbols[offset].Name
if rel, ok := relocations[offset]; ok {
if err = ec.relocateInstruction(&ins, rel); err != nil {
return nil, 0, xerrors.Errorf("offset %d: can't relocate instruction: %w", offset, err)
}
}
insns = append(insns, ins)
offset += n
}
}
func (ec *elfCode) loadMaps(mapSections map[elf.SectionIndex]*elf.Section) (map[string]*MapSpec, error) {
func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) error {
var (
maps = make(map[string]*MapSpec)
b = make([]byte, 1)
typ = elf.ST_TYPE(rel.Info)
bind = elf.ST_BIND(rel.Info)
name = rel.Name
)
if typ == elf.STT_SECTION {
// Symbols with section type do not have a name set. Get it
// from the section itself.
idx := int(rel.Section)
if idx > len(ec.Sections) {
return xerrors.New("out-of-bounds section index")
}
name = ec.Sections[idx].Name
}
outer:
switch {
case ins.OpCode == asm.LoadImmOp(asm.DWord):
// There are two distinct types of a load from a map:
// a direct one, where the value is extracted without
// a call to map_lookup_elem in eBPF, and an indirect one
// that goes via the helper. They are distinguished by
// different relocations.
switch typ {
case elf.STT_SECTION:
// This is a direct load since the referenced symbol is a
// section. Weirdly, the offset of the real symbol in the
// section is encoded in the instruction stream.
if bind != elf.STB_LOCAL {
return xerrors.Errorf("direct load: %s: unsupported relocation %s", name, bind)
}
// For some reason, clang encodes the offset of the symbol its
// section in the first basic BPF instruction, while the kernel
// expects it in the second one.
ins.Constant <<= 32
ins.Src = asm.PseudoMapValue
case elf.STT_NOTYPE:
if bind == elf.STB_GLOBAL && rel.Section == elf.SHN_UNDEF {
// This is a relocation generated by inline assembly.
// We can't do more than assigning ins.Reference.
break outer
}
// This is an ELF generated on clang < 8, which doesn't tag
// relocations appropriately.
fallthrough
case elf.STT_OBJECT:
if bind != elf.STB_GLOBAL {
return xerrors.Errorf("load: %s: unsupported binding: %s", name, bind)
}
ins.Src = asm.PseudoMapFD
default:
return xerrors.Errorf("load: %s: unsupported relocation: %s", name, typ)
}
// Mark the instruction as needing an update when creating the
// collection.
if err := ins.RewriteMapPtr(-1); err != nil {
return err
}
case ins.OpCode.JumpOp() == asm.Call:
if ins.Src != asm.PseudoCall {
return xerrors.Errorf("call: %s: incorrect source register", name)
}
switch typ {
case elf.STT_NOTYPE, elf.STT_FUNC:
if bind != elf.STB_GLOBAL {
return xerrors.Errorf("call: %s: unsupported binding: %s", name, bind)
}
case elf.STT_SECTION:
if bind != elf.STB_LOCAL {
return xerrors.Errorf("call: %s: unsupported binding: %s", name, bind)
}
// The function we want to call is in the indicated section,
// at the offset encoded in the instruction itself. Reverse
// the calculation to find the real function we're looking for.
// A value of -1 references the first instruction in the section.
offset := int64(int32(ins.Constant)+1) * asm.InstructionSize
if offset < 0 {
return xerrors.Errorf("call: %s: invalid offset %d", name, offset)
}
sym, ok := ec.symbolsPerSection[rel.Section][uint64(offset)]
if !ok {
return xerrors.Errorf("call: %s: no symbol at offset %d", name, offset)
}
ins.Constant = -1
name = sym.Name
default:
return xerrors.Errorf("call: %s: invalid symbol type %s", name, typ)
}
default:
return xerrors.Errorf("relocation for unsupported instruction: %s", ins.OpCode)
}
ins.Reference = name
return nil
}
func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.SectionIndex]*elf.Section) error {
for idx, sec := range mapSections {
syms := ec.symbolsPerSection[idx]
if len(syms) == 0 {
return nil, errors.Errorf("section %v: no symbols", sec.Name)
return xerrors.Errorf("section %v: no symbols", sec.Name)
}
if sec.Size%uint64(len(syms)) != 0 {
return nil, errors.Errorf("section %v: map descriptors are not of equal size", sec.Name)
return xerrors.Errorf("section %v: map descriptors are not of equal size", sec.Name)
}
var (
@@ -217,66 +383,212 @@ func (ec *elfCode) loadMaps(mapSections map[elf.SectionIndex]*elf.Section) (map[
size = sec.Size / uint64(len(syms))
)
for i, offset := 0, uint64(0); i < len(syms); i, offset = i+1, offset+size {
mapSym := syms[offset]
if mapSym == "" {
fmt.Println(syms)
return nil, errors.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset)
mapSym, ok := syms[offset]
if !ok {
return xerrors.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset)
}
if maps[mapSym] != nil {
return nil, errors.Errorf("section %v: map %v already exists", sec.Name, mapSym)
if maps[mapSym.Name] != nil {
return xerrors.Errorf("section %v: map %v already exists", sec.Name, mapSym)
}
lr := io.LimitReader(r, int64(size))
var spec MapSpec
spec := MapSpec{
Name: SanitizeName(mapSym.Name, -1),
}
switch {
case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil:
return nil, errors.Errorf("map %v: missing type", mapSym)
return xerrors.Errorf("map %v: missing type", mapSym)
case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil:
return nil, errors.Errorf("map %v: missing key size", mapSym)
return xerrors.Errorf("map %v: missing key size", mapSym)
case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil:
return nil, errors.Errorf("map %v: missing value size", mapSym)
return xerrors.Errorf("map %v: missing value size", mapSym)
case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil:
return nil, errors.Errorf("map %v: missing max entries", mapSym)
return xerrors.Errorf("map %v: missing max entries", mapSym)
case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil:
return nil, errors.Errorf("map %v: missing flags", mapSym)
return xerrors.Errorf("map %v: missing flags", mapSym)
}
for {
_, err := lr.Read(b)
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
if b[0] != 0 {
return nil, errors.Errorf("map %v: unknown and non-zero fields in definition", mapSym)
}
if _, err := io.Copy(internal.DiscardZeroes{}, lr); err != nil {
return xerrors.Errorf("map %v: unknown and non-zero fields in definition", mapSym)
}
maps[mapSym] = &spec
maps[mapSym.Name] = &spec
}
}
return maps, nil
return nil
}
func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec, mapSections map[elf.SectionIndex]*elf.Section, spec *btf.Spec) error {
if spec == nil {
return xerrors.Errorf("missing BTF")
}
for idx, sec := range mapSections {
syms := ec.symbolsPerSection[idx]
if len(syms) == 0 {
return xerrors.Errorf("section %v: no symbols", sec.Name)
}
for _, sym := range syms {
name := sym.Name
if maps[name] != nil {
return xerrors.Errorf("section %v: map %v already exists", sec.Name, sym)
}
btfMap, btfMapMembers, err := spec.Map(name)
if err != nil {
return xerrors.Errorf("map %v: can't get BTF: %w", name, err)
}
spec, err := mapSpecFromBTF(btfMap, btfMapMembers)
if err != nil {
return xerrors.Errorf("map %v: %w", name, err)
}
maps[name] = spec
}
}
return nil
}
func mapSpecFromBTF(btfMap *btf.Map, btfMapMembers []btf.Member) (*MapSpec, error) {
var (
mapType, flags, maxEntries uint32
err error
)
for _, member := range btfMapMembers {
switch member.Name {
case "type":
mapType, err = uintFromBTF(member.Type)
if err != nil {
return nil, xerrors.Errorf("can't get type: %w", err)
}
case "map_flags":
flags, err = uintFromBTF(member.Type)
if err != nil {
return nil, xerrors.Errorf("can't get BTF map flags: %w", err)
}
case "max_entries":
maxEntries, err = uintFromBTF(member.Type)
if err != nil {
return nil, xerrors.Errorf("can't get BTF map max entries: %w", err)
}
case "key":
case "value":
default:
return nil, xerrors.Errorf("unrecognized field %s in BTF map definition", member.Name)
}
}
keySize, err := btf.Sizeof(btf.MapKey(btfMap))
if err != nil {
return nil, xerrors.Errorf("can't get size of BTF key: %w", err)
}
valueSize, err := btf.Sizeof(btf.MapValue(btfMap))
if err != nil {
return nil, xerrors.Errorf("can't get size of BTF value: %w", err)
}
return &MapSpec{
Type: MapType(mapType),
KeySize: uint32(keySize),
ValueSize: uint32(valueSize),
MaxEntries: maxEntries,
Flags: flags,
BTF: btfMap,
}, nil
}
// uintFromBTF resolves the __uint macro, which is a pointer to a sized
// array, e.g. for int (*foo)[10], this function will return 10.
func uintFromBTF(typ btf.Type) (uint32, error) {
ptr, ok := typ.(*btf.Pointer)
if !ok {
return 0, xerrors.Errorf("not a pointer: %v", typ)
}
arr, ok := ptr.Target.(*btf.Array)
if !ok {
return 0, xerrors.Errorf("not a pointer to array: %v", typ)
}
return arr.Nelems, nil
}
func (ec *elfCode) loadDataSections(maps map[string]*MapSpec, dataSections map[elf.SectionIndex]*elf.Section, spec *btf.Spec) error {
if spec == nil {
return xerrors.New("data sections require BTF, make sure all consts are marked as static")
}
for _, sec := range dataSections {
btfMap, err := spec.Datasec(sec.Name)
if err != nil {
return err
}
data, err := sec.Data()
if err != nil {
return xerrors.Errorf("data section %s: can't get contents: %w", sec.Name, err)
}
if uint64(len(data)) > math.MaxUint32 {
return xerrors.Errorf("data section %s: contents exceed maximum size", sec.Name)
}
mapSpec := &MapSpec{
Name: SanitizeName(sec.Name, -1),
Type: Array,
KeySize: 4,
ValueSize: uint32(len(data)),
MaxEntries: 1,
Contents: []MapKV{{uint32(0), data}},
BTF: btfMap,
}
switch sec.Name {
case ".rodata":
mapSpec.Flags = unix.BPF_F_RDONLY_PROG
mapSpec.Freeze = true
case ".bss":
// The kernel already zero-initializes the map
mapSpec.Contents = nil
}
maps[sec.Name] = mapSpec
}
return nil
}
func getProgType(v string) (ProgramType, AttachType) {
types := map[string]ProgramType{
// From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c#n3568
"socket": SocketFilter,
"seccomp": SocketFilter,
"kprobe/": Kprobe,
"kretprobe/": Kprobe,
"tracepoint/": TracePoint,
"xdp": XDP,
"perf_event": PerfEvent,
"sockops": SockOps,
"sk_skb": SkSKB,
"sk_msg": SkMsg,
"lirc_mode2": LircMode2,
"flow_dissector": FlowDissector,
"socket": SocketFilter,
"seccomp": SocketFilter,
"kprobe/": Kprobe,
"uprobe/": Kprobe,
"kretprobe/": Kprobe,
"uretprobe/": Kprobe,
"tracepoint/": TracePoint,
"raw_tracepoint/": RawTracepoint,
"xdp": XDP,
"perf_event": PerfEvent,
"lwt_in": LWTIn,
"lwt_out": LWTOut,
"lwt_xmit": LWTXmit,
"lwt_seg6local": LWTSeg6Local,
"sockops": SockOps,
"sk_skb": SkSKB,
"sk_msg": SkMsg,
"lirc_mode2": LircMode2,
"flow_dissector": FlowDissector,
"cgroup_skb/": CGroupSKB,
"cgroup/dev": CGroupDevice,
@@ -333,38 +645,40 @@ func getProgType(v string) (ProgramType, AttachType) {
return UnspecifiedProgram, AttachNone
}
func (ec *elfCode) loadRelocations(sec *elf.Section) (map[uint64]string, error) {
rels := make(map[uint64]string)
if sec == nil {
return rels, nil
}
func (ec *elfCode) loadRelocations(sections map[elf.SectionIndex]*elf.Section) (map[elf.SectionIndex]map[uint64]elf.Symbol, error) {
result := make(map[elf.SectionIndex]map[uint64]elf.Symbol)
for idx, sec := range sections {
rels := make(map[uint64]elf.Symbol)
if sec.Entsize < 16 {
return nil, errors.New("rels are less than 16 bytes")
}
r := sec.Open()
for off := uint64(0); off < sec.Size; off += sec.Entsize {
ent := io.LimitReader(r, int64(sec.Entsize))
var rel elf.Rel64
if binary.Read(ent, ec.ByteOrder, &rel) != nil {
return nil, errors.Errorf("can't parse relocation at offset %v", off)
if sec.Entsize < 16 {
return nil, xerrors.Errorf("section %s: relocations are less than 16 bytes", sec.Name)
}
symNo := int(elf.R_SYM64(rel.Info) - 1)
if symNo >= len(ec.symbols) {
return nil, errors.Errorf("relocation at offset %d: symbol %v doesnt exist", off, symNo)
r := sec.Open()
for off := uint64(0); off < sec.Size; off += sec.Entsize {
ent := io.LimitReader(r, int64(sec.Entsize))
var rel elf.Rel64
if binary.Read(ent, ec.ByteOrder, &rel) != nil {
return nil, xerrors.Errorf("can't parse relocation at offset %v", off)
}
symNo := int(elf.R_SYM64(rel.Info) - 1)
if symNo >= len(ec.symbols) {
return nil, xerrors.Errorf("relocation at offset %d: symbol %v doesnt exist", off, symNo)
}
rels[rel.Off] = ec.symbols[symNo]
}
rels[rel.Off] = ec.symbols[symNo].Name
result[idx] = rels
}
return rels, nil
return result, nil
}
func symbolsPerSection(symbols []elf.Symbol) map[elf.SectionIndex]map[uint64]string {
result := make(map[elf.SectionIndex]map[uint64]string)
for i, sym := range symbols {
func symbolsPerSection(symbols []elf.Symbol) map[elf.SectionIndex]map[uint64]elf.Symbol {
result := make(map[elf.SectionIndex]map[uint64]elf.Symbol)
for _, sym := range symbols {
switch elf.ST_TYPE(sym.Info) {
case elf.STT_NOTYPE:
// Older versions of LLVM doesn't tag
@@ -378,15 +692,19 @@ func symbolsPerSection(symbols []elf.Symbol) map[elf.SectionIndex]map[uint64]str
continue
}
if sym.Section == elf.SHN_UNDEF || sym.Section >= elf.SHN_LORESERVE {
continue
}
if sym.Name == "" {
continue
}
idx := sym.Section
if _, ok := result[idx]; !ok {
result[idx] = make(map[uint64]string)
result[idx] = make(map[uint64]elf.Symbol)
}
result[idx][sym.Value] = symbols[i].Name
result[idx][sym.Value] = sym
}
return result
}

View File

@@ -1,19 +0,0 @@
package ebpf
import (
"sync"
)
type featureTest struct {
Fn func() bool
once sync.Once
result bool
}
func (ft *featureTest) Result() bool {
ft.once.Do(func() {
ft.result = ft.Fn()
})
return ft.result
}

View File

@@ -3,6 +3,6 @@ module github.com/cilium/ebpf
go 1.12
require (
github.com/pkg/errors v0.8.1
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
)

View File

@@ -1,4 +1,6 @@
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -5,11 +5,23 @@ go_library(
srcs = [
"cpu.go",
"endian.go",
"errors.go",
"fd.go",
"feature.go",
"io.go",
"ptr.go",
"ptr_32_be.go",
"ptr_32_le.go",
"ptr_64.go",
"syscall.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/cilium/ebpf/internal",
importpath = "github.com/cilium/ebpf/internal",
visibility = ["//vendor/github.com/cilium/ebpf:__subpackages__"],
deps = ["//vendor/github.com/pkg/errors:go_default_library"],
deps = [
"//vendor/github.com/cilium/ebpf/internal/unix:go_default_library",
"//vendor/golang.org/x/xerrors:go_default_library",
],
)
filegroup(
@@ -23,6 +35,7 @@ filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/cilium/ebpf/internal/btf:all-srcs",
"//vendor/github.com/cilium/ebpf/internal/unix:all-srcs",
],
tags = ["automanaged"],

36
vendor/github.com/cilium/ebpf/internal/btf/BUILD generated vendored Normal file
View File

@@ -0,0 +1,36 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"btf.go",
"btf_types.go",
"doc.go",
"ext_info.go",
"strings.go",
"types.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/cilium/ebpf/internal/btf",
importpath = "github.com/cilium/ebpf/internal/btf",
visibility = ["//vendor/github.com/cilium/ebpf:__subpackages__"],
deps = [
"//vendor/github.com/cilium/ebpf/asm:go_default_library",
"//vendor/github.com/cilium/ebpf/internal:go_default_library",
"//vendor/github.com/cilium/ebpf/internal/unix:go_default_library",
"//vendor/golang.org/x/xerrors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

619
vendor/github.com/cilium/ebpf/internal/btf/btf.go generated vendored Normal file
View File

@@ -0,0 +1,619 @@
package btf
import (
"bytes"
"debug/elf"
"encoding/binary"
"io"
"io/ioutil"
"math"
"reflect"
"unsafe"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/unix"
"golang.org/x/xerrors"
)
const btfMagic = 0xeB9F
// Errors returned by BTF functions.
var (
ErrNotSupported = internal.ErrNotSupported
)
// Spec represents decoded BTF.
type Spec struct {
rawTypes []rawType
strings stringTable
types map[string][]Type
funcInfos map[string]extInfo
lineInfos map[string]extInfo
byteOrder binary.ByteOrder
}
type btfHeader struct {
Magic uint16
Version uint8
Flags uint8
HdrLen uint32
TypeOff uint32
TypeLen uint32
StringOff uint32
StringLen uint32
}
// LoadSpecFromReader reads BTF sections from an ELF.
//
// Returns a nil Spec and no error if no BTF was present.
func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
file, err := elf.NewFile(rd)
if err != nil {
return nil, err
}
defer file.Close()
var (
btfSection *elf.Section
btfExtSection *elf.Section
sectionSizes = make(map[string]uint32)
)
for _, sec := range file.Sections {
switch sec.Name {
case ".BTF":
btfSection = sec
case ".BTF.ext":
btfExtSection = sec
default:
if sec.Type != elf.SHT_PROGBITS && sec.Type != elf.SHT_NOBITS {
break
}
if sec.Size > math.MaxUint32 {
return nil, xerrors.Errorf("section %s exceeds maximum size", sec.Name)
}
sectionSizes[sec.Name] = uint32(sec.Size)
}
}
if btfSection == nil {
return nil, nil
}
symbols, err := file.Symbols()
if err != nil {
return nil, xerrors.Errorf("can't read symbols: %v", err)
}
variableOffsets := make(map[variable]uint32)
for _, symbol := range symbols {
if idx := symbol.Section; idx >= elf.SHN_LORESERVE && idx <= elf.SHN_HIRESERVE {
// Ignore things like SHN_ABS
continue
}
secName := file.Sections[symbol.Section].Name
if _, ok := sectionSizes[secName]; !ok {
continue
}
if symbol.Value > math.MaxUint32 {
return nil, xerrors.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name)
}
variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value)
}
rawTypes, rawStrings, err := parseBTF(btfSection.Open(), file.ByteOrder)
if err != nil {
return nil, err
}
err = fixupDatasec(rawTypes, rawStrings, sectionSizes, variableOffsets)
if err != nil {
return nil, err
}
types, err := inflateRawTypes(rawTypes, rawStrings)
if err != nil {
return nil, err
}
var (
funcInfos = make(map[string]extInfo)
lineInfos = make(map[string]extInfo)
)
if btfExtSection != nil {
funcInfos, lineInfos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, rawStrings)
if err != nil {
return nil, xerrors.Errorf("can't read ext info: %w", err)
}
}
return &Spec{
rawTypes: rawTypes,
types: types,
strings: rawStrings,
funcInfos: funcInfos,
lineInfos: lineInfos,
byteOrder: file.ByteOrder,
}, nil
}
func parseBTF(btf io.ReadSeeker, bo binary.ByteOrder) ([]rawType, stringTable, error) {
rawBTF, err := ioutil.ReadAll(btf)
if err != nil {
return nil, nil, xerrors.Errorf("can't read BTF: %v", err)
}
rd := bytes.NewReader(rawBTF)
var header btfHeader
if err := binary.Read(rd, bo, &header); err != nil {
return nil, nil, xerrors.Errorf("can't read header: %v", err)
}
if header.Magic != btfMagic {
return nil, nil, xerrors.Errorf("incorrect magic value %v", header.Magic)
}
if header.Version != 1 {
return nil, nil, xerrors.Errorf("unexpected version %v", header.Version)
}
if header.Flags != 0 {
return nil, nil, xerrors.Errorf("unsupported flags %v", header.Flags)
}
remainder := int64(header.HdrLen) - int64(binary.Size(&header))
if remainder < 0 {
return nil, nil, xerrors.New("header is too short")
}
if _, err := io.CopyN(internal.DiscardZeroes{}, rd, remainder); err != nil {
return nil, nil, xerrors.Errorf("header padding: %v", err)
}
if _, err := rd.Seek(int64(header.HdrLen+header.StringOff), io.SeekStart); err != nil {
return nil, nil, xerrors.Errorf("can't seek to start of string section: %v", err)
}
rawStrings, err := readStringTable(io.LimitReader(rd, int64(header.StringLen)))
if err != nil {
return nil, nil, xerrors.Errorf("can't read type names: %w", err)
}
if _, err := rd.Seek(int64(header.HdrLen+header.TypeOff), io.SeekStart); err != nil {
return nil, nil, xerrors.Errorf("can't seek to start of type section: %v", err)
}
rawTypes, err := readTypes(io.LimitReader(rd, int64(header.TypeLen)), bo)
if err != nil {
return nil, nil, xerrors.Errorf("can't read types: %w", err)
}
return rawTypes, rawStrings, nil
}
type variable struct {
section string
name string
}
func fixupDatasec(rawTypes []rawType, rawStrings stringTable, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) error {
for i, rawType := range rawTypes {
if rawType.Kind() != kindDatasec {
continue
}
name, err := rawStrings.Lookup(rawType.NameOff)
if err != nil {
return err
}
size, ok := sectionSizes[name]
if !ok {
return xerrors.Errorf("data section %s: missing size", name)
}
rawTypes[i].SizeType = size
secinfos := rawType.data.([]btfVarSecinfo)
for j, secInfo := range secinfos {
id := int(secInfo.Type - 1)
if id >= len(rawTypes) {
return xerrors.Errorf("data section %s: invalid type id %d for variable %d", name, id, j)
}
varName, err := rawStrings.Lookup(rawTypes[id].NameOff)
if err != nil {
return xerrors.Errorf("data section %s: can't get name for type %d: %w", name, id, err)
}
offset, ok := variableOffsets[variable{name, varName}]
if !ok {
return xerrors.Errorf("data section %s: missing offset for variable %s", name, varName)
}
secinfos[j].Offset = offset
}
}
return nil
}
func (s *Spec) marshal(bo binary.ByteOrder) ([]byte, error) {
var (
buf bytes.Buffer
header = new(btfHeader)
headerLen = binary.Size(header)
)
// Reserve space for the header. We have to write it last since
// we don't know the size of the type section yet.
_, _ = buf.Write(make([]byte, headerLen))
// Write type section, just after the header.
for _, typ := range s.rawTypes {
if err := typ.Marshal(&buf, bo); err != nil {
return nil, xerrors.Errorf("can't marshal BTF: %w", err)
}
}
typeLen := uint32(buf.Len() - headerLen)
// Write string section after type section.
_, _ = buf.Write(s.strings)
// Fill out the header, and write it out.
header = &btfHeader{
Magic: btfMagic,
Version: 1,
Flags: 0,
HdrLen: uint32(headerLen),
TypeOff: 0,
TypeLen: typeLen,
StringOff: typeLen,
StringLen: uint32(len(s.strings)),
}
raw := buf.Bytes()
err := binary.Write(sliceWriter(raw[:headerLen]), bo, header)
if err != nil {
return nil, xerrors.Errorf("can't write header: %v", err)
}
return raw, nil
}
type sliceWriter []byte
func (sw sliceWriter) Write(p []byte) (int, error) {
if len(p) != len(sw) {
return 0, xerrors.New("size doesn't match")
}
return copy(sw, p), nil
}
// Program finds the BTF for a specific section.
//
// Length is the number of bytes in the raw BPF instruction stream.
//
// Returns an error if there is no BTF.
func (s *Spec) Program(name string, length uint64) (*Program, error) {
if length == 0 {
return nil, xerrors.New("length musn't be zero")
}
funcInfos, funcOK := s.funcInfos[name]
lineInfos, lineOK := s.lineInfos[name]
if !funcOK && !lineOK {
return nil, xerrors.Errorf("no BTF for program %s", name)
}
return &Program{s, length, funcInfos, lineInfos}, nil
}
// Map finds the BTF for a map.
//
// Returns an error if there is no BTF for the given name.
func (s *Spec) Map(name string) (*Map, []Member, error) {
var mapVar Var
if err := s.FindType(name, &mapVar); err != nil {
return nil, nil, err
}
mapStruct, ok := mapVar.Type.(*Struct)
if !ok {
return nil, nil, xerrors.Errorf("expected struct, have %s", mapVar.Type)
}
var key, value Type
for _, member := range mapStruct.Members {
switch member.Name {
case "key":
key = member.Type
case "value":
value = member.Type
}
}
if key == nil {
return nil, nil, xerrors.Errorf("map %s: missing 'key' in type", name)
}
if value == nil {
return nil, nil, xerrors.Errorf("map %s: missing 'value' in type", name)
}
return &Map{s, key, value}, mapStruct.Members, nil
}
// Datasec returns the BTF required to create maps which represent data sections.
func (s *Spec) Datasec(name string) (*Map, error) {
var datasec Datasec
if err := s.FindType(name, &datasec); err != nil {
return nil, xerrors.Errorf("data section %s: can't get BTF: %w", name, err)
}
return &Map{s, &Void{}, &datasec}, nil
}
var errNotFound = xerrors.New("not found")
// FindType searches for a type with a specific name.
//
// hint determines the type of the returned Type.
//
// Returns an error if there is no or multiple matches.
func (s *Spec) FindType(name string, typ Type) error {
var (
wanted = reflect.TypeOf(typ)
candidate Type
)
for _, typ := range s.types[name] {
if reflect.TypeOf(typ) != wanted {
continue
}
if candidate != nil {
return xerrors.Errorf("type %s: multiple candidates for %T", name, typ)
}
candidate = typ
}
if candidate == nil {
return xerrors.Errorf("type %s: %w", name, errNotFound)
}
value := reflect.Indirect(reflect.ValueOf(copyType(candidate)))
reflect.Indirect(reflect.ValueOf(typ)).Set(value)
return nil
}
// Handle is a reference to BTF loaded into the kernel.
type Handle struct {
fd *internal.FD
}
// NewHandle loads BTF into the kernel.
//
// Returns ErrNotSupported if BTF is not supported.
func NewHandle(spec *Spec) (*Handle, error) {
if err := haveBTF(); err != nil {
return nil, err
}
if spec.byteOrder != internal.NativeEndian {
return nil, xerrors.Errorf("can't load %s BTF on %s", spec.byteOrder, internal.NativeEndian)
}
btf, err := spec.marshal(internal.NativeEndian)
if err != nil {
return nil, xerrors.Errorf("can't marshal BTF: %w", err)
}
if uint64(len(btf)) > math.MaxUint32 {
return nil, xerrors.New("BTF exceeds the maximum size")
}
attr := &bpfLoadBTFAttr{
btf: internal.NewSlicePointer(btf),
btfSize: uint32(len(btf)),
}
fd, err := bpfLoadBTF(attr)
if err != nil {
logBuf := make([]byte, 64*1024)
attr.logBuf = internal.NewSlicePointer(logBuf)
attr.btfLogSize = uint32(len(logBuf))
attr.btfLogLevel = 1
_, logErr := bpfLoadBTF(attr)
return nil, internal.ErrorWithLog(err, logBuf, logErr)
}
return &Handle{fd}, nil
}
// Close destroys the handle.
//
// Subsequent calls to FD will return an invalid value.
func (h *Handle) Close() error {
return h.fd.Close()
}
// FD returns the file descriptor for the handle.
func (h *Handle) FD() int {
value, err := h.fd.Value()
if err != nil {
return -1
}
return int(value)
}
// Map is the BTF for a map.
type Map struct {
spec *Spec
key, value Type
}
// MapSpec should be a method on Map, but is a free function
// to hide it from users of the ebpf package.
func MapSpec(m *Map) *Spec {
return m.spec
}
// MapKey should be a method on Map, but is a free function
// to hide it from users of the ebpf package.
func MapKey(m *Map) Type {
return m.key
}
// MapValue should be a method on Map, but is a free function
// to hide it from users of the ebpf package.
func MapValue(m *Map) Type {
return m.value
}
// Program is the BTF information for a stream of instructions.
type Program struct {
spec *Spec
length uint64
funcInfos, lineInfos extInfo
}
// ProgramSpec returns the Spec needed for loading function and line infos into the kernel.
//
// This is a free function instead of a method to hide it from users
// of package ebpf.
func ProgramSpec(s *Program) *Spec {
return s.spec
}
// ProgramAppend the information from other to the Program.
//
// This is a free function instead of a method to hide it from users
// of package ebpf.
func ProgramAppend(s, other *Program) error {
funcInfos, err := s.funcInfos.append(other.funcInfos, s.length)
if err != nil {
return xerrors.Errorf("func infos: %w", err)
}
lineInfos, err := s.lineInfos.append(other.lineInfos, s.length)
if err != nil {
return xerrors.Errorf("line infos: %w", err)
}
s.length += other.length
s.funcInfos = funcInfos
s.lineInfos = lineInfos
return nil
}
// ProgramFuncInfos returns the binary form of BTF function infos.
//
// This is a free function instead of a method to hide it from users
// of package ebpf.
func ProgramFuncInfos(s *Program) (recordSize uint32, bytes []byte, err error) {
bytes, err = s.funcInfos.MarshalBinary()
if err != nil {
return 0, nil, err
}
return s.funcInfos.recordSize, bytes, nil
}
// ProgramLineInfos returns the binary form of BTF line infos.
//
// This is a free function instead of a method to hide it from users
// of package ebpf.
func ProgramLineInfos(s *Program) (recordSize uint32, bytes []byte, err error) {
bytes, err = s.lineInfos.MarshalBinary()
if err != nil {
return 0, nil, err
}
return s.lineInfos.recordSize, bytes, nil
}
type bpfLoadBTFAttr struct {
btf internal.Pointer
logBuf internal.Pointer
btfSize uint32
btfLogSize uint32
btfLogLevel uint32
}
func bpfLoadBTF(attr *bpfLoadBTFAttr) (*internal.FD, error) {
const _BTFLoad = 18
fd, err := internal.BPF(_BTFLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
if err != nil {
return nil, err
}
return internal.NewFD(uint32(fd)), nil
}
func minimalBTF(bo binary.ByteOrder) []byte {
const minHeaderLength = 24
var (
types struct {
Integer btfType
Var btfType
btfVar struct{ Linkage uint32 }
}
typLen = uint32(binary.Size(&types))
strings = []byte{0, 'a', 0}
header = btfHeader{
Magic: btfMagic,
Version: 1,
HdrLen: minHeaderLength,
TypeOff: 0,
TypeLen: typLen,
StringOff: typLen,
StringLen: uint32(len(strings)),
}
)
// We use a BTF_KIND_VAR here, to make sure that
// the kernel understands BTF at least as well as we
// do. BTF_KIND_VAR was introduced ~5.1.
types.Integer.SetKind(kindPointer)
types.Var.NameOff = 1
types.Var.SetKind(kindVar)
types.Var.SizeType = 1
buf := new(bytes.Buffer)
_ = binary.Write(buf, bo, &header)
_ = binary.Write(buf, bo, &types)
buf.Write(strings)
return buf.Bytes()
}
var haveBTF = internal.FeatureTest("BTF", "5.1", func() bool {
btf := minimalBTF(internal.NativeEndian)
fd, err := bpfLoadBTF(&bpfLoadBTFAttr{
btf: internal.NewSlicePointer(btf),
btfSize: uint32(len(btf)),
})
if err == nil {
fd.Close()
}
// Check for EINVAL specifically, rather than err != nil since we
// otherwise misdetect due to insufficient permissions.
return !xerrors.Is(err, unix.EINVAL)
})

245
vendor/github.com/cilium/ebpf/internal/btf/btf_types.go generated vendored Normal file
View File

@@ -0,0 +1,245 @@
package btf
import (
"encoding/binary"
"fmt"
"io"
"golang.org/x/xerrors"
)
// btfKind describes a Type.
type btfKind uint8
// Equivalents of the BTF_KIND_* constants.
const (
kindUnknown btfKind = iota
kindInt
kindPointer
kindArray
kindStruct
kindUnion
kindEnum
kindForward
kindTypedef
kindVolatile
kindConst
kindRestrict
// Added ~4.20
kindFunc
kindFuncProto
// Added ~5.1
kindVar
kindDatasec
)
const (
btfTypeKindShift = 24
btfTypeKindLen = 4
btfTypeVlenShift = 0
btfTypeVlenMask = 16
)
// btfType is equivalent to struct btf_type in Documentation/bpf/btf.rst.
type btfType struct {
NameOff uint32
/* "info" bits arrangement
* bits 0-15: vlen (e.g. # of struct's members)
* bits 16-23: unused
* bits 24-27: kind (e.g. int, ptr, array...etc)
* bits 28-30: unused
* bit 31: kind_flag, currently used by
* struct, union and fwd
*/
Info uint32
/* "size" is used by INT, ENUM, STRUCT and UNION.
* "size" tells the size of the type it is describing.
*
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
* FUNC and FUNC_PROTO.
* "type" is a type_id referring to another type.
*/
SizeType uint32
}
func (k btfKind) String() string {
switch k {
case kindUnknown:
return "Unknown"
case kindInt:
return "Integer"
case kindPointer:
return "Pointer"
case kindArray:
return "Array"
case kindStruct:
return "Struct"
case kindUnion:
return "Union"
case kindEnum:
return "Enumeration"
case kindForward:
return "Forward"
case kindTypedef:
return "Typedef"
case kindVolatile:
return "Volatile"
case kindConst:
return "Const"
case kindRestrict:
return "Restrict"
case kindFunc:
return "Function"
case kindFuncProto:
return "Function Proto"
case kindVar:
return "Variable"
case kindDatasec:
return "Section"
default:
return fmt.Sprintf("Unknown (%d)", k)
}
}
func mask(len uint32) uint32 {
return (1 << len) - 1
}
func (bt *btfType) info(len, shift uint32) uint32 {
return (bt.Info >> shift) & mask(len)
}
func (bt *btfType) setInfo(value, len, shift uint32) {
bt.Info &^= mask(len) << shift
bt.Info |= (value & mask(len)) << shift
}
func (bt *btfType) Kind() btfKind {
return btfKind(bt.info(btfTypeKindLen, btfTypeKindShift))
}
func (bt *btfType) SetKind(kind btfKind) {
bt.setInfo(uint32(kind), btfTypeKindLen, btfTypeKindShift)
}
func (bt *btfType) Vlen() int {
return int(bt.info(btfTypeVlenMask, btfTypeVlenShift))
}
func (bt *btfType) SetVlen(vlen int) {
bt.setInfo(uint32(vlen), btfTypeVlenMask, btfTypeVlenShift)
}
func (bt *btfType) Type() TypeID {
// TODO: Panic here if wrong kind?
return TypeID(bt.SizeType)
}
func (bt *btfType) Size() uint32 {
// TODO: Panic here if wrong kind?
return bt.SizeType
}
type rawType struct {
btfType
data interface{}
}
func (rt *rawType) Marshal(w io.Writer, bo binary.ByteOrder) error {
if err := binary.Write(w, bo, &rt.btfType); err != nil {
return err
}
if rt.data == nil {
return nil
}
return binary.Write(w, bo, rt.data)
}
type btfArray struct {
Type TypeID
IndexType TypeID
Nelems uint32
}
type btfMember struct {
NameOff uint32
Type TypeID
Offset uint32
}
type btfVarSecinfo struct {
Type TypeID
Offset uint32
Size uint32
}
type btfVariable struct {
Linkage uint32
}
type btfEnum struct {
NameOff uint32
Val int32
}
type btfParam struct {
NameOff uint32
Type TypeID
}
func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) {
var (
header btfType
types []rawType
)
for id := TypeID(1); ; id++ {
if err := binary.Read(r, bo, &header); err == io.EOF {
return types, nil
} else if err != nil {
return nil, xerrors.Errorf("can't read type info for id %v: %v", id, err)
}
var data interface{}
switch header.Kind() {
case kindInt:
data = new(uint32)
case kindPointer:
case kindArray:
data = new(btfArray)
case kindStruct:
fallthrough
case kindUnion:
data = make([]btfMember, header.Vlen())
case kindEnum:
data = make([]btfEnum, header.Vlen())
case kindForward:
case kindTypedef:
case kindVolatile:
case kindConst:
case kindRestrict:
case kindFunc:
case kindFuncProto:
data = make([]btfParam, header.Vlen())
case kindVar:
data = new(btfVariable)
case kindDatasec:
data = make([]btfVarSecinfo, header.Vlen())
default:
return nil, xerrors.Errorf("type id %v: unknown kind: %v", id, header.Kind())
}
if data == nil {
types = append(types, rawType{header, nil})
continue
}
if err := binary.Read(r, bo, data); err != nil {
return nil, xerrors.Errorf("type id %d: kind %v: can't read %T: %v", id, header.Kind(), data, err)
}
types = append(types, rawType{header, data})
}
}

8
vendor/github.com/cilium/ebpf/internal/btf/doc.go generated vendored Normal file
View File

@@ -0,0 +1,8 @@
// Package btf handles data encoded according to the BPF Type Format.
//
// The canonical documentation lives in the Linux kernel repository and is
// available at https://www.kernel.org/doc/html/latest/bpf/btf.html
//
// The API is very much unstable. You should only use this via the main
// ebpf library.
package btf

182
vendor/github.com/cilium/ebpf/internal/btf/ext_info.go generated vendored Normal file
View File

@@ -0,0 +1,182 @@
package btf
import (
"bytes"
"encoding/binary"
"io"
"io/ioutil"
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal"
"golang.org/x/xerrors"
)
type btfExtHeader struct {
Magic uint16
Version uint8
Flags uint8
HdrLen uint32
FuncInfoOff uint32
FuncInfoLen uint32
LineInfoOff uint32
LineInfoLen uint32
}
func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (funcInfo, lineInfo map[string]extInfo, err error) {
var header btfExtHeader
if err := binary.Read(r, bo, &header); err != nil {
return nil, nil, xerrors.Errorf("can't read header: %v", err)
}
if header.Magic != btfMagic {
return nil, nil, xerrors.Errorf("incorrect magic value %v", header.Magic)
}
if header.Version != 1 {
return nil, nil, xerrors.Errorf("unexpected version %v", header.Version)
}
if header.Flags != 0 {
return nil, nil, xerrors.Errorf("unsupported flags %v", header.Flags)
}
remainder := int64(header.HdrLen) - int64(binary.Size(&header))
if remainder < 0 {
return nil, nil, xerrors.New("header is too short")
}
// Of course, the .BTF.ext header has different semantics than the
// .BTF ext header. We need to ignore non-null values.
_, err = io.CopyN(ioutil.Discard, r, remainder)
if err != nil {
return nil, nil, xerrors.Errorf("header padding: %v", err)
}
if _, err := r.Seek(int64(header.HdrLen+header.FuncInfoOff), io.SeekStart); err != nil {
return nil, nil, xerrors.Errorf("can't seek to function info section: %v", err)
}
funcInfo, err = parseExtInfo(io.LimitReader(r, int64(header.FuncInfoLen)), bo, strings)
if err != nil {
return nil, nil, xerrors.Errorf("function info: %w", err)
}
if _, err := r.Seek(int64(header.HdrLen+header.LineInfoOff), io.SeekStart); err != nil {
return nil, nil, xerrors.Errorf("can't seek to line info section: %v", err)
}
lineInfo, err = parseExtInfo(io.LimitReader(r, int64(header.LineInfoLen)), bo, strings)
if err != nil {
return nil, nil, xerrors.Errorf("line info: %w", err)
}
return funcInfo, lineInfo, nil
}
type btfExtInfoSec struct {
SecNameOff uint32
NumInfo uint32
}
type extInfoRecord struct {
InsnOff uint64
Opaque []byte
}
type extInfo struct {
recordSize uint32
records []extInfoRecord
}
func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) {
if other.recordSize != ei.recordSize {
return extInfo{}, xerrors.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize)
}
records := make([]extInfoRecord, 0, len(ei.records)+len(other.records))
records = append(records, ei.records...)
for _, info := range other.records {
records = append(records, extInfoRecord{
InsnOff: info.InsnOff + offset,
Opaque: info.Opaque,
})
}
return extInfo{ei.recordSize, records}, nil
}
func (ei extInfo) MarshalBinary() ([]byte, error) {
if len(ei.records) == 0 {
return nil, nil
}
buf := bytes.NewBuffer(make([]byte, 0, int(ei.recordSize)*len(ei.records)))
for _, info := range ei.records {
// The kernel expects offsets in number of raw bpf instructions,
// while the ELF tracks it in bytes.
insnOff := uint32(info.InsnOff / asm.InstructionSize)
if err := binary.Write(buf, internal.NativeEndian, insnOff); err != nil {
return nil, xerrors.Errorf("can't write instruction offset: %v", err)
}
buf.Write(info.Opaque)
}
return buf.Bytes(), nil
}
func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]extInfo, error) {
var recordSize uint32
if err := binary.Read(r, bo, &recordSize); err != nil {
return nil, xerrors.Errorf("can't read record size: %v", err)
}
if recordSize < 4 {
// Need at least insnOff
return nil, xerrors.New("record size too short")
}
result := make(map[string]extInfo)
for {
var infoHeader btfExtInfoSec
if err := binary.Read(r, bo, &infoHeader); err == io.EOF {
return result, nil
} else if err != nil {
return nil, xerrors.Errorf("can't read ext info header: %v", err)
}
secName, err := strings.Lookup(infoHeader.SecNameOff)
if err != nil {
return nil, xerrors.Errorf("can't get section name: %w", err)
}
if infoHeader.NumInfo == 0 {
return nil, xerrors.Errorf("section %s has invalid number of records", secName)
}
var records []extInfoRecord
for i := uint32(0); i < infoHeader.NumInfo; i++ {
var byteOff uint32
if err := binary.Read(r, bo, &byteOff); err != nil {
return nil, xerrors.Errorf("section %v: can't read extended info offset: %v", secName, err)
}
buf := make([]byte, int(recordSize-4))
if _, err := io.ReadFull(r, buf); err != nil {
return nil, xerrors.Errorf("section %v: can't read record: %v", secName, err)
}
if byteOff%asm.InstructionSize != 0 {
return nil, xerrors.Errorf("section %v: offset %v is not aligned with instruction size", secName, byteOff)
}
records = append(records, extInfoRecord{uint64(byteOff), buf})
}
result[secName] = extInfo{
recordSize,
records,
}
}
}

60
vendor/github.com/cilium/ebpf/internal/btf/strings.go generated vendored Normal file
View File

@@ -0,0 +1,60 @@
package btf
import (
"bytes"
"io"
"io/ioutil"
"golang.org/x/xerrors"
)
type stringTable []byte
func readStringTable(r io.Reader) (stringTable, error) {
contents, err := ioutil.ReadAll(r)
if err != nil {
return nil, xerrors.Errorf("can't read string table: %v", err)
}
if len(contents) < 1 {
return nil, xerrors.New("string table is empty")
}
if contents[0] != '\x00' {
return nil, xerrors.New("first item in string table is non-empty")
}
if contents[len(contents)-1] != '\x00' {
return nil, xerrors.New("string table isn't null terminated")
}
return stringTable(contents), nil
}
func (st stringTable) Lookup(offset uint32) (string, error) {
if int64(offset) > int64(^uint(0)>>1) {
return "", xerrors.Errorf("offset %d overflows int", offset)
}
pos := int(offset)
if pos >= len(st) {
return "", xerrors.Errorf("offset %d is out of bounds", offset)
}
if pos > 0 && st[pos-1] != '\x00' {
return "", xerrors.Errorf("offset %d isn't start of a string", offset)
}
str := st[pos:]
end := bytes.IndexByte(str, '\x00')
if end == -1 {
return "", xerrors.Errorf("offset %d isn't null terminated", offset)
}
return string(str[:end]), nil
}
func (st stringTable) LookupName(offset uint32) (Name, error) {
str, err := st.Lookup(offset)
return Name(str), err
}

586
vendor/github.com/cilium/ebpf/internal/btf/types.go generated vendored Normal file
View File

@@ -0,0 +1,586 @@
package btf
import (
"math"
"golang.org/x/xerrors"
)
const maxTypeDepth = 32
// TypeID identifies a type in a BTF section.
type TypeID uint32
// ID implements part of the Type interface.
func (tid TypeID) ID() TypeID {
return tid
}
// Type represents a type described by BTF.
type Type interface {
ID() TypeID
// Make a copy of the type, without copying Type members.
copy() Type
walk(*copyStack)
}
// Name identifies a type.
//
// Anonymous types have an empty name.
type Name string
func (n Name) name() string {
return string(n)
}
// Void is the unit type of BTF.
type Void struct{}
func (v Void) ID() TypeID { return 0 }
func (v Void) copy() Type { return Void{} }
func (v Void) walk(*copyStack) {}
// Int is an integer of a given length.
type Int struct {
TypeID
Name
// The size of the integer in bytes.
Size uint32
}
func (i *Int) size() uint32 { return i.Size }
func (i *Int) walk(*copyStack) {}
func (i *Int) copy() Type {
cpy := *i
return &cpy
}
// Pointer is a pointer to another type.
type Pointer struct {
TypeID
Target Type
}
func (p *Pointer) size() uint32 { return 8 }
func (p *Pointer) walk(cs *copyStack) { cs.push(&p.Target) }
func (p *Pointer) copy() Type {
cpy := *p
return &cpy
}
// Array is an array with a fixed number of elements.
type Array struct {
TypeID
Type Type
Nelems uint32
}
func (arr *Array) walk(cs *copyStack) { cs.push(&arr.Type) }
func (arr *Array) copy() Type {
cpy := *arr
return &cpy
}
// Struct is a compound type of consecutive members.
type Struct struct {
TypeID
Name
// The size of the struct including padding, in bytes
Size uint32
Members []Member
}
func (s *Struct) size() uint32 { return s.Size }
func (s *Struct) walk(cs *copyStack) {
for i := range s.Members {
cs.push(&s.Members[i].Type)
}
}
func (s *Struct) copy() Type {
cpy := *s
cpy.Members = make([]Member, len(s.Members))
copy(cpy.Members, s.Members)
return &cpy
}
// Union is a compound type where members occupy the same memory.
type Union struct {
TypeID
Name
// The size of the union including padding, in bytes.
Size uint32
Members []Member
}
func (u *Union) size() uint32 { return u.Size }
func (u *Union) walk(cs *copyStack) {
for i := range u.Members {
cs.push(&u.Members[i].Type)
}
}
func (u *Union) copy() Type {
cpy := *u
cpy.Members = make([]Member, len(u.Members))
copy(cpy.Members, u.Members)
return &cpy
}
// Member is part of a Struct or Union.
//
// It is not a valid Type.
type Member struct {
Name
Type Type
Offset uint32
}
// Enum lists possible values.
type Enum struct {
TypeID
Name
}
func (e *Enum) size() uint32 { return 4 }
func (e *Enum) walk(*copyStack) {}
func (e *Enum) copy() Type {
cpy := *e
return &cpy
}
// Fwd is a forward declaration of a Type.
type Fwd struct {
TypeID
Name
}
func (f *Fwd) walk(*copyStack) {}
func (f *Fwd) copy() Type {
cpy := *f
return &cpy
}
// Typedef is an alias of a Type.
type Typedef struct {
TypeID
Name
Type Type
}
func (td *Typedef) walk(cs *copyStack) { cs.push(&td.Type) }
func (td *Typedef) copy() Type {
cpy := *td
return &cpy
}
// Volatile is a modifier.
type Volatile struct {
TypeID
Type Type
}
func (v *Volatile) walk(cs *copyStack) { cs.push(&v.Type) }
func (v *Volatile) copy() Type {
cpy := *v
return &cpy
}
// Const is a modifier.
type Const struct {
TypeID
Type Type
}
func (c *Const) walk(cs *copyStack) { cs.push(&c.Type) }
func (c *Const) copy() Type {
cpy := *c
return &cpy
}
// Restrict is a modifier.
type Restrict struct {
TypeID
Type Type
}
func (r *Restrict) walk(cs *copyStack) { cs.push(&r.Type) }
func (r *Restrict) copy() Type {
cpy := *r
return &cpy
}
// Func is a function definition.
type Func struct {
TypeID
Name
Type Type
}
func (f *Func) walk(cs *copyStack) { cs.push(&f.Type) }
func (f *Func) copy() Type {
cpy := *f
return &cpy
}
// FuncProto is a function declaration.
type FuncProto struct {
TypeID
Return Type
// Parameters not supported yet
}
func (fp *FuncProto) walk(cs *copyStack) { cs.push(&fp.Return) }
func (fp *FuncProto) copy() Type {
cpy := *fp
return &cpy
}
// Var is a global variable.
type Var struct {
TypeID
Name
Type Type
}
func (v *Var) walk(cs *copyStack) { cs.push(&v.Type) }
func (v *Var) copy() Type {
cpy := *v
return &cpy
}
// Datasec is a global program section containing data.
type Datasec struct {
TypeID
Name
Size uint32
Vars []VarSecinfo
}
func (ds *Datasec) size() uint32 { return ds.Size }
func (ds *Datasec) walk(cs *copyStack) {
for i := range ds.Vars {
cs.push(&ds.Vars[i].Type)
}
}
func (ds *Datasec) copy() Type {
cpy := *ds
cpy.Vars = make([]VarSecinfo, len(ds.Vars))
copy(cpy.Vars, ds.Vars)
return &cpy
}
// VarSecinfo describes variable in a Datasec
type VarSecinfo struct {
Type Type
Offset uint32
Size uint32
}
type sizer interface {
size() uint32
}
var (
_ sizer = (*Int)(nil)
_ sizer = (*Pointer)(nil)
_ sizer = (*Struct)(nil)
_ sizer = (*Union)(nil)
_ sizer = (*Enum)(nil)
_ sizer = (*Datasec)(nil)
)
// Sizeof returns the size of a type in bytes.
//
// Returns an error if the size can't be computed.
func Sizeof(typ Type) (int, error) {
var (
n = int64(1)
elem int64
)
for i := 0; i < maxTypeDepth; i++ {
switch v := typ.(type) {
case *Array:
if n > 0 && int64(v.Nelems) > math.MaxInt64/n {
return 0, xerrors.New("overflow")
}
// Arrays may be of zero length, which allows
// n to be zero as well.
n *= int64(v.Nelems)
typ = v.Type
continue
case sizer:
elem = int64(v.size())
case *Typedef:
typ = v.Type
continue
case *Volatile:
typ = v.Type
continue
case *Const:
typ = v.Type
continue
case *Restrict:
typ = v.Type
continue
default:
return 0, xerrors.Errorf("unrecognized type %T", typ)
}
if n > 0 && elem > math.MaxInt64/n {
return 0, xerrors.New("overflow")
}
size := n * elem
if int64(int(size)) != size {
return 0, xerrors.New("overflow")
}
return int(size), nil
}
return 0, xerrors.New("exceeded type depth")
}
// copy a Type recursively.
//
// typ may form a cycle.
func copyType(typ Type) Type {
var (
copies = make(map[Type]Type)
work copyStack
)
for t := &typ; t != nil; t = work.pop() {
// *t is the identity of the type.
if cpy := copies[*t]; cpy != nil {
*t = cpy
continue
}
cpy := (*t).copy()
copies[*t] = cpy
*t = cpy
// Mark any nested types for copying.
cpy.walk(&work)
}
return typ
}
// copyStack keeps track of pointers to types which still
// need to be copied.
type copyStack []*Type
// push adds a type to the stack.
func (cs *copyStack) push(t *Type) {
*cs = append(*cs, t)
}
// pop returns the topmost Type, or nil.
func (cs *copyStack) pop() *Type {
n := len(*cs)
if n == 0 {
return nil
}
t := (*cs)[n-1]
*cs = (*cs)[:n-1]
return t
}
type namer interface {
name() string
}
var _ namer = Name("")
// inflateRawTypes takes a list of raw btf types linked via type IDs, and turns
// it into a graph of Types connected via pointers.
//
// Returns a map of named types (so, where NameOff is non-zero). Since BTF ignores
// compilation units, multiple types may share the same name. A Type may form a
// cyclic graph by pointing at itself.
func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map[string][]Type, err error) {
type fixupDef struct {
id TypeID
expectedKind btfKind
typ *Type
}
var fixups []fixupDef
fixup := func(id TypeID, expectedKind btfKind, typ *Type) {
fixups = append(fixups, fixupDef{id, expectedKind, typ})
}
convertMembers := func(raw []btfMember) ([]Member, error) {
// NB: The fixup below relies on pre-allocating this array to
// work, since otherwise append might re-allocate members.
members := make([]Member, 0, len(raw))
for i, btfMember := range raw {
name, err := rawStrings.LookupName(btfMember.NameOff)
if err != nil {
return nil, xerrors.Errorf("can't get name for member %d: %w", i, err)
}
members = append(members, Member{
Name: name,
Offset: btfMember.Offset,
})
}
for i := range members {
fixup(raw[i].Type, kindUnknown, &members[i].Type)
}
return members, nil
}
types := make([]Type, 0, len(rawTypes))
types = append(types, Void{})
namedTypes = make(map[string][]Type)
for i, raw := range rawTypes {
var (
// Void is defined to always be type ID 0, and is thus
// omitted from BTF.
id = TypeID(i + 1)
typ Type
)
name, err := rawStrings.LookupName(raw.NameOff)
if err != nil {
return nil, xerrors.Errorf("can't get name for type id %d: %w", id, err)
}
switch raw.Kind() {
case kindInt:
typ = &Int{id, name, raw.Size()}
case kindPointer:
ptr := &Pointer{id, nil}
fixup(raw.Type(), kindUnknown, &ptr.Target)
typ = ptr
case kindArray:
btfArr := raw.data.(*btfArray)
// IndexType is unused according to btf.rst.
// Don't make it available right now.
arr := &Array{id, nil, btfArr.Nelems}
fixup(btfArr.Type, kindUnknown, &arr.Type)
typ = arr
case kindStruct:
members, err := convertMembers(raw.data.([]btfMember))
if err != nil {
return nil, xerrors.Errorf("struct %s (id %d): %w", name, id, err)
}
typ = &Struct{id, name, raw.Size(), members}
case kindUnion:
members, err := convertMembers(raw.data.([]btfMember))
if err != nil {
return nil, xerrors.Errorf("union %s (id %d): %w", name, id, err)
}
typ = &Union{id, name, raw.Size(), members}
case kindEnum:
typ = &Enum{id, name}
case kindForward:
typ = &Fwd{id, name}
case kindTypedef:
typedef := &Typedef{id, name, nil}
fixup(raw.Type(), kindUnknown, &typedef.Type)
typ = typedef
case kindVolatile:
volatile := &Volatile{id, nil}
fixup(raw.Type(), kindUnknown, &volatile.Type)
typ = volatile
case kindConst:
cnst := &Const{id, nil}
fixup(raw.Type(), kindUnknown, &cnst.Type)
typ = cnst
case kindRestrict:
restrict := &Restrict{id, nil}
fixup(raw.Type(), kindUnknown, &restrict.Type)
typ = restrict
case kindFunc:
fn := &Func{id, name, nil}
fixup(raw.Type(), kindFuncProto, &fn.Type)
typ = fn
case kindFuncProto:
fp := &FuncProto{id, nil}
fixup(raw.Type(), kindUnknown, &fp.Return)
typ = fp
case kindVar:
v := &Var{id, name, nil}
fixup(raw.Type(), kindUnknown, &v.Type)
typ = v
case kindDatasec:
btfVars := raw.data.([]btfVarSecinfo)
vars := make([]VarSecinfo, 0, len(btfVars))
for _, btfVar := range btfVars {
vars = append(vars, VarSecinfo{
Offset: btfVar.Offset,
Size: btfVar.Size,
})
}
for i := range vars {
fixup(btfVars[i].Type, kindVar, &vars[i].Type)
}
typ = &Datasec{id, name, raw.SizeType, vars}
default:
return nil, xerrors.Errorf("type id %d: unknown kind: %v", id, raw.Kind())
}
types = append(types, typ)
if namer, ok := typ.(namer); ok {
if name := namer.name(); name != "" {
namedTypes[name] = append(namedTypes[name], typ)
}
}
}
for _, fixup := range fixups {
i := int(fixup.id)
if i >= len(types) {
return nil, xerrors.Errorf("reference to invalid type id: %d", fixup.id)
}
// Default void (id 0) to unknown
rawKind := kindUnknown
if i > 0 {
rawKind = rawTypes[i-1].Kind()
}
if expected := fixup.expectedKind; expected != kindUnknown && rawKind != expected {
return nil, xerrors.Errorf("expected type id %d to have kind %s, found %s", fixup.id, expected, rawKind)
}
*fixup.typ = types[i]
}
return namedTypes, nil
}

View File

@@ -2,10 +2,9 @@ package internal
import (
"fmt"
"os"
"io/ioutil"
"strings"
"sync"
"github.com/pkg/errors"
)
var sysCPU struct {
@@ -18,45 +17,44 @@ var sysCPU struct {
// Logical CPU numbers must be of the form 0-n
func PossibleCPUs() (int, error) {
sysCPU.once.Do(func() {
sysCPU.num, sysCPU.err = parseCPUs("/sys/devices/system/cpu/possible")
sysCPU.num, sysCPU.err = parseCPUsFromFile("/sys/devices/system/cpu/possible")
})
return sysCPU.num, sysCPU.err
}
var onlineCPU struct {
once sync.Once
err error
num int
}
// OnlineCPUs returns the number of currently online CPUs
// Logical CPU numbers must be of the form 0-n
func OnlineCPUs() (int, error) {
onlineCPU.once.Do(func() {
onlineCPU.num, onlineCPU.err = parseCPUs("/sys/devices/system/cpu/online")
})
return onlineCPU.num, onlineCPU.err
}
// parseCPUs parses the number of cpus from sysfs,
// in the format of "/sys/devices/system/cpu/{possible,online,..}.
// Logical CPU numbers must be of the form 0-n
func parseCPUs(path string) (int, error) {
file, err := os.Open(path)
func parseCPUsFromFile(path string) (int, error) {
spec, err := ioutil.ReadFile(path)
if err != nil {
return 0, err
}
defer file.Close()
n, err := parseCPUs(string(spec))
if err != nil {
return 0, fmt.Errorf("can't parse %s: %v", path, err)
}
return n, nil
}
// parseCPUs parses the number of cpus from a string produced
// by bitmap_list_string() in the Linux kernel.
// Multiple ranges are rejected, since they can't be unified
// into a single number.
// This is the format of /sys/devices/system/cpu/possible, it
// is not suitable for /sys/devices/system/cpu/online, etc.
func parseCPUs(spec string) (int, error) {
if strings.Trim(spec, "\n") == "0" {
return 1, nil
}
var low, high int
n, _ := fmt.Fscanf(file, "%d-%d", &low, &high)
if n < 1 || low != 0 {
return 0, errors.Wrapf(err, "%s has unknown format", path)
n, err := fmt.Sscanf(spec, "%d-%d\n", &low, &high)
if n != 2 || err != nil {
return 0, fmt.Errorf("invalid format: %s", spec)
}
if n == 1 {
high = low
if low != 0 {
return 0, fmt.Errorf("CPU spec doesn't start at zero: %s", spec)
}
// cpus is 0 indexed

47
vendor/github.com/cilium/ebpf/internal/errors.go generated vendored Normal file
View File

@@ -0,0 +1,47 @@
package internal
import (
"bytes"
"fmt"
"strings"
"github.com/cilium/ebpf/internal/unix"
"golang.org/x/xerrors"
)
// ErrorWithLog returns an error that includes logs from the
// kernel verifier.
//
// logErr should be the error returned by the syscall that generated
// the log. It is used to check for truncation of the output.
func ErrorWithLog(err error, log []byte, logErr error) error {
logStr := strings.Trim(CString(log), "\t\r\n ")
if xerrors.Is(logErr, unix.ENOSPC) {
logStr += " (truncated...)"
}
return &VerifierError{err, logStr}
}
// VerifierError includes information from the eBPF verifier.
type VerifierError struct {
cause error
log string
}
func (le *VerifierError) Error() string {
if le.log == "" {
return le.cause.Error()
}
return fmt.Sprintf("%s: %s", le.cause, le.log)
}
// CString turns a NUL / zero terminated byte buffer into a string.
func CString(in []byte) string {
inLen := bytes.IndexByte(in, 0)
if inLen == -1 {
return ""
}
return string(in[:inLen])
}

63
vendor/github.com/cilium/ebpf/internal/fd.go generated vendored Normal file
View File

@@ -0,0 +1,63 @@
package internal
import (
"runtime"
"strconv"
"github.com/cilium/ebpf/internal/unix"
"golang.org/x/xerrors"
)
var ErrClosedFd = xerrors.New("use of closed file descriptor")
type FD struct {
raw int64
}
func NewFD(value uint32) *FD {
fd := &FD{int64(value)}
runtime.SetFinalizer(fd, (*FD).Close)
return fd
}
func (fd *FD) String() string {
return strconv.FormatInt(fd.raw, 10)
}
func (fd *FD) Value() (uint32, error) {
if fd.raw < 0 {
return 0, ErrClosedFd
}
return uint32(fd.raw), nil
}
func (fd *FD) Close() error {
if fd.raw < 0 {
return nil
}
value := int(fd.raw)
fd.raw = -1
fd.Forget()
return unix.Close(value)
}
func (fd *FD) Forget() {
runtime.SetFinalizer(fd, nil)
}
func (fd *FD) Dup() (*FD, error) {
if fd.raw < 0 {
return nil, ErrClosedFd
}
dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 0)
if err != nil {
return nil, xerrors.Errorf("can't dup fd: %v", err)
}
return NewFD(uint32(dup)), nil
}

93
vendor/github.com/cilium/ebpf/internal/feature.go generated vendored Normal file
View File

@@ -0,0 +1,93 @@
package internal
import (
"fmt"
"sync"
"golang.org/x/xerrors"
)
// ErrNotSupported indicates that a feature is not supported by the current kernel.
var ErrNotSupported = xerrors.New("not supported")
// UnsupportedFeatureError is returned by FeatureTest() functions.
type UnsupportedFeatureError struct {
// The minimum Linux mainline version required for this feature.
// Used for the error string, and for sanity checking during testing.
MinimumVersion Version
// The name of the feature that isn't supported.
Name string
}
func (ufe *UnsupportedFeatureError) Error() string {
return fmt.Sprintf("%s not supported (requires >= %s)", ufe.Name, ufe.MinimumVersion)
}
// Is indicates that UnsupportedFeatureError is ErrNotSupported.
func (ufe *UnsupportedFeatureError) Is(target error) bool {
return target == ErrNotSupported
}
// FeatureTest wraps a function so that it is run at most once.
//
// name should identify the tested feature, while version must be in the
// form Major.Minor[.Patch].
//
// Returns a descriptive UnsupportedFeatureError if the feature is not available.
func FeatureTest(name, version string, fn func() bool) func() error {
v, err := NewVersion(version)
if err != nil {
return func() error { return err }
}
var (
once sync.Once
result error
)
return func() error {
once.Do(func() {
if !fn() {
result = &UnsupportedFeatureError{
MinimumVersion: v,
Name: name,
}
}
})
return result
}
}
// A Version in the form Major.Minor.Patch.
type Version [3]uint16
// NewVersion creates a version from a string like "Major.Minor.Patch".
//
// Patch is optional.
func NewVersion(ver string) (Version, error) {
var major, minor, patch uint16
n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch)
if n < 2 {
return Version{}, xerrors.Errorf("invalid version: %s", ver)
}
return Version{major, minor, patch}, nil
}
func (v Version) String() string {
if v[2] == 0 {
return fmt.Sprintf("v%d.%d", v[0], v[1])
}
return fmt.Sprintf("v%d.%d.%d", v[0], v[1], v[2])
}
// Less returns true if the version is less than another version.
func (v Version) Less(other Version) bool {
for i, a := range v {
if a == other[i] {
continue
}
return a < other[i]
}
return false
}

16
vendor/github.com/cilium/ebpf/internal/io.go generated vendored Normal file
View File

@@ -0,0 +1,16 @@
package internal
import "golang.org/x/xerrors"
// DiscardZeroes makes sure that all written bytes are zero
// before discarding them.
type DiscardZeroes struct{}
func (DiscardZeroes) Write(p []byte) (int, error) {
for _, b := range p {
if b != 0 {
return 0, xerrors.New("encountered non-zero byte")
}
}
return len(p), nil
}

30
vendor/github.com/cilium/ebpf/internal/ptr.go generated vendored Normal file
View File

@@ -0,0 +1,30 @@
package internal
import "unsafe"
// NewPointer creates a 64-bit pointer from an unsafe Pointer.
func NewPointer(ptr unsafe.Pointer) Pointer {
return Pointer{ptr: ptr}
}
// NewSlicePointer creates a 64-bit pointer from a byte slice.
func NewSlicePointer(buf []byte) Pointer {
if len(buf) == 0 {
return Pointer{}
}
return Pointer{ptr: unsafe.Pointer(&buf[0])}
}
// NewStringPointer creates a 64-bit pointer from a string.
func NewStringPointer(str string) Pointer {
if str == "" {
return Pointer{}
}
// The kernel expects strings to be zero terminated
buf := make([]byte, len(str)+1)
copy(buf, str)
return Pointer{ptr: unsafe.Pointer(&buf[0])}
}

View File

@@ -1,14 +1,14 @@
// +build armbe mips mips64p32
package ebpf
package internal
import (
"unsafe"
)
// ptr wraps an unsafe.Pointer to be 64bit to
// Pointer wraps an unsafe.Pointer to be 64bit to
// conform to the syscall specification.
type syscallPtr struct {
type Pointer struct {
pad uint32
ptr unsafe.Pointer
}

View File

@@ -1,14 +1,14 @@
// +build 386 amd64p32 arm mipsle mips64p32le
package ebpf
package internal
import (
"unsafe"
)
// ptr wraps an unsafe.Pointer to be 64bit to
// Pointer wraps an unsafe.Pointer to be 64bit to
// conform to the syscall specification.
type syscallPtr struct {
type Pointer struct {
ptr unsafe.Pointer
pad uint32
}

View File

@@ -1,14 +1,14 @@
// +build !386,!amd64p32,!arm,!mipsle,!mips64p32le
// +build !armbe,!mips,!mips64p32
package ebpf
package internal
import (
"unsafe"
)
// ptr wraps an unsafe.Pointer to be 64bit to
// Pointer wraps an unsafe.Pointer to be 64bit to
// conform to the syscall specification.
type syscallPtr struct {
type Pointer struct {
ptr unsafe.Pointer
}

23
vendor/github.com/cilium/ebpf/internal/syscall.go generated vendored Normal file
View File

@@ -0,0 +1,23 @@
package internal
import (
"runtime"
"unsafe"
"github.com/cilium/ebpf/internal/unix"
)
// BPF wraps SYS_BPF.
//
// Any pointers contained in attr must use the Pointer type from this package.
func BPF(cmd int, attr unsafe.Pointer, size uintptr) (uintptr, error) {
r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size)
runtime.KeepAlive(attr)
var err error
if errNo != 0 {
err = errNo
}
return r1, err
}

View File

@@ -10,10 +10,16 @@ import (
const (
ENOENT = linux.ENOENT
EEXIST = linux.EEXIST
EAGAIN = linux.EAGAIN
ENOSPC = linux.ENOSPC
EINVAL = linux.EINVAL
EPOLLIN = linux.EPOLLIN
EINTR = linux.EINTR
ESRCH = linux.ESRCH
ENODEV = linux.ENODEV
BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG
BPF_F_WRONLY_PROG = linux.BPF_F_WRONLY_PROG
BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN
BPF_TAG_SIZE = linux.BPF_TAG_SIZE
SYS_BPF = linux.SYS_BPF
@@ -31,6 +37,7 @@ const (
PERF_SAMPLE_RAW = linux.PERF_SAMPLE_RAW
PERF_FLAG_FD_CLOEXEC = linux.PERF_FLAG_FD_CLOEXEC
RLIM_INFINITY = linux.RLIM_INFINITY
RLIMIT_MEMLOCK = linux.RLIMIT_MEMLOCK
)
// Statfs_t is a wrapper
@@ -117,3 +124,26 @@ type PerfEventAttr = linux.PerfEventAttr
func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {
return linux.PerfEventOpen(attr, pid, cpu, groupFd, flags)
}
// Utsname is a wrapper
type Utsname = linux.Utsname
// Uname is a wrapper
func Uname(buf *Utsname) (err error) {
return linux.Uname(buf)
}
// Getpid is a wrapper
func Getpid() int {
return linux.Getpid()
}
// Gettid is a wrapper
func Gettid() int {
return linux.Gettid()
}
// Tgkill is a wrapper
func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) {
return linux.Tgkill(tgid, tid, sig)
}

View File

@@ -12,9 +12,15 @@ var errNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime
const (
ENOENT = syscall.ENOENT
EEXIST = syscall.EEXIST
EAGAIN = syscall.EAGAIN
ENOSPC = syscall.ENOSPC
EINVAL = syscall.EINVAL
EINTR = syscall.EINTR
ESRCH = syscall.ESRCH
ENODEV = syscall.ENODEV
BPF_F_RDONLY_PROG = 0
BPF_F_WRONLY_PROG = 0
BPF_OBJ_NAME_LEN = 0x10
BPF_TAG_SIZE = 0x8
SYS_BPF = 321
@@ -32,6 +38,8 @@ const (
PerfBitWatermark = 0x4000
PERF_SAMPLE_RAW = 0x400
PERF_FLAG_FD_CLOEXEC = 0x8
RLIM_INFINITY = 0x7fffffffffffffff
RLIMIT_MEMLOCK = 8
)
// Statfs_t is a wrapper
@@ -181,3 +189,28 @@ type PerfEventAttr struct {
func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {
return 0, errNonLinux
}
// Utsname is a wrapper
type Utsname struct {
Release [65]byte
}
// Uname is a wrapper
func Uname(buf *Utsname) (err error) {
return errNonLinux
}
// Getpid is a wrapper
func Getpid() int {
return -1
}
// Gettid is a wrapper
func Gettid() int {
return -1
}
// Tgkill is a wrapper
func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) {
return errNonLinux
}

View File

@@ -2,30 +2,59 @@ package ebpf
import (
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal/btf"
"golang.org/x/xerrors"
)
// link resolves bpf-to-bpf calls.
//
// Each section may contain multiple functions / labels, and is only linked
// if the program being edited references one of these functions.
// Each library may contain multiple functions / labels, and is only linked
// if prog references one of these functions.
//
// Sections must not require linking themselves.
func link(insns asm.Instructions, sections ...asm.Instructions) (asm.Instructions, error) {
for _, section := range sections {
var err error
insns, err = linkSection(insns, section)
if err != nil {
return nil, err
// Libraries also linked.
func link(prog *ProgramSpec, libs []*ProgramSpec) error {
var (
linked = make(map[*ProgramSpec]bool)
pending = []asm.Instructions{prog.Instructions}
insns asm.Instructions
)
for len(pending) > 0 {
insns, pending = pending[0], pending[1:]
for _, lib := range libs {
if linked[lib] {
continue
}
needed, err := needSection(insns, lib.Instructions)
if err != nil {
return xerrors.Errorf("linking %s: %w", lib.Name, err)
}
if !needed {
continue
}
linked[lib] = true
prog.Instructions = append(prog.Instructions, lib.Instructions...)
pending = append(pending, lib.Instructions)
if prog.BTF != nil && lib.BTF != nil {
if err := btf.ProgramAppend(prog.BTF, lib.BTF); err != nil {
return xerrors.Errorf("linking BTF of %s: %w", lib.Name, err)
}
}
}
}
return insns, nil
return nil
}
func linkSection(insns, section asm.Instructions) (asm.Instructions, error) {
func needSection(insns, section asm.Instructions) (bool, error) {
// A map of symbols to the libraries which contain them.
symbols, err := section.SymbolOffsets()
if err != nil {
return nil, err
return false, err
}
for _, ins := range insns {
@@ -33,7 +62,7 @@ func linkSection(insns, section asm.Instructions) (asm.Instructions, error) {
continue
}
if ins.OpCode.JumpOp() != asm.Call || ins.Src != asm.R1 {
if ins.OpCode.JumpOp() != asm.Call || ins.Src != asm.PseudoCall {
continue
}
@@ -48,11 +77,10 @@ func linkSection(insns, section asm.Instructions) (asm.Instructions, error) {
}
// At this point we know that at least one function in the
// library is called from insns. Merge the two sections.
// The rewrite of ins.Constant happens in asm.Instruction.Marshal.
return append(insns, section...), nil
// library is called from insns, so we have to link it.
return true, nil
}
// None of the functions in the section are called. Do nothing.
return insns, nil
// None of the functions in the section are called.
return false, nil
}

403
vendor/github.com/cilium/ebpf/map.go generated vendored
View File

@@ -2,14 +2,25 @@ package ebpf
import (
"fmt"
"unsafe"
"strings"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf"
"github.com/cilium/ebpf/internal/unix"
"github.com/pkg/errors"
"golang.org/x/xerrors"
)
// Errors returned by Map and MapIterator methods.
var (
ErrKeyNotExist = xerrors.New("key does not exist")
ErrKeyExist = xerrors.New("key already exists")
ErrIterationAborted = xerrors.New("iteration aborted")
)
// MapID represents the unique ID of an eBPF map
type MapID uint32
// MapSpec defines a Map.
type MapSpec struct {
// Name is passed to the kernel as a debug aid. Must only contain
@@ -20,8 +31,18 @@ type MapSpec struct {
ValueSize uint32
MaxEntries uint32
Flags uint32
// The initial contents of the map. May be nil.
Contents []MapKV
// Whether to freeze a map after setting its initial contents.
Freeze bool
// InnerMap is used as a template for ArrayOfMaps and HashOfMaps
InnerMap *MapSpec
// The BTF associated with this map.
BTF *btf.Map
}
func (ms *MapSpec) String() string {
@@ -29,16 +50,26 @@ func (ms *MapSpec) String() string {
}
// Copy returns a copy of the spec.
//
// MapSpec.Contents is a shallow copy.
func (ms *MapSpec) Copy() *MapSpec {
if ms == nil {
return nil
}
cpy := *ms
cpy.Contents = make([]MapKV, len(ms.Contents))
copy(cpy.Contents, ms.Contents)
cpy.InnerMap = ms.InnerMap.Copy()
return &cpy
}
// MapKV is used to initialize the contents of a Map.
type MapKV struct {
Key interface{}
Value interface{}
}
// Map represents a Map file descriptor.
//
// It is not safe to close a map which is used by other goroutines.
@@ -50,7 +81,7 @@ func (ms *MapSpec) Copy() *MapSpec {
// if you require custom encoding.
type Map struct {
name string
fd *bpfFD
fd *internal.FD
abi MapABI
// Per CPU maps return values larger than the size in the spec
fullValueSize int
@@ -61,13 +92,13 @@ type Map struct {
// You should not use fd after calling this function.
func NewMapFromFD(fd int) (*Map, error) {
if fd < 0 {
return nil, errors.New("invalid fd")
return nil, xerrors.New("invalid fd")
}
bpfFd := newBPFFD(uint32(fd))
bpfFd := internal.NewFD(uint32(fd))
name, abi, err := newMapABIFromFd(bpfFd)
if err != nil {
bpfFd.forget()
bpfFd.Forget()
return nil, err
}
return newMap(bpfFd, name, abi)
@@ -77,89 +108,135 @@ func NewMapFromFD(fd int) (*Map, error) {
//
// Creating a map for the first time will perform feature detection
// by creating small, temporary maps.
//
// The caller is responsible for ensuring the process' rlimit is set
// sufficiently high for locking memory during map creation. This can be done
// by calling unix.Setrlimit with unix.RLIMIT_MEMLOCK prior to calling NewMap.
func NewMap(spec *MapSpec) (*Map, error) {
if spec.BTF == nil {
return newMapWithBTF(spec, nil)
}
handle, err := btf.NewHandle(btf.MapSpec(spec.BTF))
if err != nil && !xerrors.Is(err, btf.ErrNotSupported) {
return nil, xerrors.Errorf("can't load BTF: %w", err)
}
return newMapWithBTF(spec, handle)
}
func newMapWithBTF(spec *MapSpec, handle *btf.Handle) (*Map, error) {
if spec.Type != ArrayOfMaps && spec.Type != HashOfMaps {
return createMap(spec, nil)
return createMap(spec, nil, handle)
}
if spec.InnerMap == nil {
return nil, errors.Errorf("%s requires InnerMap", spec.Type)
return nil, xerrors.Errorf("%s requires InnerMap", spec.Type)
}
template, err := createMap(spec.InnerMap, nil)
template, err := createMap(spec.InnerMap, nil, handle)
if err != nil {
return nil, err
}
defer template.Close()
return createMap(spec, template.fd)
return createMap(spec, template.fd, handle)
}
func createMap(spec *MapSpec, inner *bpfFD) (*Map, error) {
spec = spec.Copy()
func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, error) {
abi := newMapABIFromSpec(spec)
switch spec.Type {
case ArrayOfMaps:
fallthrough
case HashOfMaps:
if spec.ValueSize != 0 && spec.ValueSize != 4 {
return nil, errors.Errorf("ValueSize must be zero or four for map of map")
if err := haveNestedMaps(); err != nil {
return nil, err
}
spec.ValueSize = 4
if abi.ValueSize != 0 && abi.ValueSize != 4 {
return nil, xerrors.New("ValueSize must be zero or four for map of map")
}
abi.ValueSize = 4
case PerfEventArray:
if spec.KeySize != 0 {
return nil, errors.Errorf("KeySize must be zero for perf event array")
}
if spec.ValueSize != 0 {
return nil, errors.Errorf("ValueSize must be zero for perf event array")
}
if spec.MaxEntries == 0 {
n, err := internal.OnlineCPUs()
if err != nil {
return nil, errors.Wrap(err, "perf event array")
}
spec.MaxEntries = uint32(n)
if abi.KeySize != 0 && abi.KeySize != 4 {
return nil, xerrors.New("KeySize must be zero or four for perf event array")
}
abi.KeySize = 4
spec.KeySize = 4
spec.ValueSize = 4
if abi.ValueSize != 0 && abi.ValueSize != 4 {
return nil, xerrors.New("ValueSize must be zero or four for perf event array")
}
abi.ValueSize = 4
if abi.MaxEntries == 0 {
n, err := internal.PossibleCPUs()
if err != nil {
return nil, xerrors.Errorf("perf event array: %w", err)
}
abi.MaxEntries = uint32(n)
}
}
if abi.Flags&(unix.BPF_F_RDONLY_PROG|unix.BPF_F_WRONLY_PROG) > 0 || spec.Freeze {
if err := haveMapMutabilityModifiers(); err != nil {
return nil, xerrors.Errorf("map create: %w", err)
}
}
attr := bpfMapCreateAttr{
mapType: spec.Type,
keySize: spec.KeySize,
valueSize: spec.ValueSize,
maxEntries: spec.MaxEntries,
flags: spec.Flags,
mapType: abi.Type,
keySize: abi.KeySize,
valueSize: abi.ValueSize,
maxEntries: abi.MaxEntries,
flags: abi.Flags,
}
if inner != nil {
var err error
attr.innerMapFd, err = inner.value()
attr.innerMapFd, err = inner.Value()
if err != nil {
return nil, errors.Wrap(err, "map create")
return nil, xerrors.Errorf("map create: %w", err)
}
}
name, err := newBPFObjName(spec.Name)
if err != nil {
return nil, errors.Wrap(err, "map create")
if handle != nil && spec.BTF != nil {
attr.btfFd = uint32(handle.FD())
attr.btfKeyTypeID = btf.MapKey(spec.BTF).ID()
attr.btfValueTypeID = btf.MapValue(spec.BTF).ID()
}
if haveObjName.Result() {
attr.mapName = name
if haveObjName() == nil {
attr.mapName = newBPFObjName(spec.Name)
}
fd, err := bpfMapCreate(&attr)
if err != nil {
return nil, errors.Wrap(err, "map create")
return nil, xerrors.Errorf("map create: %w", err)
}
return newMap(fd, spec.Name, newMapABIFromSpec(spec))
m, err := newMap(fd, spec.Name, abi)
if err != nil {
return nil, err
}
if err := m.populate(spec.Contents); err != nil {
m.Close()
return nil, xerrors.Errorf("map create: can't set initial contents: %w", err)
}
if spec.Freeze {
if err := m.Freeze(); err != nil {
m.Close()
return nil, xerrors.Errorf("can't freeze map: %w", err)
}
}
return m, nil
}
func newMap(fd *bpfFD, name string, abi *MapABI) (*Map, error) {
func newMap(fd *internal.FD, name string, abi *MapABI) (*Map, error) {
m := &Map{
name,
fd,
@@ -224,9 +301,9 @@ func (m *Map) Lookup(key, valueOut interface{}) error {
*value = m
return nil
case *Map:
return errors.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil))
return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil))
case Map:
return errors.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil))
return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil))
case **Program:
p, err := unmarshalProgram(valueBytes)
@@ -238,38 +315,58 @@ func (m *Map) Lookup(key, valueOut interface{}) error {
*value = p
return nil
case *Program:
return errors.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil))
return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil))
case Program:
return errors.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil))
return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil))
default:
return unmarshalBytes(valueOut, valueBytes)
}
}
// LookupAndDelete retrieves and deletes a value from a Map.
//
// Returns ErrKeyNotExist if the key doesn't exist.
func (m *Map) LookupAndDelete(key, valueOut interface{}) error {
valuePtr, valueBytes := makeBuffer(valueOut, m.fullValueSize)
keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
if err != nil {
return xerrors.Errorf("can't marshal key: %w", err)
}
if err := bpfMapLookupAndDelete(m.fd, keyPtr, valuePtr); err != nil {
return xerrors.Errorf("lookup and delete failed: %w", err)
}
return unmarshalBytes(valueOut, valueBytes)
}
// LookupBytes gets a value from Map.
//
// Returns a nil value if a key doesn't exist.
func (m *Map) LookupBytes(key interface{}) ([]byte, error) {
valueBytes := make([]byte, m.fullValueSize)
valuePtr := newPtr(unsafe.Pointer(&valueBytes[0]))
valuePtr := internal.NewSlicePointer(valueBytes)
err := m.lookup(key, valuePtr)
if IsNotExist(err) {
if xerrors.Is(err, ErrKeyNotExist) {
return nil, nil
}
return valueBytes, err
}
func (m *Map) lookup(key interface{}, valueOut syscallPtr) error {
func (m *Map) lookup(key interface{}, valueOut internal.Pointer) error {
keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
if err != nil {
return errors.WithMessage(err, "can't marshal key")
return xerrors.Errorf("can't marshal key: %w", err)
}
err = bpfMapLookupElem(m.fd, keyPtr, valueOut)
return errors.WithMessage(err, "lookup failed")
if err = bpfMapLookupElem(m.fd, keyPtr, valueOut); err != nil {
return xerrors.Errorf("lookup failed: %w", err)
}
return nil
}
// MapUpdateFlags controls the behaviour of the Map.Update call.
@@ -297,38 +394,46 @@ func (m *Map) Put(key, value interface{}) error {
func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error {
keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
if err != nil {
return errors.WithMessage(err, "can't marshal key")
return xerrors.Errorf("can't marshal key: %w", err)
}
var valuePtr syscallPtr
var valuePtr internal.Pointer
if m.abi.Type.hasPerCPUValue() {
valuePtr, err = marshalPerCPUValue(value, int(m.abi.ValueSize))
} else {
valuePtr, err = marshalPtr(value, int(m.abi.ValueSize))
}
if err != nil {
return errors.WithMessage(err, "can't marshal value")
return xerrors.Errorf("can't marshal value: %w", err)
}
return bpfMapUpdateElem(m.fd, keyPtr, valuePtr, uint64(flags))
if err = bpfMapUpdateElem(m.fd, keyPtr, valuePtr, uint64(flags)); err != nil {
return xerrors.Errorf("update failed: %w", err)
}
return nil
}
// Delete removes a value.
//
// Returns an error if the key does not exist, see IsNotExist.
// Returns ErrKeyNotExist if the key does not exist.
func (m *Map) Delete(key interface{}) error {
keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
if err != nil {
return errors.WithMessage(err, "can't marshal key")
return xerrors.Errorf("can't marshal key: %w", err)
}
err = bpfMapDeleteElem(m.fd, keyPtr)
return errors.WithMessage(err, "can't delete key")
if err = bpfMapDeleteElem(m.fd, keyPtr); err != nil {
return xerrors.Errorf("delete failed: %w", err)
}
return nil
}
// NextKey finds the key following an initial key.
//
// See NextKeyBytes for details.
//
// Returns ErrKeyNotExist if there is no next key.
func (m *Map) NextKey(key, nextKeyOut interface{}) error {
nextKeyPtr, nextKeyBytes := makeBuffer(nextKeyOut, int(m.abi.KeySize))
@@ -340,8 +445,10 @@ func (m *Map) NextKey(key, nextKeyOut interface{}) error {
return nil
}
err := unmarshalBytes(nextKeyOut, nextKeyBytes)
return errors.WithMessage(err, "can't unmarshal next key")
if err := unmarshalBytes(nextKeyOut, nextKeyBytes); err != nil {
return xerrors.Errorf("can't unmarshal next key: %w", err)
}
return nil
}
// NextKeyBytes returns the key following an initial key as a byte slice.
@@ -349,33 +456,37 @@ func (m *Map) NextKey(key, nextKeyOut interface{}) error {
// Passing nil will return the first key.
//
// Use Iterate if you want to traverse all entries in the map.
//
// Returns nil if there are no more keys.
func (m *Map) NextKeyBytes(key interface{}) ([]byte, error) {
nextKey := make([]byte, m.abi.KeySize)
nextKeyPtr := newPtr(unsafe.Pointer(&nextKey[0]))
nextKeyPtr := internal.NewSlicePointer(nextKey)
err := m.nextKey(key, nextKeyPtr)
if IsNotExist(err) {
if xerrors.Is(err, ErrKeyNotExist) {
return nil, nil
}
return nextKey, err
}
func (m *Map) nextKey(key interface{}, nextKeyOut syscallPtr) error {
func (m *Map) nextKey(key interface{}, nextKeyOut internal.Pointer) error {
var (
keyPtr syscallPtr
keyPtr internal.Pointer
err error
)
if key != nil {
keyPtr, err = marshalPtr(key, int(m.abi.KeySize))
if err != nil {
return errors.WithMessage(err, "can't marshal key")
return xerrors.Errorf("can't marshal key: %w", err)
}
}
err = bpfMapGetNextKey(m.fd, keyPtr, nextKeyOut)
return errors.WithMessage(err, "can't get next key")
if err = bpfMapGetNextKey(m.fd, keyPtr, nextKeyOut); err != nil {
return xerrors.Errorf("next key failed: %w", err)
}
return nil
}
// Iterate traverses a map.
@@ -396,14 +507,14 @@ func (m *Map) Close() error {
return nil
}
return m.fd.close()
return m.fd.Close()
}
// FD gets the file descriptor of the Map.
//
// Calling this function is invalid after Close has been called.
func (m *Map) FD() int {
fd, err := m.fd.value()
fd, err := m.fd.Value()
if err != nil {
// Best effort: -1 is the number most likely to be an
// invalid file descriptor.
@@ -424,9 +535,9 @@ func (m *Map) Clone() (*Map, error) {
return nil, nil
}
dup, err := m.fd.dup()
dup, err := m.fd.Dup()
if err != nil {
return nil, errors.Wrap(err, "can't clone map")
return nil, xerrors.Errorf("can't clone map: %w", err)
}
return newMap(dup, m.name, &m.abi)
@@ -439,6 +550,29 @@ func (m *Map) Pin(fileName string) error {
return bpfPinObject(fileName, m.fd)
}
// Freeze prevents a map to be modified from user space.
//
// It makes no changes to kernel-side restrictions.
func (m *Map) Freeze() error {
if err := haveMapMutabilityModifiers(); err != nil {
return xerrors.Errorf("can't freeze map: %w", err)
}
if err := bpfMapFreeze(m.fd); err != nil {
return xerrors.Errorf("can't freeze map: %w", err)
}
return nil
}
func (m *Map) populate(contents []MapKV) error {
for _, kv := range contents {
if err := m.Put(kv.Key, kv.Value); err != nil {
return xerrors.Errorf("key %v: %w", kv.Key, err)
}
}
return nil
}
// LoadPinnedMap load a Map from a BPF file.
//
// The function is not compatible with nested maps.
@@ -450,7 +584,7 @@ func LoadPinnedMap(fileName string) (*Map, error) {
}
name, abi, err := newMapABIFromFd(fd)
if err != nil {
_ = fd.close()
_ = fd.Close()
return nil, err
}
return newMap(fd, name, abi)
@@ -467,29 +601,18 @@ func LoadPinnedMapExplicit(fileName string, abi *MapABI) (*Map, error) {
func unmarshalMap(buf []byte) (*Map, error) {
if len(buf) != 4 {
return nil, errors.New("map id requires 4 byte value")
return nil, xerrors.New("map id requires 4 byte value")
}
// Looking up an entry in a nested map or prog array returns an id,
// not an fd.
id := internal.NativeEndian.Uint32(buf)
fd, err := bpfGetMapFDByID(id)
if err != nil {
return nil, err
}
name, abi, err := newMapABIFromFd(fd)
if err != nil {
_ = fd.close()
return nil, err
}
return newMap(fd, name, abi)
return NewMapFromID(MapID(id))
}
// MarshalBinary implements BinaryMarshaler.
func (m *Map) MarshalBinary() ([]byte, error) {
fd, err := m.fd.value()
fd, err := m.fd.Value()
if err != nil {
return nil, err
}
@@ -499,6 +622,60 @@ func (m *Map) MarshalBinary() ([]byte, error) {
return buf, nil
}
func patchValue(value []byte, typ btf.Type, replacements map[string]interface{}) error {
replaced := make(map[string]bool)
replace := func(name string, offset, size int, replacement interface{}) error {
if offset+size > len(value) {
return xerrors.Errorf("%s: offset %d(+%d) is out of bounds", name, offset, size)
}
buf, err := marshalBytes(replacement, size)
if err != nil {
return xerrors.Errorf("marshal %s: %w", name, err)
}
copy(value[offset:offset+size], buf)
replaced[name] = true
return nil
}
switch parent := typ.(type) {
case *btf.Datasec:
for _, secinfo := range parent.Vars {
name := string(secinfo.Type.(*btf.Var).Name)
replacement, ok := replacements[name]
if !ok {
continue
}
err := replace(name, int(secinfo.Offset), int(secinfo.Size), replacement)
if err != nil {
return err
}
}
default:
return xerrors.Errorf("patching %T is not supported", typ)
}
if len(replaced) == len(replacements) {
return nil
}
var missing []string
for name := range replacements {
if !replaced[name] {
missing = append(missing, name)
}
}
if len(missing) == 1 {
return xerrors.Errorf("unknown field: %s", missing[0])
}
return xerrors.Errorf("unknown fields: %s", strings.Join(missing, ","))
}
// MapIterator iterates a Map.
//
// See Map.Iterate.
@@ -519,8 +696,6 @@ func newMapIterator(target *Map) *MapIterator {
}
}
var errIterationAborted = errors.New("iteration aborted")
// Next decodes the next key and value.
//
// Iterating a hash map from which keys are being deleted is not
@@ -556,7 +731,7 @@ func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool {
mi.prevKey = mi.prevBytes
mi.err = mi.target.Lookup(nextBytes, valueOut)
if IsNotExist(mi.err) {
if xerrors.Is(mi.err, ErrKeyNotExist) {
// Even though the key should be valid, we couldn't look up
// its value. If we're iterating a hash map this is probably
// because a concurrent delete removed the value before we
@@ -575,26 +750,50 @@ func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool {
return mi.err == nil
}
mi.err = errIterationAborted
mi.err = xerrors.Errorf("%w", ErrIterationAborted)
return false
}
// Err returns any encountered error.
//
// The method must be called after Next returns nil.
//
// Returns ErrIterationAborted if it wasn't possible to do a full iteration.
func (mi *MapIterator) Err() error {
return mi.err
}
// IsNotExist returns true if the error indicates that a
// key doesn't exist.
func IsNotExist(err error) bool {
return errors.Cause(err) == unix.ENOENT
// MapGetNextID returns the ID of the next eBPF map.
//
// Returns ErrNotExist, if there is no next eBPF map.
func MapGetNextID(startID MapID) (MapID, error) {
id, err := objGetNextID(_MapGetNextID, uint32(startID))
return MapID(id), err
}
// IsIterationAborted returns true if the iteration was aborted.
// NewMapFromID returns the map for a given id.
//
// This occurs when keys are deleted from a hash map during iteration.
func IsIterationAborted(err error) bool {
return errors.Cause(err) == errIterationAborted
// Returns ErrNotExist, if there is no eBPF map with the given id.
func NewMapFromID(id MapID) (*Map, error) {
fd, err := bpfObjGetFDByID(_MapGetFDByID, uint32(id))
if err != nil {
return nil, err
}
name, abi, err := newMapABIFromFd(fd)
if err != nil {
_ = fd.Close()
return nil, err
}
return newMap(fd, name, abi)
}
// ID returns the systemwide unique ID of the map.
func (m *Map) ID() (MapID, error) {
info, err := bpfGetMapInfoByFD(m.fd)
if err != nil {
return MapID(0), err
}
return MapID(info.id), nil
}

View File

@@ -10,20 +10,27 @@ import (
"github.com/cilium/ebpf/internal"
"github.com/pkg/errors"
"golang.org/x/xerrors"
)
func marshalPtr(data interface{}, length int) (syscallPtr, error) {
func marshalPtr(data interface{}, length int) (internal.Pointer, error) {
if data == nil {
if length == 0 {
return internal.NewPointer(nil), nil
}
return internal.Pointer{}, xerrors.New("can't use nil as key of map")
}
if ptr, ok := data.(unsafe.Pointer); ok {
return newPtr(ptr), nil
return internal.NewPointer(ptr), nil
}
buf, err := marshalBytes(data, length)
if err != nil {
return syscallPtr{}, err
return internal.Pointer{}, err
}
return newPtr(unsafe.Pointer(&buf[0])), nil
return internal.NewSlicePointer(buf), nil
}
func marshalBytes(data interface{}, length int) (buf []byte, err error) {
@@ -35,11 +42,13 @@ func marshalBytes(data interface{}, length int) (buf []byte, err error) {
case []byte:
buf = value
case unsafe.Pointer:
err = errors.New("can't marshal from unsafe.Pointer")
err = xerrors.New("can't marshal from unsafe.Pointer")
default:
var wr bytes.Buffer
err = binary.Write(&wr, internal.NativeEndian, value)
err = errors.Wrapf(err, "encoding %T", value)
if err != nil {
err = xerrors.Errorf("encoding %T: %v", value, err)
}
buf = wr.Bytes()
}
if err != nil {
@@ -47,18 +56,18 @@ func marshalBytes(data interface{}, length int) (buf []byte, err error) {
}
if len(buf) != length {
return nil, errors.Errorf("%T doesn't marshal to %d bytes", data, length)
return nil, xerrors.Errorf("%T doesn't marshal to %d bytes", data, length)
}
return buf, nil
}
func makeBuffer(dst interface{}, length int) (syscallPtr, []byte) {
func makeBuffer(dst interface{}, length int) (internal.Pointer, []byte) {
if ptr, ok := dst.(unsafe.Pointer); ok {
return newPtr(ptr), nil
return internal.NewPointer(ptr), nil
}
buf := make([]byte, length)
return newPtr(unsafe.Pointer(&buf[0])), buf
return internal.NewSlicePointer(buf), buf
}
func unmarshalBytes(data interface{}, buf []byte) error {
@@ -83,13 +92,15 @@ func unmarshalBytes(data interface{}, buf []byte) error {
*value = buf
return nil
case string:
return errors.New("require pointer to string")
return xerrors.New("require pointer to string")
case []byte:
return errors.New("require pointer to []byte")
return xerrors.New("require pointer to []byte")
default:
rd := bytes.NewReader(buf)
err := binary.Read(rd, internal.NativeEndian, value)
return errors.Wrapf(err, "decoding %T", value)
if err := binary.Read(rd, internal.NativeEndian, value); err != nil {
return xerrors.Errorf("decoding %T: %v", value, err)
}
return nil
}
}
@@ -99,21 +110,21 @@ func unmarshalBytes(data interface{}, buf []byte) error {
// Values are initialized to zero if the slice has less elements than CPUs.
//
// slice must have a type like []elementType.
func marshalPerCPUValue(slice interface{}, elemLength int) (syscallPtr, error) {
func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, error) {
sliceType := reflect.TypeOf(slice)
if sliceType.Kind() != reflect.Slice {
return syscallPtr{}, errors.New("per-CPU value requires slice")
return internal.Pointer{}, xerrors.New("per-CPU value requires slice")
}
possibleCPUs, err := internal.PossibleCPUs()
if err != nil {
return syscallPtr{}, err
return internal.Pointer{}, err
}
sliceValue := reflect.ValueOf(slice)
sliceLen := sliceValue.Len()
if sliceLen > possibleCPUs {
return syscallPtr{}, errors.Errorf("per-CPU value exceeds number of CPUs")
return internal.Pointer{}, xerrors.Errorf("per-CPU value exceeds number of CPUs")
}
alignedElemLength := align(elemLength, 8)
@@ -123,14 +134,14 @@ func marshalPerCPUValue(slice interface{}, elemLength int) (syscallPtr, error) {
elem := sliceValue.Index(i).Interface()
elemBytes, err := marshalBytes(elem, elemLength)
if err != nil {
return syscallPtr{}, err
return internal.Pointer{}, err
}
offset := i * alignedElemLength
copy(buf[offset:offset+elemLength], elemBytes)
}
return newPtr(unsafe.Pointer(&buf[0])), nil
return internal.NewSlicePointer(buf), nil
}
// unmarshalPerCPUValue decodes a buffer into a slice containing one value per
@@ -140,7 +151,7 @@ func marshalPerCPUValue(slice interface{}, elemLength int) (syscallPtr, error) {
func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) error {
slicePtrType := reflect.TypeOf(slicePtr)
if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice {
return errors.Errorf("per-cpu value requires pointer to slice")
return xerrors.Errorf("per-cpu value requires pointer to slice")
}
possibleCPUs, err := internal.PossibleCPUs()
@@ -159,7 +170,7 @@ func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) erro
step := len(buf) / possibleCPUs
if step < elemLength {
return errors.Errorf("per-cpu element length is larger than available data")
return xerrors.Errorf("per-cpu element length is larger than available data")
}
for i := 0; i < possibleCPUs; i++ {
var elem interface{}
@@ -177,7 +188,7 @@ func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) erro
err := unmarshalBytes(elem, elemBytes)
if err != nil {
return errors.Wrapf(err, "cpu %d", i)
return xerrors.Errorf("cpu %d: %w", i, err)
}
buf = buf[step:]

308
vendor/github.com/cilium/ebpf/prog.go generated vendored
View File

@@ -2,6 +2,7 @@ package ebpf
import (
"bytes"
"encoding/binary"
"fmt"
"math"
"strings"
@@ -10,14 +11,17 @@ import (
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf"
"github.com/cilium/ebpf/internal/unix"
"github.com/pkg/errors"
"golang.org/x/xerrors"
)
var (
errNotSupported = errors.New("ebpf: not supported by kernel")
)
// ErrNotSupported is returned whenever the kernel doesn't support a feature.
var ErrNotSupported = internal.ErrNotSupported
// ProgramID represents the unique ID of an eBPF program
type ProgramID uint32
const (
// Number of bytes to pad the output buffer for BPF_PROG_TEST_RUN.
@@ -50,6 +54,14 @@ type ProgramSpec struct {
Instructions asm.Instructions
License string
KernelVersion uint32
// The BTF associated with this program. Changing Instructions
// will most likely invalidate the contained data, and may
// result in errors when attempting to load it into the kernel.
BTF *btf.Program
// The byte order this program was compiled for, may be nil.
ByteOrder binary.ByteOrder
}
// Copy returns a copy of the spec.
@@ -72,7 +84,7 @@ type Program struct {
// otherwise it is empty.
VerifierLog string
fd *bpfFD
fd *internal.FD
name string
abi ProgramABI
}
@@ -90,7 +102,20 @@ func NewProgram(spec *ProgramSpec) (*Program, error) {
// Loading a program for the first time will perform
// feature detection by loading small, temporary programs.
func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, error) {
attr, err := convertProgramSpec(spec, haveObjName.Result())
if spec.BTF == nil {
return newProgramWithBTF(spec, nil, opts)
}
handle, err := btf.NewHandle(btf.ProgramSpec(spec.BTF))
if err != nil && !xerrors.Is(err, btf.ErrNotSupported) {
return nil, xerrors.Errorf("can't load BTF: %w", err)
}
return newProgramWithBTF(spec, handle, opts)
}
func newProgramWithBTF(spec *ProgramSpec, btf *btf.Handle, opts ProgramOptions) (*Program, error) {
attr, err := convertProgramSpec(spec, btf)
if err != nil {
return nil, err
}
@@ -105,55 +130,52 @@ func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er
logBuf = make([]byte, logSize)
attr.logLevel = opts.LogLevel
attr.logSize = uint32(len(logBuf))
attr.logBuf = newPtr(unsafe.Pointer(&logBuf[0]))
attr.logBuf = internal.NewSlicePointer(logBuf)
}
fd, err := bpfProgLoad(attr)
if err == nil {
prog := newProgram(fd, spec.Name, &ProgramABI{spec.Type})
prog.VerifierLog = convertCString(logBuf)
prog.VerifierLog = internal.CString(logBuf)
return prog, nil
}
truncated := errors.Cause(err) == unix.ENOSPC
logErr := err
if opts.LogLevel == 0 {
// Re-run with the verifier enabled to get better error messages.
logBuf = make([]byte, logSize)
attr.logLevel = 1
attr.logSize = uint32(len(logBuf))
attr.logBuf = newPtr(unsafe.Pointer(&logBuf[0]))
attr.logBuf = internal.NewSlicePointer(logBuf)
_, nerr := bpfProgLoad(attr)
truncated = errors.Cause(nerr) == unix.ENOSPC
_, logErr = bpfProgLoad(attr)
}
logs := convertCString(logBuf)
if truncated {
logs += "\n(truncated...)"
}
return nil, &loadError{err, logs}
err = internal.ErrorWithLog(err, logBuf, logErr)
return nil, xerrors.Errorf("can't load program: %w", err)
}
// NewProgramFromFD creates a program from a raw fd.
//
// You should not use fd after calling this function.
//
// Requires at least Linux 4.11.
func NewProgramFromFD(fd int) (*Program, error) {
if fd < 0 {
return nil, errors.New("invalid fd")
return nil, xerrors.New("invalid fd")
}
bpfFd := newBPFFD(uint32(fd))
bpfFd := internal.NewFD(uint32(fd))
name, abi, err := newProgramABIFromFd(bpfFd)
if err != nil {
bpfFd.forget()
bpfFd.Forget()
return nil, err
}
return newProgram(bpfFd, name, abi), nil
}
func newProgram(fd *bpfFD, name string, abi *ProgramABI) *Program {
func newProgram(fd *internal.FD, name string, abi *ProgramABI) *Program {
return &Program{
name: name,
fd: fd,
@@ -161,13 +183,17 @@ func newProgram(fd *bpfFD, name string, abi *ProgramABI) *Program {
}
}
func convertProgramSpec(spec *ProgramSpec, includeName bool) (*bpfProgLoadAttr, error) {
func convertProgramSpec(spec *ProgramSpec, handle *btf.Handle) (*bpfProgLoadAttr, error) {
if len(spec.Instructions) == 0 {
return nil, errors.New("Instructions cannot be empty")
return nil, xerrors.New("Instructions cannot be empty")
}
if len(spec.License) == 0 {
return nil, errors.New("License cannot be empty")
return nil, xerrors.New("License cannot be empty")
}
if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian {
return nil, xerrors.Errorf("can't load %s program on %s", spec.ByteOrder, internal.NativeEndian)
}
buf := bytes.NewBuffer(make([]byte, 0, len(spec.Instructions)*asm.InstructionSize))
@@ -178,22 +204,37 @@ func convertProgramSpec(spec *ProgramSpec, includeName bool) (*bpfProgLoadAttr,
bytecode := buf.Bytes()
insCount := uint32(len(bytecode) / asm.InstructionSize)
lic := []byte(spec.License)
attr := &bpfProgLoadAttr{
progType: spec.Type,
expectedAttachType: spec.AttachType,
insCount: insCount,
instructions: newPtr(unsafe.Pointer(&bytecode[0])),
license: newPtr(unsafe.Pointer(&lic[0])),
instructions: internal.NewSlicePointer(bytecode),
license: internal.NewStringPointer(spec.License),
kernelVersion: spec.KernelVersion,
}
name, err := newBPFObjName(spec.Name)
if err != nil {
return nil, err
if haveObjName() == nil {
attr.progName = newBPFObjName(spec.Name)
}
if includeName {
attr.progName = name
if handle != nil && spec.BTF != nil {
attr.progBTFFd = uint32(handle.FD())
recSize, bytes, err := btf.ProgramLineInfos(spec.BTF)
if err != nil {
return nil, xerrors.Errorf("can't get BTF line infos: %w", err)
}
attr.lineInfoRecSize = recSize
attr.lineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize))
attr.lineInfo = internal.NewSlicePointer(bytes)
recSize, bytes, err = btf.ProgramFuncInfos(spec.BTF)
if err != nil {
return nil, xerrors.Errorf("can't get BTF function infos: %w", err)
}
attr.funcInfoRecSize = recSize
attr.funcInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize))
attr.funcInfo = internal.NewSlicePointer(bytes)
}
return attr, nil
@@ -215,7 +256,7 @@ func (p *Program) ABI() ProgramABI {
//
// It is invalid to call this function after Close has been called.
func (p *Program) FD() int {
fd, err := p.fd.value()
fd, err := p.fd.Value()
if err != nil {
// Best effort: -1 is the number most likely to be an
// invalid file descriptor.
@@ -235,9 +276,9 @@ func (p *Program) Clone() (*Program, error) {
return nil, nil
}
dup, err := p.fd.dup()
dup, err := p.fd.Dup()
if err != nil {
return nil, errors.Wrap(err, "can't clone program")
return nil, xerrors.Errorf("can't clone program: %w", err)
}
return newProgram(dup, p.name, &p.abi), nil
@@ -247,7 +288,10 @@ func (p *Program) Clone() (*Program, error) {
//
// This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional
func (p *Program) Pin(fileName string) error {
return errors.Wrap(bpfPinObject(fileName, p.fd), "can't pin program")
if err := bpfPinObject(fileName, p.fd); err != nil {
return xerrors.Errorf("can't pin program: %w", err)
}
return nil
}
// Close unloads the program from the kernel.
@@ -256,7 +300,7 @@ func (p *Program) Close() error {
return nil
}
return p.fd.close()
return p.fd.Close()
}
// Test runs the Program in the kernel with the given input and returns the
@@ -267,57 +311,68 @@ func (p *Program) Close() error {
//
// This function requires at least Linux 4.12.
func (p *Program) Test(in []byte) (uint32, []byte, error) {
ret, out, _, err := p.testRun(in, 1)
return ret, out, err
ret, out, _, err := p.testRun(in, 1, nil)
if err != nil {
return ret, nil, xerrors.Errorf("can't test program: %w", err)
}
return ret, out, nil
}
// Benchmark runs the Program with the given input for a number of times
// and returns the time taken per iteration.
//
// The returned value is the return value of the last execution of
// the program.
// Returns the result of the last execution of the program and the time per
// run or an error. reset is called whenever the benchmark syscall is
// interrupted, and should be set to testing.B.ResetTimer or similar.
//
// Note: profiling a call to this function will skew it's results, see
// https://github.com/cilium/ebpf/issues/24
//
// This function requires at least Linux 4.12.
func (p *Program) Benchmark(in []byte, repeat int) (uint32, time.Duration, error) {
ret, _, total, err := p.testRun(in, repeat)
return ret, total, err
func (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.Duration, error) {
ret, _, total, err := p.testRun(in, repeat, reset)
if err != nil {
return ret, total, xerrors.Errorf("can't benchmark program: %w", err)
}
return ret, total, nil
}
var noProgTestRun = featureTest{
Fn: func() bool {
prog, err := NewProgram(&ProgramSpec{
Type: SocketFilter,
Instructions: asm.Instructions{
asm.LoadImm(asm.R0, 0, asm.DWord),
asm.Return(),
},
License: "MIT",
})
if err != nil {
// This may be because we lack sufficient permissions, etc.
return false
}
defer prog.Close()
var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() bool {
prog, err := NewProgram(&ProgramSpec{
Type: SocketFilter,
Instructions: asm.Instructions{
asm.LoadImm(asm.R0, 0, asm.DWord),
asm.Return(),
},
License: "MIT",
})
if err != nil {
// This may be because we lack sufficient permissions, etc.
return false
}
defer prog.Close()
fd, err := prog.fd.value()
if err != nil {
return false
}
fd, err := prog.fd.Value()
if err != nil {
return false
}
// Programs require at least 14 bytes input
in := make([]byte, 14)
attr := bpfProgTestRunAttr{
fd: fd,
dataSizeIn: uint32(len(in)),
dataIn: newPtr(unsafe.Pointer(&in[0])),
}
// Programs require at least 14 bytes input
in := make([]byte, 14)
attr := bpfProgTestRunAttr{
fd: fd,
dataSizeIn: uint32(len(in)),
dataIn: internal.NewSlicePointer(in),
}
_, err = bpfCall(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return errors.Cause(err) == unix.EINVAL
},
}
_, err = internal.BPF(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
func (p *Program) testRun(in []byte, repeat int) (uint32, []byte, time.Duration, error) {
// Check for EINVAL specifically, rather than err != nil since we
// otherwise misdetect due to insufficient permissions.
return !xerrors.Is(err, unix.EINVAL)
})
func (p *Program) testRun(in []byte, repeat int, reset func()) (uint32, []byte, time.Duration, error) {
if uint(repeat) > math.MaxUint32 {
return 0, nil, 0, fmt.Errorf("repeat is too high")
}
@@ -330,8 +385,8 @@ func (p *Program) testRun(in []byte, repeat int) (uint32, []byte, time.Duration,
return 0, nil, 0, fmt.Errorf("input is too long")
}
if noProgTestRun.Result() {
return 0, nil, 0, errNotSupported
if err := haveProgTestRun(); err != nil {
return 0, nil, 0, err
}
// Older kernels ignore the dataSizeOut argument when copying to user space.
@@ -341,7 +396,7 @@ func (p *Program) testRun(in []byte, repeat int) (uint32, []byte, time.Duration,
// See https://patchwork.ozlabs.org/cover/1006822/
out := make([]byte, len(in)+outputPad)
fd, err := p.fd.value()
fd, err := p.fd.Value()
if err != nil {
return 0, nil, 0, err
}
@@ -350,14 +405,25 @@ func (p *Program) testRun(in []byte, repeat int) (uint32, []byte, time.Duration,
fd: fd,
dataSizeIn: uint32(len(in)),
dataSizeOut: uint32(len(out)),
dataIn: newPtr(unsafe.Pointer(&in[0])),
dataOut: newPtr(unsafe.Pointer(&out[0])),
dataIn: internal.NewSlicePointer(in),
dataOut: internal.NewSlicePointer(out),
repeat: uint32(repeat),
}
_, err = bpfCall(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
if err != nil {
return 0, nil, 0, errors.Wrap(err, "can't run test")
for {
_, err = internal.BPF(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
if err == nil {
break
}
if xerrors.Is(err, unix.EINTR) {
if reset != nil {
reset()
}
continue
}
return 0, nil, 0, xerrors.Errorf("can't run test: %w", err)
}
if int(attr.dataSizeOut) > cap(out) {
@@ -373,29 +439,18 @@ func (p *Program) testRun(in []byte, repeat int) (uint32, []byte, time.Duration,
func unmarshalProgram(buf []byte) (*Program, error) {
if len(buf) != 4 {
return nil, errors.New("program id requires 4 byte value")
return nil, xerrors.New("program id requires 4 byte value")
}
// Looking up an entry in a nested map or prog array returns an id,
// not an fd.
id := internal.NativeEndian.Uint32(buf)
fd, err := bpfGetProgramFDByID(id)
if err != nil {
return nil, err
}
name, abi, err := newProgramABIFromFd(fd)
if err != nil {
_ = fd.close()
return nil, err
}
return newProgram(fd, name, abi), nil
return NewProgramFromID(ProgramID(id))
}
// MarshalBinary implements BinaryMarshaler.
func (p *Program) MarshalBinary() ([]byte, error) {
value, err := p.fd.value()
value, err := p.fd.Value()
if err != nil {
return nil, err
}
@@ -408,10 +463,10 @@ func (p *Program) MarshalBinary() ([]byte, error) {
// Attach a Program to a container object fd
func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error {
if fd < 0 {
return errors.New("invalid fd")
return xerrors.New("invalid fd")
}
pfd, err := p.fd.value()
pfd, err := p.fd.Value()
if err != nil {
return err
}
@@ -429,10 +484,10 @@ func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error {
// Detach a Program from a container object fd
func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error {
if fd < 0 {
return errors.New("invalid fd")
return xerrors.New("invalid fd")
}
pfd, err := p.fd.value()
pfd, err := p.fd.Value()
if err != nil {
return err
}
@@ -448,6 +503,8 @@ func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error {
}
// LoadPinnedProgram loads a Program from a BPF file.
//
// Requires at least Linux 4.11.
func LoadPinnedProgram(fileName string) (*Program, error) {
fd, err := bpfGetObject(fileName)
if err != nil {
@@ -456,8 +513,8 @@ func LoadPinnedProgram(fileName string) (*Program, error) {
name, abi, err := newProgramABIFromFd(fd)
if err != nil {
_ = fd.close()
return nil, err
_ = fd.Close()
return nil, xerrors.Errorf("can't get ABI for %s: %w", fileName, err)
}
return newProgram(fd, name, abi), nil
@@ -479,24 +536,37 @@ func SanitizeName(name string, replacement rune) string {
}, name)
}
type loadError struct {
cause error
verifierLog string
// ProgramGetNextID returns the ID of the next eBPF program.
//
// Returns ErrNotExist, if there is no next eBPF program.
func ProgramGetNextID(startID ProgramID) (ProgramID, error) {
id, err := objGetNextID(_ProgGetNextID, uint32(startID))
return ProgramID(id), err
}
func (le *loadError) Error() string {
if le.verifierLog == "" {
return fmt.Sprintf("failed to load program: %s", le.cause)
// NewProgramFromID returns the program for a given id.
//
// Returns ErrNotExist, if there is no eBPF program with the given id.
func NewProgramFromID(id ProgramID) (*Program, error) {
fd, err := bpfObjGetFDByID(_ProgGetFDByID, uint32(id))
if err != nil {
return nil, err
}
return fmt.Sprintf("failed to load program: %s: %s", le.cause, le.verifierLog)
name, abi, err := newProgramABIFromFd(fd)
if err != nil {
_ = fd.Close()
return nil, err
}
return newProgram(fd, name, abi), nil
}
func (le *loadError) Cause() error {
return le.cause
}
// IsNotSupported returns true if an error occurred because
// the kernel does not have support for a specific feature.
func IsNotSupported(err error) bool {
return errors.Cause(err) == errNotSupported
// ID returns the systemwide unique ID of the program.
func (p *Program) ID() (ProgramID, error) {
info, err := bpfGetProgInfoByFD(p.fd)
if err != nil {
return ProgramID(0), err
}
return ProgramID(info.id), nil
}

View File

@@ -13,6 +13,11 @@ The library is maintained by [Cloudflare](https://www.cloudflare.com) and [Ciliu
The package is production ready, but **the API is explicitly unstable
right now**. Expect to update your code if you want to follow along.
## Requirements
* A version of Go that is [supported by upstream](https://golang.org/doc/devel/release.html#policy)
* Linux 4.9, 4.19 or 5.4 (versions in-between should work, but are not tested)
## Useful resources
* [Cilium eBPF documentation](https://cilium.readthedocs.io/en/latest/bpf/#bpf-guide) (recommended)

View File

@@ -8,24 +8,22 @@ set -o pipefail
if [[ "${1:-}" = "--in-vm" ]]; then
shift
readonly home="$(mktemp --directory)"
mount -t bpf bpf /sys/fs/bpf
export CGO_ENABLED=0
export HOME="$home"
export GOFLAGS=-mod=readonly
export GOPATH=/run/go-path
export GOPROXY=file:///run/go-root/pkg/mod/cache/download
export GOCACHE=/run/go-cache
echo Running tests...
/usr/local/bin/go test -mod=vendor -coverprofile="$1/coverage.txt" -covermode=atomic -v ./...
/usr/local/bin/go test -coverprofile="$1/coverage.txt" -covermode=atomic -v ./...
touch "$1/success"
exit 0
fi
# Force Go modules, so that vendoring and building are easier.
export GO111MODULE=on
# Pull all dependencies, so that we can run tests without the
# vm having network access.
go mod vendor
go mod download
# Use sudo if /dev/kvm isn't accessible by the current user.
sudo=""
@@ -42,15 +40,19 @@ fi
readonly kernel="linux-${kernel_version}.bz"
readonly output="$(mktemp -d)"
readonly tmp_dir="$(mktemp -d)"
readonly tmp_dir="${TMPDIR:-$(mktemp -d)}"
test -e "${tmp_dir}/${kernel}" || {
echo Fetching ${kernel}
curl --fail -L "https://github.com/newtools/ci-kernels/blob/master/${kernel}?raw=true" -o "${tmp_dir}/${kernel}"
echo Fetching "${kernel}"
curl --fail -L "https://github.com/cilium/ci-kernels/blob/master/${kernel}?raw=true" -o "${tmp_dir}/${kernel}"
}
echo Testing on ${kernel_version}
$sudo virtme-run --kimg "${tmp_dir}/${kernel}" --memory 256M --pwd --rwdir=/run/output="${output}" --script-sh "$(realpath "$0") --in-vm /run/output" --qemu-opts -smp 2
echo Testing on "${kernel_version}"
$sudo virtme-run --kimg "${tmp_dir}/${kernel}" --memory 512M --pwd \
--rwdir=/run/output="${output}" \
--rodir=/run/go-path="$(go env GOPATH)" \
--rwdir=/run/go-cache="$(go env GOCACHE)" \
--script-sh "$(realpath "$0") --in-vm /run/output"
if [[ ! -e "${output}/success" ]]; then
echo "Test failed on ${kernel_version}"

View File

@@ -1,88 +1,36 @@
package ebpf
import (
"bytes"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"unsafe"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf"
"github.com/cilium/ebpf/internal/unix"
"github.com/pkg/errors"
"golang.org/x/xerrors"
)
var errClosedFd = errors.New("use of closed file descriptor")
type bpfFD struct {
raw int64
}
func newBPFFD(value uint32) *bpfFD {
fd := &bpfFD{int64(value)}
runtime.SetFinalizer(fd, (*bpfFD).close)
return fd
}
func (fd *bpfFD) String() string {
return strconv.FormatInt(fd.raw, 10)
}
func (fd *bpfFD) value() (uint32, error) {
if fd.raw < 0 {
return 0, errClosedFd
}
return uint32(fd.raw), nil
}
func (fd *bpfFD) close() error {
if fd.raw < 0 {
return nil
}
value := int(fd.raw)
fd.raw = -1
fd.forget()
return unix.Close(value)
}
func (fd *bpfFD) forget() {
runtime.SetFinalizer(fd, nil)
}
func (fd *bpfFD) dup() (*bpfFD, error) {
if fd.raw < 0 {
return nil, errClosedFd
}
dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 0)
if err != nil {
return nil, errors.Wrap(err, "can't dup fd")
}
return newBPFFD(uint32(dup)), nil
}
// Generic errors returned by BPF syscalls.
var (
ErrNotExist = xerrors.New("requested object does not exist")
)
// bpfObjName is a null-terminated string made up of
// 'A-Za-z0-9_' characters.
type bpfObjName [unix.BPF_OBJ_NAME_LEN]byte
// newBPFObjName truncates the result if it is too long.
func newBPFObjName(name string) (bpfObjName, error) {
idx := strings.IndexFunc(name, invalidBPFObjNameChar)
if idx != -1 {
return bpfObjName{}, errors.Errorf("invalid character '%c' in name '%s'", name[idx], name)
}
func newBPFObjName(name string) bpfObjName {
var result bpfObjName
copy(result[:unix.BPF_OBJ_NAME_LEN-1], name)
return result, nil
return result
}
func invalidBPFObjNameChar(char rune) bool {
dotAllowed := objNameAllowsDot() == nil
switch {
case char >= 'A' && char <= 'Z':
fallthrough
@@ -90,6 +38,8 @@ func invalidBPFObjNameChar(char rune) bool {
fallthrough
case char >= '0' && char <= '9':
fallthrough
case dotAllowed && char == '.':
fallthrough
case char == '_':
return false
default:
@@ -98,21 +48,25 @@ func invalidBPFObjNameChar(char rune) bool {
}
type bpfMapCreateAttr struct {
mapType MapType
keySize uint32
valueSize uint32
maxEntries uint32
flags uint32
innerMapFd uint32 // since 4.12 56f668dfe00d
numaNode uint32 // since 4.14 96eabe7a40aa
mapName bpfObjName // since 4.15 ad5b177bd73f
mapType MapType
keySize uint32
valueSize uint32
maxEntries uint32
flags uint32
innerMapFd uint32 // since 4.12 56f668dfe00d
numaNode uint32 // since 4.14 96eabe7a40aa
mapName bpfObjName // since 4.15 ad5b177bd73f
mapIfIndex uint32
btfFd uint32
btfKeyTypeID btf.TypeID
btfValueTypeID btf.TypeID
}
type bpfMapOpAttr struct {
mapFd uint32
padding uint32
key syscallPtr
value syscallPtr
key internal.Pointer
value internal.Pointer
flags uint64
}
@@ -127,7 +81,7 @@ type bpfMapInfo struct {
}
type bpfPinObjAttr struct {
fileName syscallPtr
fileName internal.Pointer
fd uint32
padding uint32
}
@@ -135,16 +89,23 @@ type bpfPinObjAttr struct {
type bpfProgLoadAttr struct {
progType ProgramType
insCount uint32
instructions syscallPtr
license syscallPtr
instructions internal.Pointer
license internal.Pointer
logLevel uint32
logSize uint32
logBuf syscallPtr
logBuf internal.Pointer
kernelVersion uint32 // since 4.1 2541517c32be
progFlags uint32 // since 4.11 e07b98d9bffe
progName bpfObjName // since 4.15 067cae47771c
progIfIndex uint32 // since 4.15 1f6f4cb7ba21
expectedAttachType AttachType // since 4.17 5e43f899b03a
progBTFFd uint32
funcInfoRecSize uint32
funcInfo internal.Pointer
funcInfoCnt uint32
lineInfoRecSize uint32
lineInfo internal.Pointer
lineInfoCnt uint32
}
type bpfProgInfo struct {
@@ -153,12 +114,12 @@ type bpfProgInfo struct {
tag [unix.BPF_TAG_SIZE]byte
jitedLen uint32
xlatedLen uint32
jited syscallPtr
xlated syscallPtr
jited internal.Pointer
xlated internal.Pointer
loadTime uint64 // since 4.15 cb4d2b3f03d8
createdByUID uint32
nrMapIDs uint32
mapIds syscallPtr
mapIds internal.Pointer
name bpfObjName
}
@@ -167,8 +128,8 @@ type bpfProgTestRunAttr struct {
retval uint32
dataSizeIn uint32
dataSizeOut uint32
dataIn syscallPtr
dataOut syscallPtr
dataIn internal.Pointer
dataOut internal.Pointer
repeat uint32
duration uint32
}
@@ -183,7 +144,7 @@ type bpfProgAlterAttr struct {
type bpfObjGetInfoByFDAttr struct {
fd uint32
infoLen uint32
info syscallPtr // May be either bpfMapInfo or bpfProgInfo
info internal.Pointer // May be either bpfMapInfo or bpfProgInfo
}
type bpfGetFDByIDAttr struct {
@@ -191,13 +152,19 @@ type bpfGetFDByIDAttr struct {
next uint32
}
func newPtr(ptr unsafe.Pointer) syscallPtr {
return syscallPtr{ptr: ptr}
type bpfMapFreezeAttr struct {
mapFd uint32
}
func bpfProgLoad(attr *bpfProgLoadAttr) (*bpfFD, error) {
type bpfObjGetNextIDAttr struct {
startID uint32
nextID uint32
openFlags uint32
}
func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) {
for {
fd, err := bpfCall(_ProgLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
fd, err := internal.BPF(_ProgLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
// As of ~4.20 the verifier can be interrupted by a signal,
// and returns EAGAIN in that case.
if err == unix.EAGAIN {
@@ -208,26 +175,75 @@ func bpfProgLoad(attr *bpfProgLoadAttr) (*bpfFD, error) {
return nil, err
}
return newBPFFD(uint32(fd)), nil
return internal.NewFD(uint32(fd)), nil
}
}
func bpfProgAlter(cmd int, attr *bpfProgAlterAttr) error {
_, err := bpfCall(cmd, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
_, err := internal.BPF(cmd, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
return err
}
func bpfMapCreate(attr *bpfMapCreateAttr) (*bpfFD, error) {
fd, err := bpfCall(_MapCreate, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
func bpfMapCreate(attr *bpfMapCreateAttr) (*internal.FD, error) {
fd, err := internal.BPF(_MapCreate, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
if xerrors.Is(err, os.ErrPermission) {
return nil, xerrors.New("permission denied or insufficient rlimit to lock memory for map")
}
if err != nil {
return nil, err
}
return newBPFFD(uint32(fd)), nil
return internal.NewFD(uint32(fd)), nil
}
func bpfMapLookupElem(m *bpfFD, key, valueOut syscallPtr) error {
fd, err := m.value()
var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() bool {
inner, err := bpfMapCreate(&bpfMapCreateAttr{
mapType: Array,
keySize: 4,
valueSize: 4,
maxEntries: 1,
})
if err != nil {
return false
}
defer inner.Close()
innerFd, _ := inner.Value()
nested, err := bpfMapCreate(&bpfMapCreateAttr{
mapType: ArrayOfMaps,
keySize: 4,
valueSize: 4,
maxEntries: 1,
innerMapFd: innerFd,
})
if err != nil {
return false
}
_ = nested.Close()
return true
})
var haveMapMutabilityModifiers = internal.FeatureTest("read- and write-only maps", "5.2", func() bool {
// This checks BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG. Since
// BPF_MAP_FREEZE appeared in 5.2 as well we don't do a separate check.
m, err := bpfMapCreate(&bpfMapCreateAttr{
mapType: Array,
keySize: 4,
valueSize: 4,
maxEntries: 1,
flags: unix.BPF_F_RDONLY_PROG,
})
if err != nil {
return false
}
_ = m.Close()
return true
})
func bpfMapLookupElem(m *internal.FD, key, valueOut internal.Pointer) error {
fd, err := m.Value()
if err != nil {
return err
}
@@ -237,12 +253,27 @@ func bpfMapLookupElem(m *bpfFD, key, valueOut syscallPtr) error {
key: key,
value: valueOut,
}
_, err = bpfCall(_MapLookupElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return err
_, err = internal.BPF(_MapLookupElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return wrapMapError(err)
}
func bpfMapUpdateElem(m *bpfFD, key, valueOut syscallPtr, flags uint64) error {
fd, err := m.value()
func bpfMapLookupAndDelete(m *internal.FD, key, valueOut internal.Pointer) error {
fd, err := m.Value()
if err != nil {
return err
}
attr := bpfMapOpAttr{
mapFd: fd,
key: key,
value: valueOut,
}
_, err = internal.BPF(_MapLookupAndDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return wrapMapError(err)
}
func bpfMapUpdateElem(m *internal.FD, key, valueOut internal.Pointer, flags uint64) error {
fd, err := m.Value()
if err != nil {
return err
}
@@ -253,12 +284,12 @@ func bpfMapUpdateElem(m *bpfFD, key, valueOut syscallPtr, flags uint64) error {
value: valueOut,
flags: flags,
}
_, err = bpfCall(_MapUpdateElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return err
_, err = internal.BPF(_MapUpdateElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return wrapMapError(err)
}
func bpfMapDeleteElem(m *bpfFD, key syscallPtr) error {
fd, err := m.value()
func bpfMapDeleteElem(m *internal.FD, key internal.Pointer) error {
fd, err := m.Value()
if err != nil {
return err
}
@@ -267,12 +298,12 @@ func bpfMapDeleteElem(m *bpfFD, key syscallPtr) error {
mapFd: fd,
key: key,
}
_, err = bpfCall(_MapDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return err
_, err = internal.BPF(_MapDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return wrapMapError(err)
}
func bpfMapGetNextKey(m *bpfFD, key, nextKeyOut syscallPtr) error {
fd, err := m.value()
func bpfMapGetNextKey(m *internal.FD, key, nextKeyOut internal.Pointer) error {
fd, err := m.Value()
if err != nil {
return err
}
@@ -282,46 +313,97 @@ func bpfMapGetNextKey(m *bpfFD, key, nextKeyOut syscallPtr) error {
key: key,
value: nextKeyOut,
}
_, err = bpfCall(_MapGetNextKey, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
_, err = internal.BPF(_MapGetNextKey, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return wrapMapError(err)
}
func objGetNextID(cmd int, start uint32) (uint32, error) {
attr := bpfObjGetNextIDAttr{
startID: start,
}
_, err := internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return attr.nextID, wrapObjError(err)
}
func wrapObjError(err error) error {
if err == nil {
return nil
}
if xerrors.Is(err, unix.ENOENT) {
return xerrors.Errorf("%w", ErrNotExist)
}
return xerrors.New(err.Error())
}
func wrapMapError(err error) error {
if err == nil {
return nil
}
if xerrors.Is(err, unix.ENOENT) {
return ErrKeyNotExist
}
if xerrors.Is(err, unix.EEXIST) {
return ErrKeyExist
}
return xerrors.New(err.Error())
}
func bpfMapFreeze(m *internal.FD) error {
fd, err := m.Value()
if err != nil {
return err
}
attr := bpfMapFreezeAttr{
mapFd: fd,
}
_, err = internal.BPF(_MapFreeze, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return err
}
const bpfFSType = 0xcafe4a11
func bpfPinObject(fileName string, fd *bpfFD) error {
func bpfPinObject(fileName string, fd *internal.FD) error {
dirName := filepath.Dir(fileName)
var statfs unix.Statfs_t
if err := unix.Statfs(dirName, &statfs); err != nil {
return err
}
if uint64(statfs.Type) != bpfFSType {
return errors.Errorf("%s is not on a bpf filesystem", fileName)
return xerrors.Errorf("%s is not on a bpf filesystem", fileName)
}
value, err := fd.value()
value, err := fd.Value()
if err != nil {
return err
}
_, err = bpfCall(_ObjPin, unsafe.Pointer(&bpfPinObjAttr{
fileName: newPtr(unsafe.Pointer(&[]byte(fileName)[0])),
_, err = internal.BPF(_ObjPin, unsafe.Pointer(&bpfPinObjAttr{
fileName: internal.NewStringPointer(fileName),
fd: value,
}), 16)
return errors.Wrapf(err, "pin object %s", fileName)
if err != nil {
return xerrors.Errorf("pin object %s: %w", fileName, err)
}
return nil
}
func bpfGetObject(fileName string) (*bpfFD, error) {
ptr, err := bpfCall(_ObjGet, unsafe.Pointer(&bpfPinObjAttr{
fileName: newPtr(unsafe.Pointer(&[]byte(fileName)[0])),
func bpfGetObject(fileName string) (*internal.FD, error) {
ptr, err := internal.BPF(_ObjGet, unsafe.Pointer(&bpfPinObjAttr{
fileName: internal.NewStringPointer(fileName),
}), 16)
if err != nil {
return nil, errors.Wrapf(err, "get object %s", fileName)
return nil, xerrors.Errorf("get object %s: %w", fileName, err)
}
return newBPFFD(uint32(ptr)), nil
return internal.NewFD(uint32(ptr)), nil
}
func bpfGetObjectInfoByFD(fd *bpfFD, info unsafe.Pointer, size uintptr) error {
value, err := fd.value()
func bpfGetObjectInfoByFD(fd *internal.FD, info unsafe.Pointer, size uintptr) error {
value, err := fd.Value()
if err != nil {
return err
}
@@ -330,91 +412,76 @@ func bpfGetObjectInfoByFD(fd *bpfFD, info unsafe.Pointer, size uintptr) error {
attr := bpfObjGetInfoByFDAttr{
fd: value,
infoLen: uint32(size),
info: newPtr(info),
info: internal.NewPointer(info),
}
_, err = bpfCall(_ObjGetInfoByFD, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return errors.Wrapf(err, "fd %d", value)
_, err = internal.BPF(_ObjGetInfoByFD, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
if err != nil {
return xerrors.Errorf("fd %d: %w", fd, err)
}
return nil
}
func bpfGetProgInfoByFD(fd *bpfFD) (*bpfProgInfo, error) {
func bpfGetProgInfoByFD(fd *internal.FD) (*bpfProgInfo, error) {
var info bpfProgInfo
err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info))
return &info, errors.Wrap(err, "can't get program info")
if err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil {
return nil, xerrors.Errorf("can't get program info: %w", err)
}
return &info, nil
}
func bpfGetMapInfoByFD(fd *bpfFD) (*bpfMapInfo, error) {
func bpfGetMapInfoByFD(fd *internal.FD) (*bpfMapInfo, error) {
var info bpfMapInfo
err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info))
return &info, errors.Wrap(err, "can't get map info:")
if err != nil {
return nil, xerrors.Errorf("can't get map info: %w", err)
}
return &info, nil
}
var haveObjName = featureTest{
Fn: func() bool {
name, err := newBPFObjName("feature_test")
if err != nil {
// This really is a fatal error, but it should be caught
// by the unit tests not working.
return false
}
var haveObjName = internal.FeatureTest("object names", "4.15", func() bool {
attr := bpfMapCreateAttr{
mapType: Array,
keySize: 4,
valueSize: 4,
maxEntries: 1,
mapName: newBPFObjName("feature_test"),
}
attr := bpfMapCreateAttr{
mapType: Array,
keySize: 4,
valueSize: 4,
maxEntries: 1,
mapName: name,
}
fd, err := bpfMapCreate(&attr)
if err != nil {
return false
}
fd, err := bpfMapCreate(&attr)
if err != nil {
return false
}
_ = fd.Close()
return true
})
_ = fd.close()
return true
},
}
var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() bool {
if err := haveObjName(); err != nil {
return false
}
func bpfGetMapFDByID(id uint32) (*bpfFD, error) {
// available from 4.13
attr := bpfMapCreateAttr{
mapType: Array,
keySize: 4,
valueSize: 4,
maxEntries: 1,
mapName: newBPFObjName(".test"),
}
fd, err := bpfMapCreate(&attr)
if err != nil {
return false
}
_ = fd.Close()
return true
})
func bpfObjGetFDByID(cmd int, id uint32) (*internal.FD, error) {
attr := bpfGetFDByIDAttr{
id: id,
}
ptr, err := bpfCall(_MapGetFDByID, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
if err != nil {
return nil, errors.Wrapf(err, "can't get fd for map id %d", id)
}
return newBPFFD(uint32(ptr)), nil
}
func bpfGetProgramFDByID(id uint32) (*bpfFD, error) {
// available from 4.13
attr := bpfGetFDByIDAttr{
id: id,
}
ptr, err := bpfCall(_ProgGetFDByID, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
if err != nil {
return nil, errors.Wrapf(err, "can't get fd for program id %d", id)
}
return newBPFFD(uint32(ptr)), nil
}
func bpfCall(cmd int, attr unsafe.Pointer, size uintptr) (uintptr, error) {
r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size)
runtime.KeepAlive(attr)
var err error
if errNo != 0 {
err = errNo
}
return r1, err
}
func convertCString(in []byte) string {
inLen := bytes.IndexByte(in, 0)
if inLen == -1 {
return ""
}
return string(in[:inLen])
ptr, err := internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return internal.NewFD(uint32(ptr)), wrapObjError(err)
}

View File

@@ -57,6 +57,30 @@ const (
// HashOfMaps - Each item in the hash map is another map. The inner map mustn't be a map of maps
// itself.
HashOfMaps
// DevMap - Specialized map to store references to network devices.
DevMap
// SockMap - Specialized map to store references to sockets.
SockMap
// CPUMap - Specialized map to store references to CPUs.
CPUMap
// XSKMap - Specialized map for XDP programs to store references to open sockets.
XSKMap
// SockHash - Specialized hash to store references to sockets.
SockHash
// CGroupStorage - Special map for CGroups.
CGroupStorage
// ReusePortSockArray - Specialized map to store references to sockets that can be reused.
ReusePortSockArray
// PerCPUCGroupStorage - Special per CPU map for CGroups.
PerCPUCGroupStorage
// Queue - FIFO storage for BPF programs.
Queue
// Stack - LIFO storage for BPF programs.
Stack
// SkStorage - Specialized map for local storage at SK for BPF programs.
SkStorage
// DevMapHash - Hash-based indexing scheme for references to network devices.
DevMapHash
)
// hasPerCPUValue returns true if the Map stores a value per CPU.
@@ -84,12 +108,14 @@ const (
_ProgGetFDByID
_MapGetFDByID
_ObjGetInfoByFD
)
const (
_Any = iota
_NoExist
_Exist
_ProgQuery
_RawTracepointOpen
_BTFLoad
_BTFGetFDByID
_TaskFDQuery
_MapLookupAndDeleteElem
_MapFreeze
_BTFGetNextID
)
// ProgramType of the eBPF program
@@ -149,6 +175,8 @@ const (
RawTracepointWritable
// CGroupSockopt program
CGroupSockopt
// Tracing program
Tracing
)
// AttachType of the eBPF program, needed to differentiate allowed context accesses in
@@ -183,6 +211,9 @@ const (
AttachCGroupUDP6Recvmsg
AttachCGroupGetsockopt
AttachCGroupSetsockopt
AttachTraceRawTp
AttachTraceFEntry
AttachTraceFExit
)
// AttachFlags of the eBPF program used in BPF_PROG_ATTACH command

View File

@@ -22,11 +22,23 @@ func _() {
_ = x[LPMTrie-11]
_ = x[ArrayOfMaps-12]
_ = x[HashOfMaps-13]
_ = x[DevMap-14]
_ = x[SockMap-15]
_ = x[CPUMap-16]
_ = x[XSKMap-17]
_ = x[SockHash-18]
_ = x[CGroupStorage-19]
_ = x[ReusePortSockArray-20]
_ = x[PerCPUCGroupStorage-21]
_ = x[Queue-22]
_ = x[Stack-23]
_ = x[SkStorage-24]
_ = x[DevMapHash-25]
}
const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMaps"
const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHash"
var _MapType_index = [...]uint8{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136}
var _MapType_index = [...]uint8{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136, 142, 149, 155, 161, 169, 182, 200, 219, 224, 229, 238, 248}
func (i MapType) String() string {
if i >= MapType(len(_MapType_index)-1) {
@@ -64,11 +76,12 @@ func _() {
_ = x[CGroupSysctl-23]
_ = x[RawTracepointWritable-24]
_ = x[CGroupSockopt-25]
_ = x[Tracing-26]
}
const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockopt"
const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracing"
var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258}
var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258, 265}
func (i ProgramType) String() string {
if i >= ProgramType(len(_ProgramType_index)-1) {

191
vendor/github.com/coreos/go-systemd/v22/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,191 @@
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:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
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
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.

5
vendor/github.com/coreos/go-systemd/v22/NOTICE generated vendored Normal file
View File

@@ -0,0 +1,5 @@
CoreOS Project
Copyright 2018 CoreOS, Inc
This product includes software developed at CoreOS, Inc.
(http://www.coreos.com/).

View File

@@ -10,10 +10,10 @@ go_library(
"subscription.go",
"subscription_set.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/coreos/go-systemd/dbus",
importpath = "github.com/coreos/go-systemd/dbus",
importmap = "k8s.io/kubernetes/vendor/github.com/coreos/go-systemd/v22/dbus",
importpath = "github.com/coreos/go-systemd/v22/dbus",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/godbus/dbus:go_default_library"],
deps = ["//vendor/github.com/godbus/dbus/v5:go_default_library"],
)
filegroup(

View File

@@ -23,7 +23,7 @@ import (
"strings"
"sync"
"github.com/godbus/dbus"
"github.com/godbus/dbus/v5"
)
const (

View File

@@ -20,7 +20,7 @@ import (
"path"
"strconv"
"github.com/godbus/dbus"
"github.com/godbus/dbus/v5"
)
func (c *Conn) jobComplete(signal *dbus.Signal) {
@@ -197,6 +197,12 @@ func (c *Conn) GetUnitPathProperties(path dbus.ObjectPath) (map[string]interface
return c.getProperties(path, "org.freedesktop.systemd1.Unit")
}
// GetAllProperties takes the (unescaped) unit name and returns all of its dbus object properties.
func (c *Conn) GetAllProperties(unit string) (map[string]interface{}, error) {
path := unitPath(unit)
return c.getProperties(path, "")
}
func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) {
var err error
var prop dbus.Variant

View File

@@ -15,7 +15,7 @@
package dbus
import (
"github.com/godbus/dbus"
"github.com/godbus/dbus/v5"
)
// From the systemd docs:
@@ -56,7 +56,7 @@ type execStart struct {
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
func PropExecStart(command []string, uncleanIsFailure bool) Property {
execStarts := []execStart{
execStart{
{
Path: command[0],
Args: command,
UncleanIsFailure: uncleanIsFailure,

View File

@@ -19,7 +19,7 @@ import (
"log"
"time"
"github.com/godbus/dbus"
"github.com/godbus/dbus/v5"
)
const (

View File

@@ -113,7 +113,7 @@ func SplitProtoPort(rawPort string) (string, string) {
}
func validateProto(proto string) bool {
for _, availableProto := range []string{"tcp", "udp"} {
for _, availableProto := range []string{"tcp", "udp", "sctp"} {
if availableProto == proto {
return true
}

View File

@@ -4,7 +4,6 @@ package tlsconfig
import (
"crypto/x509"
)
// SystemCertPool returns an new empty cert pool,

View File

@@ -46,8 +46,6 @@ var acceptedCBCCiphers = []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
}
// DefaultServerAcceptedCiphers should be uses by code which already has a crypto/tls
@@ -65,22 +63,34 @@ var allTLSVersions = map[uint16]struct{}{
}
// ServerDefault returns a secure-enough TLS configuration for the server TLS configuration.
func ServerDefault() *tls.Config {
return &tls.Config{
// Avoid fallback to SSL protocols < TLS1.0
MinVersion: tls.VersionTLS10,
func ServerDefault(ops ...func(*tls.Config)) *tls.Config {
tlsconfig := &tls.Config{
// Avoid fallback by default to SSL protocols < TLS1.2
MinVersion: tls.VersionTLS12,
PreferServerCipherSuites: true,
CipherSuites: DefaultServerAcceptedCiphers,
}
for _, op := range ops {
op(tlsconfig)
}
return tlsconfig
}
// ClientDefault returns a secure-enough TLS configuration for the client TLS configuration.
func ClientDefault() *tls.Config {
return &tls.Config{
func ClientDefault(ops ...func(*tls.Config)) *tls.Config {
tlsconfig := &tls.Config{
// Prefer TLS1.2 as the client minimum
MinVersion: tls.VersionTLS12,
CipherSuites: clientCipherSuites,
}
for _, op := range ops {
op(tlsconfig)
}
return tlsconfig
}
// certPool returns an X.509 certificate pool from `caFile`, the certificate file.

View File

@@ -1,46 +0,0 @@
dist: precise
language: go
go_import_path: github.com/godbus/dbus
sudo: true
go:
- 1.7.3
- 1.8.7
- 1.9.5
- 1.10.1
- tip
env:
global:
matrix:
- TARGET=amd64
- TARGET=arm64
- TARGET=arm
- TARGET=386
- TARGET=ppc64le
matrix:
fast_finish: true
allow_failures:
- go: tip
exclude:
- go: tip
env: TARGET=arm
- go: tip
env: TARGET=arm64
- go: tip
env: TARGET=386
- go: tip
env: TARGET=ppc64le
addons:
apt:
packages:
- dbus
- dbus-x11
before_install:
script:
- go test -v -race ./... # Run all the tests with the race detector enabled
- go vet ./... # go vet is the official Go static analyzer

View File

@@ -1,3 +0,0 @@
module github.com/godbus/dbus
go 1.12

50
vendor/github.com/godbus/dbus/v5/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,50 @@
dist: bionic
language: go
go_import_path: github.com/godbus/dbus
go:
- 1.11.x
- 1.12.x
- 1.13.x
- tip
matrix:
fast_finish: true
allow_failures:
- go: tip
addons:
apt:
packages:
- dbus
- dbus-x11
before_install:
- export GO111MODULE=on
script:
- go test -v -race -mod=readonly ./... # Run all the tests with the race detector enabled
- go vet ./... # go vet is the official Go static analyzer
jobs:
include:
# The build matrix doesn't cover build stages, so manually expand
# the jobs with anchors
- &multiarch
stage: "Multiarch Test"
go: 1.11.x
env: TARGETS="386 arm arm64 ppc64le"
before_install:
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
script:
- |
set -e
for target in $TARGETS; do
printf "\e[1mRunning test suite under ${target}.\e[0m\n"
GOARCH="$target" go test -v ./...
printf "\n\n"
done
- <<: *multiarch
go: 1.12.x
- <<: *multiarch
go: 1.13.x

View File

@@ -21,6 +21,7 @@ go_library(
"export.go",
"homedir.go",
"homedir_dynamic.go",
"match.go",
"message.go",
"object.go",
"server_interfaces.go",
@@ -39,8 +40,8 @@ go_library(
"variant_parser.go",
],
cgo = True,
importmap = "k8s.io/kubernetes/vendor/github.com/godbus/dbus",
importpath = "github.com/godbus/dbus",
importmap = "k8s.io/kubernetes/vendor/github.com/godbus/dbus/v5",
importpath = "github.com/godbus/dbus/v5",
visibility = ["//visibility:public"],
)

View File

@@ -77,7 +77,7 @@ func (conn *Conn) Auth(methods []Auth) error {
for _, m := range methods {
if name, data, status := m.FirstData(); bytes.Equal(v, name) {
var ok bool
err = authWriteLine(conn.transport, []byte("AUTH"), []byte(v), data)
err = authWriteLine(conn.transport, []byte("AUTH"), v, data)
if err != nil {
return err
}
@@ -127,7 +127,7 @@ func (conn *Conn) Auth(methods []Auth) error {
// tryAuth tries to authenticate with m as the mechanism, using state as the
// initial authState and in for reading input. It returns (nil, true) on
// success, (nil, false) on a REJECTED and (someErr, false) if some other
// error occured.
// error occurred.
func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, bool) {
for {
s, err := authReadLine(in)

View File

@@ -60,7 +60,7 @@ func (a authCookieSha1) HandleData(data []byte) ([]byte, AuthStatus) {
// getCookie searches for the cookie identified by id in context and returns
// the cookie content or nil. (Since HandleData can't return a specific error,
// but only whether an error occured, this function also doesn't bother to
// but only whether an error occurred, this function also doesn't bother to
// return an error.)
func (a authCookieSha1) getCookie(context, id []byte) []byte {
file, err := os.Open(a.home + "/.dbus-keyrings/" + string(context))

View File

@@ -5,7 +5,6 @@ import (
"errors"
"io"
"os"
"reflect"
"strings"
"sync"
)
@@ -31,6 +30,12 @@ var ErrClosed = errors.New("dbus: connection closed by user")
type Conn struct {
transport
ctx context.Context
cancelCtx context.CancelFunc
closeOnce sync.Once
closeErr error
busObj BusObject
unixFD bool
uuid string
@@ -38,6 +43,8 @@ type Conn struct {
handler Handler
signalHandler SignalHandler
serialGen SerialGenerator
inInt Interceptor
outInt Interceptor
names *nameTracker
calls *callTracker
@@ -190,6 +197,33 @@ func WithSerialGenerator(gen SerialGenerator) ConnOption {
}
}
// Interceptor intercepts incoming and outgoing messages.
type Interceptor func(msg *Message)
// WithIncomingInterceptor sets the given interceptor for incoming messages.
func WithIncomingInterceptor(interceptor Interceptor) ConnOption {
return func(conn *Conn) error {
conn.inInt = interceptor
return nil
}
}
// WithOutgoingInterceptor sets the given interceptor for outgoing messages.
func WithOutgoingInterceptor(interceptor Interceptor) ConnOption {
return func(conn *Conn) error {
conn.outInt = interceptor
return nil
}
}
// WithContext overrides the default context for the connection.
func WithContext(ctx context.Context) ConnOption {
return func(conn *Conn) error {
conn.ctx = ctx
return nil
}
}
// NewConn creates a new private *Conn from an already established connection.
func NewConn(conn io.ReadWriteCloser, opts ...ConnOption) (*Conn, error) {
return newConn(genericTransport{conn}, opts...)
@@ -211,6 +245,15 @@ func newConn(tr transport, opts ...ConnOption) (*Conn, error) {
return nil, err
}
}
if conn.ctx == nil {
conn.ctx = context.Background()
}
conn.ctx, conn.cancelCtx = context.WithCancel(conn.ctx)
go func() {
<-conn.ctx.Done()
conn.Close()
}()
conn.calls = newCallTracker()
if conn.handler == nil {
conn.handler = NewDefaultHandler()
@@ -237,27 +280,38 @@ func (conn *Conn) BusObject() BusObject {
// and the channels passed to Eavesdrop and Signal are closed. This method must
// not be called on shared connections.
func (conn *Conn) Close() error {
conn.outHandler.close()
if term, ok := conn.signalHandler.(Terminator); ok {
term.Terminate()
}
conn.closeOnce.Do(func() {
conn.outHandler.close()
if term, ok := conn.signalHandler.(Terminator); ok {
term.Terminate()
}
if term, ok := conn.handler.(Terminator); ok {
term.Terminate()
}
if term, ok := conn.handler.(Terminator); ok {
term.Terminate()
}
conn.eavesdroppedLck.Lock()
if conn.eavesdropped != nil {
close(conn.eavesdropped)
}
conn.eavesdroppedLck.Unlock()
conn.eavesdroppedLck.Lock()
if conn.eavesdropped != nil {
close(conn.eavesdropped)
}
conn.eavesdroppedLck.Unlock()
return conn.transport.Close()
conn.cancelCtx()
conn.closeErr = conn.transport.Close()
})
return conn.closeErr
}
// Context returns the context associated with the connection. The
// context will be cancelled when the connection is closed.
func (conn *Conn) Context() context.Context {
return conn.ctx
}
// Eavesdrop causes conn to send all incoming messages to the given channel
// without further processing. Method replies, errors and signals will not be
// sent to the appropiate channels and method calls will not be handled. If nil
// sent to the appropriate channels and method calls will not be handled. If nil
// is passed, the normal behaviour is restored.
//
// The caller has to make sure that ch is sufficiently buffered;
@@ -294,7 +348,7 @@ func (conn *Conn) inWorker() {
msg, err := conn.ReadMessage()
if err != nil {
if _, ok := err.(InvalidMessageError); !ok {
// Some read error occured (usually EOF); we can't really do
// Some read error occurred (usually EOF); we can't really do
// anything but to shut down all stuff and returns errors to all
// pending replies.
conn.Close()
@@ -323,6 +377,10 @@ func (conn *Conn) inWorker() {
// Ignore it.
continue
}
if conn.inInt != nil {
conn.inInt(msg)
}
switch msg.Type {
case TypeError:
conn.serialGen.RetireSerial(conn.calls.handleDBusError(msg))
@@ -383,11 +441,10 @@ func (conn *Conn) Object(dest string, path ObjectPath) BusObject {
return &Object{conn, dest, path}
}
func (conn *Conn) sendMessage(msg *Message) {
conn.sendMessageAndIfClosed(msg, func() {})
}
func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) {
if conn.outInt != nil {
conn.outInt(msg)
}
err := conn.outHandler.sendAndIfClosed(msg, ifClosed)
conn.calls.handleSendError(msg, err)
if err != nil {
@@ -483,7 +540,7 @@ func (conn *Conn) sendError(err error, dest string, serial uint32) {
if len(e.Body) > 0 {
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...))
}
conn.sendMessage(msg)
conn.sendMessageAndIfClosed(msg, nil)
}
// sendReply creates a method reply message corresponding to the parameters and
@@ -501,33 +558,54 @@ func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) {
if len(values) > 0 {
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
}
conn.sendMessage(msg)
conn.sendMessageAndIfClosed(msg, nil)
}
func (conn *Conn) defaultSignalAction(fn func(h *defaultSignalHandler, ch chan<- *Signal), ch chan<- *Signal) {
if !isDefaultSignalHandler(conn.signalHandler) {
return
}
handler := conn.signalHandler.(*defaultSignalHandler)
fn(handler, ch)
// AddMatchSignal registers the given match rule to receive broadcast
// signals based on their contents.
func (conn *Conn) AddMatchSignal(options ...MatchOption) error {
options = append([]MatchOption{withMatchType("signal")}, options...)
return conn.busObj.Call(
"org.freedesktop.DBus.AddMatch", 0,
formatMatchOptions(options),
).Store()
}
// RemoveMatchSignal removes the first rule that matches previously registered with AddMatchSignal.
func (conn *Conn) RemoveMatchSignal(options ...MatchOption) error {
options = append([]MatchOption{withMatchType("signal")}, options...)
return conn.busObj.Call(
"org.freedesktop.DBus.RemoveMatch", 0,
formatMatchOptions(options),
).Store()
}
// Signal registers the given channel to be passed all received signal messages.
// The caller has to make sure that ch is sufficiently buffered; if a message
// arrives when a write to c is not possible, it is discarded.
//
// Multiple of these channels can be registered at the same time.
//
// These channels are "overwritten" by Eavesdrop; i.e., if there currently is a
// channel for eavesdropped messages, this channel receives all signals, and
// none of the channels passed to Signal will receive any signals.
//
// Panics if the signal handler is not a `SignalRegistrar`.
func (conn *Conn) Signal(ch chan<- *Signal) {
conn.defaultSignalAction((*defaultSignalHandler).addSignal, ch)
handler, ok := conn.signalHandler.(SignalRegistrar)
if !ok {
panic("cannot use this method with a non SignalRegistrar handler")
}
handler.AddSignal(ch)
}
// RemoveSignal removes the given channel from the list of the registered channels.
//
// Panics if the signal handler is not a `SignalRegistrar`.
func (conn *Conn) RemoveSignal(ch chan<- *Signal) {
conn.defaultSignalAction((*defaultSignalHandler).removeSignal, ch)
handler, ok := conn.signalHandler.(SignalRegistrar)
if !ok {
panic("cannot use this method with a non SignalRegistrar handler")
}
handler.RemoveSignal(ch)
}
// SupportsUnixFDs returns whether the underlying transport supports passing of
@@ -614,18 +692,6 @@ func getTransport(address string) (transport, error) {
return nil, err
}
// dereferenceAll returns a slice that, assuming that vs is a slice of pointers
// of arbitrary types, containes the values that are obtained from dereferencing
// all elements in vs.
func dereferenceAll(vs []interface{}) []interface{} {
for i := range vs {
v := reflect.ValueOf(vs[i])
v = v.Elem()
vs[i] = v.Interface()
}
return vs
}
// getKey gets a key from a the list of keys. Returns "" on error / not found...
func getKey(s, key string) string {
for _, keyEqualsValue := range strings.Split(s, ",") {
@@ -650,7 +716,9 @@ func (h *outputHandler) sendAndIfClosed(msg *Message, ifClosed func()) error {
h.closed.lck.RLock()
defer h.closed.lck.RUnlock()
if h.closed.isClosed {
ifClosed()
if ifClosed != nil {
ifClosed()
}
return nil
}
h.sendLck.Lock()
@@ -801,7 +869,6 @@ func (tracker *callTracker) finalize(sn uint32) {
delete(tracker.calls, sn)
c.ContextCancel()
}
return
}
func (tracker *callTracker) finalizeWithBody(sn uint32, body []interface{}) {
@@ -815,7 +882,6 @@ func (tracker *callTracker) finalizeWithBody(sn uint32, body []interface{}) {
c.Body = body
c.done()
}
return
}
func (tracker *callTracker) finalizeWithError(sn uint32, err error) {
@@ -829,7 +895,6 @@ func (tracker *callTracker) finalizeWithError(sn uint32, err error) {
c.Err = err
c.done()
}
return
}
func (tracker *callTracker) finalizeAllWithError(err error) {

View File

@@ -4,7 +4,6 @@ package dbus
import (
"os"
"fmt"
)
const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket"
@@ -12,7 +11,7 @@ const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket"
func getSystemBusPlatformAddress() string {
address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
if address != "" {
return fmt.Sprintf("unix:path=%s", address)
return address
}
return defaultSystemBusAddress
}
}

View File

@@ -87,6 +87,7 @@ func setDest(dest, src reflect.Value) error {
}
if isVariant(src.Type()) && !isVariant(dest.Type()) {
src = getVariantValue(src)
return store(dest, src)
}
if !src.Type().ConvertibleTo(dest.Type()) {
return fmt.Errorf(

View File

@@ -47,7 +47,7 @@ func (h *defaultHandler) introspectPath(path ObjectPath) string {
subpath := make(map[string]struct{})
var xml bytes.Buffer
xml.WriteString("<node>")
for obj, _ := range h.objects {
for obj := range h.objects {
p := string(path)
if p != "/" {
p += "/"
@@ -57,7 +57,7 @@ func (h *defaultHandler) introspectPath(path ObjectPath) string {
subpath[node_name] = struct{}{}
}
}
for s, _ := range subpath {
for s := range subpath {
xml.WriteString("\n\t<node name=\"" + s + "\"/>")
}
xml.WriteString("\n</node>")
@@ -234,88 +234,95 @@ func (obj *exportedIntf) isFallbackInterface() bool {
//
// Deprecated: this is the default value, don't use it, it will be unexported.
func NewDefaultSignalHandler() *defaultSignalHandler {
return &defaultSignalHandler{
closeChan: make(chan struct{}),
}
}
func isDefaultSignalHandler(handler SignalHandler) bool {
_, ok := handler.(*defaultSignalHandler)
return ok
return &defaultSignalHandler{}
}
type defaultSignalHandler struct {
sync.RWMutex
closed bool
signals []chan<- *Signal
closeChan chan struct{}
mu sync.RWMutex
closed bool
signals []*signalChannelData
}
func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) {
sh.RLock()
defer sh.RUnlock()
sh.mu.RLock()
defer sh.mu.RUnlock()
if sh.closed {
return
}
for _, ch := range sh.signals {
select {
case ch <- signal:
case <-sh.closeChan:
return
default:
go func(ch chan<- *Signal) {
select {
case ch <- signal:
case <-sh.closeChan:
return
}
}(ch)
}
for _, scd := range sh.signals {
scd.deliver(signal)
}
}
func (sh *defaultSignalHandler) Init() error {
sh.Lock()
sh.signals = make([]chan<- *Signal, 0)
sh.closeChan = make(chan struct{})
sh.Unlock()
return nil
}
func (sh *defaultSignalHandler) Terminate() {
sh.Lock()
if !sh.closed {
close(sh.closeChan)
}
sh.closed = true
for _, ch := range sh.signals {
close(ch)
}
sh.signals = nil
sh.Unlock()
}
func (sh *defaultSignalHandler) addSignal(ch chan<- *Signal) {
sh.Lock()
defer sh.Unlock()
sh.mu.Lock()
defer sh.mu.Unlock()
if sh.closed {
return
}
sh.signals = append(sh.signals, ch)
for _, scd := range sh.signals {
scd.close()
close(scd.ch)
}
sh.closed = true
sh.signals = nil
}
func (sh *defaultSignalHandler) removeSignal(ch chan<- *Signal) {
sh.Lock()
defer sh.Unlock()
func (sh *defaultSignalHandler) AddSignal(ch chan<- *Signal) {
sh.mu.Lock()
defer sh.mu.Unlock()
if sh.closed {
return
}
sh.signals = append(sh.signals, &signalChannelData{
ch: ch,
done: make(chan struct{}),
})
}
func (sh *defaultSignalHandler) RemoveSignal(ch chan<- *Signal) {
sh.mu.Lock()
defer sh.mu.Unlock()
if sh.closed {
return
}
for i := len(sh.signals) - 1; i >= 0; i-- {
if ch == sh.signals[i] {
if ch == sh.signals[i].ch {
sh.signals[i].close()
copy(sh.signals[i:], sh.signals[i+1:])
sh.signals[len(sh.signals)-1] = nil
sh.signals = sh.signals[:len(sh.signals)-1]
}
}
}
type signalChannelData struct {
wg sync.WaitGroup
ch chan<- *Signal
done chan struct{}
}
func (scd *signalChannelData) deliver(signal *Signal) {
select {
case scd.ch <- signal:
case <-scd.done:
return
default:
scd.wg.Add(1)
go scd.deferredDeliver(signal)
}
}
func (scd *signalChannelData) deferredDeliver(signal *Signal) {
select {
case scd.ch <- signal:
case <-scd.done:
}
scd.wg.Done()
}
func (scd *signalChannelData) close() {
close(scd.done)
scd.wg.Wait() // wait until all spawned goroutines return
}

View File

@@ -61,7 +61,7 @@ Handling Unix file descriptors deserves special mention. To use them, you should
first check that they are supported on a connection by calling SupportsUnixFDs.
If it returns true, all method of Connection will translate messages containing
UnixFD's to messages that are accompanied by the given file descriptors with the
UnixFD values being substituted by the correct indices. Similarily, the indices
UnixFD values being substituted by the correct indices. Similarly, the indices
of incoming messages are automatically resolved. It shouldn't be necessary to use
UnixFDIndex.

View File

@@ -60,7 +60,7 @@ func (enc *encoder) binwrite(v interface{}) {
}
}
// Encode encodes the given values to the underyling reader. All written values
// Encode encodes the given values to the underlying reader. All written values
// are aligned properly as required by the D-Bus spec.
func (enc *encoder) Encode(vs ...interface{}) (err error) {
defer func() {

View File

@@ -171,7 +171,7 @@ func (conn *Conn) handleCall(msg *Message) {
}
reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...))
conn.sendMessage(reply)
conn.sendMessageAndIfClosed(reply, nil)
}
}

3
vendor/github.com/godbus/dbus/v5/go.mod generated vendored Normal file
View File

@@ -0,0 +1,3 @@
module github.com/godbus/dbus/v5
go 1.12

0
vendor/github.com/godbus/dbus/v5/go.sum generated vendored Normal file
View File

62
vendor/github.com/godbus/dbus/v5/match.go generated vendored Normal file
View File

@@ -0,0 +1,62 @@
package dbus
import (
"strings"
)
// MatchOption specifies option for dbus routing match rule. Options can be constructed with WithMatch* helpers.
// For full list of available options consult
// https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
type MatchOption struct {
key string
value string
}
func formatMatchOptions(options []MatchOption) string {
items := make([]string, 0, len(options))
for _, option := range options {
items = append(items, option.key+"='"+option.value+"'")
}
return strings.Join(items, ",")
}
// WithMatchOption creates match option with given key and value
func WithMatchOption(key, value string) MatchOption {
return MatchOption{key, value}
}
// doesn't make sense to export this option because clients can only
// subscribe to messages with signal type.
func withMatchType(typ string) MatchOption {
return WithMatchOption("type", typ)
}
// WithMatchSender sets sender match option.
func WithMatchSender(sender string) MatchOption {
return WithMatchOption("sender", sender)
}
// WithMatchSender sets interface match option.
func WithMatchInterface(iface string) MatchOption {
return WithMatchOption("interface", iface)
}
// WithMatchMember sets member match option.
func WithMatchMember(member string) MatchOption {
return WithMatchOption("member", member)
}
// WithMatchObjectPath creates match option that filters events based on given path
func WithMatchObjectPath(path ObjectPath) MatchOption {
return WithMatchOption("path", string(path))
}
// WithMatchPathNamespace sets path_namespace match option.
func WithMatchPathNamespace(namespace ObjectPath) MatchOption {
return WithMatchOption("path_namespace", string(namespace))
}
// WithMatchDestination sets destination match option.
func WithMatchDestination(destination string) MatchOption {
return WithMatchOption("destination", destination)
}

View File

@@ -38,41 +38,16 @@ func (o *Object) CallWithContext(ctx context.Context, method string, flags Flags
return <-o.createCall(ctx, method, flags, make(chan *Call, 1), args...).Done
}
// MatchOption specifies option for dbus routing match rule. Options can be constructed with WithMatch* helpers.
// For full list of available options consult
// https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
type MatchOption struct {
key string
value string
}
// WithMatchOption creates match option with given key and value
func WithMatchOption(key, value string) MatchOption {
return MatchOption{key, value}
}
// WithMatchObjectPath creates match option that filters events based on given path
func WithMatchObjectPath(path ObjectPath) MatchOption {
return MatchOption{"path", string(path)}
}
func formatMatchOptions(options []MatchOption) string {
items := make([]string, 0, len(options))
for _, option := range options {
items = append(items, option.key+"='"+option.value+"'")
}
return strings.Join(items, ",")
}
// AddMatchSignal subscribes BusObject to signals from specified interface,
// method (member). Additional filter rules can be added via WithMatch* option constructors.
// Note: To filter events by object path you have to specify this path via an option.
//
// Deprecated: use (*Conn) AddMatchSignal instead.
func (o *Object) AddMatchSignal(iface, member string, options ...MatchOption) *Call {
base := []MatchOption{
{"type", "signal"},
{"interface", iface},
{"member", member},
withMatchType("signal"),
WithMatchInterface(iface),
WithMatchMember(member),
}
options = append(base, options...)
@@ -85,11 +60,13 @@ func (o *Object) AddMatchSignal(iface, member string, options ...MatchOption) *C
// RemoveMatchSignal unsubscribes BusObject from signals from specified interface,
// method (member). Additional filter rules can be added via WithMatch* option constructors
//
// Deprecated: use (*Conn) RemoveMatchSignal instead.
func (o *Object) RemoveMatchSignal(iface, member string, options ...MatchOption) *Call {
base := []MatchOption{
{"type", "signal"},
{"interface", iface},
{"member", member},
withMatchType("signal"),
WithMatchInterface(iface),
WithMatchMember(member),
}
options = append(base, options...)

View File

@@ -77,6 +77,14 @@ type SignalHandler interface {
DeliverSignal(iface, name string, signal *Signal)
}
// SignalRegistrar manages signal delivery channels.
//
// This is an optional set of methods for `SignalHandler`.
type SignalRegistrar interface {
AddSignal(ch chan<- *Signal)
RemoveSignal(ch chan<- *Signal)
}
// A DBusError is used to convert a generic object to a D-Bus error.
//
// Any custom error mechanism may implement this interface to provide

Some files were not shown because too many files have changed in this diff Show More