github.com/imran-kn/cilium-fork@v1.6.9/pkg/bpf/bpffs_linux.go (about) 1 // Copyright 2016-2019 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 // +build linux 16 17 package bpf 18 19 import ( 20 "fmt" 21 "os" 22 "path/filepath" 23 "sync" 24 "syscall" 25 26 "github.com/cilium/cilium/pkg/components" 27 "github.com/cilium/cilium/pkg/defaults" 28 "github.com/cilium/cilium/pkg/k8s" 29 "github.com/cilium/cilium/pkg/mountinfo" 30 ) 31 32 var ( 33 // Path to where bpffs is mounted 34 mapRoot = defaults.DefaultMapRoot 35 36 // Prefix for all maps (default: tc/globals) 37 mapPrefix = defaults.DefaultMapPrefix 38 39 // Set to true on first get request to detect misorder 40 lockedDown = false 41 once sync.Once 42 readMountInfo sync.Once 43 mountInfoPrefix string 44 ) 45 46 func lockDown() { 47 lockedDown = true 48 } 49 50 func SetMapRoot(path string) { 51 if lockedDown { 52 panic("SetMapRoot() call after MapRoot was read") 53 } 54 mapRoot = path 55 } 56 57 func GetMapRoot() string { 58 once.Do(lockDown) 59 return mapRoot 60 } 61 62 func SetMapPrefix(path string) { 63 if lockedDown { 64 panic("SetMapPrefix() call after MapPrefix was read") 65 } 66 mapPrefix = path 67 } 68 69 func GetMapPrefix() string { 70 once.Do(lockDown) 71 return mapPrefix 72 } 73 74 func MapPrefixPath() string { 75 once.Do(lockDown) 76 return filepath.Join(mapRoot, mapPrefix) 77 } 78 79 func mapPathFromMountInfo(name string) string { 80 readMountInfo.Do(func() { 81 mountInfos, err := mountinfo.GetMountInfo() 82 if err != nil { 83 log.WithError(err).Fatal("Could not get mount info for map root lookup") 84 } 85 86 for _, mountInfo := range mountInfos { 87 if mountInfo.FilesystemType == mountinfo.FilesystemTypeBPFFS { 88 mountInfoPrefix = filepath.Join(mountInfo.MountPoint, mapPrefix) 89 return 90 } 91 } 92 93 log.Fatal("Could not find BPF map root") 94 }) 95 96 return filepath.Join(mountInfoPrefix, name) 97 } 98 99 // MapPath returns a path for a BPF map with a given name. 100 func MapPath(name string) string { 101 if components.IsCiliumAgent() { 102 once.Do(lockDown) 103 return filepath.Join(mapRoot, mapPrefix, name) 104 } 105 return mapPathFromMountInfo(name) 106 } 107 108 // LocalMapName returns the name for a BPF map that is local to the specified ID. 109 func LocalMapName(name string, id uint16) string { 110 return fmt.Sprintf("%s%05d", name, id) 111 } 112 113 // LocalMapPath returns the path for a BPF map that is local to the specified ID. 114 func LocalMapPath(name string, id uint16) string { 115 return MapPath(LocalMapName(name, id)) 116 } 117 118 // Environment returns a list of environment variables which are needed to make 119 // BPF programs and tc aware of the actual BPFFS mount path. 120 func Environment() []string { 121 return append( 122 os.Environ(), 123 fmt.Sprintf("CILIUM_BPF_MNT=%s", GetMapRoot()), 124 fmt.Sprintf("TC_BPF_MNT=%s", GetMapRoot()), 125 ) 126 } 127 128 var ( 129 mountOnce sync.Once 130 ) 131 132 // mountFS mounts the BPFFS filesystem into the desired mapRoot directory. 133 func mountFS() error { 134 if k8s.IsEnabled() { 135 log.Warning("================================= WARNING ==========================================") 136 log.Warning("BPF filesystem is not mounted. This will lead to network disruption when Cilium pods") 137 log.Warning("are restarted. Ensure that the BPF filesystem is mounted in the host.") 138 log.Warning("https://docs.cilium.io/en/stable/kubernetes/requirements/#mounted-bpf-filesystem") 139 log.Warning("====================================================================================") 140 } 141 142 log.Infof("Mounting BPF filesystem at %s", mapRoot) 143 144 mapRootStat, err := os.Stat(mapRoot) 145 if err != nil { 146 if os.IsNotExist(err) { 147 if err := os.MkdirAll(mapRoot, 0755); err != nil { 148 return fmt.Errorf("unable to create bpf mount directory: %s", err) 149 } 150 } else { 151 return fmt.Errorf("failed to stat the mount path %s: %s", mapRoot, err) 152 153 } 154 } else if !mapRootStat.IsDir() { 155 return fmt.Errorf("%s is a file which is not a directory", mapRoot) 156 } 157 158 if err := syscall.Mount(mapRoot, mapRoot, "bpf", 0, ""); err != nil { 159 return fmt.Errorf("failed to mount %s: %s", mapRoot, err) 160 } 161 return nil 162 } 163 164 // hasMultipleMounts checks whether the current mapRoot has only one mount. 165 func hasMultipleMounts() (bool, error) { 166 num := 0 167 168 mountInfos, err := mountinfo.GetMountInfo() 169 if err != nil { 170 return false, err 171 } 172 173 for _, mountInfo := range mountInfos { 174 if mountInfo.Root == "/" && mountInfo.MountPoint == mapRoot { 175 num++ 176 } 177 } 178 179 return num > 1, nil 180 } 181 182 // checkOrMountCustomLocation tries to check or mount the BPF filesystem in the 183 // given path. 184 func checkOrMountCustomLocation(bpfRoot string) error { 185 SetMapRoot(bpfRoot) 186 187 // Check whether the custom location has a BPFFS mount. 188 mounted, bpffsInstance, err := mountinfo.IsMountFS(mountinfo.FilesystemTypeBPFFS, bpfRoot) 189 if err != nil { 190 return err 191 } 192 193 // If the custom location has no mount, let's mount BPFFS there. 194 if !mounted { 195 SetMapRoot(bpfRoot) 196 if err := mountFS(); err != nil { 197 return err 198 } 199 200 return nil 201 } 202 203 // If the custom location already has a mount with some other filesystem than 204 // BPFFS, return the error. 205 if !bpffsInstance { 206 return fmt.Errorf("mount in the custom directory %s has a different filesystem than BPFFS", bpfRoot) 207 } 208 209 log.Infof("Detected mounted BPF filesystem at %s", mapRoot) 210 211 return nil 212 } 213 214 // checkOrMountDefaultLocations tries to check or mount the BPF filesystem in 215 // standard locations, which are: 216 // - /sys/fs/bpf 217 // - /run/cilium/bpffs 218 // There is a procedure of determining which directory is going to be used: 219 // 1. Checking whether BPFFS filesystem is mounted in /sys/fs/bpf. 220 // 2. If there is no mount, then mount BPFFS in /sys/fs/bpf and finish there. 221 // 3. If there is a BPFFS mount, finish there. 222 // 4. If there is a mount, but with the other filesystem, then it means that most 223 // probably Cilium is running inside container which has mounted /sys/fs/bpf 224 // from host, but host doesn't have proper BPFFS mount, so that mount is just 225 // the empty directory. In that case, mount BPFFS under /run/cilium/bpffs. 226 func checkOrMountDefaultLocations() error { 227 // Check whether /sys/fs/bpf has a BPFFS mount. 228 mounted, bpffsInstance, err := mountinfo.IsMountFS(mountinfo.FilesystemTypeBPFFS, mapRoot) 229 if err != nil { 230 return err 231 } 232 233 // If /sys/fs/bpf is not mounted at all, we should mount 234 // BPFFS there. 235 if !mounted { 236 if err := mountFS(); err != nil { 237 return err 238 } 239 240 return nil 241 } 242 243 if !bpffsInstance { 244 // If /sys/fs/bpf has a mount but with some other filesystem 245 // than BPFFS, it means that Cilium is running inside container 246 // and /sys/fs/bpf is not mounted on host. We should mount BPFFS 247 // in /run/cilium/bpffs automatically. This will allow operation 248 // of Cilium but will result in unmounting of the filesystem 249 // when the pod is restarted. This in turn will cause resources 250 // such as the connection tracking table of the BPF programs to 251 // be released which will cause all connections into local 252 // containers to be dropped. User is going to be warned. 253 log.Warnf("BPF filesystem is going to be mounted automatically "+ 254 "in %s. However, it probably means that Cilium is running "+ 255 "inside container and BPFFS is not mounted on the host. "+ 256 "for more information, see: https://cilium.link/err-bpf-mount", 257 defaults.DefaultMapRootFallback, 258 ) 259 SetMapRoot(defaults.DefaultMapRootFallback) 260 261 cMounted, cBpffsInstance, err := mountinfo.IsMountFS(mountinfo.FilesystemTypeBPFFS, mapRoot) 262 if err != nil { 263 return err 264 } 265 if !cMounted { 266 if err := mountFS(); err != nil { 267 return err 268 } 269 } else if !cBpffsInstance { 270 log.Fatalf("%s is mounted but has a different filesystem than BPFFS", defaults.DefaultMapRootFallback) 271 } 272 } 273 274 log.Infof("Detected mounted BPF filesystem at %s", mapRoot) 275 276 return nil 277 } 278 279 func checkOrMountFS(bpfRoot string) error { 280 if bpfRoot == "" { 281 if err := checkOrMountDefaultLocations(); err != nil { 282 return err 283 } 284 } else { 285 if err := checkOrMountCustomLocation(bpfRoot); err != nil { 286 return err 287 } 288 } 289 290 multipleMounts, err := hasMultipleMounts() 291 if err != nil { 292 return err 293 } 294 if multipleMounts { 295 return fmt.Errorf("multiple mount points detected at %s", mapRoot) 296 } 297 298 return nil 299 } 300 301 // CheckOrMountFS checks or mounts the BPF filesystem and then 302 // opens/creates/deletes all maps which have previously been scheduled to be 303 // opened/created/deleted. 304 func CheckOrMountFS(bpfRoot string) { 305 mountOnce.Do(func() { 306 if err := checkOrMountFS(bpfRoot); err != nil { 307 log.WithError(err).Fatal("Unable to mount BPF filesystem") 308 } 309 }) 310 }