blob: 5bd257b0ee50115e9cb902093fac0cc34bb4f85c [file] [log] [blame]
// Copyright 2018 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 netstack
import (
"context"
"fmt"
"syscall/zx/dispatch"
"testing"
"netstack/fidlconv"
"fidl/fuchsia/hardware/ethernet"
"fidl/fuchsia/net"
"fidl/fuchsia/net/name"
"fidl/fuchsia/net/stack"
"fidl/fuchsia/netstack"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"gvisor.dev/gvisor/pkg/tcpip"
)
func TestValidateIPAddressMask(t *testing.T) {
for _, tc := range []struct {
addr tcpip.Address
prefixLen uint8
want bool
}{
{addr: "\x0a\x0b\xe0\x00", prefixLen: 32, want: true},
{addr: "\x0a\x0b\xe0\x00", prefixLen: 20, want: true},
{addr: "\x0a\x0b\xe0\x00", prefixLen: 19, want: true},
{addr: "\x0a\x0b\xe0\x00", prefixLen: 18, want: false},
{addr: "\x0a\x0b\xef\x00", prefixLen: 25, want: true},
{addr: "\x0a\x0b\xef\x00", prefixLen: 24, want: true},
{addr: "\x0a\x0b\xef\x00", prefixLen: 23, want: false},
{addr: "\x00\x00\x00\x00", prefixLen: 0, want: true},
{addr: "\x00\x00\x00\x00", prefixLen: 32, want: true},
{addr: "\x00\x00\x00\x00", prefixLen: 33, want: false},
} {
addr := fidlconv.ToNetIpAddress(tc.addr)
if got := validateSubnet(net.Subnet{Addr: addr, PrefixLen: tc.prefixLen}); got != tc.want {
t.Errorf("got validateSubnet(%v) = %t, want = %t", addr, got, tc.want)
}
}
}
func TestFuchsiaNetStack(t *testing.T) {
t.Run("Add and Delete Forwarding Entries", func(t *testing.T) {
ns := newNetstack(t)
eth := deviceForAddEth(ethernet.Info{}, t)
if _, err := ns.addEth(testTopoPath, netstack.InterfaceConfig{Name: testDeviceName}, &eth); err != nil {
t.Fatal(err)
}
ni := stackImpl{ns: ns}
table, err := ni.GetForwardingTable(context.Background())
AssertNoError(t, err)
var destInvalid, destNic, destNextHop stack.ForwardingDestination
destInvalid.SetDeviceId(789)
destNic.SetDeviceId(1)
destNextHop.SetNextHop(fidlconv.ToNetIpAddress("\xc0\xa8\x20\x01"))
nonexistentSubnet := net.Subnet{
Addr: fidlconv.ToNetIpAddress("\xaa\x0b\xe0\x00"),
PrefixLen: 19,
}
badMaskSubnet := net.Subnet{
Addr: fidlconv.ToNetIpAddress("\xc0\xa8\x11\x01"),
PrefixLen: 19,
}
localSubnet := net.Subnet{
Addr: fidlconv.ToNetIpAddress("\xc0\xa8\x20\x00"),
PrefixLen: 19,
}
defaultSubnet := net.Subnet{
Addr: fidlconv.ToNetIpAddress("\x00\x00\x00\x00"),
PrefixLen: 0,
}
invalidRoute := stack.ForwardingEntry{
Subnet: localSubnet,
Destination: destInvalid,
}
localRoute := stack.ForwardingEntry{
Subnet: localSubnet,
Destination: destNic,
}
defaultRoute := stack.ForwardingEntry{
Subnet: defaultSubnet,
Destination: destNextHop,
}
// Add an invalid entry.
addResult, err := ni.AddForwardingEntry(context.Background(), invalidRoute)
AssertNoError(t, err)
if addResult != stack.StackAddForwardingEntryResultWithErr(stack.ErrorInvalidArgs) {
t.Errorf("got ni.AddForwardingEntry(%#v) = %#v, want = Err(ErrorInvalidArgs)", invalidRoute, addResult)
}
// Add a local subnet route.
addResult, err = ni.AddForwardingEntry(context.Background(), localRoute)
AssertNoError(t, err)
if addResult != stack.StackAddForwardingEntryResultWithResponse(stack.StackAddForwardingEntryResponse{}) {
t.Fatalf("got ni.AddForwardingEntry(%#v) = %#v, want = Response()", localRoute, addResult)
}
table, err = ni.GetForwardingTable(context.Background())
AssertNoError(t, err)
expectedTable := []stack.ForwardingEntry{localRoute}
if diff := cmp.Diff(table, expectedTable, cmpopts.IgnoreTypes(struct{}{})); diff != "" {
t.Fatalf("forwarding table mismatch (-want +got):\n%s", diff)
}
// Add the same entry again.
addResult, err = ni.AddForwardingEntry(context.Background(), localRoute)
AssertNoError(t, err)
if addResult != stack.StackAddForwardingEntryResultWithResponse(stack.StackAddForwardingEntryResponse{}) {
t.Errorf("got ni.AddForwardingEntry(%#v) = %#v, want = Response()", localRoute, addResult)
}
table, err = ni.GetForwardingTable(context.Background())
AssertNoError(t, err)
if diff := cmp.Diff(table, expectedTable, cmpopts.IgnoreTypes(struct{}{})); diff != "" {
t.Fatalf("forwarding table mismatch (-want +got):\n%s", diff)
}
// Add default route.
addResult, err = ni.AddForwardingEntry(context.Background(), defaultRoute)
AssertNoError(t, err)
if addResult != stack.StackAddForwardingEntryResultWithResponse(stack.StackAddForwardingEntryResponse{}) {
t.Errorf("got ni.AddForwardingEntry(%#v) = %#v, want = Response()", defaultRoute, addResult)
}
table, err = ni.GetForwardingTable(context.Background())
AssertNoError(t, err)
expectedTable = append(expectedTable, defaultRoute)
if diff := cmp.Diff(table, expectedTable, cmpopts.IgnoreTypes(struct{}{})); diff != "" {
t.Fatalf("forwarding table mismatch (-want +got):\n%s", diff)
}
// Remove nonexistent subnet.
delResult, err := ni.DelForwardingEntry(context.Background(), nonexistentSubnet)
AssertNoError(t, err)
if delResult != stack.StackDelForwardingEntryResultWithErr(stack.ErrorNotFound) {
t.Errorf("got ni.DelForwardingEntry(%#v) = %#v, want = Err(ErrorNotFound)", nonexistentSubnet, delResult)
}
// Remove subnet with bad subnet mask.
delResult, err = ni.DelForwardingEntry(context.Background(), badMaskSubnet)
AssertNoError(t, err)
if delResult != stack.StackDelForwardingEntryResultWithErr(stack.ErrorInvalidArgs) {
t.Errorf("got ni.DelForwardingEntry(%#v) = %#v, want = Err(ErrorInvalidArgs)", badMaskSubnet, delResult)
}
// Remove local route.
delResult, err = ni.DelForwardingEntry(context.Background(), localSubnet)
AssertNoError(t, err)
if delResult != stack.StackDelForwardingEntryResultWithResponse(stack.StackDelForwardingEntryResponse{}) {
t.Fatalf("got ni.DelForwardingEntry(%#v) = Err(%s), want = Response()", localRoute, delResult.Err)
}
table, err = ni.GetForwardingTable(context.Background())
AssertNoError(t, err)
expectedTable = []stack.ForwardingEntry{defaultRoute}
if diff := cmp.Diff(table, expectedTable, cmpopts.IgnoreTypes(struct{}{})); diff != "" {
t.Fatalf("forwarding table mismatch (-want +got):\n%s", diff)
}
// Remove default route.
delResult, err = ni.DelForwardingEntry(context.Background(), defaultSubnet)
AssertNoError(t, err)
if delResult != stack.StackDelForwardingEntryResultWithResponse(stack.StackDelForwardingEntryResponse{}) {
t.Fatalf("got ni.DelForwardingEntry(%#v) = Err(%s), want = Response()", localRoute, delResult.Err)
}
table, err = ni.GetForwardingTable(context.Background())
AssertNoError(t, err)
expectedTable = []stack.ForwardingEntry{}
if diff := cmp.Diff(table, expectedTable, cmpopts.IgnoreTypes(struct{}{})); diff != "" {
t.Fatalf("forwarding table mismatch (-want +got):\n%s", diff)
}
})
t.Run("Enable and Disable PacketFilter", func(t *testing.T) {
ns := newNetstack(t)
eth := deviceForAddEth(ethernet.Info{}, t)
if _, err := ns.addEth(testTopoPath, netstack.InterfaceConfig{Name: testDeviceName}, &eth); err != nil {
t.Fatal(err)
}
ni := stackImpl{ns: ns}
{
result, err := ni.EnablePacketFilter(context.Background(), 1)
AssertNoError(t, err)
if result != stack.StackEnablePacketFilterResultWithResponse(stack.StackEnablePacketFilterResponse{}) {
t.Fatalf("got ni.EnablePacketFilter(1) = %#v, want = Response()", result)
}
enabled, err := ni.isPacketFilterEnabled(1)
AssertNoError(t, err)
if !enabled {
t.Fatalf("got ni.isPacketFilterEnabled(1) = Response(%v), want = Response(t)", enabled)
}
}
{
result, err := ni.DisablePacketFilter(context.Background(), 1)
AssertNoError(t, err)
if result != stack.StackDisablePacketFilterResultWithResponse(stack.StackDisablePacketFilterResponse{}) {
t.Fatalf("got ni.DisablePacketFilter(1) = %#v, want = Response()", result)
}
enabled, err := ni.isPacketFilterEnabled(1)
AssertNoError(t, err)
if enabled {
t.Fatalf("got ni.isPacketFilterEnabled(1) = Response(%v), want = Response(f)", enabled)
}
}
{
result, err := ni.EnablePacketFilter(context.Background(), 1)
AssertNoError(t, err)
if result != stack.StackEnablePacketFilterResultWithResponse(stack.StackEnablePacketFilterResponse{}) {
t.Fatalf("got ni.EnablePacketFilter(1) = %#v, want = Response()", result)
}
enabled, err := ni.isPacketFilterEnabled(1)
AssertNoError(t, err)
if !enabled {
t.Fatalf("got ni.isPacketFilterEnabled(1) = Response(%v), want = Response(t)", enabled)
}
}
})
t.Run("Enable and Disable IP Forwarding", func(t *testing.T) {
ns := newNetstack(t)
eth := deviceForAddEth(ethernet.Info{}, t)
if _, err := ns.addEth(testTopoPath, netstack.InterfaceConfig{Name: testDeviceName}, &eth); err != nil {
t.Fatal(err)
}
ni := stackImpl{ns: ns}
err := ni.EnableIpForwarding(context.Background())
AssertNoError(t, err)
enabled := ni.isIpForwardingEnabled()
if !enabled {
t.Fatalf("got ni.isIpForwardingEnabled() = %v, want = t", enabled)
}
err = ni.DisableIpForwarding(context.Background())
AssertNoError(t, err)
enabled = ni.isIpForwardingEnabled()
AssertNoError(t, err)
if enabled {
t.Fatalf("got ni.isIpForwardingEnabled() = %v, want = false", enabled)
}
err = ni.EnableIpForwarding(context.Background())
AssertNoError(t, err)
enabled = ni.isIpForwardingEnabled()
if !enabled {
t.Fatalf("got ni.isIpForwardingEnabled() = %v, want = t", enabled)
}
})
}
func TestDnsServerWatcher(t *testing.T) {
ns := newNetstack(t)
{
dispatcher, err := dispatch.NewDispatcher()
if err != nil {
t.Fatal(err)
}
defer dispatcher.Close()
ns.dispatcher = dispatcher
}
watcherCollection := newDnsServerWatcherCollection(ns.dispatcher, ns.dnsClient)
ni := stackImpl{ns: ns, dnsWatchers: watcherCollection}
request, watcher, err := name.NewDnsServerWatcherWithCtxInterfaceRequest()
if err != nil {
t.Fatalf("failed to create watcher request: %s", err)
}
defer func() {
_ = request.Close()
_ = watcher.Close()
}()
if err := ni.GetDnsServerWatcher(context.Background(), request); err != nil {
t.Fatalf("failed to get watcher: %s", err)
}
}
func (ni *stackImpl) isPacketFilterEnabled(id uint64) (bool, error) {
nicInfo, ok := ni.ns.stack.NICInfo()[tcpip.NICID(id)]
if !ok {
return false, fmt.Errorf("NICInfo %d not found", id)
}
ifs := nicInfo.Context.(*ifState)
return ifs.filterEndpoint.IsEnabled(), nil
}
func (ni *stackImpl) isIpForwardingEnabled() bool {
return ni.ns.stack.Forwarding()
}