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  }