Skip to content

Commit

Permalink
add program bypass support
Browse files Browse the repository at this point in the history
  • Loading branch information
brycekahle committed May 15, 2024
1 parent 623f5d0 commit 547af68
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 28 deletions.
67 changes: 67 additions & 0 deletions manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ type Manager struct {
netlinkSocketCache *netlinkSocketCache
state state
stateLock sync.RWMutex
bypassIndexes map[string]uint32
maxBypassIndex uint32

// Probes - List of probes handled by the manager
Probes []*Probe
Expand Down Expand Up @@ -447,6 +449,8 @@ func (m *Manager) InitWithOptions(elf io.ReaderAt, options Options) error {
if m.options.DefaultRingBufferSize == 0 {
m.options.DefaultRingBufferSize = os.Getpagesize() * 16
}
// start with 1, so we know if programs even have a valid index set
m.maxBypassIndex = 1

// perform a quick sanity check on the provided probes and maps
if err := m.sanityCheck(); err != nil {
Expand Down Expand Up @@ -522,6 +526,12 @@ func (m *Manager) InitWithOptions(elf io.ReaderAt, options Options) error {
}
}

var bypassMap *Map
if _, ok := m.collectionSpec.Maps[bypassMapName]; ok {
bypassMap = &Map{Name: bypassMapName}
m.Maps = append(m.Maps, bypassMap)
}

// Match Maps and program specs
if err = m.matchSpecs(); err != nil {
m.stateLock.Unlock()
Expand All @@ -530,6 +540,50 @@ func (m *Manager) InitWithOptions(elf io.ReaderAt, options Options) error {

// Configure activated probes
m.activateProbes()

// setup bypass indexes and add constant editors
if bypassMap != nil {
m.bypassIndexes = make(map[string]uint32, len(m.collectionSpec.Programs))
for progName, prog := range m.collectionSpec.Programs {
if prog.Type == ebpf.SocketFilter {
continue
}
m.options.ConstantEditors = append(m.options.ConstantEditors, ConstantEditor{
Name: "bypass_program_index",
Value: uint64(m.maxBypassIndex),
ProbeIdentificationPairs: []ProbeIdentificationPair{{
EBPFFuncName: progName,
}},
})
m.bypassIndexes[progName] = m.maxBypassIndex
m.maxBypassIndex += 1
}

for _, mProbe := range m.Probes {
if idx, ok := m.bypassIndexes[mProbe.GetEBPFFuncName()]; ok {
mProbe.bypassIndex = idx
mProbe.bypassMap = bypassMap
}
}
cpus, err := ebpf.PossibleCPU()
if err != nil {
m.stateLock.Unlock()
return err
}
if len(bypassSlice) == 0 {
bypassSlice = make([]uint32, cpus)
for i := range bypassSlice {
bypassSlice[i] = 1
}
}
if len(enableSlice) == 0 {
enableSlice = make([]uint32, cpus)
for i := range enableSlice {
enableSlice[i] = 0
}
}
}

m.state = initialized
m.stateLock.Unlock()
resetManager := func(m *Manager) {
Expand Down Expand Up @@ -908,6 +962,19 @@ func (m *Manager) AddHook(UID string, newProbe *Probe) error {
newProbe.program = clonedProg
newProbe.programSpec = progSpec

var bypassMap *Map
for _, mp := range m.Maps {
if mp.Name == bypassMapName {
bypassMap = mp
break
}
}
bypassIndex, ok := m.bypassIndexes[newProbe.EBPFFuncName]
if ok && bypassMap != nil {
newProbe.bypassIndex = bypassIndex
newProbe.bypassMap = bypassMap
}

// init program
if err = newProbe.init(m); err != nil {
// clean up
Expand Down
12 changes: 0 additions & 12 deletions perf_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,6 @@ func (pe *perfEventLink) Close() error {
return pe.fd.Close()
}

func (pe *perfEventLink) Pause() error {
return ioctlPerfEventDisable(pe.fd)
}

func (pe *perfEventLink) Resume() error {
return ioctlPerfEventEnable(pe.fd)
}

func attachPerfEvent(pe *perfEventLink, prog *ebpf.Program) error {
if err := ioctlPerfEventSetBPF(pe.fd, prog.FD()); err != nil {
return fmt.Errorf("set perf event bpf: %w", err)
Expand Down Expand Up @@ -211,7 +203,3 @@ func ioctlPerfEventSetBPF(perfEventOpenFD *fd, progFD int) error {
func ioctlPerfEventEnable(perfEventOpenFD *fd) error {
return unix.IoctlSetInt(int(perfEventOpenFD.raw), unix.PERF_EVENT_IOC_ENABLE, 0)
}

func ioctlPerfEventDisable(perfEventOpenFD *fd) error {
return unix.IoctlSetInt(int(perfEventOpenFD.raw), unix.PERF_EVENT_IOC_DISABLE, 0)
}
51 changes: 35 additions & 16 deletions probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ const (
AttachWithProbeEvents
)

const bypassMapName = "program_bypassed"

var bypassSlice []uint32
var enableSlice []uint32

// Probe - Main eBPF probe wrapper. This structure is used to store the required data to attach a loaded eBPF
// program to its hook point.
type Probe struct {
Expand All @@ -45,6 +50,8 @@ type Probe struct {
tcFilter netlink.BpfFilter
tcClsActQdisc netlink.Qdisc
progLink io.Closer
bypassIndex uint32
bypassMap *Map

// lastError - stores the last error that the probe encountered, it is used to surface a more useful error message
// when one of the validators (see Options.ActivatedProbes) fails.
Expand Down Expand Up @@ -583,17 +590,23 @@ func (p *Probe) pause() error {
}

v, ok := p.progLink.(pauser)
if !ok {
return fmt.Errorf("pause not supported for program type %s", p.programSpec.Type)
if ok {
if err := v.Pause(); err != nil {
p.lastError = err
return fmt.Errorf("error pausing probe %s: %w", p.ProbeIdentificationPair, err)
}
p.state = paused
return nil
}

if err := v.Pause(); err != nil {
p.lastError = err
return fmt.Errorf("error pausing probe %s: %w", p.ProbeIdentificationPair, err)
if p.bypassIndex > 0 && p.bypassMap != nil {
if err := p.bypassMap.array.Update(p.bypassIndex, bypassSlice, ebpf.UpdateExist); err != nil {
return err
}
p.state = paused
return nil
}

p.state = paused
return nil
return fmt.Errorf("pause not supported for program %s", p.ProbeIdentificationPair)
}

func (p *Probe) resume() error {
Expand All @@ -604,17 +617,23 @@ func (p *Probe) resume() error {
}

v, ok := p.progLink.(pauser)
if !ok {
return fmt.Errorf("resume not supported for program type %s", p.programSpec.Type)
if ok {
if err := v.Resume(); err != nil {
p.lastError = err
return fmt.Errorf("error resuming probe %s: %w", p.ProbeIdentificationPair, err)
}
p.state = running
return nil
}

if err := v.Resume(); err != nil {
p.lastError = err
return fmt.Errorf("error resuming probe %s: %w", p.ProbeIdentificationPair, err)
if p.bypassIndex > 0 && p.bypassMap != nil {
if err := p.bypassMap.array.Update(p.bypassIndex, enableSlice, ebpf.UpdateExist); err != nil {
return err
}
p.state = running
return nil
}

p.state = running
return nil
return fmt.Errorf("resume not supported for program %s", p.ProbeIdentificationPair)
}

// Detach - Detaches the probe from its hook point depending on the program type and the provided parameters. This
Expand Down

0 comments on commit 547af68

Please sign in to comment.