github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/pkg/maps/cidrmap/cidrmap.go (about) 1 // Copyright 2016-2017 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cidrmap 16 17 import ( 18 "fmt" 19 "net" 20 "unsafe" 21 22 "github.com/cilium/cilium/pkg/bpf" 23 "github.com/cilium/cilium/pkg/logging" 24 "github.com/cilium/cilium/pkg/logging/logfields" 25 26 "github.com/sirupsen/logrus" 27 ) 28 29 var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "map-cidr") 30 31 const ( 32 MapName = "cilium_cidr_" 33 MaxEntries = 16384 34 ) 35 36 // CIDRMap refers to an LPM trie map at 'path'. 37 type CIDRMap struct { 38 path string 39 Fd int 40 AddrSize int // max prefix length in bytes, 4 for IPv4, 16 for IPv6 41 Prefixlen uint32 42 43 // PrefixIsDynamic determines whether it's valid for entries to have 44 // a prefix length that is not equal to the Prefixlen above 45 PrefixIsDynamic bool 46 } 47 48 const ( 49 LPM_MAP_VALUE_SIZE = 1 50 ) 51 52 type cidrKey struct { 53 Prefixlen uint32 54 Net [16]byte 55 } 56 57 func (cm *CIDRMap) cidrKeyInit(cidr net.IPNet) (key cidrKey) { 58 ones, _ := cidr.Mask.Size() 59 key.Prefixlen = uint32(ones) 60 // IPv4 address can be represented by 16 byte slice in 'cidr.IP', 61 // in which case the address is at the end of the slice. 62 copy(key.Net[:], cidr.IP[len(cidr.IP)-cm.AddrSize:len(cidr.IP)]) 63 return 64 } 65 66 func (cm *CIDRMap) keyCidrInit(key cidrKey) (cidr net.IPNet) { 67 cidr.Mask = net.CIDRMask(int(key.Prefixlen), cm.AddrSize*8) 68 cidr.IP = make(net.IP, cm.AddrSize) 69 copy(cidr.IP[len(cidr.IP)-cm.AddrSize:len(cidr.IP)], key.Net[:]) 70 return 71 } 72 73 // checkPrefixlen checks whether it's valid to manipulate elements in the map 74 // with the specified key. If it's unsupported, it returns an error. 75 func (cm *CIDRMap) checkPrefixlen(key *cidrKey, operation string) error { 76 if cm.Prefixlen != 0 && 77 ((cm.PrefixIsDynamic && cm.Prefixlen < key.Prefixlen) || 78 (!cm.PrefixIsDynamic && cm.Prefixlen != key.Prefixlen)) { 79 return fmt.Errorf("Unable to %s element with dynamic prefix length cm.Prefixlen=%d key.Prefixlen=%d", 80 operation, cm.Prefixlen, key.Prefixlen) 81 } 82 return nil 83 } 84 85 // InsertCIDR inserts an entry to 'cm' with key 'cidr'. Value is currently not 86 // used. 87 func (cm *CIDRMap) InsertCIDR(cidr net.IPNet) error { 88 key := cm.cidrKeyInit(cidr) 89 entry := [LPM_MAP_VALUE_SIZE]byte{} 90 if err := cm.checkPrefixlen(&key, "update"); err != nil { 91 return err 92 } 93 log.WithField(logfields.Path, cm.path).Debugf("Inserting CIDR entry %s", cidr.String()) 94 return bpf.UpdateElement(cm.Fd, unsafe.Pointer(&key), unsafe.Pointer(&entry), 0) 95 } 96 97 // DeleteCIDR deletes an entry from 'cm' with key 'cidr'. 98 func (cm *CIDRMap) DeleteCIDR(cidr net.IPNet) error { 99 key := cm.cidrKeyInit(cidr) 100 if err := cm.checkPrefixlen(&key, "delete"); err != nil { 101 return err 102 } 103 log.WithField(logfields.Path, cm.path).Debugf("Removing CIDR entry %s", cidr.String()) 104 return bpf.DeleteElement(cm.Fd, unsafe.Pointer(&key)) 105 } 106 107 // CIDRExists returns true if 'cidr' exists in map 'cm' 108 func (cm *CIDRMap) CIDRExists(cidr net.IPNet) bool { 109 key := cm.cidrKeyInit(cidr) 110 var entry [LPM_MAP_VALUE_SIZE]byte 111 return bpf.LookupElement(cm.Fd, unsafe.Pointer(&key), unsafe.Pointer(&entry)) == nil 112 } 113 114 // CIDRNext returns next CIDR entry in map 'cm' 115 func (cm *CIDRMap) CIDRNext(cidr *net.IPNet) *net.IPNet { 116 var key, keyNext cidrKey 117 if cidr != nil { 118 key = cm.cidrKeyInit(*cidr) 119 } 120 err := bpf.GetNextKey(cm.Fd, unsafe.Pointer(&key), unsafe.Pointer(&keyNext)) 121 if err != nil { 122 return nil 123 } 124 out := cm.keyCidrInit(keyNext) 125 return &out 126 } 127 128 // CIDRDump walks map 'cm' and dumps all CIDR entries 129 func (cm *CIDRMap) CIDRDump(to []string) []string { 130 var key, keyNext *net.IPNet 131 for { 132 keyNext = cm.CIDRNext(key) 133 if keyNext == nil { 134 return to 135 } 136 key = keyNext 137 to = append(to, key.String()) 138 } 139 } 140 141 // String returns the path of the map. 142 func (cm *CIDRMap) String() string { 143 if cm == nil { 144 return "" 145 } 146 return cm.path 147 } 148 149 // Close closes the FD of the given CIDRMap 150 func (cm *CIDRMap) Close() error { 151 if cm == nil { 152 return nil 153 } 154 return bpf.ObjClose(cm.Fd) 155 } 156 157 // OpenMap opens a new CIDRMap. 'bool' returns 'true' if the map was 158 // created, and 'false' if the map already existed. prefixdyn denotes 159 // whether element's prefixlen can vary and we thus need to use a LPM 160 // trie instead of hash table. 161 func OpenMap(path string, prefixlen int, prefixdyn bool) (*CIDRMap, bool, error) { 162 return OpenMapElems(path, prefixlen, prefixdyn, MaxEntries) 163 } 164 165 // OpenMapElems is the same as OpenMap only with defined maxelem as argument. 166 func OpenMapElems(path string, prefixlen int, prefixdyn bool, maxelem uint32) (*CIDRMap, bool, error) { 167 var typeMap = bpf.BPF_MAP_TYPE_LPM_TRIE 168 var prefix = 0 169 170 if prefixdyn == false { 171 typeMap = bpf.BPF_MAP_TYPE_HASH 172 prefix = prefixlen 173 } 174 if prefixlen <= 0 { 175 return nil, false, fmt.Errorf("prefixlen must be > 0") 176 } 177 bytes := (prefixlen-1)/8 + 1 178 fd, isNewMap, err := bpf.OpenOrCreateMap( 179 path, 180 typeMap, 181 uint32(unsafe.Sizeof(uint32(0))+uintptr(bytes)), 182 uint32(LPM_MAP_VALUE_SIZE), 183 maxelem, 184 bpf.BPF_F_NO_PREALLOC, 0, true, 185 ) 186 187 if err != nil { 188 log.Debug("Kernel does not support CIDR maps, using hash table instead.") 189 typeMap = bpf.BPF_MAP_TYPE_HASH 190 fd, isNewMap, err = bpf.OpenOrCreateMap( 191 path, 192 typeMap, 193 uint32(unsafe.Sizeof(uint32(0))+uintptr(bytes)), 194 uint32(LPM_MAP_VALUE_SIZE), 195 maxelem, 196 bpf.BPF_F_NO_PREALLOC, 0, true, 197 ) 198 if err != nil { 199 scopedLog := log.WithError(err).WithField(logfields.Path, path) 200 scopedLog.Warning("Failed to create CIDR map") 201 return nil, false, err 202 } 203 } 204 205 m := &CIDRMap{ 206 path: path, 207 Fd: fd, 208 AddrSize: bytes, 209 Prefixlen: uint32(prefix), 210 PrefixIsDynamic: prefixdyn, 211 } 212 213 log.WithFields(logrus.Fields{ 214 logfields.Path: path, 215 "fd": fd, 216 "LPM": typeMap == bpf.BPF_MAP_TYPE_LPM_TRIE, 217 }).Debug("Created CIDR map") 218 219 return m, isNewMap, nil 220 }