diff --git a/go.mod b/go.mod index fb60076..4ee9665 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/stretchr/testify v1.9.0 github.com/vishvananda/netlink v1.1.0 go.uber.org/zap v1.27.0 - golang.org/x/sys v0.20.0 + golang.org/x/sys v0.24.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) @@ -16,8 +16,5 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/mod v0.4.2 // indirect - golang.org/x/tools v0.1.1 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 534eec3..aa5a4b5 100644 --- a/go.sum +++ b/go.sum @@ -18,7 +18,6 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -31,18 +30,16 @@ golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/maps/loader.go b/pkg/maps/loader.go index 6af24f5..13a1541 100644 --- a/pkg/maps/loader.go +++ b/pkg/maps/loader.go @@ -168,11 +168,11 @@ type BpfMapAPIs interface { // Get map value GetMapEntry(key, value uintptr) error // Update multiple map entries - BulkUpdateMapEntry(keyvalue map[uintptr]uintptr) error + BulkUpdateMapEntry(keyvalue map[string][]byte) error // Delete multiple map entries BulkDeleteMapEntry(keyvalue map[uintptr]uintptr) error // Wrapper for delete and update map entries - BulkRefreshMapEntries(newMapContents map[string]uintptr) error + BulkRefreshMapEntries(newMapContents map[string][]byte) error // Retrieve map info from pin path GetMapFromPinPath(pinPath string) (BpfMapInfo, error) } @@ -456,9 +456,12 @@ func (m *BpfMap) BulkDeleteMapEntry(keyvalue map[uintptr]uintptr) error { return nil } -func (m *BpfMap) BulkUpdateMapEntry(keyvalue map[uintptr]uintptr) error { +func (m *BpfMap) BulkUpdateMapEntry(keyvalue map[string][]byte) error { for k, v := range keyvalue { - err := m.UpdateMapEntry(k, v) + keyByte := []byte(k) + keyPtr := uintptr(unsafe.Pointer(&keyByte[0])) + valuePtr := uintptr(unsafe.Pointer(&v[0])) + err := m.UpdateMapEntry(keyPtr, valuePtr) if err != nil { log.Infof("One of the element update failed hence returning from bulk update") return err @@ -468,33 +471,23 @@ func (m *BpfMap) BulkUpdateMapEntry(keyvalue map[uintptr]uintptr) error { return nil } -func (m *BpfMap) BulkRefreshMapEntries(newMapContents map[string]uintptr) error { - - // 1. Construct i/p to bulkMap - keyvaluePtr := make(map[uintptr]uintptr) - - for k, v := range newMapContents { - keyByte := []byte(k) - log.Infof("Converted string to bytearray %v", keyByte) - keyPtr := uintptr(unsafe.Pointer(&keyByte[0])) - keyvaluePtr[keyPtr] = v - } +func (m *BpfMap) BulkRefreshMapEntries(newMapContents map[string][]byte) error { - // 2. Update all map entries - err := m.BulkUpdateMapEntry(keyvaluePtr) + // 1. Update all map entries + err := m.BulkUpdateMapEntry(newMapContents) if err != nil { log.Errorf("refresh map failed: during update %v", err) return err } - // 3. Read all map entries + // 2. Read all map entries retrievedMapKeyList, err := m.GetAllMapKeys() if err != nil { log.Errorf("get all map keys failed: during Refresh %v", err) return err } - // 4. Delete stale Keys + // 3. Delete stale Keys log.Infof("Check for stale entries and got %d entries from BPF map", len(retrievedMapKeyList)) for _, key := range retrievedMapKeyList { log.Infof("Checking if key %s is deletable", key) diff --git a/pkg/maps/mocks/ebpf_mocks.go b/pkg/maps/mocks/ebpf_mocks.go index 58dfed5..1586b3c 100644 --- a/pkg/maps/mocks/ebpf_mocks.go +++ b/pkg/maps/mocks/ebpf_mocks.go @@ -49,7 +49,7 @@ func (mr *MockBpfMapAPIsMockRecorder) BulkDeleteMapEntry(arg0 interface{}) *gomo } // BulkRefreshMapEntries mocks base method. -func (m *MockBpfMapAPIs) BulkRefreshMapEntries(arg0 map[string]uintptr) error { +func (m *MockBpfMapAPIs) BulkRefreshMapEntries(arg0 map[string][]byte) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BulkRefreshMapEntries", arg0) ret0, _ := ret[0].(error) @@ -63,7 +63,7 @@ func (mr *MockBpfMapAPIsMockRecorder) BulkRefreshMapEntries(arg0 interface{}) *g } // BulkUpdateMapEntry mocks base method. -func (m *MockBpfMapAPIs) BulkUpdateMapEntry(arg0 map[uintptr]uintptr) error { +func (m *MockBpfMapAPIs) BulkUpdateMapEntry(arg0 map[string][]byte) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BulkUpdateMapEntry", arg0) ret0, _ := ret[0].(error) diff --git a/test/main.go b/test/main.go index 338254c..697fef0 100644 --- a/test/main.go +++ b/test/main.go @@ -1,7 +1,9 @@ package main import ( + "encoding/binary" "fmt" + "net" "os" "strings" "syscall" @@ -60,6 +62,8 @@ func main() { {Name: "Test loading Maps without Program", Func: TestLoadMapWithNoProg}, {Name: "Test loading Map operations", Func: TestMapOperations}, {Name: "Test updating Map size", Func: TestLoadMapWithCustomSize}, + {Name: "Test bulk Map operations", Func: TestBulkMapOperations}, + {Name: "Test bulk refresh Map operations", Func: TestBulkRefreshMapOperations}, } testSummary := make(map[string]string) @@ -340,3 +344,143 @@ func TestLoadMapWithCustomSize() error { return nil } + +func TestBulkMapOperations() error { + gosdkClient := goelf.New() + _, loadedMap, err := gosdkClient.LoadBpfFile("c/test-map.bpf.elf", "operations") + if err != nil { + fmt.Println("Load BPF failed", "err:", err) + return err + } + + for mapName, _ := range loadedMap { + fmt.Println("Map Info: ", "Name: ", mapName) + } + + type BPFInetTrieKey struct { + Prefixlen uint32 + Addr [4]byte + } + + const numEntries = 32 * 1000 // 32K entries + + // Create 32K entries + mapToUpdate, ok := loadedMap["ingress_map"] + if !ok { + return fmt.Errorf("map 'ingress_map' not found") + } + + for i := 0; i < numEntries; i++ { + dummykey := BPFInetTrieKey{ + Prefixlen: 32, + Addr: [4]byte{byte(192 + i/256), byte(168 + (i/256)%256), byte(i % 256), 0}, + } + dummyvalue := uint32(40) + + err = mapToUpdate.CreateMapEntry(uintptr(unsafe.Pointer(&dummykey)), uintptr(unsafe.Pointer(&dummyvalue))) + if err != nil { + fmt.Println("Unable to Insert into eBPF map: ", err) + return err + } + } + fmt.Println("Created 32K entries successfully") + + // Update 32K entries + for i := 0; i < numEntries; i++ { + dummykey := BPFInetTrieKey{ + Prefixlen: 32, + Addr: [4]byte{byte(192 + i/256), byte(168 + (i/256)%256), byte(i % 256), 0}, + } + dummyvalue := uint32(20) + + err = mapToUpdate.UpdateMapEntry(uintptr(unsafe.Pointer(&dummykey)), uintptr(unsafe.Pointer(&dummyvalue))) + if err != nil { + fmt.Println("Unable to Update into eBPF map: ", err) + return err + } + } + fmt.Println("Updated 32K entries successfully") + + return nil +} + +func ComputeTrieKey(n net.IPNet) []byte { + prefixLen, _ := n.Mask.Size() + key := make([]byte, 8) + + // Set the prefix length + key[0] = byte(prefixLen) + + // Set the IP address + copy(key[4:], n.IP.To4()) + + fmt.Printf("Key: %v\n", key) + return key +} + +type BPFInetTrieKey struct { + Prefixlen uint32 + Addr [4]byte +} + +func bpfInetTrieKeyToIPNet(key BPFInetTrieKey) net.IPNet { + ip := net.IPv4(key.Addr[0], key.Addr[1], key.Addr[2], key.Addr[3]) + return net.IPNet{ + IP: ip, + Mask: net.CIDRMask(int(key.Prefixlen), 32), + } +} + +func TestBulkRefreshMapOperations() error { + gosdkClient := goelf.New() + _, loadedMap, err := gosdkClient.LoadBpfFile("c/test-map.bpf.elf", "operations") + if err != nil { + fmt.Println("Load BPF failed", "err:", err) + return err + } + + for mapName, _ := range loadedMap { + fmt.Println("Map Info: ", "Name: ", mapName) + } + + const numEntries = 32 * 1000 // 32K entries + // Create 32K entries + mapToUpdate, ok := loadedMap["ingress_map"] + if !ok { + return fmt.Errorf("map 'ingress_map' not found") + } + + newMapContents := make(map[string][]byte, numEntries) + for i := 0; i < numEntries; i++ { + dummykey := BPFInetTrieKey{ + Prefixlen: 32, + Addr: [4]byte{byte(1 + i/65536), byte(0 + (i/256)%256), byte(i % 256), 0}, + } + dummyvalue := uint32(40) + + err = mapToUpdate.CreateMapEntry(uintptr(unsafe.Pointer(&dummykey)), uintptr(unsafe.Pointer(&dummyvalue))) + if err != nil { + fmt.Println("Unable to Insert into eBPF map: ", err) + return err + } + dummyvalue = uint32(50) + ipnet := bpfInetTrieKeyToIPNet(dummykey) + fmt.Println(ipnet) + keyByte := ComputeTrieKey(ipnet) + dummyValueByteArray := make([]byte, 4) + binary.LittleEndian.PutUint32(dummyValueByteArray, dummyvalue) + newMapContents[string(keyByte)] = dummyValueByteArray + + } + fmt.Println("Created 32K entries successfully") + + // Update 32K entries + err = mapToUpdate.BulkRefreshMapEntries(newMapContents) + if err != nil { + fmt.Println("Unable to Bulk Refresh eBPF map: ", err) + return err + } + fmt.Println("Updated 32K entries successfully") + + return nil +}