github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/examples/map_in_map/main.go (about) 1 // An example of using maps within maps. This example demonstrates a few 2 // features. Firstly, creating eBPF map specifications in pure Go 3 // (typically you'd see them being generated from a loaded ELF). 4 // Additionally, creating maps and placing them in other maps (with 5 // dynamically sized inner maps). 6 package main 7 8 import ( 9 "log" 10 "math/rand" 11 "time" 12 13 "github.com/cilium/ebpf" 14 "github.com/cilium/ebpf/rlimit" 15 ) 16 17 const BPF_F_INNER_MAP = 0x1000 18 19 func main() { 20 // Allow the current process to lock memory for eBPF resources. 21 if err := rlimit.RemoveMemlock(); err != nil { 22 log.Fatal(err) 23 } 24 25 // We're creating a map spec in pure Go here, but a map spec like 26 // this can be loaded from an ELF too. 27 outerMapSpec := ebpf.MapSpec{ 28 Name: "outer_map", 29 Type: ebpf.ArrayOfMaps, 30 KeySize: 4, // 4 bytes for u32 31 ValueSize: 4, 32 MaxEntries: 5, // We'll have 5 maps inside this map 33 Contents: make([]ebpf.MapKV, 5), 34 InnerMap: &ebpf.MapSpec{ 35 Name: "inner_map", 36 Type: ebpf.Array, 37 KeySize: 4, // 4 bytes for u32 38 ValueSize: 4, // 4 bytes for u32 39 40 // This flag is required for dynamically sized inner maps. 41 // Added in linux 5.10. 42 Flags: BPF_F_INNER_MAP, 43 44 // We set this to 1 now, but this inner map spec gets copied 45 // and altered later. 46 MaxEntries: 1, 47 }, 48 } 49 50 r := rand.New(rand.NewSource(time.Now().UnixNano())) 51 52 // For each entry we want to create in the outer map... 53 for i := uint32(0); i < outerMapSpec.MaxEntries; i++ { 54 // Copy the inner map spec 55 innerMapSpec := outerMapSpec.InnerMap.Copy() 56 57 // Randomly generate inner map length 58 innerMapSpec.MaxEntries = uint32(r.Intn(50) + 1) // Can't be zero. 59 60 // populate the inner map contents 61 innerMapSpec.Contents = make([]ebpf.MapKV, innerMapSpec.MaxEntries) 62 63 for j := range innerMapSpec.Contents { 64 innerMapSpec.Contents[uint32(j)] = ebpf.MapKV{Key: uint32(j), Value: uint32(0xCAFE)} 65 } 66 67 // Create the inner map 68 innerMap, err := ebpf.NewMap(innerMapSpec) 69 if err != nil { 70 log.Fatalf("inner_map: %v", err) 71 } 72 // In this example we close all references to maps before exit. 73 // But typically you may actually want to hold on to the map 74 // reference so that you control the lifecycle of the map. For 75 // the inner (nested) map though, it's safe to close the file 76 // descriptor in userspace once the outer map holds a reference 77 // in the kernel. 78 defer innerMap.Close() 79 80 // Inner map is created successfully and lives in the kernel, 81 // let's add it to the contents of the outer map spec. 82 outerMapSpec.Contents[i] = ebpf.MapKV{Key: i, Value: innerMap} 83 } 84 85 // All inner maps are created and inserted into the outer map spec, 86 // time to create the outer map. 87 outerMap, err := ebpf.NewMap(&outerMapSpec) 88 if err != nil { 89 log.Fatalf("outer_map: %v", err) 90 } 91 defer outerMap.Close() 92 93 // The outer map is created successfully and lives happily in the 94 // kernel. Let's iterate over the map in the kernel to see what's 95 // been made. 96 mapIter := outerMap.Iterate() 97 var outerMapKey uint32 98 var innerMapID ebpf.MapID 99 for mapIter.Next(&outerMapKey, &innerMapID) { 100 // With maps that contain maps, performing a lookup doesn't give 101 // you the map directly, instead it gives you an ID, which you 102 // can then use to get a full map pointer. 103 innerMap, err := ebpf.NewMapFromID(innerMapID) 104 if err != nil { 105 log.Fatal(err) 106 } 107 108 innerMapInfo, err := innerMap.Info() 109 if err != nil { 110 log.Fatal(err) 111 } 112 113 log.Printf("outerMapKey %d, innerMap.Info: %+v", outerMapKey, innerMapInfo) 114 } 115 }