| // 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 ( |
| "fmt" |
| "strings" |
| "syscall/zx" |
| "testing" |
| "time" |
| |
| "fidl/fuchsia/hardware/ethernet" |
| "fidl/fuchsia/net" |
| "fidl/fuchsia/net/stack" |
| "fidl/fuchsia/netstack" |
| ethernetext "fidlext/fuchsia/hardware/ethernet" |
| |
| "netstack/dhcp" |
| "netstack/dns" |
| "netstack/fidlconv" |
| "netstack/link/eth" |
| "netstack/routes" |
| "netstack/util" |
| |
| "github.com/google/go-cmp/cmp" |
| |
| "github.com/google/netstack/tcpip" |
| "github.com/google/netstack/tcpip/header" |
| "github.com/google/netstack/tcpip/network/arp" |
| "github.com/google/netstack/tcpip/network/ipv4" |
| "github.com/google/netstack/tcpip/network/ipv6" |
| tcpipstack "github.com/google/netstack/tcpip/stack" |
| ) |
| |
| const ( |
| testDeviceName string = "testdevice" |
| testTopoPath string = "/fake/ethernet/device" |
| testV4Address tcpip.Address = tcpip.Address("\xc0\xa8\x2a\x10") |
| testV6Address tcpip.Address = tcpip.Address("\xc0\xa8\x2a\x10\xc0\xa8\x2a\x10\xc0\xa8\x2a\x10\xc0\xa8\x2a\x10") |
| ) |
| |
| func TestNicName(t *testing.T) { |
| ns := newNetstack(t) |
| |
| eth := deviceForAddEth(ethernet.Info{}, t) |
| ifs, err := ns.addEth(testTopoPath, netstack.InterfaceConfig{Name: testDeviceName}, ð) |
| if err != nil { |
| t.Fatal(err) |
| } |
| ifs.mu.Lock() |
| if ifs.mu.name != testDeviceName { |
| t.Errorf("ifs.mu.name = %v, want = %v", ifs.mu.name, testDeviceName) |
| } |
| ifs.mu.Unlock() |
| } |
| |
| func TestNotStartedByDefault(t *testing.T) { |
| ns := newNetstack(t) |
| |
| startCalled := false |
| eth := deviceForAddEth(ethernet.Info{}, t) |
| eth.StartImpl = func() (int32, error) { |
| startCalled = true |
| return int32(zx.ErrOk), nil |
| } |
| |
| if _, err := ns.addEth(testTopoPath, netstack.InterfaceConfig{Name: testDeviceName}, ð); err != nil { |
| t.Fatal(err) |
| } |
| |
| if startCalled { |
| t.Error("expected no calls to ethernet.Device.Start by addEth") |
| } |
| } |
| |
| func TestMulticastPromiscuousModeEnabledByDefault(t *testing.T) { |
| ns := newNetstack(t) |
| |
| multicastPromiscuousModeEnabled := false |
| eth := deviceForAddEth(ethernet.Info{}, t) |
| eth.ConfigMulticastSetPromiscuousModeImpl = func(enabled bool) (int32, error) { |
| multicastPromiscuousModeEnabled = enabled |
| return int32(zx.ErrOk), nil |
| } |
| |
| if _, err := ns.addEth(testTopoPath, netstack.InterfaceConfig{Name: testDeviceName}, ð); err != nil { |
| t.Fatal(err) |
| } |
| |
| if !multicastPromiscuousModeEnabled { |
| t.Error("expected a call to ConfigMulticastSetPromiscuousMode(true) by addEth") |
| } |
| } |
| |
| func TestDhcpConfiguration(t *testing.T) { |
| ns := newNetstack(t) |
| |
| ipAddressConfig := netstack.IpAddressConfig{} |
| ipAddressConfig.SetDhcp(true) |
| |
| d := deviceForAddEth(ethernet.Info{}, t) |
| d.StopImpl = func() error { return nil } |
| ifs, err := ns.addEth(testTopoPath, netstack.InterfaceConfig{Name: testDeviceName, IpAddressConfig: ipAddressConfig}, &d) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| ifs.mu.Lock() |
| if ifs.mu.dhcp.Client == nil { |
| t.Error("no dhcp client") |
| } |
| |
| if ifs.mu.dhcp.enabled { |
| t.Error("expected dhcp to be disabled") |
| } |
| |
| if ifs.mu.dhcp.running() { |
| t.Error("expected dhcp client to be stopped initially") |
| } |
| |
| ifs.setDHCPStatusLocked(true) |
| ifs.mu.Unlock() |
| |
| ifs.eth.Up() |
| |
| ifs.mu.Lock() |
| if !ifs.mu.dhcp.enabled { |
| t.Error("expected dhcp to be enabled") |
| } |
| |
| if !ifs.mu.dhcp.running() { |
| t.Error("expected dhcp client to be running") |
| } |
| ifs.mu.Unlock() |
| |
| ifs.eth.Down() |
| |
| ifs.mu.Lock() |
| if ifs.mu.dhcp.running() { |
| t.Error("expected dhcp client to be stopped on eth down") |
| } |
| if !ifs.mu.dhcp.enabled { |
| t.Error("expected dhcp configuration to be preserved on eth down") |
| } |
| ifs.mu.Unlock() |
| |
| ifs.eth.Up() |
| |
| ifs.mu.Lock() |
| if !ifs.mu.dhcp.running() { |
| t.Error("expected dhcp client to be running on eth restart") |
| } |
| if !ifs.mu.dhcp.enabled { |
| t.Error("expected dhcp configuration to be preserved on eth restart") |
| } |
| ifs.mu.Unlock() |
| } |
| |
| func TestUniqueFallbackNICNames(t *testing.T) { |
| ns := newNetstack(t) |
| |
| d1 := deviceForAddEth(ethernet.Info{}, t) |
| ifs1, err := ns.addEth(testTopoPath, netstack.InterfaceConfig{}, &d1) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| d2 := deviceForAddEth(ethernet.Info{}, t) |
| ifs2, err := ns.addEth(testTopoPath, netstack.InterfaceConfig{}, &d2) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if ifs1.mu.name == ifs2.mu.name { |
| t.Fatalf("got (%+v).Name == (%+v).Name, want non-equal", ifs1, ifs2) |
| } |
| } |
| |
| func TestStaticIPConfiguration(t *testing.T) { |
| ns := newNetstack(t) |
| |
| addr := fidlconv.ToNetIpAddress(testV4Address) |
| ifAddr := stack.InterfaceAddress{IpAddress: addr, PrefixLen: 32} |
| d := deviceForAddEth(ethernet.Info{}, t) |
| d.StopImpl = func() error { return nil } |
| ifs, err := ns.addEth(testTopoPath, netstack.InterfaceConfig{Name: testDeviceName}, &d) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if err := ns.addInterfaceAddr(uint64(ifs.nicid), ifAddr); err != nil { |
| t.Fatal(err) |
| } |
| |
| ifs.mu.Lock() |
| if info, err := ifs.toNetInterface2Locked(); err != nil { |
| t.Errorf("couldn't get interface info: %s", err) |
| } else if got := fidlconv.ToTCPIPAddress(info.Addr); got != testV4Address { |
| t.Errorf("got ifs.toNetInterface2Locked().Addr = %+v, want = %+v", got, testV4Address) |
| } |
| |
| if ifs.mu.dhcp.enabled { |
| t.Error("expected dhcp state to be disabled initially") |
| } |
| ifs.mu.Unlock() |
| |
| ifs.eth.Down() |
| |
| ifs.mu.Lock() |
| if ifs.mu.dhcp.enabled { |
| t.Error("expected dhcp state to remain disabled after bringing interface down") |
| } |
| if ifs.mu.dhcp.running() { |
| t.Error("expected dhcp state to remain stopped after bringing interface down") |
| } |
| ifs.mu.Unlock() |
| |
| ifs.eth.Up() |
| |
| ifs.mu.Lock() |
| if ifs.mu.dhcp.enabled { |
| t.Error("expected dhcp state to remain disabled after restarting interface") |
| } |
| |
| ifs.setDHCPStatusLocked(true) |
| if !ifs.mu.dhcp.enabled { |
| t.Error("expected dhcp state to become enabled after manually enabling it") |
| } |
| if !ifs.mu.dhcp.running() { |
| t.Error("expected dhcp state running") |
| } |
| ifs.mu.Unlock() |
| } |
| |
| func TestWLANStaticIPConfiguration(t *testing.T) { |
| arena, err := eth.NewArena() |
| if err != nil { |
| t.Fatal(err) |
| } |
| ns := &Netstack{ |
| arena: arena, |
| } |
| ns.mu.ifStates = make(map[tcpip.NICID]*ifState) |
| ns.mu.stack = tcpipstack.New( |
| []string{ |
| ipv4.ProtocolName, |
| ipv6.ProtocolName, |
| arp.ProtocolName, |
| }, nil, tcpipstack.Options{}) |
| |
| ns.OnInterfacesChanged = func([]netstack.NetInterface2) {} |
| addr := fidlconv.ToNetIpAddress(testV4Address) |
| ifAddr := stack.InterfaceAddress{IpAddress: addr, PrefixLen: 32} |
| d := deviceForAddEth(ethernet.Info{Features: ethernet.InfoFeatureWlan}, t) |
| ifs, err := ns.addEth(testTopoPath, netstack.InterfaceConfig{Name: testDeviceName}, &d) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if err := ns.addInterfaceAddr(uint64(ifs.nicid), ifAddr); err != nil { |
| t.Fatal(err) |
| } |
| |
| if info, err := ifs.toNetInterface2Locked(); err != nil { |
| t.Errorf("couldn't get interface info: %s", err) |
| } else if got := fidlconv.ToTCPIPAddress(info.Addr); got != testV4Address { |
| t.Errorf("got ifs.toNetInterface2Locked().Addr = %+v, want = %+v", got, testV4Address) |
| } |
| } |
| |
| func newNetstack(t *testing.T) *Netstack { |
| arena, err := eth.NewArena() |
| if err != nil { |
| t.Fatal(err) |
| } |
| ns := &Netstack{ |
| arena: arena, |
| } |
| ns.mu.ifStates = make(map[tcpip.NICID]*ifState) |
| ns.mu.stack = tcpipstack.New( |
| []string{ |
| ipv4.ProtocolName, |
| ipv6.ProtocolName, |
| arp.ProtocolName, |
| }, nil, tcpipstack.Options{}) |
| |
| // We need to initialize the DNS client, since adding/removing interfaces |
| // sets the DNS servers on that interface, which requires that dnsClient |
| // exist. |
| ns.dnsClient = dns.NewClient(ns.mu.stack) |
| ns.OnInterfacesChanged = func([]netstack.NetInterface2) {} |
| return ns |
| } |
| |
| func TestAddRemoveListInterfaceAddresses(t *testing.T) { |
| ns := newNetstack(t) |
| d := deviceForAddEth(ethernet.Info{}, t) |
| ifState, err := ns.addEth(testTopoPath, netstack.InterfaceConfig{}, &d) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| checkDefaultAddress := func(t *testing.T) { |
| t.Helper() |
| var info netstack.NetInterface2 |
| interfaces, found := ns.getNetInterfaces2Locked(), false |
| for _, ni := range interfaces { |
| if ni.Id == uint32(ifState.nicid) { |
| found = true |
| info = ni |
| } |
| } |
| if !found { |
| t.Fatalf("NIC %d not found in %+v", ifState.nicid, interfaces) |
| } |
| if got, want := info.Addr, fidlconv.ToNetIpAddress(zeroIpAddr); got != want { |
| t.Errorf("got Addr = %+v, want = %+v", got, want) |
| } |
| if got, want := info.Netmask, fidlconv.ToNetIpAddress(tcpip.Address(zeroIpMask)); got != want { |
| t.Errorf("got Netmask = %+v, want = %+v", got, want) |
| } |
| } |
| |
| t.Run("defaults", checkDefaultAddress) |
| |
| tests := []struct { |
| name string |
| protocol tcpip.NetworkProtocolNumber |
| ip tcpip.Address |
| prefixesBySpecificity []uint8 |
| }{ |
| {ipv4.ProtocolName, ipv4.ProtocolNumber, tcpip.Address(strings.Repeat("\x01", header.IPv4AddressSize)), []uint8{32, 24, 16, 8}}, |
| {ipv6.ProtocolName, ipv6.ProtocolNumber, tcpip.Address(strings.Repeat("\x01", header.IPv6AddressSize)), []uint8{128, 64, 32, 8}}, |
| } |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| // Because prefixesBySpecificity is ordered from most to least constrained and we check that the |
| // most recently added prefix length lines up with the netmask we read, this test implicitly |
| // asserts that the least-constrained subnet is used to compute the netmask. |
| for _, prefixLenToAdd := range test.prefixesBySpecificity { |
| t.Run(fmt.Sprintf("prefixLenToAdd=%d", prefixLenToAdd), func(t *testing.T) { |
| addr := stack.InterfaceAddress{ |
| IpAddress: fidlconv.ToNetIpAddress(test.ip), |
| PrefixLen: prefixLenToAdd, |
| } |
| |
| if err := ns.addInterfaceAddr(uint64(ifState.nicid), addr); err != nil { |
| t.Fatalf("got ns.addInterfaceAddr(_) = %s want = nil", err) |
| } |
| |
| t.Run(netstack.NetstackName, func(t *testing.T) { |
| interfaces := ns.getNetInterfaces2Locked() |
| info, found := netstack.NetInterface2{}, false |
| for _, i := range interfaces { |
| if tcpip.NICID(i.Id) == ifState.nicid { |
| info = i |
| found = true |
| break |
| } |
| } |
| if !found { |
| t.Fatalf("couldn't find NIC ID %d in %+v", ifState.nicid, interfaces) |
| } |
| |
| switch test.protocol { |
| case ipv4.ProtocolNumber: |
| if got, want := info.Addr, addr.IpAddress; got != want { |
| t.Errorf("got Addr = %+v, want = %+v", got, want) |
| } |
| if got, want := info.Netmask, getNetmask(prefixLenToAdd, 8*header.IPv4AddressSize); got != want { |
| t.Errorf("got Netmask = %+v, want = %+v", got, want) |
| } |
| case ipv6.ProtocolNumber: |
| found := false |
| want := net.Subnet{ |
| Addr: addr.IpAddress, |
| PrefixLen: addr.PrefixLen, |
| } |
| for _, got := range info.Ipv6addrs { |
| if got == want { |
| found = true |
| break |
| } |
| } |
| if !found { |
| t.Errorf("could not find addr %+v in %+v", addr, info.Ipv6addrs) |
| } |
| default: |
| t.Fatalf("protocol number %d not covered", test.protocol) |
| } |
| }) |
| |
| t.Run(stack.StackName, func(t *testing.T) { |
| interfaces := ns.getNetInterfaces() |
| for _, i := range interfaces { |
| for _, a := range i.Properties.Addresses { |
| if a == addr { |
| return |
| } |
| } |
| } |
| t.Errorf("could not find addr %+v in %+v", addr, interfaces) |
| }) |
| }) |
| } |
| |
| // From least to most specific, remove each interface address and assert that the |
| // next-most-specific interface address' prefix length is reflected in the netmask read. |
| for i := len(test.prefixesBySpecificity) - 1; i >= 0; i-- { |
| prefixLenToRemove := test.prefixesBySpecificity[i] |
| t.Run(fmt.Sprintf("prefixLenToRemove=%d", prefixLenToRemove), func(t *testing.T) { |
| addr := stack.InterfaceAddress{ |
| IpAddress: fidlconv.ToNetIpAddress(test.ip), |
| PrefixLen: prefixLenToRemove, |
| } |
| if err := ns.removeInterfaceAddress(ifState.nicid, test.protocol, fidlconv.ToTCPIPAddress(addr.IpAddress), addr.PrefixLen); err != nil { |
| t.Fatalf("got ns.removeInterfaceAddress(_) = %s want = nil", err) |
| } |
| |
| t.Run(stack.StackName, func(t *testing.T) { |
| interfaces := ns.getNetInterfaces() |
| for _, i := range interfaces { |
| for _, a := range i.Properties.Addresses { |
| if a == addr { |
| t.Errorf("unexpectedly found addr %+v in %+v", addr, interfaces) |
| } |
| } |
| } |
| }) |
| |
| t.Run(netstack.NetstackName, func(t *testing.T) { |
| var info netstack.NetInterface2 |
| interfaces, found := ns.getNetInterfaces2Locked(), false |
| for _, ni := range interfaces { |
| if ni.Id == uint32(ifState.nicid) { |
| info = ni |
| found = true |
| } |
| } |
| |
| if !found { |
| t.Fatalf("couldn't find NIC %d in %+v", ifState.nicid, interfaces) |
| } |
| |
| switch test.protocol { |
| case ipv4.ProtocolNumber: |
| if i > 0 { |
| want := getNetmask(test.prefixesBySpecificity[i-1], 8*header.IPv4AddressSize) |
| if got := info.Netmask; got != want { |
| t.Errorf("got Netmask = %+v, want = %+v", got, want) |
| } |
| } else { |
| checkDefaultAddress(t) |
| } |
| |
| case ipv6.ProtocolNumber: |
| if i > 0 { |
| prefixesRemaining := test.prefixesBySpecificity[:i-1] |
| for _, p := range prefixesRemaining { |
| want, found := net.Subnet{PrefixLen: p, Addr: addr.IpAddress}, false |
| removed := net.Subnet{PrefixLen: prefixLenToRemove, Addr: addr.IpAddress} |
| for _, got := range info.Ipv6addrs { |
| if got == want { |
| found = true |
| } |
| if got == removed { |
| t.Fatalf("got Ipv6addrs = %+v contained removed = %+v", got, removed) |
| } |
| } |
| if !found { |
| t.Errorf("got Ipv6addrs = %+v did not contain want = %+v", info.Ipv6addrs, want) |
| } |
| } |
| } |
| default: |
| t.Fatalf("protocol number %d not covered", test.protocol) |
| } |
| }) |
| }) |
| } |
| }) |
| } |
| } |
| |
| func TestAddRouteParameterValidation(t *testing.T) { |
| ns := newNetstack(t) |
| d := deviceForAddEth(ethernet.Info{}, t) |
| interfaceAddress, prefix := tcpip.Address("\xf0\xf0\xf0\xf0"), uint8(24) |
| subnetLocalAddress := tcpip.Address("\xf0\xf0\xf0\xf1") |
| ifState, err := ns.addEth(testTopoPath, netstack.InterfaceConfig{}, &d) |
| if err != nil { |
| t.Fatalf("got ns.addEth(_) = _, %s want = _, nil", err) |
| } |
| |
| if err := ns.addInterfaceAddress(ifState.nicid, ipv4.ProtocolNumber, interfaceAddress, prefix); err != nil { |
| t.Fatalf("ns.addInterfaceAddress(%d, %d, %s, %d) = %s", ifState.nicid, ipv4.ProtocolNumber, interfaceAddress, prefix, err) |
| } |
| |
| tests := []struct { |
| name string |
| route tcpip.Route |
| metric routes.Metric |
| dynamic bool |
| shouldPanic bool |
| shouldError bool |
| }{ |
| { |
| // TODO(NET-2244): don't panic when given invalid route destinations |
| name: "zero-length destination", |
| route: tcpip.Route{ |
| Destination: tcpip.Address(""), |
| Mask: tcpip.AddressMask(header.IPv4Broadcast), |
| Gateway: testV4Address, |
| NIC: ifState.nicid, |
| }, |
| metric: routes.Metric(0), |
| shouldPanic: true, |
| }, |
| { |
| // TODO(NET-2244): don't panic when given invalid route destinations |
| name: "invalid destination", |
| route: tcpip.Route{ |
| Destination: tcpip.Address("\xff"), |
| Mask: tcpip.AddressMask(header.IPv4Broadcast), |
| Gateway: testV4Address, |
| NIC: ifState.nicid, |
| }, |
| metric: routes.Metric(0), |
| shouldPanic: true, |
| }, |
| { |
| name: "IPv4 destination no NIC invalid gateway", |
| route: tcpip.Route{ |
| Destination: testV4Address, |
| Mask: tcpip.AddressMask(header.IPv4Broadcast), |
| Gateway: testV4Address, |
| NIC: 0, |
| }, |
| metric: routes.Metric(0), |
| shouldError: true, |
| }, |
| { |
| name: "IPv6 destination no NIC invalid gateway", |
| route: tcpip.Route{ |
| Destination: testV6Address, |
| Mask: tcpip.AddressMask(strings.Repeat("\xff", 16)), |
| Gateway: testV6Address, |
| NIC: 0, |
| }, |
| metric: routes.Metric(0), |
| shouldError: true, |
| }, |
| { |
| name: "IPv4 destination no NIC valid gateway", |
| route: tcpip.Route{ |
| Destination: testV4Address, |
| Mask: tcpip.AddressMask(header.IPv4Broadcast), |
| Gateway: subnetLocalAddress, |
| NIC: 0, |
| }, |
| }, |
| { |
| name: "zero length gateway", |
| route: tcpip.Route{ |
| Destination: testV4Address, |
| Mask: tcpip.AddressMask(header.IPv4Broadcast), |
| Gateway: tcpip.Address(""), |
| NIC: ifState.nicid, |
| }, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| defer func() { |
| r := recover() |
| if got := r != nil; got != test.shouldPanic { |
| t.Logf("recover() = %v", r) |
| t.Errorf("got (recover() != nil) = %t; want = %t", got, test.shouldPanic) |
| } |
| }() |
| |
| err := ns.AddRoute(test.route, test.metric, test.dynamic) |
| if got := err != nil; got != test.shouldError { |
| t.Logf("err = %v", err) |
| t.Errorf("got (ns.AddRoute(_) != nil) = %t, want = %t", got, test.shouldError) |
| } |
| }) |
| } |
| } |
| |
| func TestDHCPAcquired(t *testing.T) { |
| ns := newNetstack(t) |
| d := deviceForAddEth(ethernet.Info{}, t) |
| ifState, err := ns.addEth(testTopoPath, netstack.InterfaceConfig{}, &d) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| serverAddress := []byte(testV4Address) |
| serverAddress[len(serverAddress)-1]++ |
| gatewayAddress := serverAddress |
| gatewayAddress[len(gatewayAddress)-1]++ |
| const defaultLeaseLength = 60 * time.Second |
| |
| tests := []struct { |
| name string |
| oldAddr, newAddr tcpip.Address |
| oldSubnet, newSubnet tcpip.Subnet |
| config dhcp.Config |
| expectedRouteTable []routes.ExtendedRoute |
| }{ |
| { |
| name: "subnet mask provided", |
| oldAddr: "", |
| newAddr: testV4Address, |
| oldSubnet: tcpip.Subnet{}, |
| newSubnet: func() tcpip.Subnet { |
| subnet, err := tcpip.NewSubnet(util.ApplyMask(testV4Address, util.DefaultMask(testV4Address)), util.DefaultMask(testV4Address)) |
| if err != nil { |
| t.Fatal(err) |
| } |
| return subnet |
| }(), |
| config: dhcp.Config{ |
| ServerAddress: tcpip.Address(serverAddress), |
| Gateway: tcpip.Address(serverAddress), |
| SubnetMask: util.DefaultMask(testV4Address), |
| DNS: []tcpip.Address{tcpip.Address(gatewayAddress)}, |
| LeaseLength: defaultLeaseLength, |
| }, |
| expectedRouteTable: []routes.ExtendedRoute{ |
| { |
| Route: tcpip.Route{ |
| Destination: util.Parse("192.168.42.0"), |
| Mask: tcpip.AddressMask(util.Parse("255.255.255.0")), |
| NIC: 1, |
| }, |
| Metric: 0, |
| MetricTracksInterface: true, |
| Dynamic: true, |
| Enabled: false, |
| }, |
| { |
| Route: tcpip.Route{ |
| Destination: util.Parse("0.0.0.0"), |
| Mask: tcpip.AddressMask(util.Parse("0.0.0.0")), |
| Gateway: util.Parse("192.168.42.18"), |
| NIC: 1, |
| }, |
| Metric: 0, |
| MetricTracksInterface: true, |
| Dynamic: true, |
| Enabled: false, |
| }, |
| { |
| Route: tcpip.Route{ |
| Destination: util.Parse("::"), |
| Mask: tcpip.AddressMask(util.Parse("::")), |
| NIC: 1, |
| }, |
| Metric: 0, |
| MetricTracksInterface: true, |
| Dynamic: true, |
| Enabled: false, |
| }, |
| }, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| ifState.dhcpAcquired(test.oldAddr, test.newAddr, test.oldSubnet, test.newSubnet, test.config) |
| ifState.mu.Lock() |
| hasDynamicAddr := ifState.mu.hasDynamicAddr |
| dnsServers := ifState.mu.dnsServers |
| ifState.mu.Unlock() |
| |
| if got, want := hasDynamicAddr, true; got != want { |
| t.Errorf("got ifState.mu.hasDynamicAddr = %t, want = %t", got, want) |
| } |
| |
| if diff := cmp.Diff(dnsServers, test.config.DNS); diff != "" { |
| t.Errorf("ifState.mu.dnsServers mismatch (-want +got):\n%s", diff) |
| } |
| |
| if diff := cmp.Diff(ifState.ns.GetExtendedRouteTable(), test.expectedRouteTable); diff != "" { |
| t.Errorf("GetExtendedRouteTable() mismatch (-want +got):\n%s", diff) |
| } |
| |
| ns.mu.Lock() |
| infoMap := ns.mu.stack.NICInfo() |
| subnetMap := ns.mu.stack.NICSubnets() |
| ns.mu.Unlock() |
| if info, ok := infoMap[ifState.nicid]; ok { |
| found := false |
| for _, address := range info.ProtocolAddresses { |
| if address.Protocol == ipv4.ProtocolNumber { |
| switch address.Address { |
| case test.oldAddr: |
| t.Errorf("expired address %s was not removed from NIC addresses %v", test.oldAddr, info.ProtocolAddresses) |
| case test.newAddr: |
| found = true |
| } |
| } |
| } |
| |
| if !found { |
| t.Errorf("new address %s was not added to NIC addresses %v", test.newAddr, info.ProtocolAddresses) |
| } |
| } else { |
| t.Errorf("NIC %d not found in %v", ifState.nicid, infoMap) |
| } |
| if subnets, ok := subnetMap[ifState.nicid]; ok { |
| found := false |
| for _, subnet := range subnets { |
| switch subnet { |
| case test.oldSubnet: |
| t.Errorf("expired subnet %s/%d was not removed from NIC subnets", test.oldSubnet.ID(), test.oldSubnet.Prefix()) |
| for _, subnet := range subnets { |
| t.Logf("%s/%d", subnet.ID(), subnet.Prefix()) |
| } |
| case test.newSubnet: |
| found = true |
| } |
| } |
| |
| if !found { |
| t.Errorf("new subnet %s/%d was not added to NIC subnets", test.newSubnet.ID(), test.newSubnet.Prefix()) |
| for _, subnet := range subnets { |
| t.Logf("%s/%d", subnet.ID(), subnet.Prefix()) |
| } |
| } |
| } else { |
| t.Errorf("NIC %d not found in %v", ifState.nicid, subnetMap) |
| } |
| }) |
| } |
| } |
| |
| func getNetmask(prefix uint8, bits int) net.IpAddress { |
| return fidlconv.ToNetIpAddress(tcpip.Address(util.CIDRMask(int(prefix), bits))) |
| } |
| |
| // Returns an ethernetext.Device struct that implements |
| // ethernet.Device and can be started and stopped. |
| // |
| // Reports the passed in ethernet.Info when Device#GetInfo is called. |
| func deviceForAddEth(info ethernet.Info, t *testing.T) ethernetext.Device { |
| return ethernetext.Device{ |
| TB: t, |
| GetInfoImpl: func() (ethernet.Info, error) { return info, nil }, |
| SetClientNameImpl: func(string) (int32, error) { return 0, nil }, |
| GetStatusImpl: func() (uint32, error) { |
| return uint32(eth.LinkUp), nil |
| }, |
| GetFifosImpl: func() (int32, *ethernet.Fifos, error) { |
| return int32(zx.ErrOk), ðernet.Fifos{ |
| TxDepth: 1, |
| }, nil |
| }, |
| SetIoBufferImpl: func(zx.VMO) (int32, error) { |
| return int32(zx.ErrOk), nil |
| }, |
| StartImpl: func() (int32, error) { |
| return int32(zx.ErrOk), nil |
| }, |
| ConfigMulticastSetPromiscuousModeImpl: func(bool) (int32, error) { |
| return int32(zx.ErrOk), nil |
| }, |
| } |
| } |