1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package chaosdaemon
17
18 import (
19 "context"
20 "fmt"
21 "strings"
22
23 "github.com/go-logr/logr"
24 "github.com/golang/protobuf/ptypes/empty"
25
26 "github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
27 "github.com/chaos-mesh/chaos-mesh/pkg/bpm"
28 pb "github.com/chaos-mesh/chaos-mesh/pkg/chaosdaemon/pb"
29 "github.com/chaos-mesh/chaos-mesh/pkg/chaosdaemon/util"
30 )
31
32 const (
33 ipsetExistErr = "set with the same name already exists"
34 ipExistErr = "it's already added"
35 ipsetNewNameExistErr = "a set with the new name already exists"
36 )
37
38 func (s *DaemonServer) FlushIPSets(ctx context.Context, req *pb.IPSetsRequest) (*empty.Empty, error) {
39 log := s.getLoggerFromContext(ctx)
40 log.Info("flush ipset", "request", req)
41
42 pid, err := s.crClient.GetPidFromContainerID(ctx, req.ContainerId)
43 if err != nil {
44 log.Error(err, "error while getting PID")
45 return nil, err
46 }
47
48 for _, ipset := range req.Ipsets {
49
50
51
52
53
54
55
56
57 ipset := ipset
58 s.IPSetLocker.Lock(ipset.Name)
59 err := flushIPSet(ctx, log, req.EnterNS, pid, ipset)
60 s.IPSetLocker.Unlock(ipset.Name)
61 if err != nil {
62 return nil, err
63 }
64 }
65
66 return &empty.Empty{}, nil
67 }
68
69 func flushIPSet(ctx context.Context, log logr.Logger, enterNS bool, pid uint32, set *pb.IPSet) error {
70 name := set.Name
71
72
73 tmpName := fmt.Sprintf("%sold", name)
74
75 ipSetType := v1alpha1.IPSetType(set.Type)
76 var values []string
77
78 switch ipSetType {
79 case v1alpha1.SetIPSet:
80 values = set.SetNames
81 case v1alpha1.NetIPSet:
82 values = set.Cidrs
83 case v1alpha1.NetPortIPSet:
84 for _, cidrAndPort := range set.CidrAndPorts {
85 values = append(values, fmt.Sprintf("%s,%d", cidrAndPort.Cidr, cidrAndPort.Port))
86 }
87 default:
88 return fmt.Errorf("unexpected IP set type: %s", ipSetType)
89 }
90
91
92
93 if err := createIPSet(ctx, log, enterNS, pid, tmpName, ipSetType); err != nil {
94 return err
95 }
96
97
98 for _, value := range values {
99 if err := addToIPSet(ctx, log, enterNS, pid, tmpName, value); err != nil {
100 return err
101 }
102 }
103
104
105 err := renameIPSet(ctx, log, enterNS, pid, tmpName, name)
106
107 return err
108 }
109
110 func createIPSet(ctx context.Context, log logr.Logger, enterNS bool, pid uint32, name string, ipSetType v1alpha1.IPSetType) error {
111
112 if len(name) > 31 {
113 name = name[:31]
114 }
115
116 processBuilder := bpm.DefaultProcessBuilder("ipset", "create", name, string(ipSetType)).SetContext(ctx)
117 if enterNS {
118 processBuilder = processBuilder.SetNS(pid, bpm.NetNS)
119 }
120
121 cmd := processBuilder.Build(ctx)
122 log.Info("create ipset", "command", cmd.String())
123
124 out, err := cmd.CombinedOutput()
125 if err != nil {
126 output := string(out)
127 if !strings.Contains(output, ipsetExistErr) {
128 log.Error(err, "ipset create error", "command", cmd.String(), "output", output)
129 return util.EncodeOutputToError(out, err)
130 }
131
132 processBuilder = bpm.DefaultProcessBuilder("ipset", "flush", name).SetContext(ctx)
133 if enterNS {
134 processBuilder = processBuilder.SetNS(pid, bpm.NetNS)
135 }
136
137 cmd = processBuilder.Build(ctx)
138 log.Info("flush ipset", "command", cmd.String())
139
140 out, err := cmd.CombinedOutput()
141 if err != nil {
142 log.Error(err, "ipset flush error", "command", cmd.String(), "output", string(out))
143 return util.EncodeOutputToError(out, err)
144 }
145 }
146
147 return nil
148 }
149
150 func addToIPSet(ctx context.Context, log logr.Logger, enterNS bool, pid uint32, name string, value string) error {
151 processBuilder := bpm.DefaultProcessBuilder("ipset", "add", name, value).SetContext(ctx)
152 if enterNS {
153 processBuilder = processBuilder.SetNS(pid, bpm.NetNS)
154 }
155 cmd := processBuilder.Build(ctx)
156 log.Info("add to ipset", "command", cmd.String())
157
158 out, err := cmd.CombinedOutput()
159 if err != nil {
160 output := string(out)
161 if !strings.Contains(output, ipExistErr) {
162 log.Error(err, "ipset add error", "command", cmd.String(), "output", output)
163 return util.EncodeOutputToError(out, err)
164 }
165 }
166
167 return nil
168 }
169
170 func renameIPSet(ctx context.Context, log logr.Logger, enterNS bool, pid uint32, oldName string, newName string) error {
171 processBuilder := bpm.DefaultProcessBuilder("ipset", "rename", oldName, newName).SetContext(ctx)
172 if enterNS {
173 processBuilder = processBuilder.SetNS(pid, bpm.NetNS)
174 }
175
176 cmd := processBuilder.Build(ctx)
177 log.Info("rename ipset", "command", cmd.String())
178
179 out, err := cmd.CombinedOutput()
180 if err != nil {
181 output := string(out)
182 if !strings.Contains(output, ipsetNewNameExistErr) {
183 log.Error(err, "rename ipset failed", "command", cmd.String(), "output", output)
184 return util.EncodeOutputToError(out, err)
185 }
186
187
188 processBuilder = bpm.DefaultProcessBuilder("ipset", "swap", oldName, newName).SetContext(ctx)
189 if enterNS {
190 processBuilder = processBuilder.SetNS(pid, bpm.NetNS)
191 }
192 cmd := processBuilder.Build(ctx)
193 log.Info("swap ipset", "command", cmd.String())
194
195 out, err := cmd.CombinedOutput()
196 if err != nil {
197 log.Error(err, "swap ipset failed", "command", cmd.String(), "output", string(out))
198 return util.EncodeOutputToError(out, err)
199 }
200 }
201 return nil
202 }
203