Repository: cilium/ebpf
Pull request #41 (2020-01-03 11:06:44)

Push/pop/peek
This PR adds the bpfMapLookupAndDelete functionality - which only is implemented for stacks and queues on kernel side [1].

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/bpf/syscall.c#n1219

florianl: Reading your points, I agree with you to keep the API more stable. So I will update the PR accordingly.
florianl: Thanks for your review!
i've squashed the commits :)

Patch:
diff --git a/map.go b/map.go
index 1c09418..86eff1a 100644
--- a/map.go
+++ b/map.go
@@ -274,6 +274,22 @@ func (m *Map) Lookup(key, valueOut interface{}) error {
 	}
 }
 
+// LookupAndDelete retrieves and deletes a value from a Map.
+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 errors.WithMessage(err, "can't marshal key")
+	}
+
+	if err := bpfMapLookupAndDelete(m.fd, keyPtr, valuePtr); err != nil {
+		return errors.WithMessage(err, "lookup and delete and delete failed")
+	}
+
+	return unmarshalBytes(valueOut, valueBytes)
+}
+
 // LookupBytes gets a value from Map.
 //
 // Returns a nil value if a key doesn't exist.
diff --git a/marshalers.go b/marshalers.go
index 197f153..64b7d4d 100644
--- a/marshalers.go
+++ b/marshalers.go
@@ -14,6 +14,13 @@ import (
 )
 
 func marshalPtr(data interface{}, length int) (internal.Pointer, error) {
+	if data == nil {
+		if length == 0 {
+			return internal.NewPointer(nil), nil
+		}
+		return internal.Pointer{}, errors.New("can't use nil as key of map")
+	}
+
 	if ptr, ok := data.(unsafe.Pointer); ok {
 		return internal.NewPointer(ptr), nil
 	}
diff --git a/syscalls.go b/syscalls.go
index 20ce05d..70f8c12 100644
--- a/syscalls.go
+++ b/syscalls.go
@@ -5,8 +5,8 @@ import (
 	"strings"
 	"unsafe"
 
-	"github.com/cilium/ebpf/internal/btf"
 	"github.com/cilium/ebpf/internal"
+	"github.com/cilium/ebpf/internal/btf"
 	"github.com/cilium/ebpf/internal/unix"
 
 	"github.com/pkg/errors"
@@ -222,6 +222,21 @@ func bpfMapLookupElem(m *internal.FD, key, valueOut internal.Pointer) error {
 	return 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(_MapLookupAndDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
+	return err
+}
+
 func bpfMapUpdateElem(m *internal.FD, key, valueOut internal.Pointer, flags uint64) error {
 	fd, err := m.Value()
 	if err != nil {
diff --git a/types.go b/types.go
index 35f7183..6a0228d 100644
--- a/types.go
+++ b/types.go
@@ -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.
diff --git a/types_string.go b/types_string.go
index 4813437..f41ba77 100644
--- a/types_string.go
+++ b/types_string.go
@@ -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) {


Test patch:
diff --git a/map_test.go b/map_test.go
index f15bd2d..e71dd1c 100644
--- a/map_test.go
+++ b/map_test.go
@@ -144,6 +144,63 @@ func createArray(t *testing.T) *Map {
 	return m
 }
 
+func TestMapQueue(t *testing.T) {
+
+	testutils.SkipOnOldKernel(t, "4.20", "map type queue")
+
+	m, err := NewMap(&MapSpec{
+		Type:       Queue,
+		ValueSize:  4,
+		MaxEntries: 2,
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer m.Close()
+
+	if err := m.Put(nil, uint32(42)); err != nil {
+		t.Fatal("Can't put 42:", err)
+	}
+
+	if err := m.Put(nil, uint32(4242)); err != nil {
+		t.Fatal("Can't put 4242:", err)
+	}
+
+	var v uint32
+	if err := m.Lookup(nil, &v); err != nil {
+		t.Fatal("Can't lookup element:", err)
+	}
+	if v != 42 {
+		t.Error("Want value 42, got", v)
+	}
+
+	v = 0
+	if err := m.LookupAndDelete(nil, &v); err != nil {
+		t.Fatal("Can't lookup and delete element:", err)
+	}
+	if v != 42 {
+		t.Error("Want value 42, got", v)
+	}
+
+	if err := m.Lookup(nil, &v); err != nil {
+		t.Fatal("Can't lookup element:", err)
+	}
+	if v != 4242 {
+		t.Error("Want value 4242, got", v)
+	}
+	v = 0
+	if err := m.LookupAndDelete(nil, &v); err != nil {
+		t.Fatal("Can't lookup and delete element:", err)
+	}
+	if v != 4242 {
+		t.Error("Want value 4242, got", v)
+	}
+
+	if err := m.LookupAndDelete(nil, &v); !IsNotExist(err) {
+		t.Fatal("Lookup and delete on empty Queue:", err)
+	}
+}
+
 func TestMapInMap(t *testing.T) {
 	for _, typ := range []MapType{ArrayOfMaps, HashOfMaps} {
 		t.Run(typ.String(), func(t *testing.T) {