github.com/zhyoulun/cilium@v1.6.12/pkg/sockops/sockops.go (about) 1 // Copyright 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 sockops 16 17 import ( 18 "context" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "os/exec" 23 "path/filepath" 24 "strconv" 25 "strings" 26 "time" 27 28 "github.com/cilium/cilium/pkg/bpf" 29 "github.com/cilium/cilium/pkg/cgroups" 30 "github.com/cilium/cilium/pkg/datapath/loader" 31 "github.com/cilium/cilium/pkg/defaults" 32 "github.com/cilium/cilium/pkg/logging" 33 "github.com/cilium/cilium/pkg/logging/logfields" 34 "github.com/cilium/cilium/pkg/option" 35 36 "github.com/sirupsen/logrus" 37 ) 38 39 var ( 40 // Default prefix for map objects 41 mapPrefix = defaults.DefaultMapPrefix 42 43 contextTimeout = 5 * time.Minute 44 ) 45 46 const ( 47 cSockops = "bpf_sockops.c" 48 oSockops = "bpf_sockops.o" 49 eSockops = "bpf_sockops" 50 51 cIPC = "bpf_redir.c" 52 oIPC = "bpf_redir.o" 53 eIPC = "bpf_redir" 54 55 sockMap = "cilium_sock_ops" 56 ) 57 58 var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "sockops") 59 60 // BPF programs and sockmaps working on cgroups 61 func bpftoolMapAttach(progID string, mapID string) error { 62 prog := "bpftool" 63 64 args := []string{"prog", "attach", "id", progID, "msg_verdict", "id", mapID} 65 log.WithFields(logrus.Fields{ 66 "bpftool": prog, 67 "args": args, 68 }).Debug("Map Attach BPF Object:") 69 out, err := exec.Command(prog, args...).CombinedOutput() 70 if err != nil { 71 return fmt.Errorf("Failed to attach prog(%s) to map(%s): %s: %s", progID, mapID, err, out) 72 } 73 return nil 74 } 75 76 // #bpftool cgroup attach $cgrp sock_ops /sys/fs/bpf/$bpfObject 77 func bpftoolAttach(bpfObject string) error { 78 prog := "bpftool" 79 bpffs := filepath.Join(bpf.GetMapRoot(), bpfObject) 80 cgrp := cgroups.GetCgroupRoot() 81 82 args := []string{"cgroup", "attach", cgrp, "sock_ops", "pinned", bpffs} 83 log.WithFields(logrus.Fields{ 84 "bpftool": prog, 85 "args": args, 86 }).Debug("Attach BPF Object:") 87 out, err := exec.Command(prog, args...).CombinedOutput() 88 if err != nil { 89 return fmt.Errorf("Failed to attach %s: %s: %s", bpfObject, err, out) 90 } 91 return nil 92 } 93 94 // #bpftool cgroup detach $cgrp sock_ops /sys/fs/bpf/$bpfObject 95 func bpftoolDetach(bpfObject string) error { 96 prog := "bpftool" 97 bpffs := filepath.Join(bpf.GetMapRoot(), bpfObject) 98 cgrp := cgroups.GetCgroupRoot() 99 100 args := []string{"cgroup", "detach", cgrp, "sock_ops", "pinned", bpffs} 101 log.WithFields(logrus.Fields{ 102 "bpftool": prog, 103 "args": args, 104 }).Debug("Detach BPF Object:") 105 out, err := exec.Command(prog, args...).CombinedOutput() 106 if err != nil { 107 return fmt.Errorf("Failed to detach %s: %s: %s", bpfObject, err, out) 108 } 109 return nil 110 111 } 112 113 // #bpftool prog load $bpfObject /sys/fs/bpf/sockops 114 func bpftoolLoad(bpfObject string, bpfFsFile string) error { 115 sockopsMaps := [...]string{ 116 "cilium_lxc", 117 "cilium_ipcache", 118 "cilium_metric", 119 "cilium_events", 120 "cilium_sock_ops", 121 "cilium_ep_to_policy", 122 "cilium_proxy4", "cilium_proxy6", 123 "cilium_lb6_reverse_nat", "cilium_lb4_reverse_nat", 124 "cilium_lb6_services", "cilium_lb4_services", 125 "cilium_lb6_rr_seq", "cilium_lb4_seq", 126 "cilium_lb6_rr_seq", "cilium_lb4_seq", 127 } 128 129 prog := "bpftool" 130 var mapArgList []string 131 bpffs := filepath.Join(bpf.GetMapRoot(), bpfFsFile) 132 133 maps, err := ioutil.ReadDir(filepath.Join(bpf.GetMapRoot(), "/tc/globals/")) 134 if err != nil { 135 return err 136 } 137 138 for _, f := range maps { 139 // Ignore all backing files 140 if strings.HasPrefix(f.Name(), "..") { 141 continue 142 } 143 144 use := func() bool { 145 for _, n := range sockopsMaps { 146 if f.Name() == n { 147 return true 148 } 149 } 150 return false 151 }() 152 153 if !use { 154 continue 155 } 156 157 mapString := []string{"map", "name", f.Name(), "pinned", filepath.Join(bpf.GetMapRoot(), "/tc/globals/", f.Name())} 158 mapArgList = append(mapArgList, mapString...) 159 } 160 161 args := []string{"-m", "prog", "load", bpfObject, bpffs} 162 args = append(args, mapArgList...) 163 log.WithFields(logrus.Fields{ 164 "bpftool": prog, 165 "args": args, 166 }).Debug("Load BPF Object:") 167 out, err := exec.Command(prog, args...).CombinedOutput() 168 if err != nil { 169 return fmt.Errorf("Failed to load %s: %s: %s", bpfObject, err, out) 170 } 171 return nil 172 } 173 174 // #rm $bpfObject 175 func bpftoolUnload(bpfObject string) { 176 bpffs := filepath.Join(bpf.GetMapRoot(), bpfObject) 177 178 os.Remove(bpffs) 179 } 180 181 // #bpftool prog show pinned /sys/fs/bpf/ 182 func bpftoolGetProgID(progName string) (string, error) { 183 bpffs := filepath.Join(bpf.GetMapRoot(), progName) 184 prog := "bpftool" 185 186 args := []string{"prog", "show", "pinned", bpffs} 187 log.WithFields(logrus.Fields{ 188 "bpftool": prog, 189 "args": args, 190 }).Debug("GetProgID:") 191 output, err := exec.Command(prog, args...).CombinedOutput() 192 if err != nil { 193 return "", fmt.Errorf("Failed to load %s: %s: %s", progName, err, output) 194 } 195 196 // Scrap the prog_id out of the bpftool output after libbpf is dual licensed 197 // we will use programatic API. 198 s := strings.Fields(string(output)) 199 if s[0] == "" { 200 return "", fmt.Errorf("Failed to find prog %s: %s", progName, err) 201 } 202 progID := strings.Split(s[0], ":") 203 return progID[0], nil 204 } 205 206 // #bpftool prog show pinned /sys/fs/bpf/bpf_sockops 207 // #bpftool map show id 21 208 func bpftoolGetMapID(progName string, mapName string) (int, error) { 209 bpffs := filepath.Join(bpf.GetMapRoot(), progName) 210 prog := "bpftool" 211 212 args := []string{"prog", "show", "pinned", bpffs} 213 log.WithFields(logrus.Fields{ 214 "bpftool": prog, 215 "args": args, 216 }).Debug("GetMapID:") 217 output, err := exec.Command(prog, args...).CombinedOutput() 218 if err != nil { 219 return 0, fmt.Errorf("Failed to load %s: %s: %s", progName, err, output) 220 } 221 222 // Find the mapID out of the bpftool output 223 s := strings.Fields(string(output)) 224 for i := range s { 225 if s[i] == "map_ids" { 226 id := strings.Split(s[i+1], ",") 227 for j := range id { 228 args := []string{"map", "show", "id", id[j]} 229 output, err := exec.Command(prog, args...).CombinedOutput() 230 if err != nil { 231 return 0, err 232 } 233 234 if strings.Contains(string(output), mapName) { 235 mapID, _ := strconv.Atoi(id[j]) 236 return mapID, nil 237 } 238 } 239 break 240 } 241 } 242 return 0, nil 243 } 244 245 // #bpftool map pin id map_id /sys/fs/bpf/tc/globals 246 func bpftoolPinMapID(mapName string, mapID int) error { 247 mapFile := filepath.Join(bpf.GetMapRoot(), mapPrefix, mapName) 248 prog := "bpftool" 249 250 args := []string{"map", "pin", "id", strconv.Itoa(mapID), mapFile} 251 log.WithFields(logrus.Fields{ 252 "bpftool": prog, 253 "args": args, 254 }).Debug("Map pin:") 255 out, err := exec.Command(prog, args...).CombinedOutput() 256 if err != nil { 257 return fmt.Errorf("Failed to pin map %d(%s): %s: %s", mapID, mapName, err, out) 258 } 259 260 return nil 261 } 262 263 // #clang ... | llc ... 264 func bpfCompileProg(src string, dst string) error { 265 ctx, cancel := context.WithTimeout(context.Background(), contextTimeout) 266 defer cancel() 267 268 srcpath := filepath.Join("sockops", src) 269 outpath := filepath.Join(dst) 270 271 err := loader.Compile(ctx, srcpath, outpath) 272 if err != nil { 273 return fmt.Errorf("failed compile %s: %s", srcpath, err) 274 } 275 return nil 276 } 277 278 func bpfLoadMapProg(object string, load string) error { 279 sockops := object 280 sockopsObj := filepath.Join(option.Config.StateDir, sockops) 281 sockopsLoad := load 282 283 err := bpftoolLoad(sockopsObj, sockopsLoad) 284 if err != nil { 285 return err 286 } 287 288 progID, err := bpftoolGetProgID(load) 289 if err != nil { 290 return err 291 } 292 293 _mapID, err := bpftoolGetMapID("bpf_sockops", sockMap) 294 mapID := strconv.Itoa(_mapID) 295 if err != nil { 296 return err 297 } 298 299 err = bpftoolMapAttach(progID, mapID) 300 if err != nil { 301 return err 302 } 303 return nil 304 } 305 306 // SkmsgEnable will compile and attach the SK_MSG programs to the 307 // sockmap. After this all sockets added to the cilium_sock_ops will 308 // have sendmsg/sendfile calls running through BPF program. 309 func SkmsgEnable() error { 310 err := bpfCompileProg(cIPC, oIPC) 311 if err != nil { 312 log.Error(err) 313 return err 314 } 315 316 err = bpfLoadMapProg(oIPC, eIPC) 317 if err != nil { 318 log.Error(err) 319 return err 320 } 321 log.Info("Sockmsg Enabled, bpf_redir loaded") 322 return nil 323 } 324 325 // SkmsgDisable "unloads" the SK_MSG program. This simply deletes 326 // the file associated with the program. 327 func SkmsgDisable() { 328 bpftoolUnload(eIPC) 329 } 330 331 // First user of sockops root is sockops load programs so we ensure the sockops 332 // root path no longer changes. 333 func bpfLoadAttachProg(object string, load string, mapName string) (int, int, error) { 334 sockopsObj := filepath.Join(option.Config.StateDir, object) 335 mapID := 0 336 337 err := bpftoolLoad(sockopsObj, load) 338 if err != nil { 339 return 0, 0, err 340 } 341 err = bpftoolAttach(load) 342 if err != nil { 343 return 0, 0, err 344 } 345 346 if mapName != "" { 347 mapID, err = bpftoolGetMapID(load, mapName) 348 if err != nil { 349 return 0, mapID, err 350 } 351 352 err = bpftoolPinMapID(mapName, mapID) 353 if err != nil { 354 return 0, mapID, err 355 } 356 } 357 return 0, mapID, nil 358 } 359 360 // SockmapEnable will compile sockops programs and attach the sockops programs 361 // to the cgroup. After this all TCP connect events will be filtered by a BPF 362 // sockops program. 363 func SockmapEnable() error { 364 err := bpfCompileProg(cSockops, oSockops) 365 if err != nil { 366 log.Error(err) 367 return err 368 } 369 progID, mapID, err := bpfLoadAttachProg(oSockops, eSockops, sockMap) 370 if err != nil { 371 log.Error(err) 372 return err 373 } 374 log.Infof("Sockmap Enabled: bpf_sockops prog_id %d and map_id %d loaded", progID, mapID) 375 return nil 376 } 377 378 // SockmapDisable will detach any sockmap programs from cgroups then "unload" 379 // all the programs and maps associated with it. Here "unload" just means 380 // deleting the file associated with the map. 381 func SockmapDisable() { 382 mapName := filepath.Join(mapPrefix, sockMap) 383 bpftoolDetach(eSockops) 384 bpftoolUnload(eSockops) 385 bpftoolUnload(mapName) 386 }