github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/iterator_test.go (about)

     1  package gobpfld
     2  
     3  import (
     4  	"math/rand"
     5  	"testing"
     6  	"unsafe"
     7  
     8  	"github.com/dylandreimerink/gobpfld/bpfsys"
     9  	"github.com/dylandreimerink/gobpfld/bpftypes"
    10  	"github.com/dylandreimerink/gobpfld/kernelsupport"
    11  )
    12  
    13  var sizeOfmapIterTestKey = uint32(unsafe.Sizeof(mapIterTestKey{}))
    14  
    15  type mapIterTestKey struct {
    16  	A uint32
    17  	B uint64
    18  }
    19  
    20  var sizeOfmapIterTestValue = uint32(unsafe.Sizeof(mapIterTestValue{}))
    21  
    22  type mapIterTestValue struct {
    23  	C uint64
    24  	D byte
    25  }
    26  
    27  func testHashMapIteratorHappyPath(t *testing.T, hashMap *HashMap, iter MapIterator, maxEntries int) {
    28  	err := hashMap.Load()
    29  	if err != nil {
    30  		t.Fatal(err)
    31  	}
    32  	defer hashMap.Close()
    33  
    34  	// Just a simple go map used to verify BPF map behavior.
    35  	verificationMap := make(map[mapIterTestKey]mapIterTestValue, maxEntries)
    36  
    37  	// Fill the whole map verification map
    38  	for len(verificationMap) < maxEntries {
    39  		key := mapIterTestKey{
    40  			A: rand.Uint32(),
    41  			B: rand.Uint64(),
    42  		}
    43  
    44  		_, ok := verificationMap[key]
    45  		if !ok {
    46  			verificationMap[key] = mapIterTestValue{
    47  				C: rand.Uint64(),
    48  				D: byte(rand.Intn(255)),
    49  			}
    50  		}
    51  	}
    52  
    53  	// Copy verification map to BPF map, so contents are identical
    54  	for k, v := range verificationMap {
    55  		hashMap.Set(&k, &v, bpfsys.BPFMapElemAny)
    56  	}
    57  
    58  	var k mapIterTestKey
    59  	var v mapIterTestValue
    60  	err = iter.Init(&k, &v)
    61  	if err != nil {
    62  		t.Fatal(err)
    63  	}
    64  
    65  	for {
    66  		updated, err := iter.Next()
    67  		if err != nil {
    68  			t.Fatal(err)
    69  		}
    70  		if !updated {
    71  			break
    72  		}
    73  
    74  		verifyValue, ok := verificationMap[k]
    75  		if !ok {
    76  			t.Fatalf("key %v doesn't exist in verification map", k)
    77  		}
    78  		if verifyValue.C != v.C || verifyValue.D != v.D {
    79  			t.Fatalf("value %v from BPF map doesn't match value in verification map %v", k, verifyValue)
    80  		}
    81  
    82  		// Delete key from verification map, so we know we have read it exactly once
    83  		delete(verificationMap, k)
    84  	}
    85  
    86  	if len(verificationMap) > 0 {
    87  		t.Fatalf("not all values in verification map have been read, %d still left", len(verificationMap))
    88  	}
    89  }
    90  
    91  func TestSingleLookupIteratorHappyPath(t *testing.T) {
    92  	const maxEntries = 128
    93  	hashMap := HashMap{
    94  		AbstractMap: AbstractMap{
    95  			Name: MustNewObjName("test"),
    96  			Definition: BPFMapDef{
    97  				Type:       bpftypes.BPF_MAP_TYPE_HASH,
    98  				KeySize:    sizeOfmapIterTestKey,
    99  				ValueSize:  sizeOfmapIterTestValue,
   100  				MaxEntries: maxEntries,
   101  			},
   102  		},
   103  	}
   104  	iter := &singleLookupIterator{
   105  		BPFMap: &hashMap,
   106  	}
   107  
   108  	testHashMapIteratorHappyPath(t, &hashMap, iter, maxEntries)
   109  }
   110  
   111  func TestBatchLookupIteratorHappyPath(t *testing.T) {
   112  	if !kernelsupport.CurrentFeatures.API.Has(kernelsupport.KFeatAPIMapBatchOps) {
   113  		t.Skip("Skip because the feature is not supported by current kernel version")
   114  	}
   115  
   116  	const maxEntries = 128
   117  	hashMap := HashMap{
   118  		AbstractMap: AbstractMap{
   119  			Name: MustNewObjName("test"),
   120  			Definition: BPFMapDef{
   121  				Type:       bpftypes.BPF_MAP_TYPE_HASH,
   122  				KeySize:    sizeOfmapIterTestKey,
   123  				ValueSize:  sizeOfmapIterTestValue,
   124  				MaxEntries: maxEntries,
   125  			},
   126  		},
   127  	}
   128  	iter := &batchLookupIterator{
   129  		BPFMap: &hashMap,
   130  	}
   131  
   132  	testHashMapIteratorHappyPath(t, &hashMap, iter, maxEntries)
   133  }
   134  
   135  func TestIterForEachHappyPath(t *testing.T) {
   136  	const maxEntries = 128
   137  	arrayMap := ArrayMap{
   138  		AbstractMap: AbstractMap{
   139  			Name: MustNewObjName("test"),
   140  			Definition: BPFMapDef{
   141  				Type:       bpftypes.BPF_MAP_TYPE_ARRAY,
   142  				KeySize:    4, // Array maps always have 32bit keys (4 bytes)
   143  				ValueSize:  sizeOfmapIterTestValue,
   144  				MaxEntries: maxEntries,
   145  			},
   146  		},
   147  	}
   148  
   149  	err := arrayMap.Load()
   150  	if err != nil {
   151  		t.Fatal(err)
   152  	}
   153  	defer arrayMap.Close()
   154  
   155  	// Just a simple go map used to verify BPF map behavior.
   156  	verificationMap := make([]mapIterTestValue, maxEntries)
   157  
   158  	// Fill the whole map verification map
   159  	for i := 0; i < maxEntries; i++ {
   160  		verificationMap[i] = mapIterTestValue{
   161  			C: rand.Uint64(),
   162  			D: byte(rand.Intn(255)),
   163  		}
   164  	}
   165  
   166  	// Copy verification map to BPF map, so contents are identical
   167  	for k, v := range verificationMap {
   168  		err := arrayMap.Set(uint32(k), &v, bpfsys.BPFMapElemAny)
   169  		if err != nil {
   170  			t.Fatal(err)
   171  		}
   172  	}
   173  
   174  	readMap := make([]bool, len(verificationMap))
   175  
   176  	var k uint32
   177  	var v mapIterTestValue
   178  	MapIterForEach(arrayMap.Iterator(), &k, &v, func(key, value interface{}) error {
   179  		if int(k) >= len(verificationMap) {
   180  			t.Fatalf("key %v doesn't exist in verification map", k)
   181  		}
   182  		verifyValue := verificationMap[k]
   183  		if verifyValue.C != v.C || verifyValue.D != v.D {
   184  			t.Fatalf("value at %d %v from BPF map doesn't match value in verification map %v", k, v, verifyValue)
   185  		}
   186  		if readMap[k] {
   187  			t.Fatalf("double read of same key %d", k)
   188  		}
   189  
   190  		readMap[k] = true
   191  
   192  		return nil
   193  	})
   194  
   195  	for k, v := range readMap {
   196  		if !v {
   197  			t.Fatalf("key %d was not read", k)
   198  		}
   199  	}
   200  }
   201  
   202  func testArrayMapIteratorHappyPath(t *testing.T, arrayMap *ArrayMap, iter MapIterator, maxEntries int) {
   203  	err := arrayMap.Load()
   204  	if err != nil {
   205  		t.Fatal(err)
   206  	}
   207  	defer arrayMap.Close()
   208  
   209  	// Just a simple go map used to verify BPF map behavior.
   210  	verificationMap := make([]mapIterTestValue, maxEntries)
   211  
   212  	// Fill the whole map verification map
   213  	for i := 0; i < maxEntries; i++ {
   214  		verificationMap[i] = mapIterTestValue{
   215  			C: rand.Uint64(),
   216  			D: byte(rand.Intn(255)),
   217  		}
   218  	}
   219  
   220  	// Copy verification map to BPF map, so contents are identical
   221  	for k, v := range verificationMap {
   222  		arrayMap.Set(uint32(k), &v, bpfsys.BPFMapElemAny)
   223  	}
   224  
   225  	var k uint32
   226  	var v mapIterTestValue
   227  	err = iter.Init(&k, &v)
   228  	if err != nil {
   229  		t.Fatal(err)
   230  	}
   231  
   232  	readMap := make([]bool, len(verificationMap))
   233  
   234  	for {
   235  		updated, err := iter.Next()
   236  		if err != nil {
   237  			t.Fatal(err)
   238  		}
   239  		if !updated {
   240  			break
   241  		}
   242  
   243  		if int(k) >= len(verificationMap) {
   244  			t.Fatalf("key %v doesn't exist in verification map", k)
   245  		}
   246  		verifyValue := verificationMap[k]
   247  		if verifyValue.C != v.C || verifyValue.D != v.D {
   248  			t.Fatalf("value at %d %v from BPF map doesn't match value in verification map %v", k, v, verifyValue)
   249  		}
   250  		if readMap[k] {
   251  			t.Fatalf("double read of same key %d", k)
   252  		}
   253  
   254  		readMap[k] = true
   255  	}
   256  
   257  	for k, v := range readMap {
   258  		if !v {
   259  			t.Fatalf("key %d was not read", k)
   260  		}
   261  	}
   262  }
   263  
   264  func TestMMappedIteratorHappyPath(t *testing.T) {
   265  	if !kernelsupport.CurrentFeatures.API.Has(kernelsupport.KFeatAPIMapMMap) {
   266  		t.Skip("Skip because the feature is not supported by current kernel version")
   267  	}
   268  
   269  	const maxEntries = 128
   270  	arrayMap := ArrayMap{
   271  		AbstractMap: AbstractMap{
   272  			Name: MustNewObjName("test"),
   273  			Definition: BPFMapDef{
   274  				Type:       bpftypes.BPF_MAP_TYPE_ARRAY,
   275  				KeySize:    4, // Array maps always have 32bit keys (4 bytes)
   276  				ValueSize:  sizeOfmapIterTestValue,
   277  				MaxEntries: maxEntries,
   278  				Flags:      bpftypes.BPFMapFlagsMMapable, // Must be loaded as mmappable
   279  			},
   280  		},
   281  	}
   282  	iter := &mmappedIterator{
   283  		am: &arrayMap,
   284  	}
   285  
   286  	testArrayMapIteratorHappyPath(t, &arrayMap, iter, maxEntries)
   287  }
   288  
   289  func TestSingleMapIteratorHappyPath(t *testing.T) {
   290  	if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapArrayOfMaps) {
   291  		t.Skip("Skip because the feature is not supported by current kernel version")
   292  	}
   293  
   294  	const maxEntries = 8
   295  	arrayOfMaps := &ArrayOfMapsMap{
   296  		AbstractMap: AbstractMap{
   297  			Name: MustNewObjName("test"),
   298  			Definition: BPFMapDef{
   299  				Type:       bpftypes.BPF_MAP_TYPE_ARRAY_OF_MAPS,
   300  				KeySize:    4, // Array maps always have 32bit keys (4 bytes)
   301  				ValueSize:  4, // FD's is always 32bit (4 bytes)
   302  				MaxEntries: maxEntries,
   303  			},
   304  		},
   305  	}
   306  	iter := &singleMapLookupIterator{
   307  		BPFMap: arrayOfMaps,
   308  	}
   309  
   310  	// Just a simple go map used to verify BPF map behavior.
   311  	verificationMap := make([]BPFMap, maxEntries)
   312  
   313  	innerDef := BPFMapDef{
   314  		Type:       bpftypes.BPF_MAP_TYPE_ARRAY,
   315  		KeySize:    4,
   316  		ValueSize:  8,
   317  		MaxEntries: 5,
   318  	}
   319  
   320  	// In some kernels <5.10 call inner maps must be the same size
   321  	if kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapDynamicInnerMap) {
   322  		innerDef.Flags |= bpftypes.BPFMapFlagsInnerMap
   323  	}
   324  
   325  	// Fill the whole map verification map
   326  	for i := 0; i < maxEntries; i++ {
   327  		verificationMap[i] = &ArrayMap{
   328  			AbstractMap: AbstractMap{
   329  				Name:       MustNewObjName("inner"),
   330  				Definition: innerDef,
   331  			},
   332  		}
   333  	}
   334  
   335  	// Must set the inner map def before loading
   336  	arrayOfMaps.InnerMapDef = innerDef
   337  
   338  	err := arrayOfMaps.Load()
   339  	if err != nil {
   340  		t.Fatal(err)
   341  	}
   342  	defer arrayOfMaps.Close()
   343  
   344  	// Copy verification map to BPF map, so contents are identical
   345  	for k, v := range verificationMap {
   346  		// Must load every inner map before it can be set in a map-in-map
   347  		err = v.Load()
   348  		if err != nil {
   349  			t.Fatal(err)
   350  		}
   351  		defer v.Close()
   352  
   353  		err = arrayOfMaps.Set(uint32(k), v, bpfsys.BPFMapElemAny)
   354  		if err != nil {
   355  			t.Fatal(err)
   356  		}
   357  	}
   358  
   359  	var k uint32
   360  	var v BPFMap
   361  	err = iter.Init(&k, &v)
   362  	if err != nil {
   363  		t.Fatal(err)
   364  	}
   365  
   366  	readMap := make([]bool, len(verificationMap))
   367  
   368  	for {
   369  		updated, err := iter.Next()
   370  		if err != nil {
   371  			t.Fatal(err)
   372  		}
   373  		if !updated {
   374  			break
   375  		}
   376  
   377  		if int(k) >= len(verificationMap) {
   378  			t.Fatalf("key %v doesn't exist in verification map", k)
   379  		}
   380  		verifyValue := verificationMap[k]
   381  		if verifyValue != v {
   382  			t.Fatalf("value at %d %v from BPF map doesn't match value in verification map %v", k, v, verifyValue)
   383  		}
   384  		if readMap[k] {
   385  			t.Fatalf("double read of same key %d", k)
   386  		}
   387  
   388  		readMap[k] = true
   389  	}
   390  
   391  	for k, v := range readMap {
   392  		if !v {
   393  			t.Fatalf("key %d was not read", k)
   394  		}
   395  	}
   396  }
   397  
   398  func TestSingleMapIteratorHashOfMapHappyPath(t *testing.T) {
   399  	if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapHashOfMaps) {
   400  		t.Skip("Skip because the feature is not supported by current kernel version")
   401  	}
   402  
   403  	const maxEntries = 8
   404  	hashOfMaps := &HashOfMapsMap{
   405  		AbstractMap: AbstractMap{
   406  			Name: MustNewObjName("test"),
   407  			Definition: BPFMapDef{
   408  				Type:       bpftypes.BPF_MAP_TYPE_HASH_OF_MAPS,
   409  				KeySize:    4, // Array maps always have 32bit keys (4 bytes)
   410  				ValueSize:  4, // FD's is always 32bit (4 bytes)
   411  				MaxEntries: maxEntries,
   412  			},
   413  		},
   414  	}
   415  	iter := &singleMapLookupIterator{
   416  		BPFMap: hashOfMaps,
   417  	}
   418  
   419  	// Just a simple go map used to verify BPF map behavior.
   420  	verificationMap := make([]BPFMap, maxEntries)
   421  
   422  	innerDef := BPFMapDef{
   423  		Type:       bpftypes.BPF_MAP_TYPE_ARRAY,
   424  		KeySize:    4,
   425  		ValueSize:  8,
   426  		MaxEntries: 5,
   427  	}
   428  
   429  	// In some kernels <5.10 call inner maps must be the same size
   430  	if kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapDynamicInnerMap) {
   431  		innerDef.Flags |= bpftypes.BPFMapFlagsInnerMap
   432  	}
   433  
   434  	// Fill the whole map verification map
   435  	for i := 0; i < maxEntries; i++ {
   436  		verificationMap[i] = &ArrayMap{
   437  			AbstractMap: AbstractMap{
   438  				Name:       MustNewObjName("inner"),
   439  				Definition: innerDef,
   440  			},
   441  		}
   442  	}
   443  
   444  	// Must set the inner map def before loading
   445  	hashOfMaps.InnerMapDef = innerDef
   446  
   447  	err := hashOfMaps.Load()
   448  	if err != nil {
   449  		t.Fatal(err)
   450  	}
   451  	defer hashOfMaps.Close()
   452  
   453  	// Copy verification map to BPF map, so contents are identical
   454  	for k, v := range verificationMap {
   455  		// Must load every inner map before it can be set in a map-in-map
   456  		err = v.Load()
   457  		if err != nil {
   458  			t.Fatal(err)
   459  		}
   460  		defer v.Close()
   461  
   462  		kCopy := uint32(k)
   463  		err = hashOfMaps.Set(&kCopy, v, bpfsys.BPFMapElemAny)
   464  		if err != nil {
   465  			t.Fatal(err)
   466  		}
   467  	}
   468  
   469  	var k uint32
   470  	var v BPFMap
   471  	err = iter.Init(&k, &v)
   472  	if err != nil {
   473  		t.Fatal(err)
   474  	}
   475  
   476  	readMap := make([]bool, len(verificationMap))
   477  
   478  	for {
   479  		updated, err := iter.Next()
   480  		if err != nil {
   481  			t.Fatal(err)
   482  		}
   483  		if !updated {
   484  			break
   485  		}
   486  
   487  		if int(k) >= len(verificationMap) {
   488  			t.Fatalf("key %v doesn't exist in verification map", k)
   489  		}
   490  		verifyValue := verificationMap[k]
   491  		if verifyValue != v {
   492  			t.Fatalf("value at %d %v from BPF map doesn't match value in verification map %v", k, v, verifyValue)
   493  		}
   494  		if readMap[k] {
   495  			t.Fatalf("double read of same key %d", k)
   496  		}
   497  
   498  		readMap[k] = true
   499  	}
   500  
   501  	for k, v := range readMap {
   502  		if !v {
   503  			t.Fatalf("key %d was not read", k)
   504  		}
   505  	}
   506  }