blob: feef3d8eb38452d812f998ff4008201c2223be66 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package filter
import (
"testing"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
)
func TestPortRangeIsvalid(t *testing.T) {
var tests = []struct {
description string
portRange PortRange
valid bool
}{
{
"any port",
PortRange{0, 0},
true,
},
{
"start can't be zero",
PortRange{0, 3},
false,
},
{
"port 1",
PortRange{1, 1},
true,
},
{
"port range 1 to 3",
PortRange{1, 3},
true,
},
{
"start can't be larger than end",
PortRange{2, 1},
false,
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got := test.portRange.IsValid()
want := test.valid
if got != want {
t.Errorf("got=%t, want=%t", got, want)
}
})
}
}
func TestPortRangeContains(t *testing.T) {
var tests = []struct {
description string
portRange PortRange
port uint16
contained bool
}{
{
"any port contains 1",
PortRange{0, 0},
1,
true,
},
{
"any port contains 5",
PortRange{0, 0},
5,
true,
},
{
"port 2 doesn't contain 1",
PortRange{2, 2},
1,
false,
},
{
"port 2 contains 2",
PortRange{2, 2},
2,
true,
},
{
"port 2 doesn't cantain 3",
PortRange{2, 2},
3,
false,
},
{
"range 2 to 4 doesn't contain 1",
PortRange{2, 4},
1,
false,
},
{
"range 2 to 4 contains 2",
PortRange{2, 4},
2,
true,
},
{
"range 2 to 4 contains 3",
PortRange{2, 4},
3,
true,
},
{
"range 2 to 4 contains 4",
PortRange{2, 4},
4,
true,
},
{
"range 2 to 4 doesn't contain 5",
PortRange{2, 4},
5,
false,
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got := test.portRange.Contains(test.port)
want := test.contained
if got != want {
t.Errorf("got=%t, want=%t", got, want)
}
})
}
}
func TestPortRangeLength(t *testing.T) {
var tests = []struct {
description string
portRange PortRange
length uint16
err error
}{
{
"the length of any port is 0",
PortRange{0, 0},
0,
nil,
},
{
"range start can't be zero if range end is not zero",
PortRange{0, 3},
0,
ErrBadPortRange,
},
{
"range start can't be larger than range end",
PortRange{2, 1},
0,
ErrBadPortRange,
},
{
"the length of a single port is 1 ",
PortRange{2, 2},
1,
nil,
},
{
"the length of range 2 to 4 is 3",
PortRange{2, 4},
3,
nil,
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got, err := test.portRange.Length()
if err != test.err {
t.Errorf("err=%v, want=%v", err, test.err)
}
want := test.length
if got != want {
t.Errorf("got=%d, want=%d", got, want)
}
})
}
}
func TestRuleIsValid(t *testing.T) {
var tests = []struct {
description string
rule Rule
want bool
}{
{
"srcSubnet is nil",
Rule{
srcSubnet: nil,
srcPortRange: PortRange{1, 2},
dstSubnet: func() *tcpip.Subnet {
subnet, err := tcpip.NewSubnet("\x0b\x00\x00\x00", "\xff\x00\x00\x00")
if err != nil {
t.Fatal(err)
}
return &subnet
}(),
dstPortRange: PortRange{10, 15},
},
true,
},
{
"dstSubnet is nil",
Rule{
srcSubnet: func() *tcpip.Subnet {
subnet, err := tcpip.NewSubnet("\x0b\x00\x00\x00", "\xff\x00\x00\x00")
if err != nil {
t.Fatal(err)
}
return &subnet
}(),
srcPortRange: PortRange{1, 2},
dstSubnet: nil,
dstPortRange: PortRange{10, 15},
},
true,
},
{
"both srcSubnet and dstSubnet are IPv4",
Rule{
srcSubnet: func() *tcpip.Subnet {
subnet, err := tcpip.NewSubnet("\x0a\x00\x00\x00", "\xff\x00\x00\x00")
if err != nil {
t.Fatal(err)
}
return &subnet
}(),
srcPortRange: PortRange{1, 2},
dstSubnet: func() *tcpip.Subnet {
subnet, err := tcpip.NewSubnet("\x0b\x00\x00\x00", "\xff\x00\x00\x00")
if err != nil {
t.Fatal(err)
}
return &subnet
}(),
dstPortRange: PortRange{10, 15},
},
true,
},
{
"srcSubnet is IPv4, and dstSubnet are IPv6",
Rule{
srcSubnet: func() *tcpip.Subnet {
subnet, err := tcpip.NewSubnet("\x0a\x00\x00\x00", "\xff\x00\x00\x00")
if err != nil {
t.Fatal(err)
}
return &subnet
}(),
srcPortRange: PortRange{1, 2},
dstSubnet: func() *tcpip.Subnet {
subnet, err := tcpip.NewSubnet(
"\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
"\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
if err != nil {
t.Fatal(err)
}
return &subnet
}(),
dstPortRange: PortRange{10, 15},
},
false,
},
{
"srcPortRange is not valid",
Rule{
srcSubnet: func() *tcpip.Subnet {
subnet, err := tcpip.NewSubnet("\x0a\x00\x00\x00", "\xff\x00\x00\x00")
if err != nil {
t.Fatal(err)
}
return &subnet
}(),
srcPortRange: PortRange{2, 1},
dstSubnet: func() *tcpip.Subnet {
subnet, err := tcpip.NewSubnet("\x0b\x00\x00\x00", "\xff\x00\x00\x00")
if err != nil {
t.Fatal(err)
}
return &subnet
}(),
dstPortRange: PortRange{10, 15},
},
false,
},
{
"dstPortRange is not valid",
Rule{
srcSubnet: func() *tcpip.Subnet {
subnet, err := tcpip.NewSubnet("\x0a\x00\x00\x00", "\xff\x00\x00\x00")
if err != nil {
t.Fatal(err)
}
return &subnet
}(),
srcPortRange: PortRange{1, 2},
dstSubnet: func() *tcpip.Subnet {
subnet, err := tcpip.NewSubnet("\x0b\x00\x00\x00", "\xff\x00\x00\x00")
if err != nil {
t.Fatal(err)
}
return &subnet
}(),
dstPortRange: PortRange{15, 10},
},
false,
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got := test.rule.IsValid()
if got != test.want {
t.Errorf("got=%t, want=%t", got, test.want)
}
})
}
}
func TestRuleMatch(t *testing.T) {
subnet, err := tcpip.NewSubnet("\x0a\x00\x00\x00", "\xff\x00\x00\x00")
if err != nil {
t.Fatal(err)
}
var tests = []struct {
description string
rule Rule
dir Direction
transProto tcpip.TransportProtocolNumber
srcAddr tcpip.Address
srcPort uint16
dstAddr tcpip.Address
dstPort uint16
want bool
}{
{
"transProto is any",
Rule{
direction: Incoming,
srcSubnet: &subnet,
srcPortRange: PortRange{100, 100},
},
Incoming,
header.TCPProtocolNumber,
"\x0a\x00\x00\x00",
100,
"\x0a\x00\x00\x02",
200,
true,
},
{
"transProto is TCP",
Rule{
action: Drop,
direction: Incoming,
transProto: header.TCPProtocolNumber,
srcSubnet: &subnet,
srcPortRange: PortRange{100, 100},
},
Incoming,
header.TCPProtocolNumber,
"\x0a\x00\x00\x00",
100,
"\x0a\x00\x00\x02",
200,
true,
},
{
"transProto doesn't match",
Rule{
direction: Incoming,
transProto: header.TCPProtocolNumber,
srcSubnet: &subnet,
srcPortRange: PortRange{100, 100},
},
Incoming,
header.UDPProtocolNumber,
"\x0a\x00\x00\x00",
100,
"\x0a\x00\x00\x02",
200,
false,
},
{
"directions doesn't match",
Rule{
direction: Incoming,
srcSubnet: &subnet,
srcPortRange: PortRange{100, 100},
},
Outgoing,
header.UDPProtocolNumber,
"\x0a\x00\x00\x00",
100,
"\x0a\x00\x00\x02",
200,
false,
},
{
"srcSubnet doesn't contain the address",
Rule{
direction: Incoming,
srcSubnet: &subnet,
srcPortRange: PortRange{100, 100},
},
Incoming,
header.UDPProtocolNumber,
"\x0b\x00\x00\x00",
100,
"\x0a\x00\x00\x02",
200,
false,
},
{
"srcSubnet doesn't contain the address (invert match is on)",
Rule{
direction: Incoming,
srcSubnet: &subnet,
srcSubnetInvertMatch: true,
srcPortRange: PortRange{100, 100},
},
Incoming,
header.UDPProtocolNumber,
"\x0b\x00\x00\x00",
100,
"\x0a\x00\x00\x02",
200,
true,
},
{
"dstSubnet contains the address",
Rule{
direction: Incoming,
dstSubnet: &subnet,
dstPortRange: PortRange{200, 200},
},
Incoming,
header.UDPProtocolNumber,
"\x0b\x00\x00\x00",
100,
"\x0a\x00\x00\x02",
200,
true,
},
{
"dstSubnet doesn't contains the address (invert_match is on)",
Rule{
direction: Incoming,
dstSubnet: &subnet,
dstSubnetInvertMatch: true,
dstPortRange: PortRange{200, 200},
},
Incoming,
header.UDPProtocolNumber,
"\x0b\x00\x00\x00",
100,
"\x0c\x00\x00\x02",
200,
true,
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got := test.rule.Match(test.dir, test.transProto, test.srcAddr, test.srcPort, test.dstAddr, test.dstPort)
if got != test.want {
t.Errorf("got=%t, want=%t", got, test.want)
}
})
}
}
func TestNATIsValid(t *testing.T) {
var tests = []struct {
description string
nat NAT
want bool
}{
{
"both srcSubnet and newSrcAddr are IPv4",
NAT{
srcSubnet: func() *tcpip.Subnet {
subnet, err := tcpip.NewSubnet("\x0a\x00\x00\x00", "\xff\x00\x00\x00")
if err != nil {
t.Fatal(err)
}
return &subnet
}(),
newSrcAddr: "\x0b\x00\x00\x00",
},
true,
},
{
"srcSubnet is IPv4 and newSrcAddr is IPv6",
NAT{
srcSubnet: func() *tcpip.Subnet {
subnet, err := tcpip.NewSubnet("\x0a\x00\x00\x00", "\xff\x00\x00\x00")
if err != nil {
t.Fatal(err)
}
return &subnet
}(),
newSrcAddr: "\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
},
false,
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got := test.nat.IsValid()
if got != test.want {
t.Errorf("got=%t, want=%t", got, test.want)
}
})
}
}
func TestNATMatch(t *testing.T) {
subnet, err := tcpip.NewSubnet("\x0a\x00\x00\x00", "\xff\x00\x00\x00")
if err != nil {
t.Fatal(err)
}
var tests = []struct {
description string
nat NAT
transProto tcpip.TransportProtocolNumber
srcAddr tcpip.Address
want bool
}{
{
"transProto is any",
NAT{
srcSubnet: &subnet,
newSrcAddr: "\x0b\x00\x00\x00",
},
header.TCPProtocolNumber,
"\x0a\x00\x00\x00",
true,
},
{
"transProto is TCP",
NAT{
transProto: header.TCPProtocolNumber,
srcSubnet: &subnet,
newSrcAddr: "\x0b\x00\x00\x00",
},
header.TCPProtocolNumber,
"\x0a\x00\x00\x00",
true,
},
{
"transProto doesn't match",
NAT{
transProto: header.TCPProtocolNumber,
srcSubnet: &subnet,
newSrcAddr: "\x0b\x00\x00\x00",
},
header.UDPProtocolNumber,
"\x0a\x00\x00\x00",
false,
},
{
"srcSubnet doesn't contain the address",
NAT{
srcSubnet: &subnet,
newSrcAddr: "\x0b\x00\x00\x00",
},
header.UDPProtocolNumber,
"\x0b\x00\x00\x00",
false,
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got := test.nat.Match(test.transProto, test.srcAddr)
if got != test.want {
t.Errorf("got=%t, want=%t", got, test.want)
}
})
}
}
func TestRDRIsValid(t *testing.T) {
var tests = []struct {
description string
rdr RDR
valid bool
}{
{
"dstPortRange can't be any",
RDR{
transProto: header.UDPProtocolNumber,
dstAddr: "\x0a\x00\x00\x00",
dstPortRange: PortRange{0, 0},
newDstAddr: "\x0b\x00\x00\x00",
newDstPortRange: PortRange{1, 3},
},
false,
},
{
"newDstPortRange can't be any",
RDR{
transProto: header.UDPProtocolNumber,
dstAddr: "\x0a\x00\x00\x00",
dstPortRange: PortRange{1, 3},
newDstAddr: "\x0b\x00\x00\x00",
newDstPortRange: PortRange{0, 0},
},
false,
},
{
"dstPortRange is not valid",
RDR{
transProto: header.UDPProtocolNumber,
dstAddr: "\x0a\x00\x00\x00",
dstPortRange: PortRange{3, 1},
newDstAddr: "\x0b\x00\x00\x00",
newDstPortRange: PortRange{1, 3},
},
false,
},
{
"newDstPortRange is not valid",
RDR{
transProto: header.UDPProtocolNumber,
dstAddr: "\x0a\x00\x00\x00",
dstPortRange: PortRange{1, 3},
newDstAddr: "\x0b\x00\x00\x00",
newDstPortRange: PortRange{3, 1},
},
false,
},
{
"transProto is any",
RDR{
dstAddr: "\x0a\x00\x00\x00",
dstPortRange: PortRange{1, 3},
newDstAddr: "\x0b\x00\x00\x00",
newDstPortRange: PortRange{1, 3},
},
true,
},
{
"PortRanges must have the same length",
RDR{
transProto: header.UDPProtocolNumber,
dstAddr: "\x0a\x00\x00\x00",
dstPortRange: PortRange{1, 2},
newDstAddr: "\x0b\x00\x00\x00",
newDstPortRange: PortRange{1, 3},
},
false,
},
{
"dstAddr is IPv4 and newDstAddr is IPv6",
RDR{
transProto: header.UDPProtocolNumber,
dstAddr: "\x0a\x00\x00\x00",
dstPortRange: PortRange{1, 2},
newDstAddr: "\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
newDstPortRange: PortRange{1, 2},
},
false,
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got := test.rdr.IsValid()
want := test.valid
if got != want {
t.Errorf("got=%t, want=%t", got, want)
}
})
}
}
func TestRDRMatch(t *testing.T) {
var tests = []struct {
description string
rdr RDR
transProto tcpip.TransportProtocolNumber
dstAddr tcpip.Address
dstPort uint16
want bool
}{
{
"transProto is any; it matches TCP",
RDR{
dstAddr: "\x0a\x00\x00\x00",
dstPortRange: PortRange{100, 101},
newDstAddr: "\x0b\x00\x00\x00",
newDstPortRange: PortRange{100, 101},
},
header.TCPProtocolNumber,
"\x0a\x00\x00\x00",
100,
true,
},
{
"transProto is any; it matches UDP",
RDR{
dstAddr: "\x0a\x00\x00\x00",
dstPortRange: PortRange{100, 101},
newDstAddr: "\x0b\x00\x00\x00",
newDstPortRange: PortRange{100, 101},
},
header.UDPProtocolNumber,
"\x0a\x00\x00\x00",
100,
true,
},
{
"transProto is TCP",
RDR{
transProto: header.TCPProtocolNumber,
dstAddr: "\x0a\x00\x00\x00",
dstPortRange: PortRange{100, 101},
newDstAddr: "\x0b\x00\x00\x00",
newDstPortRange: PortRange{100, 101},
},
header.TCPProtocolNumber,
"\x0a\x00\x00\x00",
100,
true,
},
{
"transProto doesn't match",
RDR{
transProto: header.TCPProtocolNumber,
dstAddr: "\x0a\x00\x00\x00",
dstPortRange: PortRange{100, 101},
newDstAddr: "\x0b\x00\x00\x00",
newDstPortRange: PortRange{100, 101},
},
header.UDPProtocolNumber,
"\x0a\x00\x00\x00",
100,
false,
},
{
"dstAddr doesn't match",
RDR{
dstAddr: "\x0a\x00\x00\x00",
dstPortRange: PortRange{100, 101},
newDstAddr: "\x0b\x00\x00\x00",
newDstPortRange: PortRange{100, 101},
},
header.UDPProtocolNumber,
"\x0a\x00\x00\x01",
100,
false,
},
{
"dstPortRange doesn't contain the port",
RDR{
dstAddr: "\x0a\x00\x00\x00",
dstPortRange: PortRange{100, 101},
newDstAddr: "\x0b\x00\x00\x00",
newDstPortRange: PortRange{100, 101},
},
header.UDPProtocolNumber,
"\x0a\x00\x00\x00",
200,
false,
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got := test.rdr.Match(test.transProto, test.dstAddr, test.dstPort)
if got != test.want {
t.Errorf("got=%t, want=%t", got, test.want)
}
})
}
}