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

     1  //go:build bpftests
     2  // +build bpftests
     3  
     4  package gobpfld
     5  
     6  import (
     7  	"fmt"
     8  	"math/rand"
     9  	"net"
    10  	"reflect"
    11  	"testing"
    12  	"unsafe"
    13  
    14  	"github.com/dylandreimerink/gobpfld/bpfsys"
    15  	"github.com/dylandreimerink/gobpfld/bpftypes"
    16  	"github.com/dylandreimerink/gobpfld/kernelsupport"
    17  )
    18  
    19  // TestLPMTrieMapHappyPath executes property based happy path testing, comparing the LPM map to a golang map, assuming the go map is always
    20  // correct.
    21  func TestLPMTrieMapHappyPath(t *testing.T) {
    22  	if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrie) {
    23  		t.Skip("LPM tree not supported by current kernel version")
    24  	}
    25  	if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrieNextKey) {
    26  		t.Skip("LPM tree iteration not supported by current kernel version")
    27  	}
    28  
    29  	const maxEntries = 128
    30  
    31  	m := &LPMTrieMap{
    32  		AbstractMap: AbstractMap{
    33  			Name: MustNewObjName("lpmtest"),
    34  			Definition: BPFMapDef{
    35  				Type:       bpftypes.BPF_MAP_TYPE_LPM_TRIE,
    36  				KeySize:    uint32(unsafe.Sizeof(LPMTrieIPv4Key{})),
    37  				ValueSize:  8,
    38  				MaxEntries: maxEntries + 1,
    39  				Flags:      bpftypes.BPFMapFlagsNoPreAlloc,
    40  			},
    41  		},
    42  	}
    43  
    44  	err := m.Load()
    45  	if err != nil {
    46  		t.Fatal(err)
    47  	}
    48  	defer m.Close()
    49  
    50  	verificationMap := make(map[LPMTrieIPv4Key]uint64)
    51  
    52  	randKey := func() *LPMTrieIPv4Key {
    53  		_, cidr, err := net.ParseCIDR(fmt.Sprintf(
    54  			"%d.%d.%d.%d/%d",
    55  			rand.Intn(255),
    56  			rand.Intn(255),
    57  			rand.Intn(255),
    58  			rand.Intn(255),
    59  			rand.Intn(32),
    60  		))
    61  		if err != nil {
    62  			t.Fatal(err)
    63  		}
    64  
    65  		// If the key already exists, generate another one
    66  		return LPMKeyFromNetwork(*cidr).(*LPMTrieIPv4Key)
    67  	}
    68  
    69  	// Fill the verification map
    70  	for i := 0; i < maxEntries; i++ {
    71  		key := randKey()
    72  		if _, ok := verificationMap[*key]; ok {
    73  			i--
    74  			continue
    75  		}
    76  
    77  		verificationMap[*key] = rand.Uint64()
    78  	}
    79  
    80  	// Transfer contents to BPF map
    81  	for k, v := range verificationMap {
    82  		// Copy the key, we can't pass a pointer to a temp variable
    83  		kCopy := k
    84  		vCopy := v
    85  		err = m.Set(&kCopy, &vCopy, bpfsys.BPFMapElemAny)
    86  		if err != nil {
    87  			t.Fatal(err)
    88  		}
    89  	}
    90  
    91  	// Perform less permutations when the -short flag is passed
    92  	limit := 1000
    93  	if testing.Short() {
    94  		limit = 100
    95  	}
    96  
    97  	// Fail if the maps don't match
    98  	verify := func() {
    99  		read := make(map[LPMTrieIPv4Key]struct{}, len(verificationMap))
   100  		for k := range verificationMap {
   101  			read[k] = struct{}{}
   102  		}
   103  
   104  		var (
   105  			k LPMTrieIPv4Key
   106  			v uint64
   107  		)
   108  		var iter MapIterator = &singleLookupIterator{BPFMap: m}
   109  
   110  		// If batch ops in LPM maps are supported, use a batch iterator half of the time
   111  		if kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrieBatchOps) && rand.Int()%2 == 0 {
   112  			iter = &batchLookupIterator{BPFMap: m}
   113  		}
   114  
   115  		err = iter.Init(&k, &v)
   116  		if err != nil {
   117  			t.Fatal(err)
   118  		}
   119  		for {
   120  			updated, err := iter.Next()
   121  			if err != nil {
   122  				t.Fatal(err)
   123  			}
   124  			if !updated {
   125  				break
   126  			}
   127  
   128  			verifyValue, ok := verificationMap[k]
   129  			if !ok {
   130  				t.Fatal("key in map doesn't exist in verification map")
   131  			}
   132  			if verifyValue != v {
   133  				t.Fatal("value doesn't match")
   134  			}
   135  			delete(read, k)
   136  		}
   137  		if err != nil {
   138  			t.Fatal(err)
   139  		}
   140  
   141  		if len(read) > 0 {
   142  			t.Fatalf("no all keys were read (%d/%d)", len(read), len(verificationMap))
   143  		}
   144  	}
   145  
   146  	// Verify initial state
   147  	verify()
   148  
   149  	// Return a random key from the verification map
   150  	randValKey := func() LPMTrieIPv4Key {
   151  		kIndex := rand.Intn(len(verificationMap) - 1)
   152  		ii := 0
   153  		for k := range verificationMap {
   154  			if ii == kIndex {
   155  				return k
   156  			}
   157  			ii++
   158  		}
   159  
   160  		t.Fatal("should not be possible")
   161  		return LPMTrieIPv4Key{}
   162  	}
   163  
   164  	// Update, delete, and add single keys and values
   165  	for i := 0; i < limit; i++ {
   166  		switch i % 3 {
   167  		case 0:
   168  			// Update
   169  			newVal := rand.Uint64()
   170  			k := randValKey()
   171  
   172  			verificationMap[k] = newVal
   173  			err := m.Set(&k, &newVal, bpfsys.BPFMapElemExists)
   174  			if err != nil {
   175  				t.Fatal(err)
   176  			}
   177  
   178  		case 1:
   179  			// Delete
   180  			k := randValKey()
   181  
   182  			delete(verificationMap, k)
   183  			err := m.Delete(&k)
   184  			if err != nil {
   185  				t.Fatal(err)
   186  			}
   187  
   188  		case 2:
   189  			// Add
   190  			newVal := rand.Uint64()
   191  			k := randKey()
   192  
   193  			verificationMap[*k] = newVal
   194  			err := m.Set(k, &newVal, bpfsys.BPFMapElemNoExists)
   195  			if err != nil {
   196  				t.Fatal(err)
   197  			}
   198  		}
   199  
   200  		verify()
   201  	}
   202  
   203  	// Can't execute the rest of the test if the current kernel doesn't support bulk ops
   204  	if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrieBatchOps) {
   205  		return
   206  	}
   207  
   208  	// Update, delete, and add 10 keys and values at a time
   209  	const batchSize = 10
   210  	for i := 0; i < limit; i++ {
   211  		keys := make([]LPMTrieIPv4Key, batchSize)
   212  		values := make([]uint64, batchSize)
   213  
   214  		switch i % 3 {
   215  		case 0:
   216  			// Update
   217  
   218  			// Get random set of existing keys
   219  			for j := 0; j < batchSize; j++ {
   220  				randKey := randValKey()
   221  				exists := false
   222  				for _, v := range keys {
   223  					if v == randKey {
   224  						exists = true
   225  					}
   226  				}
   227  				if exists {
   228  					j--
   229  					continue
   230  				}
   231  				keys[j] = randKey
   232  			}
   233  
   234  			for j := 0; j < batchSize; j++ {
   235  				// Gen new value
   236  				values[j] = rand.Uint64()
   237  
   238  				// Update value in verification map
   239  				verificationMap[keys[j]] = values[j]
   240  			}
   241  
   242  			_, err := m.SetBatch(&keys, &values, bpfsys.BPFMapElemExists, batchSize)
   243  			if err != nil {
   244  				t.Fatal(err)
   245  			}
   246  
   247  		case 1:
   248  			// Delete
   249  
   250  			// Get random set of existing keys
   251  			for j := 0; j < batchSize; j++ {
   252  				randKey := randValKey()
   253  				exists := false
   254  				for _, v := range keys {
   255  					if v == randKey {
   256  						exists = true
   257  					}
   258  				}
   259  				if exists {
   260  					j--
   261  					continue
   262  				}
   263  				keys[j] = randKey
   264  				delete(verificationMap, randKey)
   265  			}
   266  
   267  			_, err := m.DeleteBatch(&keys, batchSize)
   268  			if err != nil {
   269  				t.Fatal(err)
   270  			}
   271  
   272  		case 2:
   273  			// Add
   274  
   275  			// Get random set of existing keys
   276  			for j := 0; j < batchSize; j++ {
   277  				randKey := *randKey()
   278  				exists := false
   279  				for _, v := range keys {
   280  					if v == randKey {
   281  						exists = true
   282  					}
   283  				}
   284  				if exists {
   285  					j--
   286  					continue
   287  				}
   288  				keys[j] = randKey
   289  			}
   290  
   291  			for j := 0; j < batchSize; j++ {
   292  				// Gen new value
   293  				values[j] = rand.Uint64()
   294  
   295  				// Update value in verification map
   296  				verificationMap[keys[j]] = values[j]
   297  			}
   298  
   299  			_, err := m.SetBatch(&keys, &values, bpfsys.BPFMapElemNoExists, batchSize)
   300  			if err != nil {
   301  				t.Fatal(err)
   302  			}
   303  		}
   304  
   305  		verify()
   306  	}
   307  }
   308  
   309  func TestLPMKeyFromNetwork(t *testing.T) {
   310  	cases := []struct {
   311  		Name     string
   312  		CIDR     string
   313  		Expected LPMTrieKey
   314  	}{
   315  		{
   316  			Name: "IPv4 happy path",
   317  			CIDR: "127.0.0.1/32",
   318  			Expected: &LPMTrieIPv4Key{
   319  				Address: [4]byte{127, 0, 0, 1},
   320  				Prefix:  32,
   321  			},
   322  		},
   323  		{
   324  			Name: "IPv4 happy path 2",
   325  			CIDR: "192.168.12.0/24",
   326  			Expected: &LPMTrieIPv4Key{
   327  				Address: [4]byte{192, 168, 12, 0},
   328  				Prefix:  24,
   329  			},
   330  		},
   331  		{
   332  			Name: "IPv6 happy path",
   333  			CIDR: "::1/128",
   334  			Expected: &LPMTrieIPv6Key{
   335  				Address: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
   336  				Prefix:  128,
   337  			},
   338  		},
   339  		{
   340  			Name: "IPv6 happy path 2",
   341  			CIDR: "::192.168.12.0/120",
   342  			Expected: &LPMTrieIPv6Key{
   343  				Address: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 168, 12, 0},
   344  				Prefix:  120,
   345  			},
   346  		},
   347  	}
   348  
   349  	for _, testCase := range cases {
   350  		t.Run(t.Name()+"_"+testCase.Name, func(tt *testing.T) {
   351  			_, ipNet, err := net.ParseCIDR(testCase.CIDR)
   352  			if err != nil {
   353  				tt.Fatal(err)
   354  			}
   355  
   356  			key := LPMKeyFromNetwork(*ipNet)
   357  			if !reflect.DeepEqual(key, testCase.Expected) {
   358  				tt.Fatal("keys not equal")
   359  			}
   360  		})
   361  	}
   362  }