-
Notifications
You must be signed in to change notification settings - Fork 51
/
cgnetcls.go
293 lines (226 loc) · 7.62 KB
/
cgnetcls.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
// +build linux,!darwin,!windows
//Package cgnetcls implements functionality to manage classid for processes belonging to different cgroups
package cgnetcls
import (
"bufio"
"errors"
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
"sync/atomic"
"syscall"
"github.com/kardianos/osext"
"go.uber.org/zap"
)
const (
// TriremeBasePath is the base path of the Trireme tree in cgroups
TriremeBasePath = "/trireme"
// CgroupNameTag is the tag for the cgroup name
CgroupNameTag = "@cgroup_name"
// CgroupMarkTag is the tag for the cgroup mark
CgroupMarkTag = "@cgroup_mark"
// PortTag is the tag for a port
PortTag = "@usr:port"
markFile = "/net_cls.classid"
procs = "/cgroup.procs"
releaseAgentConfFile = "/release_agent"
notifyOnReleaseFile = "/notify_on_release"
initialmarkval = 100
)
var basePath = "/sys/fs/cgroup/net_cls"
var markval uint64 = initialmarkval
//Empty receiver struct
type netCls struct {
markchan chan uint64
ReleaseAgentPath string
}
//Initialize only ince
func init() {
mountCgroupController()
}
// Creategroup creates a cgroup/net_cls structure and writes the allocated classid to the file.
// To add a new process to this cgroup we need to write to the cgroup file
func (s *netCls) Creategroup(cgroupname string) error {
if !strings.HasPrefix(cgroupname, "/") {
cgroupname = "/" + cgroupname
}
//Create the directory structure
_, err := os.Stat(basePath + procs)
if os.IsNotExist(err) {
syscall.Mount("cgroup", basePath, "cgroup", 0, "net_cls,net_prio")
}
os.MkdirAll((basePath + TriremeBasePath + cgroupname), 0700)
//Write to the notify on release file and release agent files
if s.ReleaseAgentPath != "" {
err = ioutil.WriteFile(basePath+releaseAgentConfFile, []byte(s.ReleaseAgentPath), 0644)
if err != nil {
return fmt.Errorf("Failed to register a release agent error %s", err.Error())
}
err = ioutil.WriteFile(basePath+notifyOnReleaseFile, []byte("1"), 0644)
if err != nil {
return fmt.Errorf("Failed to write to the notify file %s", err.Error())
}
err = ioutil.WriteFile(basePath+TriremeBasePath+notifyOnReleaseFile, []byte("1"), 0644)
if err != nil {
return fmt.Errorf("Failed to write to the notify file %s", err.Error())
}
err = ioutil.WriteFile(basePath+TriremeBasePath+cgroupname+notifyOnReleaseFile, []byte("1"), 0644)
if err != nil {
return fmt.Errorf("Failed to write to the notify file %s", err.Error())
}
}
return nil
}
//AssignMark writes the mark value to net_cls.classid file.
func (s *netCls) AssignMark(cgroupname string, mark uint64) error {
if !strings.HasPrefix(cgroupname, "/") {
cgroupname = "/" + cgroupname
}
_, err := os.Stat(basePath + TriremeBasePath + cgroupname)
if os.IsNotExist(err) {
return errors.New("Cgroup does not exist")
}
//16 is the base since the mark file expects hexadecimal values
markval := "0x" + (strconv.FormatUint(mark, 16))
if err := ioutil.WriteFile(basePath+TriremeBasePath+cgroupname+markFile, []byte(markval), 0644); err != nil {
return errors.New("Failed to write to net_cls.classid file for new cgroup")
}
return nil
}
// AddProcess adds the process to the net_cls group
func (s *netCls) AddProcess(cgroupname string, pid int) error {
if !strings.HasPrefix(cgroupname, "/") {
cgroupname = "/" + cgroupname
}
_, err := os.Stat(basePath + TriremeBasePath + cgroupname)
if os.IsNotExist(err) {
return errors.New("Cannot add process. Cgroup does not exist")
}
PID := []byte(strconv.Itoa(pid))
if err := syscall.Kill(pid, 0); err != nil {
return nil
}
if err := ioutil.WriteFile(basePath+TriremeBasePath+cgroupname+procs, PID, 0644); err != nil {
return errors.New("Cannot add process. Failed to add process to cgroup")
}
return nil
}
//RemoveProcess removes the process from the cgroup by writing the pid to the
//top of net_cls cgroup cgroup.procs
func (s *netCls) RemoveProcess(cgroupname string, pid int) error {
if !strings.HasPrefix(cgroupname, "/") {
cgroupname = "/" + cgroupname
}
_, err := os.Stat(basePath + TriremeBasePath + cgroupname)
if os.IsNotExist(err) {
return errors.New("Cannot clean up process. Cgroup does not exist")
}
data, err := ioutil.ReadFile(basePath + procs)
if err != nil || !strings.Contains(string(data), strconv.Itoa(pid)) {
return errors.New("Cannot cleanup process. Process is not a part of this cgroup")
}
if err := ioutil.WriteFile(basePath+procs, []byte(strconv.Itoa(pid)), 0644); err != nil {
return errors.New("Cannot clean up process. Failed to remove process to cgroup")
}
return nil
}
// DeleteCgroup assumes the cgroup is already empty and destroys the directory structure.
// It will return an error if the group is not empty. Use RempoveProcess to remove all processes
// Before we try deletion
func (s *netCls) DeleteCgroup(cgroupname string) error {
if !strings.HasPrefix(cgroupname, "/") {
cgroupname = "/" + cgroupname
}
_, err := os.Stat(basePath + TriremeBasePath + cgroupname)
if os.IsNotExist(err) {
zap.L().Debug("Group already deleted", zap.Error(err))
return nil
}
err = os.Remove(basePath + TriremeBasePath + cgroupname)
if err != nil {
return fmt.Errorf("Failed to delete cgroup %s error returned %s", cgroupname, err.Error())
}
return nil
}
//Deletebasepath removes the base aporeto directory which comes as a separate event when we are not managing any processes
func (s *netCls) Deletebasepath(cgroupName string) bool {
if !strings.HasPrefix(cgroupName, "/") {
cgroupName = "/" + cgroupName
}
if cgroupName == TriremeBasePath {
os.Remove(basePath + cgroupName)
return true
}
return false
}
func mountCgroupController() {
mounts, _ := ioutil.ReadFile("/proc/mounts")
sc := bufio.NewScanner(strings.NewReader(string(mounts)))
var netCls = false
var cgroupMount string
for sc.Scan() {
if strings.HasPrefix(sc.Text(), "cgroup") {
cgroupMount = strings.Split(sc.Text(), " ")[1]
cgroupMount = cgroupMount[:strings.LastIndex(cgroupMount, "/")]
if strings.Contains(sc.Text(), "net_cls") {
basePath = strings.Split(sc.Text(), " ")[1]
netCls = true
return
}
}
}
if len(cgroupMount) == 0 {
zap.L().Error("Cgroups are not enabled or net_cls is not mounted")
return
}
if !netCls {
basePath = cgroupMount + "/net_cls"
os.MkdirAll(basePath, 0700)
syscall.Mount("cgroup", basePath, "cgroup", 0, "net_cls,net_prio")
return
}
}
// NewDockerCgroupNetController returns a handle to call functions on the cgroup net_cls controller
func NewDockerCgroupNetController() Cgroupnetcls {
controller := &netCls{
markchan: make(chan uint64),
ReleaseAgentPath: "",
}
return controller
}
//NewCgroupNetController returns a handle to call functions on the cgroup net_cls controller
func NewCgroupNetController(releasePath string) Cgroupnetcls {
binpath, _ := osext.Executable()
controller := &netCls{
markchan: make(chan uint64),
ReleaseAgentPath: binpath,
}
if releasePath != "" {
controller.ReleaseAgentPath = releasePath
}
return controller
}
// MarkVal returns a new Mark Value
func MarkVal() uint64 {
return atomic.AddUint64(&markval, 1)
}
// ListCgroupProcesses lists the processes of the cgroup
func ListCgroupProcesses(cgroupname string) ([]string, error) {
_, err := os.Stat(basePath + TriremeBasePath + cgroupname)
if os.IsNotExist(err) {
return []string{}, errors.New("Cgroup does not exist")
}
data, err := ioutil.ReadFile(basePath + TriremeBasePath + cgroupname + "/cgroup.procs")
if err != nil {
return []string{}, errors.New("Cannot read procs file")
}
procs := []string{}
for _, line := range strings.Split(string(data), "\n") {
if len(line) > 0 {
procs = append(procs, string(line))
}
}
return procs, nil
}