github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/features/map.go (about)

     1  package features
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"unsafe"
     8  
     9  	"github.com/cilium/ebpf"
    10  	"github.com/cilium/ebpf/internal"
    11  	"github.com/cilium/ebpf/internal/sys"
    12  	"github.com/cilium/ebpf/internal/unix"
    13  )
    14  
    15  // HaveMapType probes the running kernel for the availability of the specified map type.
    16  //
    17  // See the package documentation for the meaning of the error return value.
    18  func HaveMapType(mt ebpf.MapType) error {
    19  	return haveMapTypeMatrix.Result(mt)
    20  }
    21  
    22  func probeCgroupStorageMap(mt sys.MapType) error {
    23  	// keySize needs to be sizeof(struct{u32 + u64}) = 12 (+ padding = 16)
    24  	// by using unsafe.Sizeof(int) we are making sure that this works on 32bit and 64bit archs
    25  	return createMap(&sys.MapCreateAttr{
    26  		MapType:    mt,
    27  		ValueSize:  4,
    28  		KeySize:    uint32(8 + unsafe.Sizeof(int(0))),
    29  		MaxEntries: 0,
    30  	})
    31  }
    32  
    33  func probeStorageMap(mt sys.MapType) error {
    34  	// maxEntries needs to be 0
    35  	// BPF_F_NO_PREALLOC needs to be set
    36  	// btf* fields need to be set
    37  	// see alloc_check for local_storage map types
    38  	err := createMap(&sys.MapCreateAttr{
    39  		MapType:        mt,
    40  		KeySize:        4,
    41  		ValueSize:      4,
    42  		MaxEntries:     0,
    43  		MapFlags:       unix.BPF_F_NO_PREALLOC,
    44  		BtfKeyTypeId:   1,
    45  		BtfValueTypeId: 1,
    46  		BtfFd:          ^uint32(0),
    47  	})
    48  	if errors.Is(err, unix.EBADF) {
    49  		// Triggered by BtfFd.
    50  		return nil
    51  	}
    52  	return err
    53  }
    54  
    55  func probeNestedMap(mt sys.MapType) error {
    56  	// assign invalid innerMapFd to pass validation check
    57  	// will return EBADF
    58  	err := probeMap(&sys.MapCreateAttr{
    59  		MapType:    mt,
    60  		InnerMapFd: ^uint32(0),
    61  	})
    62  	if errors.Is(err, unix.EBADF) {
    63  		return nil
    64  	}
    65  	return err
    66  }
    67  
    68  func probeMap(attr *sys.MapCreateAttr) error {
    69  	if attr.KeySize == 0 {
    70  		attr.KeySize = 4
    71  	}
    72  	if attr.ValueSize == 0 {
    73  		attr.ValueSize = 4
    74  	}
    75  	attr.MaxEntries = 1
    76  	return createMap(attr)
    77  }
    78  
    79  func createMap(attr *sys.MapCreateAttr) error {
    80  	fd, err := sys.MapCreate(attr)
    81  	if err == nil {
    82  		fd.Close()
    83  		return nil
    84  	}
    85  
    86  	switch {
    87  	// EINVAL occurs when attempting to create a map with an unknown type.
    88  	// E2BIG occurs when MapCreateAttr contains non-zero bytes past the end
    89  	// of the struct known by the running kernel, meaning the kernel is too old
    90  	// to support the given map type.
    91  	case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):
    92  		return ebpf.ErrNotSupported
    93  	}
    94  
    95  	return err
    96  }
    97  
    98  var haveMapTypeMatrix = internal.FeatureMatrix[ebpf.MapType]{
    99  	ebpf.Hash:           {Version: "3.19"},
   100  	ebpf.Array:          {Version: "3.19"},
   101  	ebpf.ProgramArray:   {Version: "4.2"},
   102  	ebpf.PerfEventArray: {Version: "4.3"},
   103  	ebpf.PerCPUHash:     {Version: "4.6"},
   104  	ebpf.PerCPUArray:    {Version: "4.6"},
   105  	ebpf.StackTrace: {
   106  		Version: "4.6",
   107  		Fn: func() error {
   108  			return probeMap(&sys.MapCreateAttr{
   109  				MapType:   sys.BPF_MAP_TYPE_STACK_TRACE,
   110  				ValueSize: 8, // sizeof(uint64)
   111  			})
   112  		},
   113  	},
   114  	ebpf.CGroupArray: {Version: "4.8"},
   115  	ebpf.LRUHash:     {Version: "4.10"},
   116  	ebpf.LRUCPUHash:  {Version: "4.10"},
   117  	ebpf.LPMTrie: {
   118  		Version: "4.11",
   119  		Fn: func() error {
   120  			// keySize and valueSize need to be sizeof(struct{u32 + u8}) + 1 + padding = 8
   121  			// BPF_F_NO_PREALLOC needs to be set
   122  			return probeMap(&sys.MapCreateAttr{
   123  				MapType:   sys.BPF_MAP_TYPE_LPM_TRIE,
   124  				KeySize:   8,
   125  				ValueSize: 8,
   126  				MapFlags:  unix.BPF_F_NO_PREALLOC,
   127  			})
   128  		},
   129  	},
   130  	ebpf.ArrayOfMaps: {
   131  		Version: "4.12",
   132  		Fn:      func() error { return probeNestedMap(sys.BPF_MAP_TYPE_ARRAY_OF_MAPS) },
   133  	},
   134  	ebpf.HashOfMaps: {
   135  		Version: "4.12",
   136  		Fn:      func() error { return probeNestedMap(sys.BPF_MAP_TYPE_HASH_OF_MAPS) },
   137  	},
   138  	ebpf.DevMap:   {Version: "4.14"},
   139  	ebpf.SockMap:  {Version: "4.14"},
   140  	ebpf.CPUMap:   {Version: "4.15"},
   141  	ebpf.XSKMap:   {Version: "4.18"},
   142  	ebpf.SockHash: {Version: "4.18"},
   143  	ebpf.CGroupStorage: {
   144  		Version: "4.19",
   145  		Fn:      func() error { return probeCgroupStorageMap(sys.BPF_MAP_TYPE_CGROUP_STORAGE) },
   146  	},
   147  	ebpf.ReusePortSockArray: {Version: "4.19"},
   148  	ebpf.PerCPUCGroupStorage: {
   149  		Version: "4.20",
   150  		Fn:      func() error { return probeCgroupStorageMap(sys.BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) },
   151  	},
   152  	ebpf.Queue: {
   153  		Version: "4.20",
   154  		Fn: func() error {
   155  			return createMap(&sys.MapCreateAttr{
   156  				MapType:    sys.BPF_MAP_TYPE_QUEUE,
   157  				KeySize:    0,
   158  				ValueSize:  4,
   159  				MaxEntries: 1,
   160  			})
   161  		},
   162  	},
   163  	ebpf.Stack: {
   164  		Version: "4.20",
   165  		Fn: func() error {
   166  			return createMap(&sys.MapCreateAttr{
   167  				MapType:    sys.BPF_MAP_TYPE_STACK,
   168  				KeySize:    0,
   169  				ValueSize:  4,
   170  				MaxEntries: 1,
   171  			})
   172  		},
   173  	},
   174  	ebpf.SkStorage: {
   175  		Version: "5.2",
   176  		Fn:      func() error { return probeStorageMap(sys.BPF_MAP_TYPE_SK_STORAGE) },
   177  	},
   178  	ebpf.DevMapHash: {Version: "5.4"},
   179  	ebpf.StructOpsMap: {
   180  		Version: "5.6",
   181  		Fn: func() error {
   182  			// StructOps requires setting a vmlinux type id, but id 1 will always
   183  			// resolve to some type of integer. This will cause ENOTSUPP.
   184  			err := probeMap(&sys.MapCreateAttr{
   185  				MapType:               sys.BPF_MAP_TYPE_STRUCT_OPS,
   186  				BtfVmlinuxValueTypeId: 1,
   187  			})
   188  			if errors.Is(err, sys.ENOTSUPP) {
   189  				// ENOTSUPP means the map type is at least known to the kernel.
   190  				return nil
   191  			}
   192  			return err
   193  		},
   194  	},
   195  	ebpf.RingBuf: {
   196  		Version: "5.8",
   197  		Fn: func() error {
   198  			// keySize and valueSize need to be 0
   199  			// maxEntries needs to be power of 2 and PAGE_ALIGNED
   200  			return createMap(&sys.MapCreateAttr{
   201  				MapType:    sys.BPF_MAP_TYPE_RINGBUF,
   202  				KeySize:    0,
   203  				ValueSize:  0,
   204  				MaxEntries: uint32(os.Getpagesize()),
   205  			})
   206  		},
   207  	},
   208  	ebpf.InodeStorage: {
   209  		Version: "5.10",
   210  		Fn:      func() error { return probeStorageMap(sys.BPF_MAP_TYPE_INODE_STORAGE) },
   211  	},
   212  	ebpf.TaskStorage: {
   213  		Version: "5.11",
   214  		Fn:      func() error { return probeStorageMap(sys.BPF_MAP_TYPE_TASK_STORAGE) },
   215  	},
   216  }
   217  
   218  func init() {
   219  	for mt, ft := range haveMapTypeMatrix {
   220  		ft.Name = mt.String()
   221  		if ft.Fn == nil {
   222  			// Avoid referring to the loop variable in the closure.
   223  			mt := sys.MapType(mt)
   224  			ft.Fn = func() error { return probeMap(&sys.MapCreateAttr{MapType: mt}) }
   225  		}
   226  	}
   227  }
   228  
   229  // MapFlags document which flags may be feature probed.
   230  type MapFlags = sys.MapFlags
   231  
   232  // Flags which may be feature probed.
   233  const (
   234  	BPF_F_NO_PREALLOC = sys.BPF_F_NO_PREALLOC
   235  	BPF_F_RDONLY_PROG = sys.BPF_F_RDONLY_PROG
   236  	BPF_F_WRONLY_PROG = sys.BPF_F_WRONLY_PROG
   237  	BPF_F_MMAPABLE    = sys.BPF_F_MMAPABLE
   238  	BPF_F_INNER_MAP   = sys.BPF_F_INNER_MAP
   239  )
   240  
   241  // HaveMapFlag probes the running kernel for the availability of the specified map flag.
   242  //
   243  // Returns an error if flag is not one of the flags declared in this package.
   244  // See the package documentation for the meaning of the error return value.
   245  func HaveMapFlag(flag MapFlags) (err error) {
   246  	return haveMapFlagsMatrix.Result(flag)
   247  }
   248  
   249  func probeMapFlag(attr *sys.MapCreateAttr) error {
   250  	// For now, we do not check if the map type is supported because we only support
   251  	// probing for flags defined on arrays and hashes that are always supported.
   252  	// In the future, if we allow probing on flags defined on newer types, checking for map type
   253  	// support will be required.
   254  	if attr.MapType == sys.BPF_MAP_TYPE_UNSPEC {
   255  		attr.MapType = sys.BPF_MAP_TYPE_ARRAY
   256  	}
   257  
   258  	attr.KeySize = 4
   259  	attr.ValueSize = 4
   260  	attr.MaxEntries = 1
   261  
   262  	fd, err := sys.MapCreate(attr)
   263  	if err == nil {
   264  		fd.Close()
   265  	} else if errors.Is(err, unix.EINVAL) {
   266  		// EINVAL occurs when attempting to create a map with an unknown type or an unknown flag.
   267  		err = ebpf.ErrNotSupported
   268  	}
   269  
   270  	return err
   271  }
   272  
   273  var haveMapFlagsMatrix = internal.FeatureMatrix[MapFlags]{
   274  	BPF_F_NO_PREALLOC: {
   275  		Version: "4.6",
   276  		Fn: func() error {
   277  			return probeMapFlag(&sys.MapCreateAttr{
   278  				MapType:  sys.BPF_MAP_TYPE_HASH,
   279  				MapFlags: BPF_F_NO_PREALLOC,
   280  			})
   281  		},
   282  	},
   283  	BPF_F_RDONLY_PROG: {
   284  		Version: "5.2",
   285  		Fn: func() error {
   286  			return probeMapFlag(&sys.MapCreateAttr{
   287  				MapFlags: BPF_F_RDONLY_PROG,
   288  			})
   289  		},
   290  	},
   291  	BPF_F_WRONLY_PROG: {
   292  		Version: "5.2",
   293  		Fn: func() error {
   294  			return probeMapFlag(&sys.MapCreateAttr{
   295  				MapFlags: BPF_F_WRONLY_PROG,
   296  			})
   297  		},
   298  	},
   299  	BPF_F_MMAPABLE: {
   300  		Version: "5.5",
   301  		Fn: func() error {
   302  			return probeMapFlag(&sys.MapCreateAttr{
   303  				MapFlags: BPF_F_MMAPABLE,
   304  			})
   305  		},
   306  	},
   307  	BPF_F_INNER_MAP: {
   308  		Version: "5.10",
   309  		Fn: func() error {
   310  			return probeMapFlag(&sys.MapCreateAttr{
   311  				MapFlags: BPF_F_INNER_MAP,
   312  			})
   313  		},
   314  	},
   315  }
   316  
   317  func init() {
   318  	for mf, ft := range haveMapFlagsMatrix {
   319  		ft.Name = fmt.Sprint(mf)
   320  	}
   321  }