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 }