github.com/elfadel/cilium@v1.6.12/pkg/datapath/prefilter/prefilter.go (about) 1 // Copyright 2017-2018 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 prefilter 16 17 import ( 18 "fmt" 19 "io" 20 "net" 21 "os/exec" 22 "path" 23 "syscall" 24 25 "github.com/cilium/cilium/pkg/bpf" 26 "github.com/cilium/cilium/pkg/lock" 27 "github.com/cilium/cilium/pkg/logging" 28 "github.com/cilium/cilium/pkg/logging/logfields" 29 "github.com/cilium/cilium/pkg/maps/cidrmap" 30 "github.com/cilium/cilium/pkg/probe" 31 ) 32 33 type preFilterMapType int 34 35 const ( 36 prefixesV4Dyn preFilterMapType = iota 37 prefixesV4Fix 38 prefixesV6Dyn 39 prefixesV6Fix 40 mapCount 41 ) 42 43 const ( 44 // Arbitrary chosen for now. We don't preallocate elements, 45 // so we could bump the limit if needed later on. 46 maxLKeys = 1024 * 64 47 maxHKeys = 1024 * 1024 * 20 48 ) 49 50 type preFilterMaps [mapCount]*cidrmap.CIDRMap 51 52 type preFilterConfig struct { 53 dyn4Enabled bool 54 dyn6Enabled bool 55 fix4Enabled bool 56 fix6Enabled bool 57 } 58 59 // PreFilter holds global info on related CIDR maps participating in prefilter 60 type PreFilter struct { 61 maps preFilterMaps 62 config preFilterConfig 63 revision int64 64 mutex lock.RWMutex 65 } 66 67 var ( 68 log = logging.DefaultLogger.WithField(logfields.LogSubsys, "prefilter") 69 ) 70 71 // WriteConfig dumps the configuration for the corresponding header file 72 func (p *PreFilter) WriteConfig(fw io.Writer) { 73 p.mutex.RLock() 74 defer p.mutex.RUnlock() 75 76 fmt.Fprintf(fw, "#define CIDR4_HMAP_ELEMS %d\n", maxHKeys) 77 fmt.Fprintf(fw, "#define CIDR4_LMAP_ELEMS %d\n", maxLKeys) 78 79 fmt.Fprintf(fw, "#define CIDR4_HMAP_NAME %s\n", path.Base(p.maps[prefixesV4Fix].String())) 80 fmt.Fprintf(fw, "#define CIDR4_LMAP_NAME %s\n", path.Base(p.maps[prefixesV4Dyn].String())) 81 fmt.Fprintf(fw, "#define CIDR6_HMAP_NAME %s\n", path.Base(p.maps[prefixesV6Fix].String())) 82 fmt.Fprintf(fw, "#define CIDR6_LMAP_NAME %s\n", path.Base(p.maps[prefixesV6Dyn].String())) 83 84 if p.config.fix4Enabled { 85 fmt.Fprintf(fw, "#define CIDR4_FILTER\n") 86 if p.config.dyn4Enabled { 87 fmt.Fprintf(fw, "#define CIDR4_LPM_PREFILTER\n") 88 } 89 } 90 if p.config.fix6Enabled { 91 fmt.Fprintf(fw, "#define CIDR6_FILTER\n") 92 if p.config.dyn6Enabled { 93 fmt.Fprintf(fw, "#define CIDR6_LPM_PREFILTER\n") 94 } 95 } 96 } 97 98 func (p *PreFilter) dumpOneMap(which preFilterMapType, to []string) []string { 99 if p.maps[which] == nil { 100 return to 101 } 102 return p.maps[which].CIDRDump(to) 103 } 104 105 // Dump dumps revision and CIDRs as string slice of all participating maps 106 func (p *PreFilter) Dump(to []string) ([]string, int64) { 107 p.mutex.RLock() 108 defer p.mutex.RUnlock() 109 for i := prefixesV4Dyn; i < mapCount; i++ { 110 to = p.dumpOneMap(i, to) 111 } 112 return to, p.revision 113 } 114 115 func (p *PreFilter) selectMap(ones, bits int) preFilterMapType { 116 if bits == net.IPv4len*8 { 117 if ones == bits { 118 return prefixesV4Fix 119 } 120 return prefixesV4Dyn 121 } else if bits == net.IPv6len*8 { 122 if ones == bits { 123 return prefixesV6Fix 124 } 125 return prefixesV6Dyn 126 } else { 127 return mapCount 128 } 129 } 130 131 // Insert inserts slice of CIDRs (doh!) for the latest revision 132 func (p *PreFilter) Insert(revision int64, cidrs []net.IPNet) error { 133 var undoQueue []net.IPNet 134 var ret error 135 136 p.mutex.Lock() 137 defer p.mutex.Unlock() 138 if revision != 0 && p.revision != revision { 139 return fmt.Errorf("Latest revision is %d not %d", p.revision, revision) 140 } 141 for _, cidr := range cidrs { 142 ones, bits := cidr.Mask.Size() 143 which := p.selectMap(ones, bits) 144 if which == mapCount || p.maps[which] == nil { 145 ret = fmt.Errorf("No map enabled for CIDR string %s", cidr.String()) 146 break 147 } 148 err := p.maps[which].InsertCIDR(cidr) 149 if err != nil { 150 ret = fmt.Errorf("Error inserting CIDR string %s: %s", cidr.String(), err) 151 break 152 } else { 153 undoQueue = append(undoQueue, cidr) 154 } 155 } 156 if ret == nil { 157 p.revision++ 158 return ret 159 } 160 for _, cidr := range undoQueue { 161 ones, bits := cidr.Mask.Size() 162 which := p.selectMap(ones, bits) 163 p.maps[which].DeleteCIDR(cidr) 164 } 165 return ret 166 } 167 168 // Delete deletes slice of CIDRs (doh!) for the latest revision 169 func (p *PreFilter) Delete(revision int64, cidrs []net.IPNet) error { 170 var undoQueue []net.IPNet 171 var ret error 172 173 p.mutex.Lock() 174 defer p.mutex.Unlock() 175 if revision != 0 && p.revision != revision { 176 return fmt.Errorf("Latest revision is %d not %d", p.revision, revision) 177 } 178 for _, cidr := range cidrs { 179 ones, bits := cidr.Mask.Size() 180 which := p.selectMap(ones, bits) 181 if which == mapCount || p.maps[which] == nil { 182 return fmt.Errorf("No map enabled for CIDR string %s", cidr.String()) 183 } 184 // Lets check obvious cases first, so we don't need to painfully unroll 185 if p.maps[which].CIDRExists(cidr) == false { 186 return fmt.Errorf("No map entry for CIDR string %s", cidr.String()) 187 } 188 } 189 for _, cidr := range cidrs { 190 ones, bits := cidr.Mask.Size() 191 which := p.selectMap(ones, bits) 192 err := p.maps[which].DeleteCIDR(cidr) 193 if err != nil { 194 ret = fmt.Errorf("Error deleting CIDR string %s: %s", cidr.String(), err) 195 break 196 } else { 197 undoQueue = append(undoQueue, cidr) 198 } 199 } 200 if ret == nil { 201 p.revision++ 202 return ret 203 } 204 for _, cidr := range undoQueue { 205 ones, bits := cidr.Mask.Size() 206 which := p.selectMap(ones, bits) 207 p.maps[which].InsertCIDR(cidr) 208 } 209 return ret 210 } 211 212 func (p *PreFilter) initOneMap(which preFilterMapType) error { 213 var prefixdyn bool 214 var prefixlen int 215 var maxelems uint32 216 var path string 217 var err error 218 var skip bool 219 220 switch which { 221 case prefixesV4Dyn: 222 prefixlen = net.IPv4len * 8 223 prefixdyn = true 224 maxelems = maxLKeys 225 path = bpf.MapPath(cidrmap.MapName + "v4_dyn") 226 skip = p.config.dyn4Enabled == false 227 case prefixesV4Fix: 228 prefixlen = net.IPv4len * 8 229 prefixdyn = false 230 maxelems = maxHKeys 231 path = bpf.MapPath(cidrmap.MapName + "v4_fix") 232 skip = p.config.fix4Enabled == false 233 case prefixesV6Dyn: 234 prefixlen = net.IPv6len * 8 235 prefixdyn = true 236 maxelems = maxLKeys 237 path = bpf.MapPath(cidrmap.MapName + "v6_dyn") 238 skip = p.config.dyn6Enabled == false 239 case prefixesV6Fix: 240 prefixlen = net.IPv6len * 8 241 prefixdyn = false 242 maxelems = maxHKeys 243 path = bpf.MapPath(cidrmap.MapName + "v6_fix") 244 skip = p.config.fix4Enabled == false 245 } 246 if skip == false { 247 p.maps[which], _, err = cidrmap.OpenMapElems(path, prefixlen, prefixdyn, maxelems) 248 if err != nil { 249 return err 250 } 251 } 252 return nil 253 } 254 255 func (p *PreFilter) init() (*PreFilter, error) { 256 for i := prefixesV4Dyn; i < mapCount; i++ { 257 if err := p.initOneMap(i); err != nil { 258 return nil, err 259 } 260 } 261 return p, nil 262 } 263 264 // ProbePreFilter checks whether XDP mode is supported on given device 265 func ProbePreFilter(device, mode string) error { 266 cmd := exec.Command("ip", "-force", "link", "set", "dev", device, mode, "off") 267 if err := cmd.Start(); err != nil { 268 return fmt.Errorf("Cannot run ip command: %v", err) 269 } 270 if err := cmd.Wait(); err != nil { 271 if exiterr, ok := err.(*exec.ExitError); ok { 272 if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { 273 switch status.ExitStatus() { 274 case 2: 275 return fmt.Errorf("Mode %s not supported on device %s", mode, device) 276 default: 277 return fmt.Errorf("Prefilter not supported on OS") 278 } 279 } 280 } else { 281 return fmt.Errorf("Cannot wait for ip command: %v", err) 282 } 283 } 284 return nil 285 } 286 287 // NewPreFilter returns prefilter handle 288 func NewPreFilter() (*PreFilter, error) { 289 haveLPM := probe.HaveFullLPM() 290 if !haveLPM { 291 log.Warning("Kernel too old for full LPM map support. Needs kernel 4.16 or higher. Only enabling /32 and /128 prefixes for prefilter.") 292 } 293 c := preFilterConfig{ 294 dyn4Enabled: haveLPM, 295 dyn6Enabled: haveLPM, 296 fix4Enabled: true, 297 fix6Enabled: true, 298 } 299 p := &PreFilter{ 300 revision: 1, 301 config: c, 302 } 303 // Only needed here given we access pinned maps. 304 p.mutex.Lock() 305 defer p.mutex.Unlock() 306 return p.init() 307 }