github.com/kubeshark/ebpf@v0.9.2/features/misc.go (about) 1 package features 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "sync" 8 9 "github.com/kubeshark/ebpf" 10 "github.com/kubeshark/ebpf/asm" 11 "github.com/kubeshark/ebpf/internal" 12 "github.com/kubeshark/ebpf/internal/sys" 13 "github.com/kubeshark/ebpf/internal/unix" 14 ) 15 16 func init() { 17 miscs.miscTypes = make(map[miscType]error) 18 } 19 20 var ( 21 miscs miscCache 22 ) 23 24 type miscCache struct { 25 sync.Mutex 26 miscTypes map[miscType]error 27 } 28 29 type miscType uint32 30 31 const ( 32 // largeInsn support introduced in Linux 5.2 33 // commit c04c0d2b968ac45d6ef020316808ef6c82325a82 34 largeInsn miscType = iota 35 // boundedLoops support introduced in Linux 5.3 36 // commit 2589726d12a1b12eaaa93c7f1ea64287e383c7a5 37 boundedLoops 38 // v2ISA support introduced in Linux 4.14 39 // commit 92b31a9af73b3a3fc801899335d6c47966351830 40 v2ISA 41 // v3ISA support introduced in Linux 5.1 42 // commit 092ed0968bb648cd18e8a0430cd0a8a71727315c 43 v3ISA 44 ) 45 46 const ( 47 maxInsns = 4096 48 ) 49 50 // HaveLargeInstructions probes the running kernel if more than 4096 instructions 51 // per program are supported. 52 // 53 // See the package documentation for the meaning of the error return value. 54 func HaveLargeInstructions() error { 55 return probeMisc(largeInsn) 56 } 57 58 // HaveBoundedLoops probes the running kernel if bounded loops are supported. 59 // 60 // See the package documentation for the meaning of the error return value. 61 func HaveBoundedLoops() error { 62 return probeMisc(boundedLoops) 63 } 64 65 // HaveV2ISA probes the running kernel if instructions of the v2 ISA are supported. 66 // 67 // See the package documentation for the meaning of the error return value. 68 func HaveV2ISA() error { 69 return probeMisc(v2ISA) 70 } 71 72 // HaveV3ISA probes the running kernel if instructions of the v3 ISA are supported. 73 // 74 // See the package documentation for the meaning of the error return value. 75 func HaveV3ISA() error { 76 return probeMisc(v3ISA) 77 } 78 79 // probeMisc checks the kernel for a given supported misc by creating 80 // a specialized program probe and loading it. 81 func probeMisc(mt miscType) error { 82 mc.Lock() 83 defer mc.Unlock() 84 err, ok := miscs.miscTypes[mt] 85 if ok { 86 return err 87 } 88 89 attr, err := createMiscProbeAttr(mt) 90 if err != nil { 91 return fmt.Errorf("couldn't create the attributes for the probe: %w", err) 92 } 93 94 fd, err := sys.ProgLoad(attr) 95 96 switch { 97 // EINVAL occurs when attempting to create a program with an unknown type. 98 // E2BIG occurs when ProgLoadAttr contains non-zero bytes past the end 99 // of the struct known by the running kernel, meaning the kernel is too old 100 // to support the given map type. 101 case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG): 102 err = ebpf.ErrNotSupported 103 104 // EPERM is kept as-is and is not converted or wrapped. 105 case errors.Is(err, unix.EPERM): 106 break 107 108 // Wrap unexpected errors. 109 case err != nil: 110 err = fmt.Errorf("unexpected error during feature probe: %w", err) 111 112 default: 113 fd.Close() 114 } 115 116 miscs.miscTypes[mt] = err 117 118 return err 119 } 120 121 func createMiscProbeAttr(mt miscType) (*sys.ProgLoadAttr, error) { 122 var insns asm.Instructions 123 switch mt { 124 case largeInsn: 125 for i := 0; i < maxInsns; i++ { 126 insns = append(insns, asm.Mov.Imm(asm.R0, 1)) 127 } 128 insns = append(insns, asm.Return()) 129 case boundedLoops: 130 insns = asm.Instructions{ 131 asm.Mov.Imm(asm.R0, 10), 132 asm.Sub.Imm(asm.R0, 1).WithSymbol("loop"), 133 asm.JNE.Imm(asm.R0, 0, "loop"), 134 asm.Return(), 135 } 136 case v2ISA: 137 insns = asm.Instructions{ 138 asm.Mov.Imm(asm.R0, 0), 139 asm.JLT.Imm(asm.R0, 0, "exit"), 140 asm.Mov.Imm(asm.R0, 1), 141 asm.Return().WithSymbol("exit"), 142 } 143 case v3ISA: 144 insns = asm.Instructions{ 145 asm.Mov.Imm(asm.R0, 0), 146 asm.JLT.Imm32(asm.R0, 0, "exit"), 147 asm.Mov.Imm(asm.R0, 1), 148 asm.Return().WithSymbol("exit"), 149 } 150 default: 151 return nil, fmt.Errorf("misc probe %d not implemented", mt) 152 } 153 154 buf := bytes.NewBuffer(make([]byte, 0, insns.Size())) 155 if err := insns.Marshal(buf, internal.NativeEndian); err != nil { 156 return nil, err 157 } 158 159 bytecode := buf.Bytes() 160 instructions := sys.NewSlicePointer(bytecode) 161 162 return &sys.ProgLoadAttr{ 163 ProgType: sys.BPF_PROG_TYPE_SOCKET_FILTER, 164 Insns: instructions, 165 InsnCnt: uint32(len(bytecode) / asm.InstructionSize), 166 License: sys.NewStringPointer("MIT"), 167 }, nil 168 }