k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/proxy/ipvs/ipset/ipset_test.go (about) 1 //go:build linux 2 // +build linux 3 4 /* 5 Copyright 2017 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package ipset 21 22 import ( 23 "reflect" 24 "testing" 25 26 "k8s.io/apimachinery/pkg/util/sets" 27 "k8s.io/utils/exec" 28 fakeexec "k8s.io/utils/exec/testing" 29 ) 30 31 func TestCheckIPSetVersion(t *testing.T) { 32 testCases := []struct { 33 vstring string 34 Expect string 35 Err bool 36 }{ 37 {"ipset v4.0, protocol version: 4", "v4.0", false}, 38 {"ipset v5.1, protocol version: 5", "v5.1", false}, 39 {"ipset v6.0, protocol version: 6", "v6.0", false}, 40 {"ipset v6.1, protocol version: 6", "v6.1", false}, 41 {"ipset v6.19, protocol version: 6", "v6.19", false}, 42 {"total junk", "", true}, 43 } 44 45 for i := range testCases { 46 fcmd := fakeexec.FakeCmd{ 47 CombinedOutputScript: []fakeexec.FakeAction{ 48 // ipset version response 49 func() ([]byte, []byte, error) { return []byte(testCases[i].vstring), nil, nil }, 50 }, 51 } 52 53 fexec := &fakeexec.FakeExec{ 54 CommandScript: []fakeexec.FakeCommandAction{ 55 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 56 }, 57 } 58 59 gotVersion, err := getIPSetVersionString(fexec) 60 if (err != nil) != testCases[i].Err { 61 t.Errorf("Expected error: %v, Got error: %v", testCases[i].Err, err) 62 } 63 if err == nil { 64 if testCases[i].Expect != gotVersion { 65 t.Errorf("Expected result: %v, Got result: %v", testCases[i].Expect, gotVersion) 66 } 67 } 68 } 69 } 70 71 func TestFlushSet(t *testing.T) { 72 fcmd := fakeexec.FakeCmd{ 73 CombinedOutputScript: []fakeexec.FakeAction{ 74 // Success 75 func() ([]byte, []byte, error) { return []byte{}, nil, nil }, 76 // Success 77 func() ([]byte, []byte, error) { return []byte{}, nil, nil }, 78 }, 79 } 80 fexec := &fakeexec.FakeExec{ 81 CommandScript: []fakeexec.FakeCommandAction{ 82 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 83 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 84 }, 85 } 86 runner := New(fexec) 87 // Success. 88 err := runner.FlushSet("FOOBAR") 89 if err != nil { 90 t.Errorf("expected success, got %v", err) 91 } 92 if fcmd.CombinedOutputCalls != 1 { 93 t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) 94 } 95 if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "flush", "FOOBAR") { 96 t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0]) 97 } 98 // Flush again 99 err = runner.FlushSet("FOOBAR") 100 if err != nil { 101 t.Errorf("expected success, got %v", err) 102 } 103 } 104 105 func TestDestroySet(t *testing.T) { 106 fcmd := fakeexec.FakeCmd{ 107 CombinedOutputScript: []fakeexec.FakeAction{ 108 // Success 109 func() ([]byte, []byte, error) { return []byte{}, nil, nil }, 110 // Failure 111 func() ([]byte, []byte, error) { 112 return []byte("ipset v6.19: The set with the given name does not exist"), nil, &fakeexec.FakeExitError{Status: 1} 113 }, 114 }, 115 } 116 fexec := &fakeexec.FakeExec{ 117 CommandScript: []fakeexec.FakeCommandAction{ 118 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 119 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 120 }, 121 } 122 runner := New(fexec) 123 // Success 124 err := runner.DestroySet("FOOBAR") 125 if err != nil { 126 t.Errorf("expected success, got %v", err) 127 } 128 if fcmd.CombinedOutputCalls != 1 { 129 t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) 130 } 131 if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "destroy", "FOOBAR") { 132 t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0]) 133 } 134 // Failure 135 err = runner.DestroySet("FOOBAR") 136 if err == nil { 137 t.Errorf("expected failure, got nil") 138 } 139 } 140 141 func TestDestroyAllSets(t *testing.T) { 142 fcmd := fakeexec.FakeCmd{ 143 CombinedOutputScript: []fakeexec.FakeAction{ 144 // Success 145 func() ([]byte, []byte, error) { return []byte{}, nil, nil }, 146 // Success 147 func() ([]byte, []byte, error) { return []byte{}, nil, nil }, 148 }, 149 } 150 fexec := &fakeexec.FakeExec{ 151 CommandScript: []fakeexec.FakeCommandAction{ 152 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 153 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 154 }, 155 } 156 runner := New(fexec) 157 // Success 158 err := runner.DestroyAllSets() 159 if err != nil { 160 t.Errorf("expected success, got %v", err) 161 } 162 if fcmd.CombinedOutputCalls != 1 { 163 t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) 164 } 165 if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "destroy") { 166 t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0]) 167 } 168 // Success 169 err = runner.DestroyAllSets() 170 if err != nil { 171 t.Errorf("Unexpected failure: %v", err) 172 } 173 } 174 175 func TestCreateSet(t *testing.T) { 176 testSet := IPSet{ 177 Name: "FOOBAR", 178 SetType: HashIPPort, 179 HashFamily: ProtocolFamilyIPV4, 180 } 181 182 fcmd := fakeexec.FakeCmd{ 183 CombinedOutputScript: []fakeexec.FakeAction{ 184 // Success 185 func() ([]byte, []byte, error) { return []byte{}, nil, nil }, 186 // Success 187 func() ([]byte, []byte, error) { return []byte{}, nil, nil }, 188 // Failure 189 func() ([]byte, []byte, error) { 190 return []byte("ipset v6.19: Set cannot be created: set with the same name already exists"), nil, &fakeexec.FakeExitError{Status: 1} 191 }, 192 }, 193 } 194 fexec := &fakeexec.FakeExec{ 195 CommandScript: []fakeexec.FakeCommandAction{ 196 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 197 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 198 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 199 }, 200 } 201 runner := New(fexec) 202 // Create with ignoreExistErr = false, expect success 203 err := runner.CreateSet(&testSet, false) 204 if err != nil { 205 t.Errorf("expected success, got %v", err) 206 } 207 if fcmd.CombinedOutputCalls != 1 { 208 t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) 209 } 210 if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "create", "FOOBAR", "hash:ip,port", "family", "inet", "hashsize", "1024", "maxelem", "65536") { 211 t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0]) 212 } 213 // Create with ignoreExistErr = true, expect success 214 err = runner.CreateSet(&testSet, true) 215 if err != nil { 216 t.Errorf("expected success, got %v", err) 217 } 218 if fcmd.CombinedOutputCalls != 2 { 219 t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) 220 } 221 if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("ipset", "create", "FOOBAR", "hash:ip,port", "family", "inet", "hashsize", "1024", "maxelem", "65536", "-exist") { 222 t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) 223 } 224 // Create with ignoreExistErr = false, expect failure 225 err = runner.CreateSet(&testSet, false) 226 if err == nil { 227 t.Errorf("expected failure, got nil") 228 } 229 } 230 231 var testCases = []struct { 232 entry *Entry 233 set *IPSet 234 addCombinedOutputLog [][]string 235 delCombinedOutputLog []string 236 }{ 237 { // case 0 238 entry: &Entry{ 239 IP: "192.168.1.1", 240 Port: 53, 241 Protocol: ProtocolUDP, 242 SetType: HashIPPort, 243 }, 244 set: &IPSet{ 245 Name: "ZERO", 246 }, 247 addCombinedOutputLog: [][]string{ 248 {"ipset", "add", "ZERO", "192.168.1.1,udp:53"}, 249 {"ipset", "add", "ZERO", "192.168.1.1,udp:53", "-exist"}, 250 }, 251 delCombinedOutputLog: []string{"ipset", "del", "ZERO", "192.168.1.1,udp:53"}, 252 }, 253 { // case 1 254 entry: &Entry{ 255 IP: "192.168.1.2", 256 Port: 80, 257 Protocol: ProtocolTCP, 258 SetType: HashIPPort, 259 }, 260 set: &IPSet{ 261 Name: "UN", 262 }, 263 addCombinedOutputLog: [][]string{ 264 {"ipset", "add", "UN", "192.168.1.2,tcp:80"}, 265 {"ipset", "add", "UN", "192.168.1.2,tcp:80", "-exist"}, 266 }, 267 delCombinedOutputLog: []string{"ipset", "del", "UN", "192.168.1.2,tcp:80"}, 268 }, 269 { // case 2 270 entry: &Entry{ 271 IP: "192.168.1.3", 272 Port: 53, 273 Protocol: ProtocolUDP, 274 SetType: HashIPPortIP, 275 IP2: "10.20.30.1", 276 }, 277 set: &IPSet{ 278 Name: "DEUX", 279 }, 280 addCombinedOutputLog: [][]string{ 281 {"ipset", "add", "DEUX", "192.168.1.3,udp:53,10.20.30.1"}, 282 {"ipset", "add", "DEUX", "192.168.1.3,udp:53,10.20.30.1", "-exist"}, 283 }, 284 delCombinedOutputLog: []string{"ipset", "del", "DEUX", "192.168.1.3,udp:53,10.20.30.1"}, 285 }, 286 { // case 3 287 entry: &Entry{ 288 IP: "192.168.1.4", 289 Port: 80, 290 Protocol: ProtocolTCP, 291 SetType: HashIPPortIP, 292 IP2: "10.20.30.2", 293 }, 294 set: &IPSet{ 295 Name: "TROIS", 296 }, 297 addCombinedOutputLog: [][]string{ 298 {"ipset", "add", "TROIS", "192.168.1.4,tcp:80,10.20.30.2"}, 299 {"ipset", "add", "TROIS", "192.168.1.4,tcp:80,10.20.30.2", "-exist"}, 300 }, 301 delCombinedOutputLog: []string{"ipset", "del", "TROIS", "192.168.1.4,tcp:80,10.20.30.2"}, 302 }, 303 { // case 4 304 entry: &Entry{ 305 IP: "192.168.1.5", 306 Port: 53, 307 Protocol: ProtocolUDP, 308 SetType: HashIPPortNet, 309 Net: "10.20.30.0/24", 310 }, 311 set: &IPSet{ 312 Name: "QUATRE", 313 }, 314 addCombinedOutputLog: [][]string{ 315 {"ipset", "add", "QUATRE", "192.168.1.5,udp:53,10.20.30.0/24"}, 316 {"ipset", "add", "QUATRE", "192.168.1.5,udp:53,10.20.30.0/24", "-exist"}, 317 }, 318 delCombinedOutputLog: []string{"ipset", "del", "QUATRE", "192.168.1.5,udp:53,10.20.30.0/24"}, 319 }, 320 { // case 5 321 entry: &Entry{ 322 IP: "192.168.1.6", 323 Port: 80, 324 Protocol: ProtocolTCP, 325 SetType: HashIPPortNet, 326 Net: "10.20.40.0/24", 327 }, 328 set: &IPSet{ 329 Name: "CINQ", 330 }, 331 addCombinedOutputLog: [][]string{ 332 {"ipset", "add", "CINQ", "192.168.1.6,tcp:80,10.20.40.0/24"}, 333 {"ipset", "add", "CINQ", "192.168.1.6,tcp:80,10.20.40.0/24", "-exist"}, 334 }, 335 delCombinedOutputLog: []string{"ipset", "del", "CINQ", "192.168.1.6,tcp:80,10.20.40.0/24"}, 336 }, 337 { // case 6 338 entry: &Entry{ 339 Port: 80, 340 Protocol: ProtocolTCP, 341 SetType: BitmapPort, 342 }, 343 set: &IPSet{ 344 Name: "SIX", 345 }, 346 addCombinedOutputLog: [][]string{ 347 {"ipset", "add", "SIX", "80"}, 348 {"ipset", "add", "SIX", "80", "-exist"}, 349 }, 350 delCombinedOutputLog: []string{"ipset", "del", "SIX", "80"}, 351 }, 352 { // case 7 353 entry: &Entry{ 354 IP: "192.168.1.2", 355 Port: 80, 356 Protocol: ProtocolSCTP, 357 SetType: HashIPPort, 358 }, 359 set: &IPSet{ 360 Name: "SETTE", 361 }, 362 addCombinedOutputLog: [][]string{ 363 {"ipset", "add", "SETTE", "192.168.1.2,sctp:80"}, 364 {"ipset", "add", "SETTE", "192.168.1.2,sctp:80", "-exist"}, 365 }, 366 delCombinedOutputLog: []string{"ipset", "del", "SETTE", "192.168.1.2,sctp:80"}, 367 }, 368 } 369 370 func TestAddEntry(t *testing.T) { 371 for i := range testCases { 372 fcmd := fakeexec.FakeCmd{ 373 CombinedOutputScript: []fakeexec.FakeAction{ 374 // Success 375 func() ([]byte, []byte, error) { return []byte{}, nil, nil }, 376 // Success 377 func() ([]byte, []byte, error) { return []byte{}, nil, nil }, 378 // Failure 379 func() ([]byte, []byte, error) { 380 return []byte("ipset v6.19: Set cannot be created: set with the same name already exists"), nil, &fakeexec.FakeExitError{Status: 1} 381 }, 382 }, 383 } 384 fexec := &fakeexec.FakeExec{ 385 CommandScript: []fakeexec.FakeCommandAction{ 386 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 387 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 388 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 389 }, 390 } 391 runner := New(fexec) 392 // Create with ignoreExistErr = false, expect success 393 err := runner.AddEntry(testCases[i].entry.String(), testCases[i].set, false) 394 if err != nil { 395 t.Errorf("expected success, got %v", err) 396 } 397 if fcmd.CombinedOutputCalls != 1 { 398 t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) 399 } 400 if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll(testCases[i].addCombinedOutputLog[0]...) { 401 t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0]) 402 } 403 // Create with ignoreExistErr = true, expect success 404 err = runner.AddEntry(testCases[i].entry.String(), testCases[i].set, true) 405 if err != nil { 406 t.Errorf("expected success, got %v", err) 407 } 408 if fcmd.CombinedOutputCalls != 2 { 409 t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) 410 } 411 if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll(testCases[i].addCombinedOutputLog[1]...) { 412 t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) 413 } 414 // Create with ignoreExistErr = false, expect failure 415 err = runner.AddEntry(testCases[i].entry.String(), testCases[i].set, false) 416 if err == nil { 417 t.Errorf("expected failure, got nil") 418 } 419 } 420 } 421 422 func TestDelEntry(t *testing.T) { 423 for i := range testCases { 424 fcmd := fakeexec.FakeCmd{ 425 CombinedOutputScript: []fakeexec.FakeAction{ 426 // Success 427 func() ([]byte, []byte, error) { return []byte{}, nil, nil }, 428 // Failure 429 func() ([]byte, []byte, error) { 430 return []byte("ipset v6.19: Element cannot be deleted from the set: it's not added"), nil, &fakeexec.FakeExitError{Status: 1} 431 }, 432 }, 433 } 434 fexec := &fakeexec.FakeExec{ 435 CommandScript: []fakeexec.FakeCommandAction{ 436 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 437 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 438 }, 439 } 440 runner := New(fexec) 441 442 err := runner.DelEntry(testCases[i].entry.String(), testCases[i].set.Name) 443 if err != nil { 444 t.Errorf("expected success, got %v", err) 445 } 446 if fcmd.CombinedOutputCalls != 1 { 447 t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) 448 } 449 if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll(testCases[i].delCombinedOutputLog...) { 450 t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0]) 451 } 452 err = runner.DelEntry(testCases[i].entry.String(), testCases[i].set.Name) 453 if err == nil { 454 t.Errorf("expected failure, got nil") 455 } 456 } 457 } 458 459 func TestTestEntry(t *testing.T) { 460 testEntry := &Entry{ 461 IP: "10.120.7.100", 462 Port: 8080, 463 Protocol: ProtocolTCP, 464 SetType: HashIPPort, 465 } 466 setName := "NOT" 467 fcmd := fakeexec.FakeCmd{ 468 CombinedOutputScript: []fakeexec.FakeAction{ 469 // Success 470 func() ([]byte, []byte, error) { 471 return []byte("10.120.7.100,tcp:8080 is in set " + setName + "."), nil, nil 472 }, 473 // Failure 474 func() ([]byte, []byte, error) { 475 return []byte("192.168.1.3,tcp:8080 is NOT in set " + setName + "."), nil, &fakeexec.FakeExitError{Status: 1} 476 }, 477 }, 478 } 479 fexec := &fakeexec.FakeExec{ 480 CommandScript: []fakeexec.FakeCommandAction{ 481 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 482 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 483 }, 484 } 485 runner := New(fexec) 486 // Success 487 ok, err := runner.TestEntry(testEntry.String(), setName) 488 if err != nil { 489 t.Errorf("expected success, got %v", err) 490 } 491 if fcmd.CombinedOutputCalls != 1 { 492 t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) 493 } 494 if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "test", setName, "10.120.7.100,tcp:8080") { 495 t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0]) 496 } 497 if !ok { 498 t.Errorf("expect entry exists in test set, got not") 499 } 500 // Failure 501 ok, err = runner.TestEntry(testEntry.String(), "FOOBAR") 502 if err == nil || ok { 503 t.Errorf("expect entry doesn't exist in test set") 504 } 505 } 506 507 func TestTestEntryIPv6(t *testing.T) { 508 testEntry := &Entry{ 509 IP: "fd00:1234:5678:dead:beaf::1", 510 Port: 8080, 511 Protocol: ProtocolTCP, 512 SetType: HashIPPort, 513 } 514 setName := "NOT" 515 fcmd := fakeexec.FakeCmd{ 516 CombinedOutputScript: []fakeexec.FakeAction{ 517 // Success 518 func() ([]byte, []byte, error) { 519 return []byte("fd00:1234:5678:dead:beaf::1,tcp:8080 is in set " + setName + "."), nil, nil 520 }, 521 // Failure 522 func() ([]byte, []byte, error) { 523 return []byte("fd00::2,tcp:8080 is NOT in set FOOBAR."), nil, &fakeexec.FakeExitError{Status: 1} 524 }, 525 }, 526 } 527 fexec := &fakeexec.FakeExec{ 528 CommandScript: []fakeexec.FakeCommandAction{ 529 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 530 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 531 }, 532 } 533 runner := New(fexec) 534 // Success 535 ok, err := runner.TestEntry(testEntry.String(), setName) 536 if err != nil { 537 t.Errorf("expected success, got %v", err) 538 } 539 if fcmd.CombinedOutputCalls != 1 { 540 t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) 541 } 542 if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "test", setName, "fd00:1234:5678:dead:beaf::1,tcp:8080") { 543 t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0]) 544 } 545 if !ok { 546 t.Errorf("expect entry exists in test set, got not") 547 } 548 // Failure 549 ok, err = runner.TestEntry(testEntry.String(), "FOOBAR") 550 if err == nil || ok { 551 t.Errorf("expect entry doesn't exist in test set") 552 } 553 } 554 555 func TestListEntries(t *testing.T) { 556 557 output := `Name: foobar 558 Type: hash:ip,port 559 Revision: 2 560 Header: family inet hashsize 1024 maxelem 65536 561 Size in memory: 16592 562 References: 0 563 Members: 564 192.168.1.2,tcp:8080 565 192.168.1.1,udp:53` 566 567 emptyOutput := `Name: KUBE-NODE-PORT 568 Type: bitmap:port 569 Revision: 1 570 Header: range 0-65535 571 Size in memory: 524432 572 References: 1 573 Members: 574 575 ` 576 577 testCases := []struct { 578 output string 579 expected []string 580 }{ 581 { 582 output: output, 583 expected: []string{"192.168.1.2,tcp:8080", "192.168.1.1,udp:53"}, 584 }, 585 { 586 output: emptyOutput, 587 expected: []string{}, 588 }, 589 } 590 591 for i := range testCases { 592 fcmd := fakeexec.FakeCmd{ 593 CombinedOutputScript: []fakeexec.FakeAction{ 594 // Success 595 func() ([]byte, []byte, error) { 596 return []byte(testCases[i].output), nil, nil 597 }, 598 }, 599 } 600 fexec := &fakeexec.FakeExec{ 601 CommandScript: []fakeexec.FakeCommandAction{ 602 func(cmd string, args ...string) exec.Cmd { 603 return fakeexec.InitFakeCmd(&fcmd, cmd, args...) 604 }, 605 }, 606 } 607 runner := New(fexec) 608 // Success 609 entries, err := runner.ListEntries("foobar") 610 if err != nil { 611 t.Errorf("expected success, got: %v", err) 612 } 613 if fcmd.CombinedOutputCalls != 1 { 614 t.Errorf("expected 1 CombinedOutput() calls, got: %d", fcmd.CombinedOutputCalls) 615 } 616 if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "list", "foobar") { 617 t.Errorf("wrong CombinedOutput() log, got: %s", fcmd.CombinedOutputLog[0]) 618 } 619 if len(entries) != len(testCases[i].expected) { 620 t.Errorf("expected %d ipset entries, got: %d", len(testCases[i].expected), len(entries)) 621 } 622 if !reflect.DeepEqual(entries, testCases[i].expected) { 623 t.Errorf("expected entries: %v, got: %v", testCases[i].expected, entries) 624 } 625 } 626 } 627 628 func TestListSets(t *testing.T) { 629 output := `foo 630 bar 631 baz` 632 633 expected := []string{"foo", "bar", "baz"} 634 635 fcmd := fakeexec.FakeCmd{ 636 CombinedOutputScript: []fakeexec.FakeAction{ 637 // Success 638 func() ([]byte, []byte, error) { return []byte(output), nil, nil }, 639 }, 640 } 641 fexec := &fakeexec.FakeExec{ 642 CommandScript: []fakeexec.FakeCommandAction{ 643 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, 644 }, 645 } 646 runner := New(fexec) 647 // Success 648 list, err := runner.ListSets() 649 if err != nil { 650 t.Errorf("expected success, got: %v", err) 651 } 652 if fcmd.CombinedOutputCalls != 1 { 653 t.Errorf("expected 1 CombinedOutput() calls, got: %d", fcmd.CombinedOutputCalls) 654 } 655 if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "list", "-n") { 656 t.Errorf("wrong CombinedOutput() log, got: %s", fcmd.CombinedOutputLog[0]) 657 } 658 if len(list) != len(expected) { 659 t.Errorf("expected %d sets, got: %d", len(expected), len(list)) 660 } 661 if !reflect.DeepEqual(list, expected) { 662 t.Errorf("expected sets: %v, got: %v", expected, list) 663 } 664 } 665 666 func Test_validIPSetType(t *testing.T) { 667 testCases := []struct { 668 setType Type 669 expectErr bool 670 }{ 671 { // case[0] 672 setType: Type("foo"), 673 expectErr: true, 674 }, 675 { // case[1] 676 setType: HashIPPortNet, 677 expectErr: false, 678 }, 679 { // case[2] 680 setType: HashIPPort, 681 expectErr: false, 682 }, 683 { // case[3] 684 setType: HashIPPortIP, 685 expectErr: false, 686 }, 687 { // case[4] 688 setType: BitmapPort, 689 expectErr: false, 690 }, 691 { // case[5] 692 setType: Type(""), 693 expectErr: true, 694 }, 695 } 696 for i := range testCases { 697 err := validateIPSetType(testCases[i].setType) 698 if err != nil { 699 if !testCases[i].expectErr { 700 t.Errorf("case [%d]: unexpected mismatch, expect error[%v], got error[%v]", i, testCases[i].expectErr, err) 701 } 702 continue 703 } 704 } 705 } 706 707 func Test_validatePortRange(t *testing.T) { 708 testCases := []struct { 709 portRange string 710 expectErr bool 711 desc string 712 }{ 713 { // case[0] 714 portRange: "a-b", 715 expectErr: true, 716 desc: "invalid port number", 717 }, 718 { // case[1] 719 portRange: "1-2", 720 expectErr: false, 721 desc: "valid", 722 }, 723 { // case[2] 724 portRange: "90-1", 725 expectErr: false, 726 desc: "ipset util can accept the input of begin port number can be less than end port number", 727 }, 728 { // case[3] 729 portRange: DefaultPortRange, 730 expectErr: false, 731 desc: "default port range is valid, of course", 732 }, 733 { // case[4] 734 portRange: "12", 735 expectErr: true, 736 desc: "a single number is invalid", 737 }, 738 { // case[5] 739 portRange: "1-", 740 expectErr: true, 741 desc: "should specify end port", 742 }, 743 { // case[6] 744 portRange: "-100", 745 expectErr: true, 746 desc: "should specify begin port", 747 }, 748 { // case[7] 749 portRange: "1:100", 750 expectErr: true, 751 desc: "delimiter should be -", 752 }, 753 { // case[8] 754 portRange: "1~100", 755 expectErr: true, 756 desc: "delimiter should be -", 757 }, 758 { // case[9] 759 portRange: "1,100", 760 expectErr: true, 761 desc: "delimiter should be -", 762 }, 763 { // case[10] 764 portRange: "100-100", 765 expectErr: false, 766 desc: "begin port number can be equal to end port number", 767 }, 768 { // case[11] 769 portRange: "", 770 expectErr: true, 771 desc: "empty string is invalid", 772 }, 773 { // case[12] 774 portRange: "-1-12", 775 expectErr: true, 776 desc: "port number can not be negative value", 777 }, 778 { // case[13] 779 portRange: "-1--8", 780 expectErr: true, 781 desc: "port number can not be negative value", 782 }, 783 } 784 for i := range testCases { 785 err := validatePortRange(testCases[i].portRange) 786 if err != nil { 787 if !testCases[i].expectErr { 788 t.Errorf("case [%d]: unexpected mismatch, expect error[%v], got error[%v], desc: %s", i, testCases[i].expectErr, err, testCases[i].desc) 789 } 790 continue 791 } 792 } 793 } 794 795 func Test_validateFamily(t *testing.T) { 796 testCases := []struct { 797 family string 798 expectErr bool 799 }{ 800 { // case[0] 801 family: "foo", 802 expectErr: true, 803 }, 804 { // case[1] 805 family: ProtocolFamilyIPV4, 806 expectErr: false, 807 }, 808 { // case[2] 809 family: ProtocolFamilyIPV6, 810 expectErr: false, 811 }, 812 { // case[3] 813 family: "ipv4", 814 expectErr: true, 815 }, 816 { // case[4] 817 family: "ipv6", 818 expectErr: true, 819 }, 820 { // case[5] 821 family: "tcp", 822 expectErr: true, 823 }, 824 { // case[6] 825 family: "udp", 826 expectErr: true, 827 }, 828 { // case[7] 829 family: "", 830 expectErr: true, 831 }, 832 { // case[8] 833 family: "sctp", 834 expectErr: true, 835 }, 836 } 837 for i := range testCases { 838 err := validateHashFamily(testCases[i].family) 839 if err != nil { 840 if !testCases[i].expectErr { 841 t.Errorf("case [%d]: unexpected err: %v, desc: %s", i, err, testCases[i].family) 842 } 843 continue 844 } 845 } 846 } 847 848 func Test_validateProtocol(t *testing.T) { 849 testCases := []struct { 850 protocol string 851 valid bool 852 desc string 853 }{ 854 { // case[0] 855 protocol: "foo", 856 valid: false, 857 }, 858 { // case[1] 859 protocol: ProtocolTCP, 860 valid: true, 861 }, 862 { // case[2] 863 protocol: ProtocolUDP, 864 valid: true, 865 }, 866 { // case[3] 867 protocol: "ipv4", 868 valid: false, 869 }, 870 { // case[4] 871 protocol: "ipv6", 872 valid: false, 873 }, 874 { // case[5] 875 protocol: "TCP", 876 valid: false, 877 desc: "should be low case", 878 }, 879 { // case[6] 880 protocol: "UDP", 881 valid: false, 882 desc: "should be low case", 883 }, 884 { // case[7] 885 protocol: "", 886 valid: false, 887 }, 888 { // case[8] 889 protocol: ProtocolSCTP, 890 valid: true, 891 }, 892 } 893 for i := range testCases { 894 valid := validateProtocol(testCases[i].protocol) 895 if valid != testCases[i].valid { 896 t.Errorf("case [%d]: unexpected mismatch, expect valid[%v], got valid[%v], desc: %s", i, testCases[i].valid, valid, testCases[i].desc) 897 } 898 } 899 } 900 901 func TestValidateIPSet(t *testing.T) { 902 testCases := []struct { 903 ipset *IPSet 904 expectErr bool 905 desc string 906 }{ 907 { // case[0] 908 ipset: &IPSet{ 909 Name: "test", 910 SetType: HashIPPort, 911 HashFamily: ProtocolFamilyIPV4, 912 HashSize: 1024, 913 MaxElem: 1024, 914 }, 915 expectErr: false, 916 desc: "No Port range", 917 }, 918 { // case[1] 919 ipset: &IPSet{ 920 Name: "SET", 921 SetType: BitmapPort, 922 HashFamily: ProtocolFamilyIPV6, 923 HashSize: 65535, 924 MaxElem: 2048, 925 PortRange: DefaultPortRange, 926 }, 927 expectErr: false, 928 desc: "control case", 929 }, 930 { // case[2] 931 ipset: &IPSet{ 932 Name: "foo", 933 SetType: BitmapPort, 934 HashFamily: ProtocolFamilyIPV6, 935 HashSize: 65535, 936 MaxElem: 2048, 937 }, 938 expectErr: true, 939 desc: "should specify right port range for bitmap type set", 940 }, 941 { // case[3] 942 ipset: &IPSet{ 943 Name: "bar", 944 SetType: HashIPPort, 945 HashFamily: ProtocolFamilyIPV6, 946 HashSize: 0, 947 MaxElem: 2048, 948 }, 949 expectErr: true, 950 desc: "wrong hash size number", 951 }, 952 { // case[4] 953 ipset: &IPSet{ 954 Name: "baz", 955 SetType: HashIPPort, 956 HashFamily: ProtocolFamilyIPV6, 957 HashSize: 1024, 958 MaxElem: -1, 959 }, 960 expectErr: true, 961 desc: "wrong hash max elem number", 962 }, 963 { // case[5] 964 ipset: &IPSet{ 965 Name: "baz", 966 SetType: HashIPPortNet, 967 HashFamily: "ip", 968 HashSize: 1024, 969 MaxElem: 1024, 970 }, 971 expectErr: true, 972 desc: "wrong protocol", 973 }, 974 { // case[6] 975 ipset: &IPSet{ 976 Name: "foo-bar", 977 SetType: "xxx", 978 HashFamily: ProtocolFamilyIPV4, 979 HashSize: 1024, 980 MaxElem: 1024, 981 }, 982 expectErr: true, 983 desc: "wrong set type", 984 }, 985 } 986 for i := range testCases { 987 err := testCases[i].ipset.Validate() 988 if err != nil { 989 if !testCases[i].expectErr { 990 t.Errorf("case [%d]: unexpected mismatch, expect error[%v], got error[%v], desc: %s", i, testCases[i].expectErr, err, testCases[i].desc) 991 } 992 continue 993 } 994 } 995 } 996 997 func Test_setIPSetDefaults(t *testing.T) { 998 testCases := []struct { 999 name string 1000 set *IPSet 1001 expect *IPSet 1002 }{ 1003 { 1004 name: "test all the IPSet fields not present", 1005 set: &IPSet{ 1006 Name: "test1", 1007 }, 1008 expect: &IPSet{ 1009 Name: "test1", 1010 SetType: HashIPPort, 1011 HashFamily: ProtocolFamilyIPV4, 1012 HashSize: 1024, 1013 MaxElem: 65536, 1014 PortRange: DefaultPortRange, 1015 }, 1016 }, 1017 { 1018 name: "test all the IPSet fields present", 1019 set: &IPSet{ 1020 Name: "test2", 1021 SetType: BitmapPort, 1022 HashFamily: ProtocolFamilyIPV6, 1023 HashSize: 65535, 1024 MaxElem: 2048, 1025 PortRange: DefaultPortRange, 1026 }, 1027 expect: &IPSet{ 1028 Name: "test2", 1029 SetType: BitmapPort, 1030 HashFamily: ProtocolFamilyIPV6, 1031 HashSize: 65535, 1032 MaxElem: 2048, 1033 PortRange: DefaultPortRange, 1034 }, 1035 }, 1036 { 1037 name: "test part of the IPSet fields present", 1038 set: &IPSet{ 1039 Name: "test3", 1040 SetType: BitmapPort, 1041 HashFamily: ProtocolFamilyIPV6, 1042 HashSize: 65535, 1043 }, 1044 expect: &IPSet{ 1045 Name: "test3", 1046 SetType: BitmapPort, 1047 HashFamily: ProtocolFamilyIPV6, 1048 HashSize: 65535, 1049 MaxElem: 65536, 1050 PortRange: DefaultPortRange, 1051 }, 1052 }, 1053 } 1054 1055 for _, test := range testCases { 1056 t.Run(test.name, func(t *testing.T) { 1057 test.set.setIPSetDefaults() 1058 if !reflect.DeepEqual(test.set, test.expect) { 1059 t.Errorf("expected ipset struct: %v, got ipset struct: %v", test.expect, test.set) 1060 } 1061 }) 1062 } 1063 } 1064 1065 func Test_checkIPandProtocol(t *testing.T) { 1066 testset := &IPSet{ 1067 Name: "test1", 1068 SetType: HashIPPort, 1069 HashFamily: ProtocolFamilyIPV4, 1070 HashSize: 1024, 1071 MaxElem: 65536, 1072 PortRange: DefaultPortRange, 1073 } 1074 1075 testCases := []struct { 1076 name string 1077 entry *Entry 1078 valid bool 1079 }{ 1080 { 1081 name: "valid IP with ProtocolTCP", 1082 entry: &Entry{ 1083 SetType: HashIPPort, 1084 IP: "1.2.3.4", 1085 Protocol: ProtocolTCP, 1086 Port: 8080, 1087 }, 1088 valid: true, 1089 }, 1090 { 1091 name: "valid IP with ProtocolUDP", 1092 entry: &Entry{ 1093 SetType: HashIPPort, 1094 IP: "1.2.3.4", 1095 Protocol: ProtocolUDP, 1096 Port: 8080, 1097 }, 1098 valid: true, 1099 }, 1100 { 1101 name: "valid IP with nil Protocol", 1102 entry: &Entry{ 1103 SetType: HashIPPort, 1104 IP: "1.2.3.4", 1105 Port: 8080, 1106 }, 1107 valid: true, 1108 }, 1109 { 1110 name: "valid IP with invalid Protocol", 1111 entry: &Entry{ 1112 SetType: HashIPPort, 1113 IP: "1.2.3.4", 1114 Protocol: "invalidProtocol", 1115 Port: 8080, 1116 }, 1117 valid: false, 1118 }, 1119 { 1120 name: "invalid IP with ProtocolTCP", 1121 entry: &Entry{ 1122 SetType: HashIPPort, 1123 IP: "1.2.3.423", 1124 Protocol: ProtocolTCP, 1125 Port: 8080, 1126 }, 1127 valid: false, 1128 }, 1129 } 1130 1131 for _, test := range testCases { 1132 t.Run(test.name, func(t *testing.T) { 1133 result := test.entry.checkIPandProtocol(testset) 1134 if result != test.valid { 1135 t.Errorf("expected valid: %v, got valid: %v", test.valid, result) 1136 } 1137 }) 1138 } 1139 } 1140 1141 func Test_parsePortRange(t *testing.T) { 1142 testCases := []struct { 1143 portRange string 1144 expectErr bool 1145 beginPort int 1146 endPort int 1147 desc string 1148 }{ 1149 { // case[0] 1150 portRange: "1-100", 1151 expectErr: false, 1152 beginPort: 1, 1153 endPort: 100, 1154 }, 1155 { // case[1] 1156 portRange: "0-0", 1157 expectErr: false, 1158 beginPort: 0, 1159 endPort: 0, 1160 }, 1161 { // case[2] 1162 portRange: "100-10", 1163 expectErr: false, 1164 beginPort: 10, 1165 endPort: 100, 1166 }, 1167 { // case[3] 1168 portRange: "1024", 1169 expectErr: true, 1170 desc: "single port number is not allowed", 1171 }, 1172 { // case[4] 1173 portRange: DefaultPortRange, 1174 expectErr: false, 1175 beginPort: 0, 1176 endPort: 65535, 1177 }, 1178 { // case[5] 1179 portRange: "1-", 1180 expectErr: true, 1181 desc: "should specify end port", 1182 }, 1183 { // case[6] 1184 portRange: "-100", 1185 expectErr: true, 1186 desc: "should specify begin port", 1187 }, 1188 { // case[7] 1189 portRange: "1:100", 1190 expectErr: true, 1191 desc: "delimiter should be -", 1192 }, 1193 { // case[8] 1194 portRange: "1~100", 1195 expectErr: true, 1196 desc: "delimiter should be -", 1197 }, 1198 { // case[9] 1199 portRange: "1,100", 1200 expectErr: true, 1201 desc: "delimiter should be -", 1202 }, 1203 { // case[10] 1204 portRange: "100-100", 1205 expectErr: false, 1206 desc: "begin port number can be equal to end port number", 1207 beginPort: 100, 1208 endPort: 100, 1209 }, 1210 { // case[11] 1211 portRange: "", 1212 expectErr: false, 1213 desc: "empty string indicates default port range", 1214 beginPort: 0, 1215 endPort: 65535, 1216 }, 1217 { // case[12] 1218 portRange: "-1-12", 1219 expectErr: true, 1220 desc: "port number can not be negative value", 1221 }, 1222 { // case[13] 1223 portRange: "-1--8", 1224 expectErr: true, 1225 desc: "port number can not be negative value", 1226 }, 1227 } 1228 for i := range testCases { 1229 begin, end, err := parsePortRange(testCases[i].portRange) 1230 if err != nil { 1231 if !testCases[i].expectErr { 1232 t.Errorf("case [%d]: unexpected err: %v, desc: %s", i, err, testCases[i].desc) 1233 } 1234 continue 1235 } 1236 if begin != testCases[i].beginPort || end != testCases[i].endPort { 1237 t.Errorf("case [%d]: unexpected mismatch [beginPort, endPort] pair, expect [%d, %d], got [%d, %d], desc: %s", i, testCases[i].beginPort, testCases[i].endPort, begin, end, testCases[i].desc) 1238 } 1239 } 1240 } 1241 1242 // This is a coarse test, but it offers some modicum of confidence as the code is evolved. 1243 func TestValidateEntry(t *testing.T) { 1244 testCases := []struct { 1245 entry *Entry 1246 set *IPSet 1247 valid bool 1248 desc string 1249 }{ 1250 { // case[0] 1251 entry: &Entry{ 1252 SetType: BitmapPort, 1253 }, 1254 set: &IPSet{ 1255 PortRange: DefaultPortRange, 1256 }, 1257 valid: true, 1258 desc: "port number can be empty, default is 0. And port number is in the range of its ipset's port range", 1259 }, 1260 { // case[1] 1261 entry: &Entry{ 1262 SetType: BitmapPort, 1263 Port: 0, 1264 }, 1265 set: &IPSet{ 1266 PortRange: DefaultPortRange, 1267 }, 1268 valid: true, 1269 desc: "port number can be 0. And port number is in the range of its ipset's port range", 1270 }, 1271 { // case[2] 1272 entry: &Entry{ 1273 SetType: BitmapPort, 1274 Port: -1, 1275 }, 1276 valid: false, 1277 desc: "port number can not be negative value", 1278 }, 1279 { // case[3] 1280 entry: &Entry{ 1281 SetType: BitmapPort, 1282 Port: 1080, 1283 }, 1284 set: &IPSet{ 1285 Name: "baz", 1286 PortRange: DefaultPortRange, 1287 }, 1288 desc: "port number is in the range of its ipset's port range", 1289 valid: true, 1290 }, 1291 { // case[4] 1292 entry: &Entry{ 1293 SetType: BitmapPort, 1294 Port: 1080, 1295 }, 1296 set: &IPSet{ 1297 Name: "foo", 1298 PortRange: "0-1079", 1299 }, 1300 desc: "port number is NOT in the range of its ipset's port range", 1301 valid: false, 1302 }, 1303 { // case[5] 1304 entry: &Entry{ 1305 SetType: HashIPPort, 1306 IP: "1.2.3.4", 1307 Protocol: ProtocolTCP, 1308 Port: 8080, 1309 }, 1310 set: &IPSet{ 1311 Name: "bar", 1312 }, 1313 valid: true, 1314 }, 1315 { // case[6] 1316 entry: &Entry{ 1317 SetType: HashIPPort, 1318 IP: "1.2.3.4", 1319 Protocol: ProtocolUDP, 1320 Port: 0, 1321 }, 1322 set: &IPSet{ 1323 Name: "bar", 1324 }, 1325 valid: true, 1326 }, 1327 { // case[7] 1328 entry: &Entry{ 1329 SetType: HashIPPort, 1330 IP: "FE80:0000:0000:0000:0202:B3FF:FE1E:8329", 1331 Protocol: ProtocolTCP, 1332 Port: 1111, 1333 }, 1334 set: &IPSet{ 1335 Name: "ipv6", 1336 }, 1337 valid: true, 1338 }, 1339 { // case[8] 1340 entry: &Entry{ 1341 SetType: HashIPPort, 1342 IP: "", 1343 Protocol: ProtocolTCP, 1344 Port: 1234, 1345 }, 1346 set: &IPSet{ 1347 Name: "empty-ip", 1348 }, 1349 valid: false, 1350 }, 1351 { // case[9] 1352 entry: &Entry{ 1353 SetType: HashIPPort, 1354 IP: "1-2-3-4", 1355 Protocol: ProtocolTCP, 1356 Port: 8900, 1357 }, 1358 set: &IPSet{ 1359 Name: "bad-ip", 1360 }, 1361 valid: false, 1362 }, 1363 { // case[10] 1364 entry: &Entry{ 1365 SetType: HashIPPort, 1366 IP: "10.20.30.40", 1367 Protocol: "", 1368 Port: 8090, 1369 }, 1370 set: &IPSet{ 1371 Name: "empty-protocol", 1372 }, 1373 valid: true, 1374 }, 1375 { // case[11] 1376 entry: &Entry{ 1377 SetType: HashIPPort, 1378 IP: "10.20.30.40", 1379 Protocol: "ICMP", 1380 Port: 8090, 1381 }, 1382 set: &IPSet{ 1383 Name: "unsupported-protocol", 1384 }, 1385 valid: false, 1386 }, 1387 { // case[11] 1388 entry: &Entry{ 1389 SetType: HashIPPort, 1390 IP: "10.20.30.40", 1391 Protocol: "ICMP", 1392 Port: -1, 1393 }, 1394 set: &IPSet{ 1395 // TODO: set name string with white space? 1396 Name: "negative-port-number", 1397 }, 1398 valid: false, 1399 }, 1400 { // case[12] 1401 entry: &Entry{ 1402 SetType: HashIPPortIP, 1403 IP: "10.20.30.40", 1404 Protocol: ProtocolUDP, 1405 Port: 53, 1406 IP2: "10.20.30.40", 1407 }, 1408 set: &IPSet{ 1409 Name: "LOOP-BACK", 1410 }, 1411 valid: true, 1412 }, 1413 { // case[13] 1414 entry: &Entry{ 1415 SetType: HashIPPortIP, 1416 IP: "10.20.30.40", 1417 Protocol: ProtocolUDP, 1418 Port: 53, 1419 IP2: "", 1420 }, 1421 set: &IPSet{ 1422 Name: "empty IP2", 1423 }, 1424 valid: false, 1425 }, 1426 { // case[14] 1427 entry: &Entry{ 1428 SetType: HashIPPortIP, 1429 IP: "10.20.30.40", 1430 Protocol: ProtocolUDP, 1431 Port: 53, 1432 IP2: "foo", 1433 }, 1434 set: &IPSet{ 1435 Name: "invalid IP2", 1436 }, 1437 valid: false, 1438 }, 1439 { // case[15] 1440 entry: &Entry{ 1441 SetType: HashIPPortIP, 1442 IP: "10.20.30.40", 1443 Protocol: ProtocolTCP, 1444 Port: 0, 1445 IP2: "1.2.3.4", 1446 }, 1447 set: &IPSet{ 1448 Name: "zero port", 1449 }, 1450 valid: true, 1451 }, 1452 { // case[16] 1453 entry: &Entry{ 1454 SetType: HashIPPortIP, 1455 IP: "10::40", 1456 Protocol: ProtocolTCP, 1457 Port: 10000, 1458 IP2: "1::4", 1459 }, 1460 set: &IPSet{ 1461 Name: "IPV6", 1462 // TODO: check set's hash family 1463 }, 1464 valid: true, 1465 }, 1466 { // case[17] 1467 entry: &Entry{ 1468 SetType: HashIPPortIP, 1469 IP: "", 1470 Protocol: ProtocolTCP, 1471 Port: 1234, 1472 IP2: "1.2.3.4", 1473 }, 1474 set: &IPSet{ 1475 Name: "empty-ip", 1476 }, 1477 valid: false, 1478 }, 1479 { // case[18] 1480 entry: &Entry{ 1481 SetType: HashIPPortIP, 1482 IP: "1-2-3-4", 1483 Protocol: ProtocolTCP, 1484 Port: 8900, 1485 IP2: "10.20.30.41", 1486 }, 1487 set: &IPSet{ 1488 Name: "bad-ip", 1489 }, 1490 valid: false, 1491 }, 1492 { // case[19] 1493 entry: &Entry{ 1494 SetType: HashIPPortIP, 1495 IP: "10.20.30.40", 1496 Protocol: ProtocolSCTP, 1497 Port: 8090, 1498 IP2: "10.20.30.41", 1499 }, 1500 set: &IPSet{ 1501 Name: "sctp", 1502 }, 1503 valid: true, 1504 }, 1505 { // case[20] 1506 entry: &Entry{ 1507 SetType: HashIPPortIP, 1508 IP: "10.20.30.40", 1509 Protocol: "ICMP", 1510 Port: -1, 1511 IP2: "100.200.30.41", 1512 }, 1513 set: &IPSet{ 1514 Name: "negative-port-number", 1515 }, 1516 valid: false, 1517 }, 1518 { // case[21] 1519 entry: &Entry{ 1520 SetType: HashIPPortNet, 1521 IP: "10.20.30.40", 1522 Protocol: ProtocolTCP, 1523 Port: 53, 1524 Net: "10.20.30.0/24", 1525 }, 1526 set: &IPSet{ 1527 Name: "abc", 1528 }, 1529 valid: true, 1530 }, 1531 { // case[22] 1532 entry: &Entry{ 1533 SetType: HashIPPortNet, 1534 IP: "11.21.31.41", 1535 Protocol: ProtocolUDP, 1536 Port: 1122, 1537 Net: "", 1538 }, 1539 set: &IPSet{ 1540 Name: "empty Net", 1541 }, 1542 valid: false, 1543 }, 1544 { // case[23] 1545 entry: &Entry{ 1546 SetType: HashIPPortNet, 1547 IP: "10.20.30.40", 1548 Protocol: ProtocolUDP, 1549 Port: 8080, 1550 Net: "x-y-z-w", 1551 }, 1552 set: &IPSet{ 1553 Name: "invalid Net", 1554 }, 1555 valid: false, 1556 }, 1557 { // case[24] 1558 entry: &Entry{ 1559 SetType: HashIPPortNet, 1560 IP: "10.20.30.40", 1561 Protocol: ProtocolTCP, 1562 Port: 0, 1563 Net: "10.1.0.0/16", 1564 }, 1565 set: &IPSet{ 1566 Name: "zero port", 1567 }, 1568 valid: true, 1569 }, 1570 { // case[25] 1571 entry: &Entry{ 1572 SetType: HashIPPortNet, 1573 IP: "10::40", 1574 Protocol: ProtocolTCP, 1575 Port: 80, 1576 Net: "2001:db8::/32", 1577 }, 1578 set: &IPSet{ 1579 Name: "IPV6", 1580 // TODO: check set's hash family 1581 }, 1582 valid: true, 1583 }, 1584 { // case[26] 1585 entry: &Entry{ 1586 SetType: HashIPPortNet, 1587 IP: "", 1588 Protocol: ProtocolTCP, 1589 Port: 1234, 1590 Net: "1.2.3.4/22", 1591 }, 1592 set: &IPSet{ 1593 Name: "empty-ip", 1594 }, 1595 valid: false, 1596 }, 1597 { // case[27] 1598 entry: &Entry{ 1599 SetType: HashIPPortNet, 1600 IP: "1-2-3-4", 1601 Protocol: ProtocolTCP, 1602 Port: 8900, 1603 Net: "10.20.30.41/31", 1604 }, 1605 set: &IPSet{ 1606 Name: "bad-ip", 1607 }, 1608 valid: false, 1609 }, 1610 { // case[28] 1611 entry: &Entry{ 1612 SetType: HashIPPortIP, 1613 IP: "10.20.30.40", 1614 Protocol: "FOO", 1615 Port: 8090, 1616 IP2: "10.20.30.0/10", 1617 }, 1618 set: &IPSet{ 1619 Name: "unsupported-protocol", 1620 }, 1621 valid: false, 1622 }, 1623 { // case[29] 1624 entry: &Entry{ 1625 SetType: HashIPPortIP, 1626 IP: "10.20.30.40", 1627 Protocol: ProtocolUDP, 1628 Port: -1, 1629 IP2: "100.200.30.0/12", 1630 }, 1631 set: &IPSet{ 1632 Name: "negative-port-number", 1633 }, 1634 valid: false, 1635 }, 1636 { // case[30] 1637 entry: &Entry{ 1638 SetType: HashIPPortNet, 1639 IP: "10.20.30.40", 1640 Protocol: ProtocolTCP, 1641 Port: 53, 1642 Net: "192.168.3.0/0", 1643 }, 1644 set: &IPSet{ 1645 Name: "net mask boundary 0", 1646 }, 1647 valid: true, 1648 }, 1649 { // case[31] 1650 entry: &Entry{ 1651 SetType: HashIPPortNet, 1652 IP: "10.20.30.40", 1653 Protocol: ProtocolTCP, 1654 Port: 53, 1655 Net: "192.168.3.0/32", 1656 }, 1657 set: &IPSet{ 1658 Name: "net mask boundary 32", 1659 }, 1660 valid: true, 1661 }, 1662 { // case[32] 1663 entry: &Entry{ 1664 SetType: HashIPPortNet, 1665 IP: "10.20.30.40", 1666 Protocol: ProtocolTCP, 1667 Port: 53, 1668 Net: "192.168.3.1/33", 1669 }, 1670 set: &IPSet{ 1671 Name: "invalid net mask", 1672 }, 1673 valid: false, 1674 }, 1675 { // case[33] 1676 entry: &Entry{ 1677 SetType: HashIPPortNet, 1678 IP: "10.20.30.40", 1679 Protocol: ProtocolTCP, 1680 Port: 53, 1681 Net: "192.168.3.1/-1", 1682 }, 1683 set: &IPSet{ 1684 Name: "invalid net mask", 1685 }, 1686 valid: false, 1687 }, 1688 } 1689 for i := range testCases { 1690 valid := testCases[i].entry.Validate(testCases[i].set) 1691 if valid != testCases[i].valid { 1692 t.Errorf("case [%d]: unexpected mismatch, expect valid[%v], got valid[%v], desc: %s", i, testCases[i].valid, valid, testCases[i].entry) 1693 } 1694 } 1695 } 1696 1697 func TestEntryString(t *testing.T) { 1698 testCases := []struct { 1699 name string 1700 entry *Entry 1701 expect string 1702 }{ 1703 { 1704 name: "test when SetType is HashIPPort", 1705 entry: &Entry{ 1706 SetType: HashIPPort, 1707 IP: "1.2.3.4", 1708 Protocol: ProtocolTCP, 1709 Port: 8080, 1710 }, 1711 expect: "1.2.3.4,tcp:8080", 1712 }, 1713 { 1714 name: "test when SetType is HashIPPortIP", 1715 entry: &Entry{ 1716 SetType: HashIPPortIP, 1717 IP: "1.2.3.8", 1718 Protocol: ProtocolUDP, 1719 Port: 8081, 1720 IP2: "1.2.3.8", 1721 }, 1722 expect: "1.2.3.8,udp:8081,1.2.3.8", 1723 }, 1724 { 1725 name: "test when SetType is HashIPPortNet", 1726 entry: &Entry{ 1727 SetType: HashIPPortNet, 1728 IP: "192.168.1.2", 1729 Protocol: ProtocolUDP, 1730 Port: 80, 1731 Net: "10.0.1.0/24", 1732 }, 1733 expect: "192.168.1.2,udp:80,10.0.1.0/24", 1734 }, 1735 { 1736 name: "test when SetType is BitmapPort", 1737 entry: &Entry{ 1738 SetType: BitmapPort, 1739 Port: 80, 1740 }, 1741 expect: "80", 1742 }, 1743 { 1744 name: "test when SetType is unknown", 1745 entry: &Entry{ 1746 SetType: "unknown", 1747 IP: "192.168.1.2", 1748 Protocol: ProtocolUDP, 1749 Port: 80, 1750 Net: "10.0.1.0/24", 1751 }, 1752 expect: "", 1753 }, 1754 } 1755 1756 for _, test := range testCases { 1757 t.Run(test.name, func(t *testing.T) { 1758 result := test.entry.String() 1759 if result != test.expect { 1760 t.Errorf("Unexpected mismatch, expected: %s, got: %s", test.expect, result) 1761 } 1762 }) 1763 } 1764 }