github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/emulator/maps_hash.go (about) 1 package emulator 2 3 import ( 4 "crypto/sha256" 5 "encoding/hex" 6 "fmt" 7 8 "github.com/dylandreimerink/gobpfld/bpfsys" 9 ) 10 11 type HashMap struct { 12 AbstractMap 13 14 // Go can't use slices as map values, so what we do is we sha256 hash the slice which always results in a 15 // uniform sized array which we can use as key. Since we now don't index by the actual key, we also need to 16 // store the actual key value so we can return k/v pairs 17 18 KeysMap map[[sha256.Size]byte]*ByteMemory 19 Values map[[sha256.Size]byte]*ByteMemory 20 } 21 22 func (m *HashMap) Init() error { 23 m.KeysMap = make(map[[sha256.Size]byte]*ByteMemory) 24 m.Values = make(map[[sha256.Size]byte]*ByteMemory) 25 26 // NOTE: we always ignore the BPFMapFlagsNoPreAlloc flag since we never pre-alloc, it is an optimization which 27 // we don't need for the current purposes of the emulator. 28 29 return nil 30 } 31 32 func (m *HashMap) Keys() []RegisterValue { 33 keys := make([]RegisterValue, len(m.KeysMap)) 34 i := 0 35 for _, val := range m.KeysMap { 36 keys[i] = &MemoryPtr{ 37 Memory: val.Clone(), // Clone since we don't want to give access to a modifyable reference 38 } 39 i++ 40 } 41 return keys 42 } 43 44 func (m *HashMap) Lookup(key RegisterValue) (RegisterValue, error) { 45 keyPtr, ok := key.(PointerValue) 46 if !ok { 47 return nil, errMapKeyNoPtr 48 } 49 50 keyVal, err := keyPtr.ReadRange(0, int(m.Def.KeySize)) 51 if !ok { 52 return nil, fmt.Errorf("key read range: %w", err) 53 } 54 55 keyHash := sha256.Sum256(keyVal) 56 value, found := m.Values[keyHash] 57 if !found { 58 // Return NULL if value doesn't exist 59 return newIMM(0), nil 60 } 61 62 return &MemoryPtr{Memory: value, Offset: 0}, nil 63 } 64 65 func (m *HashMap) Update( 66 key RegisterValue, 67 value RegisterValue, 68 flags bpfsys.BPFAttrMapElemFlags, 69 ) ( 70 RegisterValue, 71 error, 72 ) { 73 keyPtr, ok := key.(PointerValue) 74 if !ok { 75 return nil, errMapKeyNoPtr 76 } 77 78 keyVal, err := keyPtr.ReadRange(0, int(m.Def.KeySize)) 79 if !ok { 80 return nil, fmt.Errorf("key read range: %w", err) 81 } 82 83 keyHash := sha256.Sum256(keyVal) 84 existingValue, found := m.Values[keyHash] 85 if !found { 86 if len(m.Values)+1 > int(m.Def.MaxEntries) { 87 // Exceeding max map size 88 return nil, errMapOutOfMemory 89 } 90 91 existingValue = &ByteMemory{ 92 MemName: fmt.Sprintf("%s[0x%s]", m.Name, hex.EncodeToString(keyVal)), 93 Backing: make([]byte, m.Def.ValueSize), 94 } 95 } 96 existingKey, found := m.KeysMap[keyHash] 97 if !found { 98 existingKey = &ByteMemory{ 99 MemName: hex.EncodeToString(keyVal), 100 Backing: make([]byte, m.Def.KeySize), 101 } 102 } 103 104 valPtr, ok := value.(PointerValue) 105 if !ok { 106 return nil, errMapValNoPtr 107 } 108 109 valVal, err := valPtr.ReadRange(0, int(m.Def.ValueSize)) 110 if !ok { 111 return nil, fmt.Errorf("val read range: %w", err) 112 } 113 114 // We can safely assing, valVal is otherwise unreferenced 115 existingValue.Backing = valVal 116 m.Values[keyHash] = existingValue 117 118 // We can safely assing, valVal is otherwise unreferenced 119 existingKey.Backing = keyVal 120 m.KeysMap[keyHash] = existingKey 121 122 return newIMM(0), nil 123 } 124 125 func (m *HashMap) Delete(key RegisterValue, flags bpfsys.BPFAttrMapElemFlags) error { 126 keyPtr, ok := key.(PointerValue) 127 if !ok { 128 return errMapKeyNoPtr 129 } 130 131 keyVal, err := keyPtr.ReadRange(0, int(m.Def.KeySize)) 132 if !ok { 133 return fmt.Errorf("key read range: %w", err) 134 } 135 keyHash := sha256.Sum256(keyVal) 136 137 delete(m.KeysMap, keyHash) 138 delete(m.Values, keyHash) 139 140 return nil 141 }