1
16
17
18
19
20
21
22
23
24
25
26
27 package v2
28
29 import (
30 "errors"
31 "fmt"
32 "math"
33
34 "github.com/cilium/ebpf/asm"
35 "github.com/opencontainers/runtime-spec/specs-go"
36 "golang.org/x/sys/unix"
37 )
38
39 const (
40
41 license = "Apache"
42 )
43
44
45 func DeviceFilter(devices []specs.LinuxDeviceCgroup) (asm.Instructions, string, error) {
46 p := &program{}
47 p.init()
48 for i := len(devices) - 1; i >= 0; i-- {
49 if err := p.appendDevice(devices[i]); err != nil {
50 return nil, "", err
51 }
52 }
53 insts, err := p.finalize()
54 return insts, license, err
55 }
56
57 type program struct {
58 insts asm.Instructions
59 hasWildCard bool
60 blockID int
61 }
62
63 func (p *program) init() {
64
65
70
71 p.insts = append(p.insts,
72 asm.LoadMem(asm.R2, asm.R1, 0, asm.Half))
73
74
75 p.insts = append(p.insts,
76 asm.LoadMem(asm.R3, asm.R1, 0, asm.Word),
77
78 asm.RSh.Imm32(asm.R3, 16))
79
80
81 p.insts = append(p.insts,
82 asm.LoadMem(asm.R4, asm.R1, 4, asm.Word))
83
84
85 p.insts = append(p.insts,
86 asm.LoadMem(asm.R5, asm.R1, 8, asm.Word))
87 }
88
89
90 func (p *program) appendDevice(dev specs.LinuxDeviceCgroup) error {
91 if p.blockID < 0 {
92 return errors.New("the program is finalized")
93 }
94 if p.hasWildCard {
95
96 return nil
97 }
98
99 bpfType := int32(-1)
100 hasType := true
101 switch dev.Type {
102 case string('c'):
103 bpfType = int32(unix.BPF_DEVCG_DEV_CHAR)
104 case string('b'):
105 bpfType = int32(unix.BPF_DEVCG_DEV_BLOCK)
106 case string('a'):
107 hasType = false
108 default:
109
110 return fmt.Errorf("invalid DeviceType %q", dev.Type)
111 }
112 if *dev.Major > math.MaxUint32 {
113 return fmt.Errorf("invalid major %d", *dev.Major)
114 }
115 if *dev.Minor > math.MaxUint32 {
116 return fmt.Errorf("invalid minor %d", *dev.Major)
117 }
118 hasMajor := *dev.Major >= 0
119 hasMinor := *dev.Minor >= 0
120 bpfAccess := int32(0)
121 for _, r := range dev.Access {
122 switch r {
123 case 'r':
124 bpfAccess |= unix.BPF_DEVCG_ACC_READ
125 case 'w':
126 bpfAccess |= unix.BPF_DEVCG_ACC_WRITE
127 case 'm':
128 bpfAccess |= unix.BPF_DEVCG_ACC_MKNOD
129 default:
130 return fmt.Errorf("unknown device access %v", r)
131 }
132 }
133
134 hasAccess := bpfAccess != (unix.BPF_DEVCG_ACC_READ | unix.BPF_DEVCG_ACC_WRITE | unix.BPF_DEVCG_ACC_MKNOD)
135
136 blockSym := fmt.Sprintf("block-%d", p.blockID)
137 nextBlockSym := fmt.Sprintf("block-%d", p.blockID+1)
138 prevBlockLastIdx := len(p.insts) - 1
139 if hasType {
140 p.insts = append(p.insts,
141
142 asm.JNE.Imm(asm.R2, bpfType, nextBlockSym),
143 )
144 }
145 if hasAccess {
146 p.insts = append(p.insts,
147
148 asm.Mov.Reg32(asm.R1, asm.R3),
149 asm.And.Imm32(asm.R1, bpfAccess),
150 asm.JEq.Imm(asm.R1, 0, nextBlockSym),
151 )
152 }
153 if hasMajor {
154 p.insts = append(p.insts,
155
156 asm.JNE.Imm(asm.R4, int32(*dev.Major), nextBlockSym),
157 )
158 }
159 if hasMinor {
160 p.insts = append(p.insts,
161
162 asm.JNE.Imm(asm.R5, int32(*dev.Minor), nextBlockSym),
163 )
164 }
165 if !hasType && !hasAccess && !hasMajor && !hasMinor {
166 p.hasWildCard = true
167 }
168 p.insts = append(p.insts, acceptBlock(dev.Allow)...)
169
170 p.insts[prevBlockLastIdx+1] = p.insts[prevBlockLastIdx+1].Sym(blockSym)
171 p.blockID++
172 return nil
173 }
174
175 func (p *program) finalize() (asm.Instructions, error) {
176 if p.hasWildCard {
177
178 return p.insts, nil
179 }
180 blockSym := fmt.Sprintf("block-%d", p.blockID)
181 p.insts = append(p.insts,
182
183 asm.Mov.Imm32(asm.R0, 0).Sym(blockSym),
184 asm.Return(),
185 )
186 p.blockID = -1
187 return p.insts, nil
188 }
189
190 func acceptBlock(accept bool) asm.Instructions {
191 v := int32(0)
192 if accept {
193 v = 1
194 }
195 return []asm.Instruction{
196
197 asm.Mov.Imm32(asm.R0, v),
198 asm.Return(),
199 }
200 }
201
View as plain text