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