github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/pkg/ebpf/ebpf_linux.go (about)

     1  // +build !rhel6
     2  
     3  package ebpf
     4  
     5  import (
     6  	"bytes"
     7  	"encoding/binary"
     8  	"os"
     9  	"path/filepath"
    10  	"strconv"
    11  	"strings"
    12  	"unsafe"
    13  
    14  	bpflib "github.com/iovisor/gobpf/elf"
    15  	"github.com/iovisor/gobpf/pkg/bpffs"
    16  	provider "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/aclprovider"
    17  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/connection"
    18  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/ebpf/bpfbuild"
    19  	"go.uber.org/zap"
    20  )
    21  
    22  type ebpfModule struct {
    23  	m          *bpflib.Module
    24  	sessionMap *bpflib.Map
    25  	bpfPath    string
    26  }
    27  
    28  type flow struct {
    29  	srcIP   uint32
    30  	dstIP   uint32
    31  	srcPort uint16
    32  	dstPort uint16
    33  }
    34  
    35  const bpfPath = "/sys/fs/bpf/"
    36  const bpfPrefix = "app-ack"
    37  
    38  func removeOldBPFFiles() {
    39  
    40  	removeFiles := func(path string, info os.FileInfo, err error) error {
    41  		if strings.Contains(path, bpfPrefix) {
    42  			if err := os.Remove(path); err != nil {
    43  				zap.L().Debug("Failed to remove file", zap.String("path", path), zap.Error(err))
    44  			}
    45  		}
    46  		return nil
    47  	}
    48  
    49  	filepath.Walk(bpfPath, removeFiles) // nolint
    50  }
    51  
    52  // IsEBPFSupported is called once by the master enforcer to test if
    53  // the system supports eBPF.
    54  func IsEBPFSupported() bool {
    55  
    56  	if err := bpffs.Mount(); err != nil {
    57  		zap.L().Info("bpf mount failed", zap.Error(err))
    58  		return false
    59  	}
    60  
    61  	var bpf BPFModule
    62  
    63  	if bpf = LoadBPF(); bpf == nil {
    64  		return false
    65  	}
    66  
    67  	if err := provider.TestIptablesPinned(bpf.GetBPFPath()); err != nil {
    68  		zap.L().Info("Kernel doesn't support iptables pinned path", zap.Error(err))
    69  		return false
    70  	}
    71  
    72  	removeOldBPFFiles()
    73  	return true
    74  }
    75  
    76  // LoadBPF loads the bpf object in the memory and also pins the bpf to the file system.
    77  func LoadBPF() BPFModule {
    78  	bpf := &ebpfModule{}
    79  
    80  	bpf.bpfPath = bpfPath + bpfPrefix + strconv.Itoa(os.Getpid())
    81  	if err := os.Remove(bpf.bpfPath); err != nil {
    82  		if !os.IsNotExist(err) {
    83  			zap.L().Debug("Failed to remove bpf file", zap.Error(err))
    84  		}
    85  	}
    86  
    87  	buf, err := bpfbuild.Asset("socket-filter-bpf.o")
    88  	if err != nil {
    89  		zap.L().Info("Failed to locate asset socket-filter-bpf", zap.Error(err))
    90  		return nil
    91  	}
    92  
    93  	reader := bytes.NewReader(buf)
    94  	m := bpflib.NewModuleFromReader(reader)
    95  
    96  	if err := m.Load(nil); err != nil {
    97  		zap.L().Info("Failed to load BPF in kernel", zap.Error(err))
    98  		return nil
    99  	}
   100  
   101  	sfAppAck := m.SocketFilter("socket/app_ack")
   102  	if sfAppAck == nil {
   103  		zap.L().Info("Failed to load socket filter app_ack")
   104  		return nil
   105  	}
   106  
   107  	if err := bpflib.PinObject(sfAppAck.Fd(), bpf.bpfPath); err != nil {
   108  		zap.L().Info("Failed to pin bpf to file system", zap.Error(err))
   109  		return nil
   110  	}
   111  
   112  	sessionMap := m.Map("sessions")
   113  	if sessionMap == nil {
   114  		zap.L().Info("Failed to load sessions map")
   115  		return nil
   116  	}
   117  
   118  	bpf.m = m
   119  	bpf.sessionMap = sessionMap
   120  
   121  	return bpf
   122  }
   123  
   124  func (ebpf *ebpfModule) CreateFlow(tcpTuple *connection.TCPTuple) {
   125  	var key flow
   126  	var val uint8
   127  
   128  	key.srcIP = binary.BigEndian.Uint32(tcpTuple.SourceAddress)
   129  	key.dstIP = binary.BigEndian.Uint32(tcpTuple.DestinationAddress)
   130  	key.srcPort = tcpTuple.SourcePort
   131  	key.dstPort = tcpTuple.DestinationPort
   132  
   133  	val = 1
   134  
   135  	err := ebpf.m.UpdateElement(ebpf.sessionMap, unsafe.Pointer(&key), unsafe.Pointer(&val), 0)
   136  	if err != nil {
   137  		zap.L().Debug("Update bpf map failed",
   138  			zap.String("packet", tcpTuple.String()),
   139  			zap.Error(err))
   140  	}
   141  }
   142  
   143  func (ebpf *ebpfModule) RemoveFlow(tcpTuple *connection.TCPTuple) {
   144  	var key flow
   145  
   146  	key.srcIP = binary.BigEndian.Uint32(tcpTuple.SourceAddress)
   147  	key.dstIP = binary.BigEndian.Uint32(tcpTuple.DestinationAddress)
   148  	key.srcPort = tcpTuple.SourcePort
   149  	key.dstPort = tcpTuple.DestinationPort
   150  
   151  	err := ebpf.m.DeleteElement(ebpf.sessionMap, unsafe.Pointer(&key))
   152  	if err != nil {
   153  		zap.L().Debug("Delete bpf map failed",
   154  			zap.String("packet", tcpTuple.String()),
   155  			zap.Error(err))
   156  	}
   157  }
   158  
   159  func (ebpf *ebpfModule) GetBPFPath() string {
   160  	return ebpf.bpfPath
   161  }
   162  
   163  func (ebpf *ebpfModule) Cleanup() {
   164  	if err := os.Remove(ebpf.bpfPath); err != nil {
   165  		zap.L().Error("Failed to remove bpf file during cleanup", zap.Error(err))
   166  	}
   167  }