github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/map_lpm_trie.go (about) 1 package gobpfld 2 3 import ( 4 "fmt" 5 "net" 6 "reflect" 7 8 "github.com/dylandreimerink/gobpfld/bpfsys" 9 "github.com/dylandreimerink/gobpfld/bpftypes" 10 "github.com/dylandreimerink/gobpfld/kernelsupport" 11 ) 12 13 var _ BPFMap = (*LPMTrieMap)(nil) 14 15 // LPMTrieMap is a specialized map type which used Longest Prefix Matching on the key when getting values from the map. 16 // LPM is commonly used in routing tables or any other application where the most specific network range should 17 // overrule settings of less specific network ranges. 18 type LPMTrieMap struct { 19 AbstractMap 20 } 21 22 func (m *LPMTrieMap) Load() error { 23 if m.Definition.Type != bpftypes.BPF_MAP_TYPE_LPM_TRIE { 24 return fmt.Errorf("map type in definition must be BPF_MAP_TYPE_LPM_TRIE when using an LPMTrieMap") 25 } 26 27 if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrie) { 28 return fmt.Errorf("LPM trie map type is not supported by the current kernel version") 29 } 30 31 err := m.load(nil) 32 if err != nil { 33 return err 34 } 35 36 err = mapRegister.add(m) 37 if err != nil { 38 return fmt.Errorf("map register: %w", err) 39 } 40 41 return nil 42 } 43 44 // Close closes the file descriptor associate with the map, this will cause the map to unload from the kernel 45 // if it is not still in use by a eBPF program, bpf FS, or a userspace program still holding a fd to the map. 46 func (m *LPMTrieMap) Close() error { 47 err := mapRegister.delete(m) 48 if err != nil { 49 return fmt.Errorf("map register: %w", err) 50 } 51 52 return m.close() 53 } 54 55 func (m *LPMTrieMap) Get(key LPMTrieKey, value interface{}) error { 56 return m.get(key, value) 57 } 58 59 // GetBatch fills the keys and values array/slice with the keys and values inside the map up to a maximum of 60 // maxBatchSize entries. The keys and values array/slice must have at least a length of maxBatchSize. 61 // The key and value of an entry is has the same index, so for example the value for keys[2] is in values[2]. 62 // Count is the amount of entries returns, partial is true if not all elements of keys and values could be set. 63 // 64 // This function is intended for small maps which can be read into userspace all at once since 65 // GetBatch can only read from the beginning of the map. If the map is to large to read all at once 66 // a iterator should be used instead of the Get or GetBatch function. 67 func (m *LPMTrieMap) GetBatch( 68 keys interface{}, 69 values interface{}, 70 maxBatchSize uint32, 71 ) ( 72 count int, 73 partial bool, 74 err error, 75 ) { 76 if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrieBatchOps) { 77 return 0, false, fmt.Errorf("batch get operation not support on LPM trie map type on current kernel version") 78 } 79 80 if err := m.checkLPMKeyBatch(keys); err != nil { 81 return 0, false, err 82 } 83 84 return m.getBatch(keys, values, maxBatchSize) 85 } 86 87 func (m *LPMTrieMap) checkLPMKeyBatch(keys interface{}) error { 88 keyType := reflect.TypeOf(keys) 89 if keyType.Kind() != reflect.Ptr { 90 return fmt.Errorf("keys argument must be a pointer") 91 } 92 93 collection := keyType.Elem() 94 var elem reflect.Type 95 96 switch collection.Kind() { 97 case reflect.Array: 98 elem = collection.Elem() 99 case reflect.Slice: 100 elem = collection.Elem() 101 default: 102 return fmt.Errorf("keys argument must be a pointer to an array or slice") 103 } 104 105 // Check if a pointer to elements of the slice would implement LPMTrieKey 106 var keyInterface LPMTrieKey 107 elemPtr := reflect.PtrTo(elem) 108 if !elemPtr.Implements(reflect.TypeOf(&keyInterface).Elem()) { 109 return fmt.Errorf("keys argument must be a pointer to an array or slice op LPMKey elements") 110 } 111 112 return nil 113 } 114 115 func (m *LPMTrieMap) Set(key LPMTrieKey, value interface{}, flags bpfsys.BPFAttrMapElemFlags) error { 116 return m.set(key, value, flags) 117 } 118 119 func (m *LPMTrieMap) SetBatch( 120 keys interface{}, 121 values interface{}, 122 flags bpfsys.BPFAttrMapElemFlags, 123 maxBatchSize uint32, 124 ) ( 125 count int, 126 err error, 127 ) { 128 if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrieBatchOps) { 129 return 0, fmt.Errorf("batch set operation not support on LPM trie map type on current kernel version") 130 } 131 132 if err := m.checkLPMKeyBatch(keys); err != nil { 133 return 0, err 134 } 135 136 return m.setBatch(keys, values, flags, maxBatchSize) 137 } 138 139 func (m *LPMTrieMap) Delete(key LPMTrieKey) error { 140 return m.delete(key) 141 } 142 143 func (m *LPMTrieMap) DeleteBatch( 144 keys interface{}, 145 maxBatchSize uint32, 146 ) ( 147 count int, 148 err error, 149 ) { 150 if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrieBatchOps) { 151 return 0, fmt.Errorf("batch delete operation not support on LPM trie map type on current kernel version") 152 } 153 154 if err := m.checkLPMKeyBatch(keys); err != nil { 155 return 0, err 156 } 157 158 return m.deleteBatch(keys, maxBatchSize) 159 } 160 161 func (m *LPMTrieMap) Iterator() MapIterator { 162 if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrieNextKey) { 163 return &errIterator{ 164 // TODO provide this feature by maintaining a map of all keys in userspace and returning a custom 165 // iterator. 166 err: fmt.Errorf("current kernel version doesn't support LPM map iteration"), 167 } 168 } 169 170 // If the kernel doesn't have support for batch lookup, use single lookup 171 if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrieBatchOps) { 172 return &singleLookupIterator{ 173 BPFMap: m, 174 } 175 } 176 177 // If there is no reason not to use the batch lookup iterator, use it 178 return &batchLookupIterator{ 179 BPFMap: m, 180 } 181 } 182 183 // LPMKeyFromNetwork converts an net.IPNet struct into an LPMTrieKey 184 func LPMKeyFromNetwork(n net.IPNet) LPMTrieKey { 185 ones, _ := n.Mask.Size() 186 if n.IP.To4() != nil { 187 key := &LPMTrieIPv4Key{ 188 Prefix: uint32(ones), 189 } 190 copy(key.Address[:], n.IP) 191 return key 192 } 193 194 key := &LPMTrieIPv6Key{ 195 Prefix: uint32(ones), 196 } 197 copy(key.Address[:], n.IP) 198 return key 199 } 200 201 type LPMTrieKey interface { 202 LPMTrieKey() 203 } 204 205 var _ LPMTrieKey = (*LPMTrieIPv4Key)(nil) 206 207 type LPMTrieIPv4Key struct { 208 Prefix uint32 209 Address [4]byte 210 } 211 212 // LPMTrieKey does nothing, it is just defined so LPMTrieIPv4Key implements LPMTrieKey 213 func (k *LPMTrieIPv4Key) LPMTrieKey() {} 214 215 var _ LPMTrieKey = (*LPMTrieIPv6Key)(nil) 216 217 type LPMTrieIPv6Key struct { 218 Prefix uint32 219 Address [16]byte 220 } 221 222 // LPMTrieKey does nothing, it is just defined so LPMTrieIPv6Key implements LPMTrieKey 223 func (k *LPMTrieIPv6Key) LPMTrieKey() {}