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