github.com/imran-kn/cilium-fork@v1.6.9/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  }