mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-03 23:40:03 +00:00 
			
		
		
		
	when the systemd cgroup manager is used, controllers not handled by systemd are created manually afterwards. libcontainer didn't correctly cleanup these cgroups that were leaked on cgroup v1. Closes: https://github.com/kubernetes/kubernetes/issues/92766 Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
		
			
				
	
	
		
			439 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			439 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package ebpf
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
	"unsafe"
 | 
						|
 | 
						|
	"github.com/cilium/ebpf/internal"
 | 
						|
	"github.com/cilium/ebpf/internal/btf"
 | 
						|
	"github.com/cilium/ebpf/internal/unix"
 | 
						|
)
 | 
						|
 | 
						|
// Generic errors returned by BPF syscalls.
 | 
						|
var (
 | 
						|
	ErrNotExist = errors.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 {
 | 
						|
	var result bpfObjName
 | 
						|
	copy(result[:unix.BPF_OBJ_NAME_LEN-1], name)
 | 
						|
	return result
 | 
						|
}
 | 
						|
 | 
						|
func invalidBPFObjNameChar(char rune) bool {
 | 
						|
	dotAllowed := objNameAllowsDot() == nil
 | 
						|
 | 
						|
	switch {
 | 
						|
	case char >= 'A' && char <= 'Z':
 | 
						|
		fallthrough
 | 
						|
	case char >= 'a' && char <= 'z':
 | 
						|
		fallthrough
 | 
						|
	case char >= '0' && char <= '9':
 | 
						|
		fallthrough
 | 
						|
	case dotAllowed && char == '.':
 | 
						|
		fallthrough
 | 
						|
	case char == '_':
 | 
						|
		return false
 | 
						|
	default:
 | 
						|
		return true
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
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
 | 
						|
	mapIfIndex     uint32
 | 
						|
	btfFd          uint32
 | 
						|
	btfKeyTypeID   btf.TypeID
 | 
						|
	btfValueTypeID btf.TypeID
 | 
						|
}
 | 
						|
 | 
						|
type bpfMapOpAttr struct {
 | 
						|
	mapFd   uint32
 | 
						|
	padding uint32
 | 
						|
	key     internal.Pointer
 | 
						|
	value   internal.Pointer
 | 
						|
	flags   uint64
 | 
						|
}
 | 
						|
 | 
						|
type bpfMapInfo struct {
 | 
						|
	mapType    uint32
 | 
						|
	id         uint32
 | 
						|
	keySize    uint32
 | 
						|
	valueSize  uint32
 | 
						|
	maxEntries uint32
 | 
						|
	flags      uint32
 | 
						|
	mapName    bpfObjName // since 4.15 ad5b177bd73f
 | 
						|
}
 | 
						|
 | 
						|
type bpfProgLoadAttr struct {
 | 
						|
	progType           ProgramType
 | 
						|
	insCount           uint32
 | 
						|
	instructions       internal.Pointer
 | 
						|
	license            internal.Pointer
 | 
						|
	logLevel           uint32
 | 
						|
	logSize            uint32
 | 
						|
	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
 | 
						|
	attachBTFID        btf.TypeID
 | 
						|
	attachProgFd       uint32
 | 
						|
}
 | 
						|
 | 
						|
type bpfProgInfo struct {
 | 
						|
	progType     uint32
 | 
						|
	id           uint32
 | 
						|
	tag          [unix.BPF_TAG_SIZE]byte
 | 
						|
	jitedLen     uint32
 | 
						|
	xlatedLen    uint32
 | 
						|
	jited        internal.Pointer
 | 
						|
	xlated       internal.Pointer
 | 
						|
	loadTime     uint64 // since 4.15 cb4d2b3f03d8
 | 
						|
	createdByUID uint32
 | 
						|
	nrMapIDs     uint32
 | 
						|
	mapIds       internal.Pointer
 | 
						|
	name         bpfObjName
 | 
						|
}
 | 
						|
 | 
						|
type bpfProgTestRunAttr struct {
 | 
						|
	fd          uint32
 | 
						|
	retval      uint32
 | 
						|
	dataSizeIn  uint32
 | 
						|
	dataSizeOut uint32
 | 
						|
	dataIn      internal.Pointer
 | 
						|
	dataOut     internal.Pointer
 | 
						|
	repeat      uint32
 | 
						|
	duration    uint32
 | 
						|
}
 | 
						|
 | 
						|
type bpfObjGetInfoByFDAttr struct {
 | 
						|
	fd      uint32
 | 
						|
	infoLen uint32
 | 
						|
	info    internal.Pointer // May be either bpfMapInfo or bpfProgInfo
 | 
						|
}
 | 
						|
 | 
						|
type bpfGetFDByIDAttr struct {
 | 
						|
	id   uint32
 | 
						|
	next uint32
 | 
						|
}
 | 
						|
 | 
						|
type bpfMapFreezeAttr struct {
 | 
						|
	mapFd uint32
 | 
						|
}
 | 
						|
 | 
						|
type bpfObjGetNextIDAttr struct {
 | 
						|
	startID   uint32
 | 
						|
	nextID    uint32
 | 
						|
	openFlags uint32
 | 
						|
}
 | 
						|
 | 
						|
func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) {
 | 
						|
	for {
 | 
						|
		fd, err := internal.BPF(internal.BPF_PROG_LOAD, 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 {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		return internal.NewFD(uint32(fd)), nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func bpfProgTestRun(attr *bpfProgTestRunAttr) error {
 | 
						|
	_, err := internal.BPF(internal.BPF_PROG_TEST_RUN, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func bpfMapCreate(attr *bpfMapCreateAttr) (*internal.FD, error) {
 | 
						|
	fd, err := internal.BPF(internal.BPF_MAP_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
 | 
						|
	if errors.Is(err, os.ErrPermission) {
 | 
						|
		return nil, errors.New("permission denied or insufficient rlimit to lock memory for map")
 | 
						|
	}
 | 
						|
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return internal.NewFD(uint32(fd)), nil
 | 
						|
}
 | 
						|
 | 
						|
var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() (bool, error) {
 | 
						|
	inner, err := bpfMapCreate(&bpfMapCreateAttr{
 | 
						|
		mapType:    Array,
 | 
						|
		keySize:    4,
 | 
						|
		valueSize:  4,
 | 
						|
		maxEntries: 1,
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		return false, err
 | 
						|
	}
 | 
						|
	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, nil
 | 
						|
	}
 | 
						|
 | 
						|
	_ = nested.Close()
 | 
						|
	return true, nil
 | 
						|
})
 | 
						|
 | 
						|
var haveMapMutabilityModifiers = internal.FeatureTest("read- and write-only maps", "5.2", func() (bool, error) {
 | 
						|
	// 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, nil
 | 
						|
	}
 | 
						|
	_ = m.Close()
 | 
						|
	return true, nil
 | 
						|
})
 | 
						|
 | 
						|
func bpfMapLookupElem(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(internal.BPF_MAP_LOOKUP_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
 | 
						|
	return wrapMapError(err)
 | 
						|
}
 | 
						|
 | 
						|
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(internal.BPF_MAP_LOOKUP_AND_DELETE_ELEM, 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
 | 
						|
	}
 | 
						|
 | 
						|
	attr := bpfMapOpAttr{
 | 
						|
		mapFd: fd,
 | 
						|
		key:   key,
 | 
						|
		value: valueOut,
 | 
						|
		flags: flags,
 | 
						|
	}
 | 
						|
	_, err = internal.BPF(internal.BPF_MAP_UPDATE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
 | 
						|
	return wrapMapError(err)
 | 
						|
}
 | 
						|
 | 
						|
func bpfMapDeleteElem(m *internal.FD, key internal.Pointer) error {
 | 
						|
	fd, err := m.Value()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	attr := bpfMapOpAttr{
 | 
						|
		mapFd: fd,
 | 
						|
		key:   key,
 | 
						|
	}
 | 
						|
	_, err = internal.BPF(internal.BPF_MAP_DELETE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
 | 
						|
	return wrapMapError(err)
 | 
						|
}
 | 
						|
 | 
						|
func bpfMapGetNextKey(m *internal.FD, key, nextKeyOut internal.Pointer) error {
 | 
						|
	fd, err := m.Value()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	attr := bpfMapOpAttr{
 | 
						|
		mapFd: fd,
 | 
						|
		key:   key,
 | 
						|
		value: nextKeyOut,
 | 
						|
	}
 | 
						|
	_, err = internal.BPF(internal.BPF_MAP_GET_NEXT_KEY, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
 | 
						|
	return wrapMapError(err)
 | 
						|
}
 | 
						|
 | 
						|
func objGetNextID(cmd internal.BPFCmd, 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 errors.Is(err, unix.ENOENT) {
 | 
						|
		return fmt.Errorf("%w", ErrNotExist)
 | 
						|
	}
 | 
						|
 | 
						|
	return errors.New(err.Error())
 | 
						|
}
 | 
						|
 | 
						|
func wrapMapError(err error) error {
 | 
						|
	if err == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	if errors.Is(err, unix.ENOENT) {
 | 
						|
		return ErrKeyNotExist
 | 
						|
	}
 | 
						|
 | 
						|
	if errors.Is(err, unix.EEXIST) {
 | 
						|
		return ErrKeyExist
 | 
						|
	}
 | 
						|
 | 
						|
	return errors.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(internal.BPF_MAP_FREEZE, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func bpfGetObjectInfoByFD(fd *internal.FD, info unsafe.Pointer, size uintptr) error {
 | 
						|
	value, err := fd.Value()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	// available from 4.13
 | 
						|
	attr := bpfObjGetInfoByFDAttr{
 | 
						|
		fd:      value,
 | 
						|
		infoLen: uint32(size),
 | 
						|
		info:    internal.NewPointer(info),
 | 
						|
	}
 | 
						|
	_, err = internal.BPF(internal.BPF_OBJ_GET_INFO_BY_FD, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("fd %d: %w", fd, err)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func bpfGetProgInfoByFD(fd *internal.FD) (*bpfProgInfo, error) {
 | 
						|
	var info bpfProgInfo
 | 
						|
	if err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil {
 | 
						|
		return nil, fmt.Errorf("can't get program info: %w", err)
 | 
						|
	}
 | 
						|
	return &info, nil
 | 
						|
}
 | 
						|
 | 
						|
func bpfGetMapInfoByFD(fd *internal.FD) (*bpfMapInfo, error) {
 | 
						|
	var info bpfMapInfo
 | 
						|
	err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info))
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("can't get map info: %w", err)
 | 
						|
	}
 | 
						|
	return &info, nil
 | 
						|
}
 | 
						|
 | 
						|
var haveObjName = internal.FeatureTest("object names", "4.15", func() (bool, error) {
 | 
						|
	attr := bpfMapCreateAttr{
 | 
						|
		mapType:    Array,
 | 
						|
		keySize:    4,
 | 
						|
		valueSize:  4,
 | 
						|
		maxEntries: 1,
 | 
						|
		mapName:    newBPFObjName("feature_test"),
 | 
						|
	}
 | 
						|
 | 
						|
	fd, err := bpfMapCreate(&attr)
 | 
						|
	if err != nil {
 | 
						|
		return false, nil
 | 
						|
	}
 | 
						|
 | 
						|
	_ = fd.Close()
 | 
						|
	return true, nil
 | 
						|
})
 | 
						|
 | 
						|
var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() (bool, error) {
 | 
						|
	if err := haveObjName(); err != nil {
 | 
						|
		return false, err
 | 
						|
	}
 | 
						|
 | 
						|
	attr := bpfMapCreateAttr{
 | 
						|
		mapType:    Array,
 | 
						|
		keySize:    4,
 | 
						|
		valueSize:  4,
 | 
						|
		maxEntries: 1,
 | 
						|
		mapName:    newBPFObjName(".test"),
 | 
						|
	}
 | 
						|
 | 
						|
	fd, err := bpfMapCreate(&attr)
 | 
						|
	if err != nil {
 | 
						|
		return false, nil
 | 
						|
	}
 | 
						|
 | 
						|
	_ = fd.Close()
 | 
						|
	return true, nil
 | 
						|
})
 | 
						|
 | 
						|
func bpfObjGetFDByID(cmd internal.BPFCmd, id uint32) (*internal.FD, error) {
 | 
						|
	attr := bpfGetFDByIDAttr{
 | 
						|
		id: id,
 | 
						|
	}
 | 
						|
	ptr, err := internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
 | 
						|
	return internal.NewFD(uint32(ptr)), wrapObjError(err)
 | 
						|
}
 |