1
2
3
4
19
20 package ipset
21
22 import (
23 "bytes"
24 "fmt"
25 "regexp"
26 "strconv"
27 "strings"
28
29 "k8s.io/klog/v2"
30 utilexec "k8s.io/utils/exec"
31 netutils "k8s.io/utils/net"
32 )
33
34 var validationError = fmt.Errorf("failed to validate entry for ipset")
35
36
37 type Interface interface {
38
39 FlushSet(set string) error
40
41 DestroySet(set string) error
42
43 DestroyAllSets() error
44
45 CreateSet(set *IPSet, ignoreExistErr bool) error
46
47 AddEntry(entry string, set *IPSet, ignoreExistErr bool) error
48
49 DelEntry(entry string, set string) error
50
51 TestEntry(entry string, set string) (bool, error)
52
53 ListEntries(set string) ([]string, error)
54
55 ListSets() ([]string, error)
56
57 GetVersion() (string, error)
58 }
59
60
61 const IPSetCmd = "ipset"
62
63
64
65
66
67
68
69
70
71
72
73
74 var EntryMemberPattern = "(?m)^(.*\n)*Members:\n"
75
76
77
78 var VersionPattern = "v[0-9]+\\.[0-9]+"
79
80
81 type IPSet struct {
82
83 Name string
84
85 SetType Type
86
87
88 HashFamily string
89
90 HashSize int
91
92 MaxElem int
93
94 PortRange string
95
96 Comment string
97 }
98
99
100 func (set *IPSet) Validate() error {
101
102 if set.SetType == HashIPPort || set.SetType == HashIPPortIP || set.SetType == HashIPPortNet {
103 if err := validateHashFamily(set.HashFamily); err != nil {
104 return err
105 }
106 }
107
108 if err := validateIPSetType(set.SetType); err != nil {
109 return err
110 }
111
112 if set.SetType == BitmapPort {
113 if err := validatePortRange(set.PortRange); err != nil {
114 return err
115 }
116 }
117
118 if set.HashSize <= 0 {
119 return fmt.Errorf("invalid HashSize: %d", set.HashSize)
120 }
121
122 if set.MaxElem <= 0 {
123 return fmt.Errorf("invalid MaxElem %d", set.MaxElem)
124 }
125
126 return nil
127 }
128
129
130 func (set *IPSet) setIPSetDefaults() {
131
132 if set.HashSize == 0 {
133 set.HashSize = 1024
134 }
135 if set.MaxElem == 0 {
136 set.MaxElem = 65536
137 }
138
139 if set.HashFamily == "" {
140 set.HashFamily = ProtocolFamilyIPV4
141 }
142
143 if len(set.SetType) == 0 {
144 set.SetType = HashIPPort
145 }
146 if len(set.PortRange) == 0 {
147 set.PortRange = DefaultPortRange
148 }
149 }
150
151
152 type Entry struct {
153
154
155 IP string
156
157 Port int
158
159
160 Protocol string
161
162
163 Net string
164
165 IP2 string
166
167 SetType Type
168 }
169
170
171 func (e *Entry) Validate(set *IPSet) bool {
172 if e.Port < 0 {
173 klog.ErrorS(validationError, "port number should be >=0", "entry", e, "port", e.Port, "ipset", set)
174 return false
175 }
176 switch e.SetType {
177 case HashIP:
178
179 if valid := e.checkIP(set); !valid {
180 return false
181 }
182 case HashIPPort:
183
184 if valid := e.checkIPandProtocol(set); !valid {
185 return false
186 }
187 case HashIPPortIP:
188
189 if valid := e.checkIPandProtocol(set); !valid {
190 return false
191 }
192
193
194 if netutils.ParseIPSloppy(e.IP2) == nil {
195 klog.ErrorS(validationError, "error parsing second ip address", "entry", e, "ip", e.IP2, "ipset", set)
196 return false
197 }
198 case HashIPPortNet:
199
200 if valid := e.checkIPandProtocol(set); !valid {
201 return false
202 }
203
204
205 if _, ipNet, err := netutils.ParseCIDRSloppy(e.Net); ipNet == nil {
206 klog.ErrorS(err, "error parsing ip net", "entry", e, "net", e.Net, "set", set)
207 return false
208 }
209 case BitmapPort:
210
211 if set == nil {
212 klog.ErrorS(validationError, "unable to reference ip set where the entry exists", "entry", e)
213 return false
214 }
215 begin, end, err := parsePortRange(set.PortRange)
216 if err != nil {
217 klog.ErrorS(err, "failed to parse set port range", "ipset", set, "portRange", set.PortRange)
218 return false
219 }
220 if e.Port < begin || e.Port > end {
221 klog.ErrorS(validationError, "port number is not in the port range of its ipset", "entry", e, "port", e.Port, "portRange", set.PortRange, "ipset", set)
222 return false
223 }
224 }
225
226 return true
227 }
228
229
230 func (e *Entry) String() string {
231 switch e.SetType {
232 case HashIP:
233
234 return fmt.Sprintf("%s", e.IP)
235 case HashIPPort:
236
237
238 return fmt.Sprintf("%s,%s:%s", e.IP, e.Protocol, strconv.Itoa(e.Port))
239 case HashIPPortIP:
240
241
242 return fmt.Sprintf("%s,%s:%s,%s", e.IP, e.Protocol, strconv.Itoa(e.Port), e.IP2)
243 case HashIPPortNet:
244
245
246 return fmt.Sprintf("%s,%s:%s,%s", e.IP, e.Protocol, strconv.Itoa(e.Port), e.Net)
247 case BitmapPort:
248
249
250 return strconv.Itoa(e.Port)
251 }
252 return ""
253 }
254
255
256 func (e *Entry) checkIPandProtocol(set *IPSet) bool {
257
258 if len(e.Protocol) == 0 {
259 e.Protocol = ProtocolTCP
260 } else if !validateProtocol(e.Protocol) {
261 return false
262 }
263 return e.checkIP(set)
264 }
265
266
267 func (e *Entry) checkIP(set *IPSet) bool {
268 if netutils.ParseIPSloppy(e.IP) == nil {
269 klog.ErrorS(validationError, "error parsing ip address", "entry", e, "ip", e.IP, "ipset", set)
270 return false
271 }
272
273 return true
274 }
275
276 type runner struct {
277 exec utilexec.Interface
278 }
279
280
281 func New(exec utilexec.Interface) Interface {
282 return &runner{
283 exec: exec,
284 }
285 }
286
287
288 func (runner *runner) CreateSet(set *IPSet, ignoreExistErr bool) error {
289
290 set.setIPSetDefaults()
291
292
293 if err := set.Validate(); err != nil {
294 return err
295 }
296 return runner.createSet(set, ignoreExistErr)
297 }
298
299
300
301 func (runner *runner) createSet(set *IPSet, ignoreExistErr bool) error {
302 args := []string{"create", set.Name, string(set.SetType)}
303 if set.SetType == HashIPPortIP || set.SetType == HashIPPort || set.SetType == HashIPPortNet || set.SetType == HashIP {
304 args = append(args,
305 "family", set.HashFamily,
306 "hashsize", strconv.Itoa(set.HashSize),
307 "maxelem", strconv.Itoa(set.MaxElem),
308 )
309 }
310 if set.SetType == BitmapPort {
311 args = append(args, "range", set.PortRange)
312 }
313 if ignoreExistErr {
314 args = append(args, "-exist")
315 }
316 if _, err := runner.exec.Command(IPSetCmd, args...).CombinedOutput(); err != nil {
317 return fmt.Errorf("error creating ipset %s, error: %v", set.Name, err)
318 }
319 return nil
320 }
321
322
323
324
325 func (runner *runner) AddEntry(entry string, set *IPSet, ignoreExistErr bool) error {
326 args := []string{"add", set.Name, entry}
327 if ignoreExistErr {
328 args = append(args, "-exist")
329 }
330 if out, err := runner.exec.Command(IPSetCmd, args...).CombinedOutput(); err != nil {
331 return fmt.Errorf("error adding entry %s, error: %v (%s)", entry, err, out)
332 }
333 return nil
334 }
335
336
337 func (runner *runner) DelEntry(entry string, set string) error {
338 if out, err := runner.exec.Command(IPSetCmd, "del", set, entry).CombinedOutput(); err != nil {
339 return fmt.Errorf("error deleting entry %s: from set: %s, error: %v (%s)", entry, set, err, out)
340 }
341 return nil
342 }
343
344
345 func (runner *runner) TestEntry(entry string, set string) (bool, error) {
346 if out, err := runner.exec.Command(IPSetCmd, "test", set, entry).CombinedOutput(); err == nil {
347 reg, e := regexp.Compile("is NOT in set " + set)
348 if e == nil && reg.MatchString(string(out)) {
349 return false, nil
350 } else if e == nil {
351 return true, nil
352 } else {
353 return false, fmt.Errorf("error testing entry: %s, error: %v", entry, e)
354 }
355 } else {
356 return false, fmt.Errorf("error testing entry %s: %v (%s)", entry, err, out)
357 }
358 }
359
360
361 func (runner *runner) FlushSet(set string) error {
362 if _, err := runner.exec.Command(IPSetCmd, "flush", set).CombinedOutput(); err != nil {
363 return fmt.Errorf("error flushing set: %s, error: %v", set, err)
364 }
365 return nil
366 }
367
368
369 func (runner *runner) DestroySet(set string) error {
370 if out, err := runner.exec.Command(IPSetCmd, "destroy", set).CombinedOutput(); err != nil {
371 return fmt.Errorf("error destroying set %s, error: %v(%s)", set, err, out)
372 }
373 return nil
374 }
375
376
377 func (runner *runner) DestroyAllSets() error {
378 if _, err := runner.exec.Command(IPSetCmd, "destroy").CombinedOutput(); err != nil {
379 return fmt.Errorf("error destroying all sets, error: %v", err)
380 }
381 return nil
382 }
383
384
385 func (runner *runner) ListSets() ([]string, error) {
386 out, err := runner.exec.Command(IPSetCmd, "list", "-n").CombinedOutput()
387 if err != nil {
388 return nil, fmt.Errorf("error listing all sets, error: %v", err)
389 }
390 return strings.Split(string(out), "\n"), nil
391 }
392
393
394 func (runner *runner) ListEntries(set string) ([]string, error) {
395 if len(set) == 0 {
396 return nil, fmt.Errorf("set name can't be nil")
397 }
398 out, err := runner.exec.Command(IPSetCmd, "list", set).CombinedOutput()
399 if err != nil {
400 return nil, fmt.Errorf("error listing set: %s, error: %v", set, err)
401 }
402 memberMatcher := regexp.MustCompile(EntryMemberPattern)
403 list := memberMatcher.ReplaceAllString(string(out[:]), "")
404 strs := strings.Split(list, "\n")
405 results := make([]string, 0)
406 for i := range strs {
407 if len(strs[i]) > 0 {
408 results = append(results, strs[i])
409 }
410 }
411 return results, nil
412 }
413
414
415 func (runner *runner) GetVersion() (string, error) {
416 return getIPSetVersionString(runner.exec)
417 }
418
419
420
421 func getIPSetVersionString(exec utilexec.Interface) (string, error) {
422 cmd := exec.Command(IPSetCmd, "--version")
423 cmd.SetStdin(bytes.NewReader([]byte{}))
424 bytes, err := cmd.CombinedOutput()
425 if err != nil {
426 return "", err
427 }
428 versionMatcher := regexp.MustCompile(VersionPattern)
429 match := versionMatcher.FindStringSubmatch(string(bytes))
430 if match == nil {
431 return "", fmt.Errorf("no ipset version found in string: %s", bytes)
432 }
433 return match[0], nil
434 }
435
436
437
438 func validatePortRange(portRange string) error {
439 strs := strings.Split(portRange, "-")
440 if len(strs) != 2 {
441 return fmt.Errorf("invalid PortRange: %q", portRange)
442 }
443 for i := range strs {
444 num, err := strconv.Atoi(strs[i])
445 if err != nil {
446 return fmt.Errorf("invalid PortRange: %q", portRange)
447 }
448 if num < 0 {
449 return fmt.Errorf("invalid PortRange: %q", portRange)
450 }
451 }
452 return nil
453 }
454
455
456 func validateIPSetType(set Type) error {
457 for _, valid := range ValidIPSetTypes {
458 if set == valid {
459 return nil
460 }
461 }
462 return fmt.Errorf("unsupported SetType: %q", set)
463 }
464
465
466 func validateHashFamily(family string) error {
467 if family == ProtocolFamilyIPV4 || family == ProtocolFamilyIPV6 {
468 return nil
469 }
470 return fmt.Errorf("unsupported HashFamily %q", family)
471 }
472
473
474
475
476 func IsNotFoundError(err error) bool {
477 es := err.Error()
478 if strings.Contains(es, "does not exist") {
479
480
481 return true
482 }
483 if strings.Contains(es, "element is missing") {
484
485
486
487 return true
488 }
489 return false
490 }
491
492
493 func validateProtocol(protocol string) bool {
494 if protocol == ProtocolTCP || protocol == ProtocolUDP || protocol == ProtocolSCTP {
495 return true
496 }
497 klog.ErrorS(validationError, "invalid protocol", "protocol", protocol, "supportedProtocols", []string{ProtocolTCP, ProtocolUDP, ProtocolSCTP})
498 return false
499 }
500
501
502
503 func parsePortRange(portRange string) (beginPort int, endPort int, err error) {
504 if len(portRange) == 0 {
505 portRange = DefaultPortRange
506 }
507
508 strs := strings.Split(portRange, "-")
509 if len(strs) != 2 {
510
511 return -1, -1, fmt.Errorf("port range should be in the format of `a-b`")
512 }
513 for i := range strs {
514 num, err := strconv.Atoi(strs[i])
515 if err != nil {
516
517 return -1, -1, err
518 }
519 if num < 0 {
520
521 return -1, -1, fmt.Errorf("port number %d should be >=0", num)
522 }
523 if i == 0 {
524 beginPort = num
525 continue
526 }
527 endPort = num
528
529 if beginPort > endPort {
530 endPort = beginPort
531 beginPort = num
532 }
533 }
534 return beginPort, endPort, nil
535 }
536
537 var _ = Interface(&runner{})
538
View as plain text