mirror of https://github.com/k3s-io/k3s
1303 lines
31 KiB
Go
1303 lines
31 KiB
Go
/*
|
|
Copyright 2017 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package ipset
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
"k8s.io/utils/exec"
|
|
fakeexec "k8s.io/utils/exec/testing"
|
|
)
|
|
|
|
func TestCheckIPSetVersion(t *testing.T) {
|
|
testCases := []struct {
|
|
vstring string
|
|
Expect string
|
|
Err bool
|
|
}{
|
|
{"ipset v4.0, protocol version: 4", "v4.0", false},
|
|
{"ipset v5.1, protocol version: 5", "v5.1", false},
|
|
{"ipset v6.0, protocol version: 6", "v6.0", false},
|
|
{"ipset v6.1, protocol version: 6", "v6.1", false},
|
|
{"ipset v6.19, protocol version: 6", "v6.19", false},
|
|
{"total junk", "", true},
|
|
}
|
|
|
|
for i := range testCases {
|
|
fcmd := fakeexec.FakeCmd{
|
|
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
|
// ipset version response
|
|
func() ([]byte, error) { return []byte(testCases[i].vstring), nil },
|
|
},
|
|
}
|
|
|
|
fexec := fakeexec.FakeExec{
|
|
CommandScript: []fakeexec.FakeCommandAction{
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
},
|
|
}
|
|
|
|
gotVersion, err := getIPSetVersionString(&fexec)
|
|
if (err != nil) != testCases[i].Err {
|
|
t.Errorf("Expected error: %v, Got error: %v", testCases[i].Err, err)
|
|
}
|
|
if err == nil {
|
|
if testCases[i].Expect != gotVersion {
|
|
t.Errorf("Expected result: %v, Got result: %v", testCases[i].Expect, gotVersion)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFlushSet(t *testing.T) {
|
|
fcmd := fakeexec.FakeCmd{
|
|
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
|
// Success
|
|
func() ([]byte, error) { return []byte{}, nil },
|
|
// Success
|
|
func() ([]byte, error) { return []byte{}, nil },
|
|
},
|
|
}
|
|
fexec := fakeexec.FakeExec{
|
|
CommandScript: []fakeexec.FakeCommandAction{
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
},
|
|
}
|
|
runner := New(&fexec)
|
|
// Success.
|
|
err := runner.FlushSet("FOOBAR")
|
|
if err != nil {
|
|
t.Errorf("expected success, got %v", err)
|
|
}
|
|
if fcmd.CombinedOutputCalls != 1 {
|
|
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
|
}
|
|
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "flush", "FOOBAR") {
|
|
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
|
}
|
|
// Flush again
|
|
err = runner.FlushSet("FOOBAR")
|
|
if err != nil {
|
|
t.Errorf("expected success, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDestroySet(t *testing.T) {
|
|
fcmd := fakeexec.FakeCmd{
|
|
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
|
// Success
|
|
func() ([]byte, error) { return []byte{}, nil },
|
|
// Failure
|
|
func() ([]byte, error) {
|
|
return []byte("ipset v6.19: The set with the given name does not exist"), &fakeexec.FakeExitError{Status: 1}
|
|
},
|
|
},
|
|
}
|
|
fexec := fakeexec.FakeExec{
|
|
CommandScript: []fakeexec.FakeCommandAction{
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
},
|
|
}
|
|
runner := New(&fexec)
|
|
// Success
|
|
err := runner.DestroySet("FOOBAR")
|
|
if err != nil {
|
|
t.Errorf("expected success, got %v", err)
|
|
}
|
|
if fcmd.CombinedOutputCalls != 1 {
|
|
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
|
}
|
|
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "destroy", "FOOBAR") {
|
|
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
|
}
|
|
// Failure
|
|
err = runner.DestroySet("FOOBAR")
|
|
if err == nil {
|
|
t.Errorf("expected failure, got nil")
|
|
}
|
|
}
|
|
|
|
func TestDestroyAllSets(t *testing.T) {
|
|
fcmd := fakeexec.FakeCmd{
|
|
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
|
// Success
|
|
func() ([]byte, error) { return []byte{}, nil },
|
|
// Success
|
|
func() ([]byte, error) { return []byte{}, nil },
|
|
},
|
|
}
|
|
fexec := fakeexec.FakeExec{
|
|
CommandScript: []fakeexec.FakeCommandAction{
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
},
|
|
}
|
|
runner := New(&fexec)
|
|
// Success
|
|
err := runner.DestroyAllSets()
|
|
if err != nil {
|
|
t.Errorf("expected success, got %v", err)
|
|
}
|
|
if fcmd.CombinedOutputCalls != 1 {
|
|
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
|
}
|
|
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "destroy") {
|
|
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
|
}
|
|
// Success
|
|
err = runner.DestroyAllSets()
|
|
if err != nil {
|
|
t.Errorf("Unexpected failure: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCreateSet(t *testing.T) {
|
|
testSet := IPSet{
|
|
Name: "FOOBAR",
|
|
SetType: HashIPPort,
|
|
HashFamily: ProtocolFamilyIPV4,
|
|
}
|
|
|
|
fcmd := fakeexec.FakeCmd{
|
|
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
|
// Success
|
|
func() ([]byte, error) { return []byte{}, nil },
|
|
// Success
|
|
func() ([]byte, error) { return []byte{}, nil },
|
|
// Failure
|
|
func() ([]byte, error) {
|
|
return []byte("ipset v6.19: Set cannot be created: set with the same name already exists"), &fakeexec.FakeExitError{Status: 1}
|
|
},
|
|
},
|
|
}
|
|
fexec := fakeexec.FakeExec{
|
|
CommandScript: []fakeexec.FakeCommandAction{
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
},
|
|
}
|
|
runner := New(&fexec)
|
|
// Create with ignoreExistErr = false, expect success
|
|
err := runner.CreateSet(&testSet, false)
|
|
if err != nil {
|
|
t.Errorf("expected success, got %v", err)
|
|
}
|
|
if fcmd.CombinedOutputCalls != 1 {
|
|
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
|
}
|
|
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "create", "FOOBAR", "hash:ip,port", "family", "inet", "hashsize", "1024", "maxelem", "65536") {
|
|
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
|
}
|
|
// Create with ignoreExistErr = true, expect success
|
|
err = runner.CreateSet(&testSet, true)
|
|
if err != nil {
|
|
t.Errorf("expected success, got %v", err)
|
|
}
|
|
if fcmd.CombinedOutputCalls != 2 {
|
|
t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
|
}
|
|
if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("ipset", "create", "FOOBAR", "hash:ip,port", "family", "inet", "hashsize", "1024", "maxelem", "65536", "-exist") {
|
|
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1])
|
|
}
|
|
// Create with ignoreExistErr = false, expect failure
|
|
err = runner.CreateSet(&testSet, false)
|
|
if err == nil {
|
|
t.Errorf("expected failure, got nil")
|
|
}
|
|
}
|
|
|
|
func TestAddEntry(t *testing.T) {
|
|
testEntry := &Entry{
|
|
IP: "192.168.1.1",
|
|
Port: 53,
|
|
Protocol: ProtocolUDP,
|
|
SetType: HashIPPort,
|
|
}
|
|
|
|
testSet := &IPSet{
|
|
Name: "FOOBAR",
|
|
}
|
|
|
|
fcmd := fakeexec.FakeCmd{
|
|
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
|
// Success
|
|
func() ([]byte, error) { return []byte{}, nil },
|
|
// Success
|
|
func() ([]byte, error) { return []byte{}, nil },
|
|
// Failure
|
|
func() ([]byte, error) {
|
|
return []byte("ipset v6.19: Set cannot be created: set with the same name already exists"), &fakeexec.FakeExitError{Status: 1}
|
|
},
|
|
},
|
|
}
|
|
fexec := fakeexec.FakeExec{
|
|
CommandScript: []fakeexec.FakeCommandAction{
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
},
|
|
}
|
|
runner := New(&fexec)
|
|
// Create with ignoreExistErr = false, expect success
|
|
err := runner.AddEntry(testEntry.String(), testSet, false)
|
|
if err != nil {
|
|
t.Errorf("expected success, got %v", err)
|
|
}
|
|
if fcmd.CombinedOutputCalls != 1 {
|
|
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
|
}
|
|
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "add", "FOOBAR", "192.168.1.1,udp:53") {
|
|
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
|
}
|
|
// Create with ignoreExistErr = true, expect success
|
|
err = runner.AddEntry(testEntry.String(), testSet, true)
|
|
if err != nil {
|
|
t.Errorf("expected success, got %v", err)
|
|
}
|
|
if fcmd.CombinedOutputCalls != 2 {
|
|
t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
|
}
|
|
if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("ipset", "add", "FOOBAR", "192.168.1.1,udp:53", "-exist") {
|
|
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1])
|
|
}
|
|
// Create with ignoreExistErr = false, expect failure
|
|
err = runner.AddEntry(testEntry.String(), testSet, false)
|
|
if err == nil {
|
|
t.Errorf("expected failure, got nil")
|
|
}
|
|
}
|
|
|
|
func TestDelEntry(t *testing.T) {
|
|
// TODO: Test more set type
|
|
testEntry := &Entry{
|
|
IP: "192.168.1.1",
|
|
Port: 53,
|
|
Protocol: ProtocolUDP,
|
|
SetType: HashIPPort,
|
|
}
|
|
|
|
fcmd := fakeexec.FakeCmd{
|
|
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
|
// Success
|
|
func() ([]byte, error) { return []byte{}, nil },
|
|
// Failure
|
|
func() ([]byte, error) {
|
|
return []byte("ipset v6.19: Element cannot be deleted from the set: it's not added"), &fakeexec.FakeExitError{Status: 1}
|
|
},
|
|
},
|
|
}
|
|
fexec := fakeexec.FakeExec{
|
|
CommandScript: []fakeexec.FakeCommandAction{
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
},
|
|
}
|
|
runner := New(&fexec)
|
|
|
|
err := runner.DelEntry(testEntry.String(), "FOOBAR")
|
|
if err != nil {
|
|
t.Errorf("expected success, got %v", err)
|
|
}
|
|
if fcmd.CombinedOutputCalls != 1 {
|
|
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
|
}
|
|
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "del", "FOOBAR", "192.168.1.1,udp:53") {
|
|
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
|
}
|
|
err = runner.DelEntry(testEntry.String(), "FOOBAR")
|
|
if err == nil {
|
|
t.Errorf("expected failure, got nil")
|
|
}
|
|
}
|
|
|
|
func TestTestEntry(t *testing.T) {
|
|
// TODO: IPv6?
|
|
testEntry := &Entry{
|
|
IP: "10.120.7.100",
|
|
Port: 8080,
|
|
Protocol: ProtocolTCP,
|
|
SetType: HashIPPort,
|
|
}
|
|
|
|
fcmd := fakeexec.FakeCmd{
|
|
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
|
// Success
|
|
func() ([]byte, error) { return []byte("10.120.7.100,tcp:8080 is in set FOOBAR."), nil },
|
|
// Failure
|
|
func() ([]byte, error) {
|
|
return []byte("192.168.1.3,tcp:8080 is NOT in set FOOBAR."), &fakeexec.FakeExitError{Status: 1}
|
|
},
|
|
},
|
|
}
|
|
fexec := fakeexec.FakeExec{
|
|
CommandScript: []fakeexec.FakeCommandAction{
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
},
|
|
}
|
|
runner := New(&fexec)
|
|
// Success
|
|
ok, err := runner.TestEntry(testEntry.String(), "FOOBAR")
|
|
if err != nil {
|
|
t.Errorf("expected success, got %v", err)
|
|
}
|
|
if fcmd.CombinedOutputCalls != 1 {
|
|
t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
|
}
|
|
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "test", "FOOBAR", "10.120.7.100,tcp:8080") {
|
|
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
|
}
|
|
if !ok {
|
|
t.Errorf("expect entry exists in test set, got not")
|
|
}
|
|
// Failure
|
|
ok, err = runner.TestEntry(testEntry.String(), "FOOBAR")
|
|
if err == nil || ok {
|
|
t.Errorf("expect entry doesn't exist in test set")
|
|
}
|
|
}
|
|
|
|
func TestListEntries(t *testing.T) {
|
|
|
|
output := `Name: foobar
|
|
Type: hash:ip,port
|
|
Revision: 2
|
|
Header: family inet hashsize 1024 maxelem 65536
|
|
Size in memory: 16592
|
|
References: 0
|
|
Members:
|
|
192.168.1.2,tcp:8080
|
|
192.168.1.1,udp:53`
|
|
|
|
emptyOutput := `Name: KUBE-NODE-PORT
|
|
Type: bitmap:port
|
|
Revision: 1
|
|
Header: range 0-65535
|
|
Size in memory: 524432
|
|
References: 1
|
|
Members:
|
|
|
|
`
|
|
|
|
testCases := []struct {
|
|
output string
|
|
expected []string
|
|
}{
|
|
{
|
|
output: output,
|
|
expected: []string{"192.168.1.2,tcp:8080", "192.168.1.1,udp:53"},
|
|
},
|
|
{
|
|
output: emptyOutput,
|
|
expected: []string{},
|
|
},
|
|
}
|
|
|
|
for i := range testCases {
|
|
fcmd := fakeexec.FakeCmd{
|
|
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
|
// Success
|
|
func() ([]byte, error) {
|
|
return []byte(testCases[i].output), nil
|
|
},
|
|
},
|
|
}
|
|
fexec := fakeexec.FakeExec{
|
|
CommandScript: []fakeexec.FakeCommandAction{
|
|
func(cmd string, args ...string) exec.Cmd {
|
|
return fakeexec.InitFakeCmd(&fcmd, cmd, args...)
|
|
},
|
|
},
|
|
}
|
|
runner := New(&fexec)
|
|
// Success
|
|
entries, err := runner.ListEntries("foobar")
|
|
if err != nil {
|
|
t.Errorf("expected success, got: %v", err)
|
|
}
|
|
if fcmd.CombinedOutputCalls != 1 {
|
|
t.Errorf("expected 1 CombinedOutput() calls, got: %d", fcmd.CombinedOutputCalls)
|
|
}
|
|
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "list", "foobar") {
|
|
t.Errorf("wrong CombinedOutput() log, got: %s", fcmd.CombinedOutputLog[0])
|
|
}
|
|
if len(entries) != len(testCases[i].expected) {
|
|
t.Errorf("expected %d ipset entries, got: %d", len(testCases[i].expected), len(entries))
|
|
}
|
|
if !reflect.DeepEqual(entries, testCases[i].expected) {
|
|
t.Errorf("expected entries: %v, got: %v", testCases[i].expected, entries)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestListSets(t *testing.T) {
|
|
output := `foo
|
|
bar
|
|
baz`
|
|
|
|
expected := []string{"foo", "bar", "baz"}
|
|
|
|
fcmd := fakeexec.FakeCmd{
|
|
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
|
// Success
|
|
func() ([]byte, error) { return []byte(output), nil },
|
|
},
|
|
}
|
|
fexec := fakeexec.FakeExec{
|
|
CommandScript: []fakeexec.FakeCommandAction{
|
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
},
|
|
}
|
|
runner := New(&fexec)
|
|
// Success
|
|
list, err := runner.ListSets()
|
|
if err != nil {
|
|
t.Errorf("expected success, got: %v", err)
|
|
}
|
|
if fcmd.CombinedOutputCalls != 1 {
|
|
t.Errorf("expected 1 CombinedOutput() calls, got: %d", fcmd.CombinedOutputCalls)
|
|
}
|
|
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "list", "-n") {
|
|
t.Errorf("wrong CombinedOutput() log, got: %s", fcmd.CombinedOutputLog[0])
|
|
}
|
|
if len(list) != len(expected) {
|
|
t.Errorf("expected %d sets, got: %d", len(expected), len(list))
|
|
}
|
|
if !reflect.DeepEqual(list, expected) {
|
|
t.Errorf("expected sets: %v, got: %v", expected, list)
|
|
}
|
|
}
|
|
|
|
func Test_validIPSetType(t *testing.T) {
|
|
testCases := []struct {
|
|
setType Type
|
|
valid bool
|
|
}{
|
|
{ // case[0]
|
|
setType: Type("foo"),
|
|
valid: false,
|
|
},
|
|
{ // case[1]
|
|
setType: HashIPPortNet,
|
|
valid: true,
|
|
},
|
|
{ // case[2]
|
|
setType: HashIPPort,
|
|
valid: true,
|
|
},
|
|
{ // case[3]
|
|
setType: HashIPPortIP,
|
|
valid: true,
|
|
},
|
|
{ // case[4]
|
|
setType: BitmapPort,
|
|
valid: true,
|
|
},
|
|
{ // case[5]
|
|
setType: Type(""),
|
|
valid: false,
|
|
},
|
|
}
|
|
for i := range testCases {
|
|
valid := validateIPSetType(testCases[i].setType)
|
|
if valid != testCases[i].valid {
|
|
t.Errorf("case [%d]: unexpected mismatch, expect valid[%v], got valid[%v]", i, testCases[i].valid, valid)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_validatePortRange(t *testing.T) {
|
|
testCases := []struct {
|
|
portRange string
|
|
valid bool
|
|
desc string
|
|
}{
|
|
{ // case[0]
|
|
portRange: "a-b",
|
|
valid: false,
|
|
desc: "invalid port number",
|
|
},
|
|
{ // case[1]
|
|
portRange: "1-2",
|
|
valid: true,
|
|
desc: "valid",
|
|
},
|
|
{ // case[2]
|
|
portRange: "90-1",
|
|
valid: true,
|
|
desc: "ipset util can accept the input of begin port number can be less than end port number",
|
|
},
|
|
{ // case[3]
|
|
portRange: DefaultPortRange,
|
|
valid: true,
|
|
desc: "default port range is valid, of course",
|
|
},
|
|
{ // case[4]
|
|
portRange: "12",
|
|
valid: false,
|
|
desc: "a single number is invalid",
|
|
},
|
|
{ // case[5]
|
|
portRange: "1-",
|
|
valid: false,
|
|
desc: "should specify end port",
|
|
},
|
|
{ // case[6]
|
|
portRange: "-100",
|
|
valid: false,
|
|
desc: "should specify begin port",
|
|
},
|
|
{ // case[7]
|
|
portRange: "1:100",
|
|
valid: false,
|
|
desc: "delimiter should be -",
|
|
},
|
|
{ // case[8]
|
|
portRange: "1~100",
|
|
valid: false,
|
|
desc: "delimiter should be -",
|
|
},
|
|
{ // case[9]
|
|
portRange: "1,100",
|
|
valid: false,
|
|
desc: "delimiter should be -",
|
|
},
|
|
{ // case[10]
|
|
portRange: "100-100",
|
|
valid: true,
|
|
desc: "begin port number can be equal to end port number",
|
|
},
|
|
{ // case[11]
|
|
portRange: "",
|
|
valid: false,
|
|
desc: "empty string is invalid",
|
|
},
|
|
{ // case[12]
|
|
portRange: "-1-12",
|
|
valid: false,
|
|
desc: "port number can not be negative value",
|
|
},
|
|
{ // case[13]
|
|
portRange: "-1--8",
|
|
valid: false,
|
|
desc: "port number can not be negative value",
|
|
},
|
|
}
|
|
for i := range testCases {
|
|
valid := validatePortRange(testCases[i].portRange)
|
|
if valid != testCases[i].valid {
|
|
t.Errorf("case [%d]: unexpected mismatch, expect valid[%v], got valid[%v], desc: %s", i, testCases[i].valid, valid, testCases[i].desc)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_validateFamily(t *testing.T) {
|
|
testCases := []struct {
|
|
family string
|
|
valid bool
|
|
}{
|
|
{ // case[0]
|
|
family: "foo",
|
|
valid: false,
|
|
},
|
|
{ // case[1]
|
|
family: ProtocolFamilyIPV4,
|
|
valid: true,
|
|
},
|
|
{ // case[2]
|
|
family: ProtocolFamilyIPV6,
|
|
valid: true,
|
|
},
|
|
{ // case[3]
|
|
family: "ipv4",
|
|
valid: false,
|
|
},
|
|
{ // case[4]
|
|
family: "ipv6",
|
|
valid: false,
|
|
},
|
|
{ // case[5]
|
|
family: "tcp",
|
|
valid: false,
|
|
},
|
|
{ // case[6]
|
|
family: "udp",
|
|
valid: false,
|
|
},
|
|
{ // case[7]
|
|
family: "",
|
|
valid: false,
|
|
},
|
|
}
|
|
for i := range testCases {
|
|
valid := validateHashFamily(testCases[i].family)
|
|
if valid != testCases[i].valid {
|
|
t.Errorf("case [%d]: unexpected mismatch, expect valid[%v], got valid[%v]", i, testCases[i].valid, valid)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_validateProtocol(t *testing.T) {
|
|
testCases := []struct {
|
|
protocol string
|
|
valid bool
|
|
desc string
|
|
}{
|
|
{ // case[0]
|
|
protocol: "foo",
|
|
valid: false,
|
|
},
|
|
{ // case[1]
|
|
protocol: ProtocolTCP,
|
|
valid: true,
|
|
},
|
|
{ // case[2]
|
|
protocol: ProtocolUDP,
|
|
valid: true,
|
|
},
|
|
{ // case[3]
|
|
protocol: "ipv4",
|
|
valid: false,
|
|
},
|
|
{ // case[4]
|
|
protocol: "ipv6",
|
|
valid: false,
|
|
},
|
|
{ // case[5]
|
|
protocol: "TCP",
|
|
valid: false,
|
|
desc: "should be low case",
|
|
},
|
|
{ // case[6]
|
|
protocol: "UDP",
|
|
valid: false,
|
|
desc: "should be low case",
|
|
},
|
|
{ // case[7]
|
|
protocol: "",
|
|
valid: false,
|
|
},
|
|
}
|
|
for i := range testCases {
|
|
valid := validateProtocol(testCases[i].protocol)
|
|
if valid != testCases[i].valid {
|
|
t.Errorf("case [%d]: unexpected mismatch, expect valid[%v], got valid[%v], desc: %s", i, testCases[i].valid, valid, testCases[i].desc)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateIPSet(t *testing.T) {
|
|
testCases := []struct {
|
|
ipset *IPSet
|
|
valid bool
|
|
desc string
|
|
}{
|
|
{ // case[0]
|
|
ipset: &IPSet{
|
|
Name: "test",
|
|
SetType: HashIPPort,
|
|
HashFamily: ProtocolFamilyIPV4,
|
|
HashSize: 1024,
|
|
MaxElem: 1024,
|
|
},
|
|
valid: true,
|
|
},
|
|
{ // case[1]
|
|
ipset: &IPSet{
|
|
Name: "SET",
|
|
SetType: BitmapPort,
|
|
HashFamily: ProtocolFamilyIPV6,
|
|
HashSize: 65535,
|
|
MaxElem: 2048,
|
|
PortRange: DefaultPortRange,
|
|
},
|
|
valid: true,
|
|
},
|
|
{ // case[2]
|
|
ipset: &IPSet{
|
|
Name: "foo",
|
|
SetType: BitmapPort,
|
|
HashFamily: ProtocolFamilyIPV6,
|
|
HashSize: 65535,
|
|
MaxElem: 2048,
|
|
},
|
|
valid: false,
|
|
desc: "should specify right port range for bitmap type set",
|
|
},
|
|
{ // case[3]
|
|
ipset: &IPSet{
|
|
Name: "bar",
|
|
SetType: BitmapPort,
|
|
HashFamily: ProtocolFamilyIPV6,
|
|
HashSize: 0,
|
|
MaxElem: 2048,
|
|
},
|
|
valid: false,
|
|
desc: "wrong hash size number",
|
|
},
|
|
{ // case[4]
|
|
ipset: &IPSet{
|
|
Name: "baz",
|
|
SetType: BitmapPort,
|
|
HashFamily: ProtocolFamilyIPV6,
|
|
HashSize: 1024,
|
|
MaxElem: -1,
|
|
},
|
|
valid: false,
|
|
desc: "wrong hash max elem number",
|
|
},
|
|
{ // case[5]
|
|
ipset: &IPSet{
|
|
Name: "baz",
|
|
SetType: HashIPPortNet,
|
|
HashFamily: "ip",
|
|
HashSize: 1024,
|
|
MaxElem: 1024,
|
|
},
|
|
valid: false,
|
|
desc: "wrong protocol",
|
|
},
|
|
{ // case[6]
|
|
ipset: &IPSet{
|
|
Name: "foo-bar",
|
|
SetType: "xxx",
|
|
HashFamily: ProtocolFamilyIPV4,
|
|
HashSize: 1024,
|
|
MaxElem: 1024,
|
|
},
|
|
valid: false,
|
|
desc: "wrong set type",
|
|
},
|
|
}
|
|
for i := range testCases {
|
|
valid := testCases[i].ipset.Validate()
|
|
if valid != testCases[i].valid {
|
|
t.Errorf("case [%d]: unexpected mismatch, expect valid[%v], got valid[%v], desc: %s", i, testCases[i].valid, valid, testCases[i].desc)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_parsePortRange(t *testing.T) {
|
|
testCases := []struct {
|
|
portRange string
|
|
expectErr bool
|
|
beginPort int
|
|
endPort int
|
|
desc string
|
|
}{
|
|
{ // case[0]
|
|
portRange: "1-100",
|
|
expectErr: false,
|
|
beginPort: 1,
|
|
endPort: 100,
|
|
},
|
|
{ // case[1]
|
|
portRange: "0-0",
|
|
expectErr: false,
|
|
beginPort: 0,
|
|
endPort: 0,
|
|
},
|
|
{ // case[2]
|
|
portRange: "100-10",
|
|
expectErr: false,
|
|
beginPort: 10,
|
|
endPort: 100,
|
|
},
|
|
{ // case[3]
|
|
portRange: "1024",
|
|
expectErr: true,
|
|
desc: "single port number is not allowed",
|
|
},
|
|
{ // case[4]
|
|
portRange: DefaultPortRange,
|
|
expectErr: false,
|
|
beginPort: 0,
|
|
endPort: 65535,
|
|
},
|
|
{ // case[5]
|
|
portRange: "1-",
|
|
expectErr: true,
|
|
desc: "should specify end port",
|
|
},
|
|
{ // case[6]
|
|
portRange: "-100",
|
|
expectErr: true,
|
|
desc: "should specify begin port",
|
|
},
|
|
{ // case[7]
|
|
portRange: "1:100",
|
|
expectErr: true,
|
|
desc: "delimiter should be -",
|
|
},
|
|
{ // case[8]
|
|
portRange: "1~100",
|
|
expectErr: true,
|
|
desc: "delimiter should be -",
|
|
},
|
|
{ // case[9]
|
|
portRange: "1,100",
|
|
expectErr: true,
|
|
desc: "delimiter should be -",
|
|
},
|
|
{ // case[10]
|
|
portRange: "100-100",
|
|
expectErr: false,
|
|
desc: "begin port number can be equal to end port number",
|
|
beginPort: 100,
|
|
endPort: 100,
|
|
},
|
|
{ // case[11]
|
|
portRange: "",
|
|
expectErr: false,
|
|
desc: "empty string indicates default port range",
|
|
beginPort: 0,
|
|
endPort: 65535,
|
|
},
|
|
{ // case[12]
|
|
portRange: "-1-12",
|
|
expectErr: true,
|
|
desc: "port number can not be negative value",
|
|
},
|
|
{ // case[13]
|
|
portRange: "-1--8",
|
|
expectErr: true,
|
|
desc: "port number can not be negative value",
|
|
},
|
|
}
|
|
for i := range testCases {
|
|
begin, end, err := parsePortRange(testCases[i].portRange)
|
|
if err != nil {
|
|
if !testCases[i].expectErr {
|
|
t.Errorf("case [%d]: unexpected err: %v, desc: %s", i, err, testCases[i].desc)
|
|
}
|
|
continue
|
|
}
|
|
if begin != testCases[i].beginPort || end != testCases[i].endPort {
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
// This is a coarse test, but it offers some modicum of confidence as the code is evolved.
|
|
func TestValidateEntry(t *testing.T) {
|
|
testCases := []struct {
|
|
entry *Entry
|
|
set *IPSet
|
|
valid bool
|
|
desc string
|
|
}{
|
|
{ // case[0]
|
|
entry: &Entry{
|
|
SetType: BitmapPort,
|
|
},
|
|
set: &IPSet{
|
|
PortRange: DefaultPortRange,
|
|
},
|
|
valid: true,
|
|
desc: "port number can be empty, default is 0. And port number is in the range of its ipset's port range",
|
|
},
|
|
{ // case[1]
|
|
entry: &Entry{
|
|
SetType: BitmapPort,
|
|
Port: 0,
|
|
},
|
|
set: &IPSet{
|
|
PortRange: DefaultPortRange,
|
|
},
|
|
valid: true,
|
|
desc: "port number can be 0. And port number is in the range of its ipset's port range",
|
|
},
|
|
{ // case[2]
|
|
entry: &Entry{
|
|
SetType: BitmapPort,
|
|
Port: -1,
|
|
},
|
|
valid: false,
|
|
desc: "port number can not be negative value",
|
|
},
|
|
{ // case[3]
|
|
entry: &Entry{
|
|
SetType: BitmapPort,
|
|
Port: 1080,
|
|
},
|
|
set: &IPSet{
|
|
Name: "baz",
|
|
PortRange: DefaultPortRange,
|
|
},
|
|
desc: "port number is in the range of its ipset's port range",
|
|
valid: true,
|
|
},
|
|
{ // case[4]
|
|
entry: &Entry{
|
|
SetType: BitmapPort,
|
|
Port: 1080,
|
|
},
|
|
set: &IPSet{
|
|
Name: "foo",
|
|
PortRange: "0-1079",
|
|
},
|
|
desc: "port number is NOT in the range of its ipset's port range",
|
|
valid: false,
|
|
},
|
|
{ // case[5]
|
|
entry: &Entry{
|
|
SetType: HashIPPort,
|
|
IP: "1.2.3.4",
|
|
Protocol: ProtocolTCP,
|
|
Port: 8080,
|
|
},
|
|
set: &IPSet{
|
|
Name: "bar",
|
|
},
|
|
valid: true,
|
|
},
|
|
{ // case[6]
|
|
entry: &Entry{
|
|
SetType: HashIPPort,
|
|
IP: "1.2.3.4",
|
|
Protocol: ProtocolUDP,
|
|
Port: 0,
|
|
},
|
|
set: &IPSet{
|
|
Name: "bar",
|
|
},
|
|
valid: true,
|
|
},
|
|
{ // case[7]
|
|
entry: &Entry{
|
|
SetType: HashIPPort,
|
|
IP: "FE80:0000:0000:0000:0202:B3FF:FE1E:8329",
|
|
Protocol: ProtocolTCP,
|
|
Port: 1111,
|
|
},
|
|
set: &IPSet{
|
|
Name: "ipv6",
|
|
},
|
|
valid: true,
|
|
},
|
|
{ // case[8]
|
|
entry: &Entry{
|
|
SetType: HashIPPort,
|
|
IP: "",
|
|
Protocol: ProtocolTCP,
|
|
Port: 1234,
|
|
},
|
|
set: &IPSet{
|
|
Name: "empty-ip",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[9]
|
|
entry: &Entry{
|
|
SetType: HashIPPort,
|
|
IP: "1-2-3-4",
|
|
Protocol: ProtocolTCP,
|
|
Port: 8900,
|
|
},
|
|
set: &IPSet{
|
|
Name: "bad-ip",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[10]
|
|
entry: &Entry{
|
|
SetType: HashIPPort,
|
|
IP: "10.20.30.40",
|
|
Protocol: "",
|
|
Port: 8090,
|
|
},
|
|
set: &IPSet{
|
|
Name: "empty-protocol",
|
|
},
|
|
valid: true,
|
|
},
|
|
{ // case[11]
|
|
entry: &Entry{
|
|
SetType: HashIPPort,
|
|
IP: "10.20.30.40",
|
|
Protocol: "ICMP",
|
|
Port: 8090,
|
|
},
|
|
set: &IPSet{
|
|
Name: "unsupported-protocol",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[11]
|
|
entry: &Entry{
|
|
SetType: HashIPPort,
|
|
IP: "10.20.30.40",
|
|
Protocol: "ICMP",
|
|
Port: -1,
|
|
},
|
|
set: &IPSet{
|
|
// TODO: set name string with white space?
|
|
Name: "negative-port-number",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[12]
|
|
entry: &Entry{
|
|
SetType: HashIPPortIP,
|
|
IP: "10.20.30.40",
|
|
Protocol: ProtocolUDP,
|
|
Port: 53,
|
|
IP2: "10.20.30.40",
|
|
},
|
|
set: &IPSet{
|
|
Name: "LOOP-BACK",
|
|
},
|
|
valid: true,
|
|
},
|
|
{ // case[13]
|
|
entry: &Entry{
|
|
SetType: HashIPPortIP,
|
|
IP: "10.20.30.40",
|
|
Protocol: ProtocolUDP,
|
|
Port: 53,
|
|
IP2: "",
|
|
},
|
|
set: &IPSet{
|
|
Name: "empty IP2",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[14]
|
|
entry: &Entry{
|
|
SetType: HashIPPortIP,
|
|
IP: "10.20.30.40",
|
|
Protocol: ProtocolUDP,
|
|
Port: 53,
|
|
IP2: "foo",
|
|
},
|
|
set: &IPSet{
|
|
Name: "invalid IP2",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[15]
|
|
entry: &Entry{
|
|
SetType: HashIPPortIP,
|
|
IP: "10.20.30.40",
|
|
Protocol: ProtocolTCP,
|
|
Port: 0,
|
|
IP2: "1.2.3.4",
|
|
},
|
|
set: &IPSet{
|
|
Name: "zero port",
|
|
},
|
|
valid: true,
|
|
},
|
|
{ // case[16]
|
|
entry: &Entry{
|
|
SetType: HashIPPortIP,
|
|
IP: "10::40",
|
|
Protocol: ProtocolTCP,
|
|
Port: 10000,
|
|
IP2: "1::4",
|
|
},
|
|
set: &IPSet{
|
|
Name: "IPV6",
|
|
// TODO: check set's hash family
|
|
},
|
|
valid: true,
|
|
},
|
|
{ // case[17]
|
|
entry: &Entry{
|
|
SetType: HashIPPortIP,
|
|
IP: "",
|
|
Protocol: ProtocolTCP,
|
|
Port: 1234,
|
|
IP2: "1.2.3.4",
|
|
},
|
|
set: &IPSet{
|
|
Name: "empty-ip",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[18]
|
|
entry: &Entry{
|
|
SetType: HashIPPortIP,
|
|
IP: "1-2-3-4",
|
|
Protocol: ProtocolTCP,
|
|
Port: 8900,
|
|
IP2: "10.20.30.41",
|
|
},
|
|
set: &IPSet{
|
|
Name: "bad-ip",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[19]
|
|
entry: &Entry{
|
|
SetType: HashIPPortIP,
|
|
IP: "10.20.30.40",
|
|
Protocol: "SCTP ",
|
|
Port: 8090,
|
|
IP2: "10.20.30.41",
|
|
},
|
|
set: &IPSet{
|
|
Name: "unsupported-protocol",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[20]
|
|
entry: &Entry{
|
|
SetType: HashIPPortIP,
|
|
IP: "10.20.30.40",
|
|
Protocol: "ICMP",
|
|
Port: -1,
|
|
IP2: "100.200.30.41",
|
|
},
|
|
set: &IPSet{
|
|
Name: "negative-port-number",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[21]
|
|
entry: &Entry{
|
|
SetType: HashIPPortNet,
|
|
IP: "10.20.30.40",
|
|
Protocol: ProtocolTCP,
|
|
Port: 53,
|
|
// TODO: CIDR /32 may not be valid
|
|
Net: "10.20.30.0/24",
|
|
},
|
|
set: &IPSet{
|
|
Name: "abc",
|
|
},
|
|
valid: true,
|
|
},
|
|
{ // case[22]
|
|
entry: &Entry{
|
|
SetType: HashIPPortNet,
|
|
IP: "11.21.31.41",
|
|
Protocol: ProtocolUDP,
|
|
Port: 1122,
|
|
Net: "",
|
|
},
|
|
set: &IPSet{
|
|
Name: "empty Net",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[23]
|
|
entry: &Entry{
|
|
SetType: HashIPPortNet,
|
|
IP: "10.20.30.40",
|
|
Protocol: ProtocolUDP,
|
|
Port: 8080,
|
|
Net: "x-y-z-w",
|
|
},
|
|
set: &IPSet{
|
|
Name: "invalid Net",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[24]
|
|
entry: &Entry{
|
|
SetType: HashIPPortNet,
|
|
IP: "10.20.30.40",
|
|
Protocol: ProtocolTCP,
|
|
Port: 0,
|
|
Net: "10.1.0.0/16",
|
|
},
|
|
set: &IPSet{
|
|
Name: "zero port",
|
|
},
|
|
valid: true,
|
|
},
|
|
{ // case[25]
|
|
entry: &Entry{
|
|
SetType: HashIPPortNet,
|
|
IP: "10::40",
|
|
Protocol: ProtocolTCP,
|
|
Port: 80,
|
|
Net: "2001:db8::/32",
|
|
},
|
|
set: &IPSet{
|
|
Name: "IPV6",
|
|
// TODO: check set's hash family
|
|
},
|
|
valid: true,
|
|
},
|
|
{ // case[26]
|
|
entry: &Entry{
|
|
SetType: HashIPPortNet,
|
|
IP: "",
|
|
Protocol: ProtocolTCP,
|
|
Port: 1234,
|
|
Net: "1.2.3.4/22",
|
|
},
|
|
set: &IPSet{
|
|
Name: "empty-ip",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[27]
|
|
entry: &Entry{
|
|
SetType: HashIPPortNet,
|
|
IP: "1-2-3-4",
|
|
Protocol: ProtocolTCP,
|
|
Port: 8900,
|
|
Net: "10.20.30.41/31",
|
|
},
|
|
set: &IPSet{
|
|
Name: "bad-ip",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[28]
|
|
entry: &Entry{
|
|
SetType: HashIPPortIP,
|
|
IP: "10.20.30.40",
|
|
Protocol: "FOO",
|
|
Port: 8090,
|
|
IP2: "10.20.30.0/10",
|
|
},
|
|
set: &IPSet{
|
|
Name: "unsupported-protocol",
|
|
},
|
|
valid: false,
|
|
},
|
|
{ // case[29]
|
|
entry: &Entry{
|
|
SetType: HashIPPortIP,
|
|
IP: "10.20.30.40",
|
|
Protocol: ProtocolUDP,
|
|
Port: -1,
|
|
IP2: "100.200.30.0/12",
|
|
},
|
|
set: &IPSet{
|
|
Name: "negative-port-number",
|
|
},
|
|
valid: false,
|
|
},
|
|
}
|
|
for i := range testCases {
|
|
valid := testCases[i].entry.Validate(testCases[i].set)
|
|
if valid != testCases[i].valid {
|
|
t.Errorf("case [%d]: unexpected mismatch, expect valid[%v], got valid[%v], desc: %s", i, testCases[i].valid, valid, testCases[i].entry)
|
|
}
|
|
}
|
|
}
|